ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于LLM与AI Agent的CI故障自动诊断与修复实战

基于LLM与AI Agent的CI故障自动诊断与修复实战 1. 需求拆解与整体设计这个 Agent 到底该做什么1.1 从痛点出发CI 故障处理的真实困境说句实话在 DevOps 这条路上走得越久越会对一件事感到抓狂——CI 变红。早上到工位第一件事不是看需求文档而是先刷一遍流水线状态。如果昨晚合入的代码挂了你不仅要面对一个红彤彤的失败图标还得从几百上千行日志里翻出真正的报错原因。更磨人的是很多故障翻来覆去就那么几类依赖装不上、格式检查不过、分支合并冲突、测试用例超时。但每一类都要人工去点开日志、检索关键字、判断根因、再动手修一个来回搞掉二十分钟到半小时很正常。要是碰上偶发失败同一份代码上午跑得好好的下午就挂你能做的只有重新触发一次流水线然后默默祈祷它别再来一次。这个项目就是想把这些重复性的排查动作交给一个基于 LLM大语言模型的 DevOps AI Agent 来自动完成。它的职责拆开看就三句话捕获 CI 故障事件分析日志定位根因尝试自动修复或给出修复建议。简单说过去你上班第一件事是看 CI 亮红灯、点开日志、翻半天、得出结论现在这个 Agent 会在红灯亮起的同时就完成日志抓取、根因分析甚至发起修复动作你只需要在关键节点上点击确认。它适合谁适合每个被 CI 日志折磨过的 DevOps 工程师、SRE、前端或后端开发者——只要你的项目在用 GitHub Actions、GitLab CI 或者 Jenkins 这类自动化流水线工具这套思路都能直接迁移过去。1.2 AI Agent 的边界哪些故障适合自动化处理哪些必须保留人工动手之前先得把边界想清楚否则很容易做成一个乱改代码的定时炸弹。我的经验是CI 故障按处理方式可以分成三层。第一层是固定动作能解决的比如锁文件过期、缓存失效、Node 版本不对、依赖源超时。这类问题的特征是有明确的操作路径Agent 只需要识别出这是什么类型的错然后执行对应的命令或脚本即可。第二层是需要读日志猜测根因的比如编译错误、单元测试断言失败、容器启动报错。这类问题需要 LLM 结合上下文做推理给出的修复方案可能有多个候选应当生成修复建议而不是直接改代码。第三层是牵一发动全身的比如测试大面积失败、安全漏洞批量出现、基础设施配置变更。这类问题无论分析结果看起来多自信都应该只输出结论给人工复核绝不能自动执行。我把这条原则总结成一个决策规则置信度高、动作范围小、可逆性强的故障才允许 Agent 自动修复其他情况一律只出分析报告。所以整个项目在设计时我把执行层单独拆出来在它前面加了一道是否自动执行的判断闸门。这个闸门是后来所有安全性的基础如果你打算复刻这个项目这一层务必保留。1.3 整体架构五层结构拆解我最终落地的架构不算复杂但是分层很清晰总共五层触发层负责感知 CI 故障事件。GitHub Actions、GitLab CI 这类平台都支持 webhook 回调CI 任务失败时自动向我们的 Agent 服务推送一条事件。采集层收到事件后调用 CI 平台的 API 拉取流水线详情、任务日志、提交信息。这里是信息喂给 Agent 的地方日志质量直接决定分析效果。分析层把采集到的原生日志做预处理、压缩、切片组织成结构化的上下文再发给 LLM 进行根因分析和修复方案生成。执行层接收分析层输出的结构化结果判断是否需要自动修复。如果需要则通过调用 CI API 重跑任务、创建 Merge Request、推送修复 commit 等方式执行动作。反馈层把最终处理结果写回工单系统、IM 群通知或者归档到数据库形成可追溯的记录。技术栈上我选了 Python FastAPI 做 Agent 服务LLM 接的是 OpenAI 兼容接口CI 平台先跑通 GitHub Actions后面再兼容 GitLab CI。这套组合的好处是生态成熟、社区资源多遇到问题几乎都能搜到现成方案。2. 工具选型与核心实现2.1 为什么选 PythonAI 生态与 CI 生态的双重契合如果你纠结过要不要用 Go、Rust 或者 Node 来写 Agent我把选 Python 的理由摊开讲。首先AI Agent 的核心是调用 LLMPython 在这块的库支持是最完整的无论是 OpenAI SDK 还是各种开源的 Agent 框架比如 LangChain、LlamaIndex几乎都是先发 Python 版本。其次CI 日志处理、API 调用、定时任务这类胶水代码Python 写起来效率极高一个requests加一个PyYAML就能搞定大部分事情。再者团队协作角度看DevOps 组里的同事多多少少都会点 Python后续维护成本比引入一门新语言低很多。当然 Python 也有短板性能和并发不算强项。但对一个处理 CI 事件的 Agent 来说事件量每天也就几十到几百个最高峰的并发请求可能也就个位数性能完全够用。如果你担心 webhook 接收端的并发可以在 FastAPI 前面加一层 Gunicorn 多 worker再加个简单队列削峰实测下来非常稳。2.2 LLM 接入方式API 调用与开源部署的取舍LLM 是整个 Agent 的大脑选型上我权衡了两条路线。第一是直接调用商业 API比如 OpenAI 的 GPT 系列或者国产的 DeepSeek、通义等模型。优点是推理能力强、上下文窗口大、省去部署烦恼缺点是有费用、数据要出网。对于 CI 日志这种敏感度不高的数据出网问题其实不大真正要关注的是费用——分析一次故障可能要消耗几千到几万 token如果 CI 故障频繁一个月累积下来是个不小的数字。第二是在内网部署开源模型比如用 Ollama 跑 Qwen、Llama 系列。优点是数据不出内网、随便调用不花钱缺点是推理速度慢、效果波动明显尤其是面对复杂日志时分析质量比商业 API 差一截。我的建议是先接商业 API 跑通全流程确认价值之后再考虑迁移开源模型。不要一开始就在开源模型上死磕效果那样会把验证思路阶段拖得很长。我在项目里用了一个LLMProvider抽象层接口统一切换模型只需要改配置项这是多模型方案落地的关键设计。2.3 CI 接入方案webhook API CLI 三管齐下CI 平台的接入是整个项目的地基。我以 GitHub Actions 为例把接入方式拆成三条线。第一条是 webhook 事件接收。GitHub 支持配置仓库级别的 webhook事件类型选择workflow_run在 CI 工作流失败时就会向指定地址 POST 一个 JSON 事件。本地开发时可以用ngrok之类的内网穿透工具把 webhook 打到本地 FastAPI实测很方便。第二条是 API 调用。拿到事件里的workflow_runID 之后调用 GitHub REST API 获取详细记录。需要关注的接口主要是两个GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs获取任务列表以及GET /repos/{owner}/{repo}/actions/runs/{run_id}/logs获取完整日志。调用时注意 API 限流GitHub 每小时 5000 次请求对个人项目完全够用。第三条是 CLI 工具兜底。某些操作 API 实现得不够友好时直接用 GitHub CLIgh反而干净利落比如gh run view id --log-failed可以快速拿到失败日志。我在执行层里大量用了gh来创建分支、提 PR、重跑任务它帮我省掉了不少 API 拼接的功夫。3. 实操过程从零搭建你的第一个 Agent3.1 项目骨架与目录设计先把项目结构放出来你可以直接照抄这个目录设计ci-agent/ ├── agent/ │ ├── __init__.py │ ├── main.py # FastAPI 入口webhook 接收 │ ├── config.py # 配置管理环境变量加载 │ ├── collector.py # 采集层GitHub API 拉取日志 │ ├── analysis.py # 分析层Prompt 组装、LLM 调用 │ ├── executor.py # 执行层自动修复动作 │ ├── decision.py # 决策层是否自动修复的判断 │ └── schemas.py # Pydantic 数据模型 ├── prompts/ │ ├── system.md # LLM 系统提示词 │ └── analysis.md # 日志分析提示词模板 ├── scripts/ │ ├── fix_lockfile.sh # 修复锁文件的示例脚本 │ ├── fix_format.sh # 自动格式化的示例脚本 │ └── approve_pr.sh # 生成修复 PR 的示例脚本 ├── requirements.txt ├── .env.example └── README.md初始化环境用常规 Python 虚拟环境核心依赖就三个fastapi、uvicorn、openai。requirements.txt里再加python-dotenv和httpx就这些没有引入重型框架。原因是这个 Agent 的逻辑本质上就是接收事件、拼提示词、调 LLM、执行动作框架太重反而干扰核心逻辑的阅读和维护。3.2 故障捕获层Webhook 接收器与日志采集故障捕获从 FastAPI 的 webhook 端点开始。核心代码如下# agent/main.py from fastapi import FastAPI, Request, BackgroundTasks from agent.schemas import WebhookEvent app FastAPI() app.post(/webhook/github) async def github_webhook(request: Request, background_tasks: BackgroundTasks): event await request.json() # 只处理 workflow_run 类型的事件 if event.get(action) completed: payload WebhookEvent( repoevent[repository][full_name], run_idevent[workflow_run][id], conclusionevent[workflow_run][conclusion], branchevent[workflow_run][head_branch], ) # 后台任务异步处理避免阻塞 webhook 响应 background_tasks.add_task(handle_ci_failure, payload) return {status: received} return {status: ignored}这里有个细节值得说一定要用后台任务处理。webhook 是同步通知机制GitHub 期望快速收到 200 响应如果你在接收端直接跑 LLM 分析耗时可能十几秒甚至更久GitHub 会判定 webhook 投递失败然后重试造成重复处理。我的做法是收到事件后立刻返回 200把耗时操作全部塞进 BackgroundTasks。采集层紧接着处理日志先调 GitHub API 拿到任务列表再筛选失败的任务记录最后拉取对应日志文本。因为完整日志可能非常大几十 MB 也不奇怪我在采集层做了切片按行数截取失败任务附近的上下文默认取报错前 100 行和报错后 50 行同时保留报错关键字附近的段落。这一步大大减轻了后续 LLM 分析的压力。3.3 分析诊断层上下文组装与 Prompt 设计分析层是 Agent 的大脑所在。日志采集完成后需要把原始日志加上仓库信息、分支信息、任务名称、失败步骤等元数据打包成一个结构体再按照提示词模板生成最终的分析请求。Prompt 设计上我经过几轮迭代最终分成两部分。系统提示词定义 Agent 的角色强调你是一个资深 DevOps 工程师你的目标是从 CI 日志中定位根因并提出修复方案分析提示词则是结构化模板把日志内容填充进去明确要求输出 JSON 格式的结果。为什么强制 JSON因为后续的决策层和执行层需要稳定地解析 LLM 的产出纯文本回复会导致解析代码写得又臭又长。我的输出结构定义成{ root_cause: 简要描述根因, confidence: 0.85, category: dependency_conflict | test_failure | format_error | infrastructure | unknown, suggestion: 建议的修复动作, auto_fix: true, fix_command: 要执行的修复命令或脚本名 }这里auto_fix字段由 LLM 初步判断但最终是否执行还要过决策层。单靠 LLM 判断不可靠必须加规则约束。我在决策层里加了硬性规则category必须在允许自动修复的白名单里confidence必须大于 0.8并且fix_command必须命中我预先定义的安全命令列表三条全过才允许自动执行。3.4 修复执行层脚本落地与自动触发执行层收到分析结果后针对不同类型的故障走不同的修复通道。这里我把最常见的修复动作拆成了三个示例脚本。第一个是fix_lockfile.sh处理依赖锁定文件的版本冲突。它在检测到package-lock.json或pnpm-lock.yaml与package.json不匹配时执行npm install --package-lock-only或pnpm install --lockfile-only重新生成锁文件然后提交并推送。第二个是fix_format.sh处理格式化检查不通过的情况。它读取配置里指定的格式化工具比如 Prettier 或 Black在本地执行格式化命令检查 diff 中只包含格式化变更后创建修复分支并推送最后发起一个带 auto-fix by CI Agent 标记的 PR。第三个是rerun_flaky.sh处理偶发测试失败。第一次失败时自动重跑同一任务如果重跑通过就标注为 flaky 并记录在案。每个脚本执行前都会做两件重要的事在测试仓库副本上先跑一遍验证确认修复有效后再推送到真实分支推送后立即从生产分支上回滚本地改动避免 Agent 运行环境本身被污染。关于自动执行还有一条铁律修复 commit 的 author 信息必须标记为 Agent 专用账号绝不能冒用开发者身份否则后续审计会非常难做。4. 核心场景跑通Agent 处理 CI 故障的完整流程4.1 场景一依赖版本冲突导致的构建失败我给你完整走一遍 Agent 处理依赖冲突故障的链路。假设一名开发者在分支上改了requirements.txt把某个 Python 库的版本从1.0.*提升到了2.0.0但另一个库的依赖约束还停留在1.x流水线跑起来后 pip 无法解析出一组满足所有约束的版本CI 直接报错。事件捕获后Agent 根据日志里 pip 解析错误的特征——比如No matching distribution found、Dependency conflict、ResolutionImpossible——判定category为dependency_conflict。LLM 再结合错误日志和两个库的版本信息给出根因a 库 2.0.0 移除了对 b 库 1.x 的支持导致依赖冲突并建议将 b 库升级到 2.x 或将 a 库回退到 1.x。auto_fix字段 LLM 给了 true置信度 0.87。决策层接住这个结果校验流程如下第一条category在自动修复白名单内通过第二条confidence大于 0.8通过第三条fix_command是预先注册过的安全命令fix_lockfile.sh通过。于是执行层调用gh创建ci-agent/fix-dependency-conflict分支在分支上修改版本约束文件推送后创建 PRPR 描述里写清楚了根因分析和改动理由并把 PR 链接贴到团队 IM 群。我实测完整耗时从 CI 失败到 PR 创建完成大约 90 秒。对比人工处理光看日志定位根因通常就要 10 分钟以上效率提升非常明显。4.2 场景二定时任务超时与资源耗尽第二个场景更隐蔽也更考验 Agent 的分析能力。某个测试用例偶尔会卡在等待某个外部服务响应上CI 流水线没有立即报错而是跑到timeout限制时间之后被强制终止然后在任务列表里显示为失败。新手工程师第一次碰到这种故障很容易误判成环境问题然后无脑重跑但 Agent 需要识别出这是代码层面的超时配置问题。采集层拿到的日志末尾通常会有Timeout exceeded或The job running on runner has exceeded the maximum execution time之类的字样。LLM 分析时会把时间戳前后的日志段落拼在一起——前面是测试卡住的位置后面是超时强制结束的提示——从而推断出某个测试步骤在等待外部资源时没有配置超时保护。修复建议有两种一是给外部调用加上显式的超时参数二是把该用例的 timeout 值调大。由于涉及代码逻辑改动auto_fix字段我让它返回 falseAgent 只生成一份带有修改建议的分析报告在报告里附上推荐的代码补丁示例然后发送给负责该模块的开发者。这里要强调一个经验超时类故障比报错类故障更值得警惕。表面上重跑一次就能过但底层可能是吞掉异常的隐藏 bug一次两次侥幸通过积累到生产环境就会变成线上事故。所以这类故障我的 Agent 绝不自动修复宁可多花一点人工成本去复核根因。5. 踩坑记录与排查技巧5.1 我踩过的 6 个坑坑一日志内容超过了模型的上下文窗口。第一次跑真实场景时GitHub 返回的日志有 8 万多字符GPT-4o 的上下文按 token 算根本放不下结果直接报错。解决方式是在采集层做日志裁剪按行数滑动窗口截取失败位置附近的段落同时把无关的debug级别日志过滤掉。目前裁到 300 行以内基本不会超。这个经验释放了一个信号采集层的数据预处理绝不是可有可无的步骤它直接决定平台适配性和成本。坑二LLM 返回的 JSON 偶尔不合法。即使 Prompt 里写明了只输出 JSON模型偶尔还会在 JSON 前后加一段解释或者在字符串里出现转义问题。我后来加了容错解析逻辑先用正则把 JSON 块提取出来再尝试解析失败就调用一次修复提示词让模型重新输出。虽然丑了点但很管用。坑三重试风暴。有一次我在执行层写了个简单的重试逻辑处理网络抖动时直接重新跑流水线。结果网络连着抖动了几分钟Agent 把同一条流水线重跑了 5 次CI 队列被塞满。后来加了一个防抖机制同一个run_id在 30 分钟内最多只处理一次重复事件直接忽略。坑四权限过度。Agent 的 GitHub Token 一开始用了写权限导致它能推送代码到受保护的主分支还差点覆盖掉别人的提交。解决方案是为 Agent 单独创建服务账号权限只授予创建分支、创建 PR、重跑工作流三项去掉直接推送主分支的权限。坑五自动修复后流水线还是红的。format 修复 PR 创建后原流水线当然还是失败的——因为分支还没合并。我一开始没意识到这一点导致 Agent 是否修复成功 的判断逻辑写反了。正确的逻辑是创建修复 PR 成功即视为已采取处理动作流水线是否变绿取决于 PR 是否被合入。所以我后来把executor的反馈结果分成了已解决和已提交修复方案待人工合入两档。坑六模型幻觉导致的错误修复建议。有一次 Agent 面对一个奇怪的编译错误给出的修复方案是升级操作系统依赖但实际上根因是两个模块间的类型定义冲突。这个教训让我下定决心加入修复验证环节任何自动执行的修复命令在推送到真实分支前必须先在一个干净的临时副本上跑一遍验证流水线真的能通过。验证不通过的修复方案直接打回重新分析。这里最重要的一条经验别让 Agent 在没有验证手段的条件下自动改代码。人和 Agent 的最大区别在于人能试想Agent 只能试错。没有验证环节的自动修复本质上是在生产环境上做实验。5.2 常见问题速查表现象排查思路解决方案webhook 收不到事件检查仓库 webhook 配置、内网穿透是否在线用curl -X POST模拟事件测试查看 FastAPI 启动日志确认路由是否注册成功日志太多导致 LLM 超上下文采集层裁剪不够按行数滑动窗口截取失败位置前 100 行 后 50 行过滤 debug 级别日志自动修复 PR 一直没生成决策层拦截了查看decision.py日志确认category、confidence、fix_command三条规则是否全部满足Agent 分析结果反复无常上下文信息不足在日志摘录基础上补充仓库名、分支名、最近提交信息、相关文件列表等元数据CI 调用 API 被限流事件风暴或日志拉取过于频繁增加缓存相同 run_id 只拉取一次日志用ghCLI 的本地缓存替代部分 API 请求模型误判超时为普通失败日志特征不够明显在 Prompt 中单独列出超时类错误的关键字特征对这类故障关闭自动修复5.3 一点额外的经验项目跑到第二个星期我开始体会到 AI Agent 在 CI 场景里真正的价值并不是替你修一个 bug而是持续积累可复用的故障知识库。每处理一次故障Agent 把根因、日志特征、修复方案、最终验证结果打包存档下次再碰到同类型故障时它可以直接从历史案例里检索匹配分析时间从几十秒缩短到几秒。这其实比我最初设想的每次都用 LLM 从头分析一遍更高效、更省钱。后续如果要扩展我建议你在采集层和决策层之间加一个基于向量数据库的历史匹配模块用相似日志片段做检索增强。有了它之后Agent 就不再是一个只会重新发明轮子的机械执行者而是真正变成了一个越用越聪明的团队角色。在我实操这段时间里最想提醒后来者的是不要急着让 Agent 做很多事情。先让它看再让它说最后才让它动手。每一步都确认稳定了再放开下一步的权限这个项目就不会失控。
返回列表