智能体报告“完成”,功能却不工作:为什么会这样,以及如何验证智能体的工作
智能体报告“完成”,是因为它只评估自己能看到的东西,通常只有单元测试,而单元测试看不到模块之间的接口、迁移、配置和端到端场景。解决办法是结构性的,而不是靠提示词:在 AGENTS.md 中写明完成定义,强制运行端到端测试,让验证脚本独占“通过”状态的标记,并引入使用干净上下文的独立评估者。
为什么会这样
使用过智能体的人都遇到过这种情况:智能体报告“完成”,但功能并不工作。它并不是故意撒谎,它只是报告自己已经验证过的东西,并真诚地相信这已经足够。这种“声明”与现实之间的落差,源于“执行者自己评估自己的工作”这一架构本身,而不是某个特定模型的缺陷。
原因有三。第一,模型容易过度自信:它回答的把握程度高于其准确度。第二,智能体在做工作的同一个上下文中评估工作,它已经说服了自己方案是正确的,此时再说一句“请更客观些”,并不会改变上下文。第三,也是最实际的一点:智能体能做的验证往往是局部的。单元测试很快就会变绿,而端到端场景却没有人运行过。
一个典型的虚假“完成”
智能体被要求实现密码重置。它修改了数据库结构,添加了接口,接入了邮件模板,运行了单元测试,全部变绿。聊天窗口里出现一句自信的“完成”。
实际情况却是:
- 迁移执行到一半失败,数据库结构处于不一致的状态;
- 邮件模板存在,但环境中没有配置邮件服务(SMTP),邮件发不出去;
- “用户点击链接 → 输入新密码 → 成功登录”这一场景从未运行过。
单元测试无法发现这些问题:数据库被 mock 掉了,邮件发送也被 mock 掉了,而端到端的运行根本没有发生。
为什么绿色的测试证明不了功能可用
单元测试在隔离状态下检验一个模块:依赖项被替换成 mock,外部环境被切断。这带来了速度和精确性,但也带来了系统性的盲区。盲区并非测试的缺陷,而是它的设计使然。绿色的单元测试只能证明每个零件单独能工作,而不能证明零件组合在一起能工作。
有四类缺陷是单元测试从根本上发现不了的:
| 缺陷类别 | 示例 | 为什么单元测试发现不了 |
|---|---|---|
| 接口不匹配 | 一个组件返回相对路径,另一个组件要求绝对路径 | 每个组件都用自己的 mock 测试,而 mock 会接受任何格式 |
| 状态传播 | 数据库结构变了,但 ORM 缓存仍是旧的 | 单元测试中既没有真实数据库,也没有缓存 |
| 资源生命周期 | 文件句柄和连接没有释放,大文件导致内存泄漏 | 测试使用的数据很小,在资源耗尽之前就结束了 |
| 环境与配置 | 缺少环境变量,SMTP 未配置,迁移未应用 | 测试没有以真实配置启动应用 |
各层验证是嵌套的:单元测试能发现的问题,集成测试也能发现;集成测试能发现的问题,端到端(E2E)测试也能发现。每一层看到的都严格多于上一层。因此,宣布“完成”的权利只应属于最高的那一层。
如何度量这个问题:verification gap
在动手修复之前,先计算一下。取智能体最近关闭的 10 个任务,对每个任务回答两个问题:智能体是否宣布“完成”,以及独立验证是否确认了这一点(你亲自走完场景,或者端到端测试通过)。不一致的比例就是你的 verification gap。
这个数字有两个用途。首先,它不需要争论“好像还能用”,直接显示问题的规模。其次,它提供了基线:每次改进流程之后,在相似的任务集上重新计算,就能看出是否有帮助。
真正的验证怎么做:三层检验
有一个很好的类比:汽车维修。车辆要经过三个工位,任何一个都不能跳过。
- 诊断(几秒):语法、类型、代码检查。发现笔误和明显错误。
- 台架(几分钟):单元测试和集成测试,应用能够启动。发动机在转,但车还停在原地。
- 路试:端到端场景,就像用户实际操作一样——通过 API、浏览器或 CLI,使用真实的数据库和配置。
顺序规则:诊断中还有错误时,不能上路;没有通过路试,车不能出厂。通过单元测试只是通过了第二个工位,而不是第三个。
可以把所有这些汇总到一个入口中:
# Makefile
check:
ruff check .
mypy app
pytest tests/unit tests/integration
e2e:
docker compose up -d db mailhog
alembic upgrade head
pytest tests/e2e
verify: check e2e
这里的 E2E 会启动真实的数据库和测试邮件服务器,应用迁移,并完整走完场景。迁移失败或邮件未配置的问题会立刻暴露,而不是等到客户那里才发现。
AGENTS.md 中的完成定义
智能体不应该自行决定“完成”是什么意思。这个标准应当写入指令文件,并且表述为可以验证的条件:
## Definition of Done(完成定义)
只有满足以下全部条件,功能才算完成:
1. make check 以退出码 0 结束
2. make e2e 以退出码 0 结束,且功能场景已写入 tests/e2e
3. 迁移可以在干净的数据库上应用并回滚
4. 新的环境变量已写入 .env.example
5. PROGRESS.md 中记录了完成的内容及验证方式
以下情况不算完成的证明:
- “代码已经写好,看起来是对的”
- “单元测试通过了”
- “我本地跑得通”
明确列出“不算完成”的情况,效果比想象中好:智能体不会再在第一次变绿时就停下来。
谁有权标记“通过”
下一步,是把“完成”状态从智能体手中完全拿走。功能保存在一个列表中(例如 feature_list.json),每项包括“行为”、“验证命令”和“状态”。只有脚本才能把某个功能改为 passing:它先运行通用的 make check,再运行该功能专属的验证命令,成功后才记录状态,并附上提交哈希作为证据。失败则功能保持 active,不存在“差不多通过了”。
{
"id": "F04",
"behavior": "用户收到带链接的邮件并修改密码",
"verification": "pytest tests/e2e/test_password_reset.py",
"state": "active"
}
这里有一个陷阱:脚本只检验 verification 字段中写的内容。如果某项架构检查属于完成定义,却没有写进功能的验证命令里,那么这个漏洞就会一直隐藏。因此,通用的 make check 必须包含所有强制检查,而不只是测试。
独立评估者
执行者的自我报告,永远不是独立的事实来源。结构性的解决办法是拆分角色:
- 规划者把需求拆分成带验收标准的功能,但不写代码;
- 生成者实现一个功能并运行测试,但不决定是否完成;
- 评估者根据检查清单给出结论。
评估者不应看到生成者的推理,否则会继承它的自信。它只拿到需求、代码差异和运行结果。在 Claude Code 中,这可以通过子智能体实现:它有自己干净的上下文。更好的做法是让评估者使用另一个模型,因为不同模型的盲区不同。“看起来正常”的结论是不允许的:每条判断都必须引用文件、行号或日志。
能让智能体自行修复的错误信息
验证只有在其输出能引导修复时才有价值。“Test failed”或者光秃秃的 AssertionError,对智能体毫无帮助。好的错误信息应当说明:哪里坏了,为什么这条规则存在,以及如何修复:
ERROR arch-check: app/services/refund.py 导入了 app/db/session.py
WHY: 服务层不直接访问数据库,只能通过仓储(repository)
FIX: 请使用 app/repositories/refund.py 中的 RefundRepository
这种格式把检查从禁令变成了提示:智能体无需你的参与就能修复违规。
人工验证仍然需要的地方
自动化并不能取代人,但它缩小了人的角色。人工验证适用于难以形式化的标准:界面外观、文案、流程的易用性。应按照验收标准的检查清单来做,而不是随便点一点。如果你第二次对智能体提出同一条意见,这就是一个信号:应该把它变成自动检查,比如一个测试、一条代码检查规则,或 make check 中的 grep 守卫。
端到端测试如何在运行之前就改变智能体的行为
端到端测试的价值不仅在于它抓到的 bug。如果智能体事先知道它的工作将由端到端场景检验,它写代码的方式就会不同:会考虑与相邻组件的契约,而不仅仅是自己的函数;会处理错误路径;也不会破坏架构边界,因为违反边界会在运行时暴露出来。验证就成了入口处的约束,而不只是出口处的过滤器。
但有一个前提:端到端测试只有在系统边界清晰时才有意义。如果架构是一团乱麻,端到端测试只能证明这团乱麻能运行,却无法显示设计意图在哪里被打破。因此应先固定各层及依赖规则,再建立端到端场景。智能体很乐于复制仓库中现有的模式,包括糟糕的模式,所以约束最好从第一天就引入。
常见错误
- 要求智能体“再仔细检查一遍”。上下文没有变,评估仍然会偏高。
- 把 100% 覆盖率当作保证。单元测试覆盖不了模块之间的接口。
- 在功能被确认之前就重构。顺序是严格的:正确性 → 性能 → 风格。
- 在端到端测试中 mock 最容易出问题的部分。数据库、迁移和配置在端到端测试中必须真实运行;只有外部付费 API 可以用测试服务器替代。
总结
当智能体报告“完成”而功能却不工作时,问题几乎总是出在验证流程上,而不是模型。计算 verification gap,在 AGENTS.md 中固定完成定义,让端到端测试成为强制项,把“完成”状态交给脚本,并加入独立的评估者。这套方案在 Harness 工程课程中,结合实际项目做了详细讲解。
常见问题
能让智能体自己运行端到端测试吗?
可以,前提是命令已写入 AGENTS.md 的完成定义,并且智能体有权使用所需工具。更可靠的做法是通过钩子或 CI 额外运行它,而不依赖智能体的记忆。
端到端测试很慢,每次修改都要运行吗?
不必。代码检查、类型检查和单元测试每次都运行;端到端测试在修改涉及多个层次时运行,并在宣布功能完成之前运行。
前端功能该如何验证?
用浏览器端到端测试(例如 Playwright)点击真实的界面;外观则通过依据检查清单的人工检查,或由评估者审阅的截图来完成。
人工代码审查能代替评估者吗?
人工审查很有价值,但成本高,无法扩展到数小时的自主工作。带评分标准的评估者可以先过滤掉明显的问题,把有争议的判断留给人来决定。
课程“Harness Engineering: инженерия надёжности AI-агентов”结合实践详细讲解了这一主题。
- Агент написал «готово», а фича не работает: verification gap и Definition of Done
- Все тесты зелёные, а фича сломана: E2E-тесты и архитектурные правила для AI-агента