编码 Agent 如何应对假完成与错误堆积
文章围绕大型遗留代码库中的编码 Agent,提出基于 MCP 的自修复执行循环,并提供项目仓库。重点讨论修改后缺少编译测试、错误日志挤占上下文,以及不安全工具调用三类故障。
AI 深度解读
编码 Agent 改完文件就宣布成功,遇到报错又把整段日志塞回上下文,这两种行为都很难支撑真实项目。Wang Guo 介绍的 zhishu-agent,把重点放在模型外面的 Harness:用隔离环境、工具拦截、强制测试和上下文压缩,约束 Agent 的执行过程。
它最值得借鉴的是一套工程思路:如何让 Agent 的「完成」有证据,让失败后的重试有边界。不过,文中的「收敛保证」和性能数字,还需要与实际验证结果分开看。
Agent 能不能交差,应该由谁决定
作者把企业代码库中的常见故障分成三类:没编译、没跑测试就交差;错误日志不断累积,挤掉最初的任务约束;工具调用越界,执行危险命令或泄露密钥。
文中用 50,000+ 行遗留代码描述应用规模,还举了一个上下文失控的例子:6 轮命令报错后,原始终端输出占到 40,000 tokens。这些数字表达了问题的量级,但没有对应的实验过程,不能当成所有 Agent 都会触发的阈值。
作者给出的解法是 Harness,也就是包在模型外面、负责执行和验收的控制层。模型负责提出修改方案,控制层决定命令能否执行、结果是否合格、失败后还能继续几次。
「任务完成」应该是验证通过后的状态,而不是模型生成的一句话。
具体流程从意图接收和规划开始,经工具拦截、隔离执行、测试验证,再根据结果结束或重试。代码修改发生在 Git worktree 或临时容器目录中;测试阶段执行 pytest -q 或 mvn test,只有退出码为 0,才允许声明完成。
测试失败时,控制层提取失败断言、行号和 diff,再交给模型修复。连续 4 次尝试仍未收敛,就进入人工升级检查点。最终发布也保留人工介入(Human-in-the-Loop,HITL)。
这里的「收敛」需要准确理解:限制重试次数,能保证流程最终停下来,却不能保证错误一定被修好。隔离工作区能减少修改互相干扰,也不能代替工具权限控制。
为什么压缩上下文,要先处理工具输出
作者认为,多文件重构容易失败的一个原因是上下文饱和。终端输出、堆栈和 500 行源码反复累积,提示词超过 30k tokens 后,早期约束可能逐渐失去作用。
zhishu-agent 的 /compact 分两步处理。
第一步,清理已经分析并处理过的临时工具输出。原始文本删除后,只留一行记录,例如 [Output pruned: 1,420 bytes, test failed on assertion line 42]。这行信息交代了输出大小和失败位置,避免下一轮重复携带整段日志。
第二步,当 token 使用量超过上下文窗口的 75%,生成结构化检查点,保存四件事:
Goal:用户最初要求什么。Decisions Made:已经修改哪些文件、作出哪些决定。Current Blocker:当前卡在哪个测试失败上。Next Step:接下来准备执行什么动作。
这套设计抓住了一个实际问题:Agent 继续工作需要的是任务状态,不一定需要每一次工具调用的完整历史。
不过,压缩也可能删掉后来才显出价值的线索。检查点如果漏掉验收条件,后续执行可能越来越偏。原文没有说明如何校验摘要,也没有讲清被清理的输出能否重新读取。
作者声称,这能减少超过 85% 的 token 消耗,并支持 20+ 轮持续运行而不退化。但文中没有交代模型、任务集、窗口大小和质量评价方式。这是项目方报告的效果,尚不足以推导出其他团队也能得到同样收益。
MCP 统一调用入口,执行规则仍要自己实现
为了对接 Anthropic、OpenAI、DeepSeek 和本地 Ollama,作者采用 MCP(Model Context Protocol),通过 JSON-RPC 2.0 组织工具通信。
文中列出三个入口:initialize 负责能力协商和协议版本握手;tools/list 负责工具发现,并采用「元信息、schema、执行」三级渐进披露;tools/call 负责实际调用,结合严格超时和 CompletableFuture 异步分发。
下面这段请求,把测试目标和超时边界放进工具参数:
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "run_test_suite",
"arguments": {
"path": "src/test/java/com/zhishu/service/PaymentTest.java",
"timeoutMs": 5000
}
},
"id": "zhishu-req-104"
}
重点看 timeoutMs: 5000:它表达了调用方的超时要求,但超时后如何终止执行中的进程,原文没有说明。JSON-RPC 请求结构本身也不会自动完成参数校验、权限检查和资源清理。
同样,三级披露是作者的工具发现策略,不能直接理解成接入 MCP 后自然具备的行为。
工具执行前,项目通过 PreToolUse 做 AST 检查和权限拦截,声称可在低于 1ms 内硬拦截 rm、dd、drop table 等危险操作;执行后,通过 PostToolUse 屏蔽 sk-... 这类密钥。这里的 AST 是抽象语法树,用来分析命令结构,而不只是匹配一段字符串。
项目提供两套实现:WG-Harness-Studio 使用 Java 17 和 Spring Boot 3.3,包含可视化 Web GUI、基于 CompletableFuture 的人工审批流程、无密钥模拟模式,以及 DeepSeek、Qwen、OpenAI 的单行配置;WG-Harness-CLI 使用 Python 3.9+,以单文件、零依赖形式展示 8 个核心模式。作者称后者可以在 5 分钟内读懂。
两套实现均以 MIT 许可证开源,架构材料位于 docs/architecture-light.svg 和 docs/HARNESS_PATTERNS.md。这些是作者给出的交付说明,文章没有展示完整运行记录或生产验证报告。
我的判断
我认同把验收权放在 Harness 中。相比只在提示词里要求「改完请测试」,强制执行验证、检查退出码、限制重试,更容易追踪 Agent 为什么结束任务。结构化失败信息也比反复追加整段日志更有针对性。
但「退出码为 0」只能证明这次执行的检查通过。测试覆盖不到的需求、没有运行的检查,仍然可能遗漏。上面的请求甚至只指定了一个 PaymentTest.java 文件,它与全量测试通过之间还有范围差异。
安全部分也缺少足够证据。原文没有展示 AST 校验器如何处理组合命令、脚本间接调用和不同解释器;低于 1ms 的拦截耗时,也不能证明拦截范围完整。日志脱敏的具体规则与覆盖范围同样没说清。
这篇适合作为 Harness 设计清单,「生产级」则仍需项目自己的验证来支撑。
和单纯换更强模型相比,这些约束直接作用于执行过程;和无限重试相比,4 次上限能明确控制消耗。但 4 次是否合适、压缩是否改善任务成功率,作者没有给对照数据。不能替这些经验选择背书。
能拿来做什么
- 给日常 AI 编程流程设置明确的完成条件。前端修改可以执行项目已有的类型检查、测试和构建,Node 项目则执行对应验证;把实际运行结果作为交付依据。
- 把一次任务放进独立 Git worktree,失败时保留可检查的修改,再设置有限重试。达到上限后,输出当前 diff、失败断言和剩余问题,交给开发者继续处理。
- 将测试失败转换成简短诊断,至少保留失败用例、文件位置和关键差异。压缩之前先确认任务目标与验收条件已经进入检查点,避免只记住代码修改。
- 自建 MCP 工具时,把参数校验、超时处理和权限限制放进工具实现。可以先用 Python 3.9+ 的单文件模式理解循环,再根据需要接入可视化审批流程。