第 5 章 · 控制机制:防止越界与过早胜利
本章目标:掌握防止智能体越界执行和过早宣布完成的控制机制,理解三层终止检查和验证-校验双闸。
5.1 过早胜利的陷阱
你让智能体实现一个"密码重置"功能。它修改了数据库 schema、写了 API 端点、添加了邮件模板、跑了单元测试(全绿),然后自信地告诉你"完成了"。但你实际运行时发现:邮件服务配置缺失导致链接发不出去;数据库迁移中途失败留下不一致的 schema;端到端流程一次都没跑过。
这不是孤立事件。2017 年 ICML 经典论文证明:现代神经网络系统性地过度自信——模型报告的置信度显著高于实际准确率。AI 编码智能体也不例外。它们"感觉"完成了,但实际离完成还很远。你的 harness 必须用外部化、基于执行的验证替代智能体的"感觉"。
5.2 滑坡效应
过早完成声明几乎总遵循相同剧本:代码看起来没问题——语法正确、逻辑似乎合理、静态分析无明显错误。但 harness 没有强制全面执行验证,智能体跳过实际运行或只跑部分测试。跑单元测试但不跑集成测试;跑测试但不检查覆盖率。最终,"代码看起来 fine"被当作"功能已完成"的证据。
信息在每个环节都有损耗。从任务规格到代码实现到运行时行为,每次转换都可能引入偏差,每次跳过的验证都放大信息不对称。
5.3 三层终止检查
智能体说:完成了
↓
第一层:首次运行 lint / typecheck
↓
第二层:然后运行测试和启动检查
↓
第三层:最后运行完整用户流程
↓
通过全部三层才算完成代码写完 + 单元测试绿
↓
但应用没真正启动 + 完整流程没跑
↓
配置、DB、外部服务问题全部隐藏
↓
所以智能体过早宣布胜利5.4 单元测试绿 ≠ 任务完成
这是最常见的陷阱,也是最危险的。智能体写完代码、跑单元测试、看到全绿,就说"完成"。但单元测试的设计哲学——隔离被测单元、mock 依赖——恰恰让它们无法检测跨组件问题:
接口不匹配:渲染器向 preload 脚本传相对路径,preload 脚本期望绝对路径。各自的单元测试都用 mock 所以都通过。问题只在端到端测试时暴露。
状态传播错误:数据库迁移改变表 schema,但 ORM 缓存层仍持有旧 schema 的缓存条目。单元测试每次都在新 mock 环境运行,这种跨层状态不一致永远不会暴露。
环境依赖:代码在测试环境(一切都 mock)表现正确,但在真实环境因配置差异、网络延迟或服务不可用而失败。
"顺便重构"是对完成判断的毒药
Claude Code 有一个常见行为模式:在核心功能通过验证前就开始重构代码、优化性能、改善风格。Knuth 的名言"premature optimization is the root of all evil"在智能体场景中有了新的含义——重构移动了已验证和未验证代码的边界,可能破坏之前隐式正确的代码路径。
自我评估的系统性偏差
Anthropic 在 2026 年研究中发现了更深层的失败模式:当被要求评估自己的工作時,智能体系统性给出过度积极的评估——即使人类观察者会判断质量明显不达标。
这个问题在主观任务(如设计美学)上尤其严重。"布局是否精致"是判断性问题,智能体可靠地偏向正面。即使在有可验证结果的任务上,智能体的判断力差也会降低其表现。
解决方案不是让智能体"更客观"。同一个模型既生成又评估,天生倾向于对自己宽容。解决方案是分离"干活的人"和"检查的人"。
独立评估智能体,专门调优为"挑剔",比让生成智能体自我评估有效得多。Anthropic 的实验数据:
| 架构 | 运行时间 | 成本 | 核心功能可用? |
|---|---|---|---|
| 单智能体(裸跑) | 20 分钟 | $9 | 否(游戏实体无响应) |
| 三智能体(规划器+生成器+评估器) | 6 小时 | $200 | 是(游戏完全可玩) |
完全相同的模型(Opus 4.5)和完全相同的提示词("build a 2D retro game editor")。唯一的区别是 harness:从"裸跑"变为"规划器展开需求 → 生成器逐功能实现 → 评估器用 Playwright 实际点击测试"。
5.5 如何防止过早完成声明
1. 外部化终止判断
在 harness 中明确定义终止条件。智能体必须满足所有条件才能宣布完成。"完成"从主观判断变为客观判定。
## 终止条件
- [ ] 所有单元测试通过 (pytest tests/ -x)
- [ ] 类型检查通过 (mypy src/ --strict)
- [ ] Lint 干净 (ruff check src/)
- [ ] 端到端测试通过 (playwright test e2e/)
- [ ] 性能基准未降级 (benchmark.sh)2. 验证-校验双闸
第一闸(验证):检查代码是否正确实现了指定行为。 第二闸(校验):检查系统级行为是否符合端到端要求。 两闸都通过才算完成。
3. 运行时反馈信号
程序执行的日志、进程状态、健康检查——这些形成 harness 判断完成质量的客观基础。
4. 完成优先级约束
先验证功能正确性,再处理性能,最后处理风格。核心功能未验证前不允许重构。
5.6 Feature List 作为范围控制
Feature List(功能清单)是控制智能体范围的有效工具:
{
"project": "my-app",
"features": [
{
"id": "auth-001",
"priority": 1,
"title": "用户登录",
"status": "passing",
"verification": ["POST /api/login returns 200", "JWT returned"]
},
{
"id": "auth-002",
"priority": 2,
"title": "用户注册",
"status": "in_progress",
"verification": ["POST /api/register creates user", "Email sent"]
}
]
}规则:
- 一次只做一个 feature
- 不标记 feature 完成除非验证通过
- 不在当前 feature 范围外修改代码(除非 blocker 迫使窄支持修复)
5.7 本章小结
- 智能体系统性过度自信,需外部验证替代"感觉"
- 三层终止检查:lint/typecheck → 测试 → 端到端流程
- 单元测试绿不等于任务完成,需验证-校验双闸
- 分离"生成者"和"评估者"是应对自我评估偏差的关键
- Feature List 控制范围,一次只做一个 feature
- 完成优先级:功能正确性 > 性能 > 风格
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. 过早完成声明的核心原因是什么?
2. 验证-校验双闸中,"校验"层检查的是什么?
3. Anthropic 实验中,三智能体架构相比单智能体裸跑的主要优势是?
4. Feature List 的主要作用是?
🛠️ 动手实践
- 为一个简单功能写完整的终止条件清单,包含至少三层验证。
- 实现验证-校验双闸:第一闸跑单元测试,第二闸跑端到端测试。
- 创建一个 Feature List JSON,跟踪 3 个 feature 的状态和验证证据。