
很早之前我一直觉得“让 AI 写代码”和“让 AI 开发出能跑的软件”是完全两件事。LLM 可以帮你起草漂亮的函数甚至能一次通过基础的单元测试但一旦放进真实项目——几十个文件、多层依赖、跨模块调用、外部接口耦合——模型生成的“修复”往往只是在制造新的矛盾。后来我开始尝试用 AI Agent 跑“构建→测试→修复”的完整闭环让 Agent 自己接收错误信息、自己动手修改、自己重新构建。跑了几轮之后我发现这个思路不仅可行而且效率远超“人看完报错再转述给 AI”的老办法。这篇文章就聊一聊我是怎么把这条开发闭环真正跑通的包括 Agent 闭环的核心逻辑、每个环节的实操方式以及踩过的坑。1. AI Agent 闭环和 CI/CD 流水线的最大区别失败是信号不是故障1.1 传统 CI 流水线为什么会停在“测试失败”这一步如果你用过 Jenkins、GitLab CI 或者 GitHub Actions一定熟悉这套流程提交代码、触发构建、跑测试、失败、通知你、你打开日志、修复、再提交。这套流水线本身已经实现了“构建→测试”的自动化但它缺了最有价值的一环——基于失败结果进行自主修复。流水线把“失败”当成中断信号本质上是把决策权交回给了人。而 AI Agent 闭环里“失败”恰恰是 Agent 行动的输入。一次编译报错、一个断言没通过、一份依赖缺失对 Agent 来说不是流程的终点而是下一个动作的依据。Agent 拿到构建日志后要自己定位问题、修改代码、重新执行构建命令然后再次观察结果。这个循环可以反复进行直到测试通过或 Agent 主动认输。我用一个直观对比来说明这件事对比维度传统 CI/CD 流水线AI Agent 开发闭环失败处理中断并通知人处理作为决策输入进入修复分支决策主体人在流水线外部Agent 在循环内部修改代码流水线基本不做Agent 直接修改并验证迭代速度依赖人多次提交Agent 连续自迭代适用前提规则明确、变更可控有足够上下文和权限边界所以Agent 闭环真正的突破点不是“自动化程度更高”而是它把“人处理失败”这个环节也纳入了自动化的范围。想实现这一点就不能只写一个自动化脚本而是要构建一个能理解项目、能读日志、能修改代码、能调起构建命令的自主系统。1.2 LLM 和 AI Agent 的分工Agent 不是模型是一个执行系统搜索热词里边有“agent 和 llm 和 ai模型 有什么区别”这里值得多说一句。LLM 是语言模型它只能做一件事——根据输入的 token 预测下一条内容。它可以和你聊天、补全代码、总结文本但它不会主动去执行mvn compile也不会在你没问的时候去打开日志文件。AI Agent 则是一个“装了手脚和眼睛”的系统。它内部可以调用 LLM 做推理决策但它还拥有工具可以执行 Shell 命令、读写文件、请求 API、遍历目录。Agent 的循环结构通常是“观察—决策—执行—再观察”。模型负责中间那个“决策”外围的脚本或者工具框架负责其他部分。比如 DeepSeek 这类模型本质上是 Agent 大脑里的一个选项你可以换不同的模型来驱动同一套闭环系统。所以跑 AI Agent 开发闭环首先你要接受一个理念模型只是零件闭环才是系统。后面所有设计都围绕这个系统展开。2. 跑通闭环的地基可复现环境、观测质量和权限护栏2.1 可复现构建环境是闭环的前提Agents 闭环里有一个隐形假设每一次构建都应该基于同一个初始环境否则 Agent 修了半天结果发现是环境变量不对或者镜像版本漂移纯属浪费 token。我的习惯是让 Agent 在容器或者隔离的虚拟环境里跑构建。比如 Python 项目用venvrequirements.txtJava 项目用 Maven Wrapper 固定版本前端项目用package-lock.json锁住依赖。如果是服务端项目直接用 Dockerfile 构建镜像这样作业系统、编译工具、运行依赖都固化在同一套环境里。在此基础上还要处理好“增量构建”和“全量构建”的关系。初次跑闭环时我会让 Agent 先做一次干净构建确认基线状态之后进入修复循环时优先用增量构建因为速度快Agent 可以在短时间内反复试错。但要注意增量构建偶尔会留下陈旧产物比如旧 class 文件、旧缓存导致修复明明生效了却没反映到结果里。遇到这种情况要让 Agent 学会“清理后重建”。搜索词里有“jenkins构建清理”“maven构建失败”其实很多此类问题就是构建目录脏了清掉/target、/build或.cache再跑就正常。2.2 测试的“可观测性”决定 Agent 的修复效率测试是 Agent 闭环的裁判这个裁判不能是个哑巴。我见过很多项目测试失败后只给一行字tests failed这对人类开发者来说都很难用对 Agent 来说更是致命。你让 Agent 修一个说不清楚错在哪的问题它只能靠猜。因此我的一个铁律是在把 Agent 接入项目之前先把测试报告改到“可读”。至少要把以下信息明确暴露出来哪个测试用例失败了失败发生在哪个文件哪个函数期望值是什么实际值是什么完整的调用堆栈尤其是在断言附近的栈帧测试运行环境有没有异常比如连不上数据库、超时、权限拒绝有了这些信息Agent 才能把“失败原因”和“代码位置”关联起来。说实话这比选哪个模型重要得多。我在多个项目里测试过同样的模型面对详细测试报告时修复成功率能提高两三成而面对贫瘠的失败信息时它往往会陷入“猜一个改法—失败—再猜”的循环。2.3 权限护栏让 Agent 在笼子里试错让 Agent 自己改代码最让人担心的就是它在仓库里乱翻乱改。我的做法是给 Agent 划定明确的“活动边界”Agent 默认只能操作当前工作目录下的项目文件不能访问系统关键目录允许执行的命令限定在构建、测试、包管理、文件读写这几类其他命令需要问询Agent 的修改范围记录成 diff每一次修复都留下痕迹方便回滚设置“人类确认节点”比如删文件、改依赖版本、动数据库结构这些操作必须停下来等确认实际跑闭环时我会把 Agent 的每次修改都做进 Git 临时分支。这样做有两个好处一是出了问题随时能重置到上次通过的状态二是可以统计 Agent 一共改了多少轮、每轮改了什么这对后期优化闭环策略很有价值。工具层面现在很多 Agent 框架都支持工具注册和回调你可以把 Shell、文件系统、HTTP 请求都注册成 Agent 的工具然后通过权限模块做白名单控制。组织工具的方向可以借鉴“AI agent skill”把一件完整的事情封装成一个带描述、带入参、带执行规范的“技能”Agent 接到任务后会按需组合技能而不是像大模型原生对话一样自由发挥。这一点能显著提高闭环的稳定性。3. 构建阶段Agent 不是猜命令而是先读项目再动手3.1 让 Agent 先认识项目再执行构建很多第一次跑 Agent 闭环的人会直接向 Agent 丢一个问题“帮我构建这个项目。”结果 Agent 在不知道项目结构的情况下瞎猜命令一会儿make一会儿mvn一会儿npm run build。这样大概率失败。正确做法是先让 Agent 完成一次“项目勘探”列出根目录文件识别项目类型。看到pom.xml就知道是 Maven 项目看到package.json就知道是 Node 项目看到pyproject.toml或requirements.txt就知道是 Python 项目。查看最外层的说明文件通常是README.md里面往往会写构建命令、启动方式、依赖要求。优先读构建描述文件比如Makefile、package.json的 scripts 段、build.gradle、Dockerfile而不是直接发指令。确认依赖安装情况必要时先跑依赖安装命令比如pip install -r requirements.txt、npm ci。你可以把这一步固化到 Agent 的系统提示词里或者写成一个工具脚本让 Agent 每次都先执行这个脚本。别小看这个“先读后做”的过程它能省掉大量无意义的失败尝试。我见过太多 Agent 第一轮构建失败原因不是代码有问题而是它根本没在正确目录下执行命令。3.2 构建失败的类型与 Agent 应做的处理构建阶段常见失败类型大致可以分成下面几类Agent 的处理策略各有不同失败类型典型场景Agent 的推荐策略依赖缺失本地缺少某个包或模块版本不匹配先安装依赖确认版本约束再重试编译错误语法错误、类型不匹配、缺引号定位到具体文件和行号读取上下文再修改资源限制内存不足、超时、磁盘写满增大超时、清理临时文件、限制并发构建环境差异本地变量、路径、系统库不一致检查环境配置和构建脚本统一路径权限问题无法写文件、命令没有权限调整权限或回到受控目录重试构建脚本本身有误Makefile 写错、参数不合法读取脚本定位逻辑错误修改后重试搜索词里出现频率很高的“maven构建失败”我顺手说一句。Maven 项目失败很多时候不是代码问题而是依赖仓库连不上、本地仓库缓存破损、插件版本和 JDK 版本不兼容。Agent 拿到这类报错不应该直接去改pom.xml的代码而是先确认网络、本地仓库和 JDK 环境。强制 Agent 把“环境诊断”放在“代码修改”前面是构建阶段的重要原则。3.3 一个容易被忽略的细节日志截取还有一件事对构建阶段特别重要日志切片。Agent 的上下文窗口有限你不能把几百 MB 的构建日志整个塞给模型。我的做法是让 Agent 只读取日志的最后 200~300 行或者先用grep -i error把错误行过滤出来同时保留错误前后的关联片段。构建失败的原因通常集中在日志末尾的异常堆栈里前边大部分进度信息都是噪声。这一步做得好不好会直接决定后续修复效率。对手拿一整份 dump 的 Agent 来说它很难准确聚焦问题而只拿关键错误的 Agent可以在第一轮就给出接近根因的判断。4. 测试阶段给 Agent 一个不会说谎的标尺4.1 测试报告要结构化别让 Agent 在信息垃圾里捞针构建通过只是闭环的起点真正的裁判是测试。测试的意义在于它给 Agent 提供了一个明确的“成功/失败”反馈信号。没有这个信号Agent 就没办法知道自己改得对不对。我建议把所有测试结果输出成结构化格式比如 JUnit XML 或者 JSON 报告。这样 Agent 可以精确地知道有多少用例通过、多少失败、失败用例的名字和原因而不是靠解析 chunky 的 console 文本去猜。举个失败信息质量的对比# 差劲的失败信息 Test failed # 合格的失败信息 test_login_success failed: assert(auth.get_token(user) expected_token) AssertionError: expected abc123, got token_expired stack: auth_service.py:42 - login.py:88后者让 Agent 能直接定位到auth_service.py第 42 行甚至能推断出问题出在 token 过期或缓存上。前者只会让 Agent 原地打转。4.2 测试范围与回归策略快失败、慢确认Agent 闭环里如果每次修复都跑全量测试迭代速度会非常慢。尤其是测试套件动辄上千条的项目跑一轮可能就要十几分钟Token 和时间都耗不起。我常用的策略是分层第一层Agent 定位到具体模块后只运行相关的定向测试。比如改了auth_service.py就先跑test_auth_service.py。第二层定向测试通过后运行所有关联模块的测试看看有没有破坏其他依赖。第三层全量回归测试。通常在 Agent 连续修复成功、准备结束循环时执行。这个“快失败、慢确认”的顺序能让 Agent 用最小成本快速迭代同时保障最终结果的可靠性。你还可以设置测试超时上限比如单条用例超过 30 秒就自动跳过并记录避免某条慢测试卡住整个闭环。4.3 小心“假通过”Agent 要验证测试真的跑了我踩过一个特别隐蔽的坑Agent 修完代码后重新执行测试命令输出显示成功闭环结束。但我后来复查发现那次测试根本没真正运行——原因是测试用例被跳过、被过滤或者测试进程因为环境变量问题静默退出只是退出码为 0。为了避免“假通过”我在闭环里会加一项检查测试运行结束后Agent 必须核对输出里的统计信息确认“运行了几个用例、通过几个、失败几个、跳过了几个”并且和预期的用例数量做对比。如果数量对不上视为测试无效。这是很容易被忽视但极其重要的一步尤其在自动化闭环里一旦裁判裁判失职后面所有修复动作都会失去方向。5. 修复阶段从日志到根因Agent 的排查链路拆分5.1 修复不能跳过“定位-假设-验证”修复阶段最考验 Agent 的推理能力也最容易失控。我的经验是强制 Agent 遵循固定的修复链路不要直接甩结论定位根据失败信息找到对应的文件、函数、条件分支。假设先用一两句话说明“为什么会失败”形成根因假设。修改基于假设做最小改动避免顺手重构无辜代码。验证重新构建并运行相关测试查找假设是否成立。回归确认修改没有破坏相邻功能。我通常要求 Agent 在动手前先输出一段“诊断小结”即使最终输出里不展示给用户看这个过程也能显著降低乱改概率。说白了如果 Agent 连“为什么失败”都解释不清楚那它的修复大概率是盲目的。5.2 修复的三个常见反模式跑闭环跑久了我发现 Agent 的修复行为有几类典型反模式需要通过设计来遏制第一类是“大面积重写”。Agent 面对一个局部 bug却把整个函数甚至整个文件重写一遍看起来逻辑更“规范”了实际引入了新的边界问题。我的做法是设置修改范围提示只修失败点不要动无关代码。第二类是“补丁叠补丁”。Agent 第一次修 A结果破坏了 B然后又修 B又破坏 C最后整个模块打满补丁逻辑混乱不堪。对这种问题我会在提示词里强调“如果连续两轮修复都产生新问题停下来重新审视根因而不是继续叠加补丁”。第三类是“修表面不修里”。比如一个 API 返回 500 错误Agent 直接把接口改成返回固定值测试确实通过了但业务逻辑完全错了。这种假修复比不修复更糟。应对办法是准备更全面的测试用例覆盖正常路径和边界路径让“表面修复”无法通过验证。5.3 典型修复案例配置文件加载失败说到具体修复搜索热词里有一条“chatgpt 无法加载 config.toml”的问题这类“配置文件加载失败”在闭环里很常见。Agent 的排查思路通常是读取报错信息确定是找不到文件、格式错误还是字段不存在检查文件路径、权限、编码格式用 TOML 解析器或者 Python 的tomllib单独验证文件内容定位到具体行修复格式问题或者补齐缺失字段再重新加载配置文件类问题Agent 往往比人更快因为它是逐字逐句读文件的不会像人一样眼睛扫过几十行配置却看不到一个多余的逗号。但前提是 Agent 要会“用工具验证”而不是“用眼睛猜测”。我会专门给 Agent 提供一小段解析脚本让它用脚本结果说话。6. 实测翻车记录死亡循环、上下文爆炸和修复退化6.1 死亡循环修复 A 破坏 B修复 B 又破坏 A我把这个放在第一个因为它真的是跑 Agent 闭环时最崩溃的体验。Agent 陷入 A/B 互相破坏的循环时能连续跑几十轮都不自知修好模块 A 的测试模块 B 挂了修好模块 BA 又挂。如果不设置终止机制它的 Token 会像漏水一样哗哗流。我后来引入了两个兜底方案。第一是“修复次数上限”闭环设置最大迭代轮数比如 8 轮超出后强制停止并输出当前状态第二是“变更回溯”每一轮修改都记录 diff如果发现 Agent 在某两个版本之间反复横跳就让它退回到最近一次“没有引入新问题”的状态。你可以在提示词里明确写You are in a loop. Stop adding patches, revert to the last stable diff and re-analyze from scratch.这种强制性的指令能有效打断死循环。6.2 上下文爆炸Agent 被日志和代码塞晕第二个高频问题是上下文爆炸。Agent 在修复过程中会不断读取新文件、解析新日志上下文越来越长最终的结果是它记不住最开始的目标开始胡言乱语甚至把不相干的代码也抄进去。我总结的应对手段有三个每次循环开始前把“初始目标 已完成修改摘要”压缩成一小段作为永久上下文而不是让对话无限累积。日志和测试报告只保留关键片段不保留全量文本。读过的文件内容尽量通过工具按需读取不要让重要文件重复进入上下文。6.3 修复退化看起来测试过了但代码不能用于生产还有一类问题发生在闭环之后。Agent 修到测试全绿但你 review diff 时会发现它为了通过测试改变了一些应对外部异常的行为比如吞掉了某种合理的错误、把失败请求静默转为成功返回。测试确实过了但代码质量是不能接受的。应对这件事我目前最有效的手段是“增强测试真实性”在测试套件里加入业务约束测试专门验证边界行为和异常处理。另一个手段是保留一个人类评审节点——Agent 修完闭环后最终 diff 必须交给开发者 review。别把 AI 闭环设计成完全无人看管没人 review 的 AI 修复和高利贷一样前期省心后期吓人。7. 最小闭环落地参考状态机、提示词和收敛判断7.1 核心循环逻辑的简洁实现聊了这么多原理最后放一个最小可运行的闭环示意图。这里我用伪代码方便你在任何语言里落地max_iterations 8 iteration 0 history [] while iteration max_iterations: iteration 1 # 1. 构建 build_result run_build() if not build_result.success: cause analyze_log(build_result.log_tail) patch agent_propose_fix(cause, history) apply_patch(patch) history.append((build_failure, cause, patch)) continue # 2. 测试 test_result run_tests() if test_result.success and test_result.executed_count expected_count: print(闭环完成) break if test_result.failed_count 0: # 测试执行异常或者被跳过 cause 测试未真正执行或跳过数异常 else: cause test_result.failure_summary() patch agent_propose_fix(cause, history) apply_patch(patch) history.append((test_failure, cause, patch)) if iteration max_iterations: print(达到最大迭代次数需要人工介入)这一小段逻辑说明了闭环的三个核心构建失败进入修复分支、测试失败进入修复分支、达到迭代上限则终止。实际工程里你还需要把 patch 做进 Git 分支把每一步的状态记录下来这样 Agent 在任意时刻失败你都能一键回滚。7.2 提示词里必须写清楚的三件事跑闭环的时候提示词不用写得很玄但有三件事必须钉死第一输出格式。要求 Agent 的修复建议必须是可直接应用的代码块而不是大段解释。比如声明“修复响应仅包含一个 diff 片段和一行摘要”这样可以省掉大量 token。第二修改边界。明确告诉 Agent“只修改与失败原因直接相关的文件不做重构不额外优化不修改无关注释。”这个约束对收敛很关键。第三失败处理。告诉 Agent“如果连续两轮修复后测试仍然失败放弃继续打补丁进入重新分析模式输出根因候选列表和下一步排查计划。”我实际用下来这三条是最有效的能把 Agent 从“自由散漫的结对程序员”变成“听话但高效的初级工程师”。7.3 怎么判断闭环真的“跑通”了最后说说收敛判断。一个闭环不是“跑了一次测试通过”就算成功。我会看三个指标收敛轮数从第一次失败到测试通过Agent 一共迭代了几轮。理想情况是 2~3 轮内收敛超过 5 轮就要警惕思路是否偏了。修改范围最终 diff 涉及多少文件、多少行。一个精准的修复应该是小范围、明确命中的。回归情况全量测试通过并且没有跳过用例说明闭环没有“作弊”。我建议你每次跑完闭环都记录这三个数据积累一段时间后你能很清楚地看到 Agent 的修复风格在哪个环节最容易出现问题然后针对性调整工具链和提示词。如果把视野拉长一点我会说“构建→测试→修复→循环”只是 AI Agent 开发闭环的起点。当这个循环足够稳接下来可以把需求解析、代码评审、部署验证这些环节也逐步交给 Agent 去拖动。但无论怎么扩展底层的核心始终没变给 Agent 一个可复现的环境、一组可观测的信号、一套有边界的工具以及一个在关键时刻说“停”的人。这几点做扎实了AI Agent 才能真正从“会聊天”变成“会干活”。