本篇代码示例:code/ 实战练习:Project 05. 让 agent 自己检查自己做的对不对
第九讲. 防止 agent 提前宣告完成
你让 agent 实现"密码重置"功能。它改了数据库 schema、写了 API 端点、加了邮件模板,跑了单元测试(全部通过),然后自信地告诉你"做完了"。你实际一跑——密码重置链接发不出去(邮件服务配置缺失)、数据库迁移半途失败(schema 不一致)、端到端流程根本没走过一遍。
这种感觉你一定不陌生——考试时把卷子写得满满当当,信心满满地第一个交卷,结果成绩出来不及格。卷子写满了,不代表做对了。
这不是偶然事件。Guo 等人 2017 年在 ICML 上的经典论文证明:现代神经网络系统性地过度自信——模型自报的置信度显著高于实际准确率。AI 编码 agent 也一样:它"觉得"做完了,但实际上差得远。你的 harness 必须用外部化的、基于执行的验证来替代 agent 的"感觉"。
滑坡效应
过早完成声明几乎总是一样的套路:代码看着还行——语法正确、逻辑似乎合理,静态检查没有明显错误。但 harness 没有强制要求全面执行验证,agent 跳过了实际运行或只跑了部分测试。跑了单元测试但跳过集成测试,跑了测试但没检查覆盖率。最后"代码看起来没问题"就被当作了"功能已完成"的证据。交卷了。
每一步都在丢失信息。从任务规范到代码实现到运行时行为,每次转换都可能引入偏差,而每次跳过的验证都加剧了信息不对称。
三层终止检查
核心概念
- 过早完成声明:agent 断言任务完成,但实际上存在未满足的正确性规范。核心问题:agent 依据代码层面的局部信心做判断,系统级正确性需要全局验证。
- 置信度校准偏差:agent 自报的完成信心与实际完成质量之间的系统性差距。对复杂多文件任务,这个偏差显著为正——agent 总是比实际做得更自信。就像学生考完试估分总是偏高。
- 终止标准:一组明确的、可执行的判定条件,定义在 harness 里。agent 必须满足所有条件才能声明完成。"完成"从主观判断变成了客观判定。
- 验证-确认双闸门:第一层验证检查"代码是否正确实现了指定行为";第二层确认检查"系统级行为是否满足端到端需求"。两层都通过才算完成。
- 运行时反馈信号:来自程序执行的日志、进程状态、健康检查。这是 harness 判定完成质量的客观基础。
- 完成优先级约束:先验证功能正确性,再处理性能,最后管风格。核心功能没验证通过之前,不许做重构。
单元测试通过 ≠ 任务完成
这是最常见的陷阱,也是最危险的一个。agent 写了代码,跑了单元测试,全部绿色,然后说"做完了"。但单元测试的设计哲学——隔离被测单元、模拟依赖——恰好使其无法检测跨组件问题:
接口不匹配:渲染进程传给预加载脚本的文件路径是相对路径,但预加载脚本期望绝对路径。各自的单元测试都用了 mock,都通过了。只有端到端跑通时才发现问题。就像乐队里每个乐手各自练习都完美,但合在一起才发现定调不一样。
状态传播错误:数据库迁移改了表结构,但 ORM 的缓存层还持有旧结构的缓存条目。单元测试每次都是全新的 mock 环境,不会暴露这种跨层状态不一致。
环境依赖性:代码在测试环境(一切 mock)行为正确,在真实环境因配置差异、网络延迟、服务不可用而失败。就像在排练室唱得很好,上台演出时音响设备出了问题。
"顺便重构"是完成判定的毒药
Claude Code 有一个常见行为模式:在核心功能还没验证通过时就开始重构代码、优化性能、改进风格。Knuth 说的"过早优化是万恶之源"在 agent 场景中有了新含义——重构会改变已完成验证和未完成验证之间的边界,可能破坏之前隐式正确的代码路径。就像你数学大题还没做完,就跑去把前面选择题的答案重新抄一遍格式——浪费时间不说,还可能抄错了。
自我评价的系统性偏差
Anthropic 在 2026 年的研究中发现了一个更深层的失败模式:当 agent 被要求评估自己的工作时,它系统性地过度正面评价——即使人类观察者认为质量明显不达标。 这就像让学生自己给自己判卷——对自己的答案总是特别宽容。
这个问题在主观任务(如设计审美)上尤其严重——"布局是否精致"是一个判断题,agent 可靠地偏向正面。即使在有可验证结果的任务上,agent 也会因为判断失误而影响表现。
解决方案不是让 agent "更客观"——同一个模型既生成又评估,内在地倾向对自己慷慨。解决方案是把"干活的人"和"检查的人"分开。 就像考试不能让学生自己批改自己的卷子——得有个独立的阅卷老师。
一个独立的评估 agent,经过专门调校为"挑剔"之后,比让生成 agent 自我评估有效得多。Anthropic 的实验数据:
| 架构 | 运行时长 | 成本 | 核心功能是否可用 |
|---|---|---|---|
| 单 agent 裸跑 | 20 分钟 | $9 | 否(游戏实体无法响应输入) |
| 三 agent(planner + generator + evaluator) | 6 小时 | $200 | 是(游戏可以正常游玩) |
这是同一个模型(Opus 4.5),同一段提示词("做一个 2D 复古游戏编辑器")。区别只在 harness——从"裸奔"到"planner 扩展需求 → generator 逐功能实现 → evaluator 用 Playwright 实际点击测试"。
来源:Anthropic: Harness design for long-running application development
怎么防止提前交卷
1. 外部化终止判定
完成判定不应该由 agent 自己做。harness 独立执行终止校验,输入是运行时信号,不是 agent 的置信度。在 CLAUDE.md 里写清楚:
## 完成定义
- 功能完成 = 端到端验证通过,不是"代码写完了"
- 必须运行的验证层级:
1. 单元测试通过
2. 集成测试通过
3. 端到端流程验证通过
- 在第 1 层没通过时,不许进入第 2 层
- 在第 2 层没通过时,不许进入第 3 层2. 构建三层终止校验
- 第一层:语法与静态分析。成本最低,信息量最小,但必须通过。这是最低限度的检查——字都没写错才能往下看。
- 第二层:运行时行为验证。测试执行、应用启动检查、关键路径验证。这是核心完成证据。不仅要写了,还要能跑。
- 第三层:系统级确认。端到端测试、集成验证、用户场景模拟。防止过早声明的最后一道防线。不仅要能跑,还要跑对。
3. 为 agent 设计好的"红笔批注"
OpenAI 在 Codex 实践中提出了一个特别有效的模式:给 agent 写的错误消息要包含修复指导。不要像阅卷老师只画个大红叉,要像好老师一样在旁边写上"这里应该怎么改"。不要用 "Test failed",而用 "Test failed: POST /api/reset-password returned 500. Check that the email service config exists in environment variables. The template file should be at templates/reset-email.html." 这种具体的、可操作的反馈让 agent 能自我修正,而不需要人类介入。
4. 捕获运行时信号
有效的运行时信号包括:
- 应用是否成功启动并达到就绪状态?
- 关键功能路径在运行时是否执行成功?
- 数据库写入、文件操作等副作用是否正确?
- 临时资源是否被清理?
实际案例
任务:实现用户密码重置功能。涉及数据库操作、邮件发送和 API 端点修改。
提前交卷路径:agent 修改数据库 schema、编写 API 端点、添加邮件模板、跑单元测试(通过)、声明完成。卷子写得满满当当。
实际扣分项:(1) 端到端流程未测试——重置链接的实际发送和验证未确认。(2) 数据库迁移在部分执行后失败,导致 schema 不一致。(3) 邮件服务配置在目标环境中缺失。
harness 介入:终止校验强制执行——(1) 启动完整应用验证重置端点可访问;(2) 执行完整重置流程;(3) 验证数据库状态一致性。所有缺陷在会话内被发现,节省了 5-10 倍的后续修复成本。独立阅卷老师批出了真正的问题。
关键要点
- agent 系统性地过度自信——置信度校准偏差是客观存在的。卷子写满了不代表做对了。
- 完成判定必须外部化——harness 独立验证,不信任 agent 的"感觉"。不能让学生自己批自己的卷子。
- 三层校验缺一不可——语法通过、行为通过、系统通过,层层递进。
- 错误消息要像好老师的红笔批注——包含具体修复步骤,让 agent 能自我修正。
- 核心功能验证通过之前不许重构——完成优先级约束是防止过早优化的关键。
延伸阅读
- On Calibration of Modern Neural Networks - Guo et al. — 证明现代深度网络系统性地过度自信
- Building Effective Agents - Anthropic — 运行时证据在完成判定中的关键作用
- Harness Engineering - OpenAI — 过早完成声明是 agent 的主要失败模式之一
- The Art of Software Testing - Myers — 测试方法层次和有效性的经典参考
练习
终止校验函数设计:为一个涉及数据库迁移和 API 修改的任务设计完整的终止校验。列出需要的运行时信号和每个信号的通过/失败标准。在一个实际任务上运行,记录它发现了哪些隐藏问题。
校准偏差测量:选 10 个不同类型的编码任务,记录 agent 的自报完成信心和实际完成质量。计算偏差值,分析它和任务复杂度的关系。
多层防御实验:对同一组任务跑三种配置——(a) 仅静态分析,(b) 加单元测试,(c) 完整三层校验。比较过早完成声明的比例和未捕获缺陷的数量。