法不净空,觉无性也。

第23章 第三级验收门——什么时候信任你的 agent

2026.07.29

Agent 说"做完了"——不是你在质疑它的诚信,是你在工程上需要一个护栏。


引子

Agent 说"改完了,测试通过了"——你怎么知道?

这不是反问,是一个需要明确回答的工程问题。自主 agent 项目面临的最大风险不是"agent 会干坏事"(那是权限问题),而是"agent 以为自己干对了,但实际没有"(这是验收问题)。

后者更隐蔽。因为它不是恶意行为——agent 没有隐瞒失败。它只是没有发现失败。它跑了一遍测试,输出了绿色,但它没注意到测试其实只跑了 30% 的用例(因为某配置项没加载),或者编译确实成功了但 lint 报警被它忽略了。

所以问题不是"agent 不靠谱",问题是:你说"做完了"的时候,谁用哪套标准验证过?

第一级:命令门——agent 自报完成

最简单的验收方式:agent 说"我做好了"。

在 CodeCoder 中,这表现为里程碑的 command 字段——agent 完成一个里程碑后,调用 milestone done 并附上自评。系统记录"agent 说做完了",然后就通过了。

这听起来很粗糙,但大部分场景下够用。当你在本机交互式开发一个功能时,你会看着终端输出、阅读代码、在头脑中做一次快速审查。如果有问题,你会立刻发现,然后把里程碑打回 needs_fix

命令门的信任假设是:有一个人在循环里。 如果验收链路的最后有一双人眼,那么 agent 的自报可以接受——人会兜底。这条假设一旦不成立(无用户模式),命令门就成了最薄弱的环节。

命令门的失败模式有两种:

  1. agent 误报完成——测试确实通过,但覆盖度不够。这是认知局限,不是撒谎
  2. agent 漏报失败——测试其实失败了,但 agent 没读输出。这是工具使用失误

两种都不是恶意行为,但结果一样:一个"做完了"的里程碑,实际没有做完。

第二级:检查门——确定性验证

检查门引入与 agent 无关的确定性检查。CodeCoder 定义了四种 CheckSpec,每种都是无歧义、可自动判定的:

  • BuildExitZero — 编译/测试命令的退出码是否为 0。不是"看起来通过了",是 exit code=0
  • NoTemplateContent — 生成的文件不包含模板残留("TODO"、"placeholder"、"your code here")
  • FileCountMin — 创建了足够数量的文件
  • MinLinesPerFile — 生成的代码不是空壳

这些检查独立于 agent 运行。Agent 不能绕过它,不能覆盖它的结果。检查的通过标准是硬编码的——不是 LLM 判断,不是"看起来合理",是"退出码等于 0"这种可编程条件。

检查门的关键设计原则是:它不是 agent 自报的补充,而是 agent 自报的替代。 如果一个里程碑配置了 BuildExitZero 检查,那么即使 agent 说"做完了",也需要等编译检查通过了才算真正的 done。代码里检查门的执行顺序在命令门之后但覆盖命令门的判断——如果检查失败,done 状态不会写入,里程碑回到 needs_fix

因为检查是确定性的,它可以在无用户模式下自动运行。事实上,headless runner 的核心验收机制就是检查门——命令门 + 检查门串联,不需要用户介入。

第三级:审查门——架构级客观评审

检查门能确定"编译通过了、文件够了",但不能确定"这个架构设计是合理的"。

审查门解决的是这个问题。它引入一个独立的只读子 agent,用结构化 rubric 评审输出的质量。CodeCoder 的四信号 rubric:

  • foundation — 基础结构是否完整(不缺失关键模块、不遗漏类型声明)
  • over_engineering — 是否过度设计(引入不必要的抽象、过早优化)
  • volume — 单次变更是否过大(改动范围与任务匹配吗)
  • terminology — 术语一致性(新代码是否遵循了 CONTEXT.md 的领域术语)

审查 agent 是只读的——它能读文件、读 diff、读代码结构,但它不能写文件、不能跑命令、不能修改任何东西。只读 agent 的权限锁定原理见篇 2「三分架构」中关于 agent 工具受限工具集的说明。这意味着即使审查 agent 是 LLM 驱动的(非确定性),它能造成的最大危害是误判——不会破坏代码。

如果审查 verdict 是 pass,里程碑通过。如果是 needs_fix,启动自恢复循环:把审查结果注入修复 prompt,让原来的 agent 在有限次重试内修正。如果重试耗尽仍然 needs_fix,headless runner 退出并报码 2(StuckNeedsFix),等待人工介入。

三级验收的递进逻辑

验收 pipeline 是串联的:

命令门 → 检查门 → 审查门 → 完成

每一道门都比前一道更严格、成本更高。关键不是让每个里程碑都走三级门,而是按风险选择门级:

场景推荐门级理由
单人交互式开发命令门有你在看,快速迭代
自动化 CI / 无用户命令门 + 检查门无人在循环,需要确定性验证
架构变更 / 公共接口三级全开需要架构级评审
生产部署三级全开风险最大,需要最严验收

任何一道门 fail → needs_fix 自恢复循环。 自恢复不是无限重试——有一个硬预算限制(默认 3 次 headless 尝试,交互式不限)。超过预算仍然 fail 的 milestone 被认为超出当前 agent 的能力范围,降级到人工处理。

代价与权衡

三级验收门提供了强大的质量保障,但也付出了不低的成本。

第一个代价是响应时间。审查门需要启动一个独立的只读子 agent,调用 LLM,等待 verdict 返回。这个过程的耗时取决于 LLM 服务的延迟——通常是 5-30 秒。审查门被配置成自动开启时,每个里程碑的完成时间从"做完即止"变成"做完 + 等待审查"。对于有 10 个里程碑的项目,多出来的等待时间可能累积到 1-5 分钟——在 CI pipeline 中这可能触发超时,需要单独调大时间预算。

第二个代价是误报成本。审查门用 LLM 评估架构合理性,但 LLM 的判断不是完美的。它可能误报过度设计(把"合理的抽象"判为 over-engineering),也可能漏报真正的架构漂移(在术语一致的表面下忽略深层矛盾)。误报会导致修复循环空转——agent 花费精力和 token 去响应一个不存在的架构问题。CodeCoder 用四信号 rubric 来结构化评审标准,但这不能完全消除误报。

第三个代价是自恢复循环的有界性不等于解决有界性。headless 模式默认 3 次修复尝试——但如果 milestone 的问题确实需要人的判断(比如接口兼容性决策),3 次重试只是消耗了 token 和时间,不会改善结果。检测"这个问题 agent 确实解决不了"并尽早降级到人工,比死磕预算上限更便宜。

第四,检查门的四种 Spec 覆盖范围有限。 BuildExitZero、NoTemplateContent、FileCountMin、MinLinesPerFile——覆盖了编译、模板、数量、体积,但不覆盖逻辑正确性。一个通过检查门的变更仍然可能是逻辑错误的。要覆盖逻辑正确性,需要审查门介入——但这回到了第一个代价。验收门的设计者需要接受:检查门不能保证"做对了",只能保证"没有明显做错"。

收尾:从检查门开始

如果你今天才开始给你的 agent 加验收门,建议从检查门开始:

  1. 配置第一条 BuildExitZero:让 agent 完成后自动编译验证
  2. 加一条 NoTemplateContent:防止 agent 生成含占位符的假代码
  3. 如果你发现 agent 经常通过编译但架构有问题——加审查门

检查门的投入产出比最高——它的确定性意味着每次执行都产生同样可预测的结果。审查门更有价值但也更贵——留着给最重要的里程碑用。

验收门解决的不是"agent 不诚实"的问题,而是"agent 和你一样会犯错"的问题。区别在于:你的错你负责,agent 的错需要系统来负责。一个没有验收门的自主 agent 不是一个产品——它是一个实验。

下一篇,我们讨论一个被低估的话题——agent 应该忘记什么。