编码 Agent 最危险的失败模式不是"写错代码",而是"写错代码却报告说完成了"。人类工程师有反馈回路:编译、跑测试、看报错。如果不给 Agent 建立同样的回路,它就只能凭语言模型的"感觉"判断对错——而这种感觉,在长程任务里会系统性地偏向乐观。验证 harness(verification harness)要解决的,正是这个"自证清白"的问题。

为什么必须有自检,而且要按节奏跑

短任务里,模型写完一段代码、人类立刻验证,问题不大。但长程编码任务——重构一个模块、实现一个跨多文件的特性——动辄几十上百步。如果只在最后一步验证,中间任何一步的偏差都会沿着后续步骤放大,等到终点才发现,往往已经积重难返、难以定位。

所以验证不能是"最后做一次",而要按节奏穿插在执行过程中。一个实用的模式是:每隔若干步,就对照规约(spec)跑一遍检查——编译、单测、或者一个轻量的契约校验。

1
2
3
4
5
6
7
8
9
10
SPEC = load("spec.md")
checkpoint_every = 4 # 每 4 步做一次对照验证

for step in plan:
apply(step)
if step.index % checkpoint_every == 0:
report = run_checks() # build + 相关单测
if not report.ok:
# 不要继续往前堆,先回到最近的绿色状态
diagnose_and_fix(report, against=SPEC)

节奏的意义在于把"错误传播距离"压短。每 4 步一个检查点,意味着任何 bug 最多污染 4 步就会被逮住,定位范围天然被框定,回滚成本也低。

让验证者是另一个 Agent,而不是同一个

写代码的 Agent 去验证自己刚写的代码,存在和人类一样的盲点——它带着"我刚才是这么想的所以这是对的"的预设。把验证交给一个独立的、fresh-context 的子 Agent,效果通常更好。

1
2
3
4
5
6
7
# 写代码的主 Agent 完成一个 checkpoint 后
verdict = spawn_subagent(
model="haiku", # 跑测试这类体力活用便宜模型
task="对照 spec.md 逐条验收:运行测试,报告每条标准 通过/不通过,"
"附上你实际看到的命令输出作为证据,禁止脑补结果",
context=[SPEC, test_command], # 不给它写代码时的纠结历史
)

这个验证者只拿到规约和待验对象,不知道之前作者绕过的弯路,因此不会替作者的思路背书。它跑测试、读真实输出、逐条对照——它的判断建立在工具结果上,而不是对代码的"印象"。

系统提示里最该写死的一条:报告前先自证

即便有了检查节奏和独立验证者,模型仍可能在叙述里"幻觉出完成状态"——说"测试已通过"“功能已实现”,但其实它根本没跑,或者跑了没看结果。对抗这种编造,最有效的不是更复杂的流程,而是在系统提示里写死一条强约束:

在报告任何"完成 / 已修复 / 测试通过"之前,必须先用本会话内的工具结果自证。没有对应的工具输出作为证据,就不许声称成功。

这条规则把"成功"从一个可以随口说出的形容词,变成了必须有证据支撑的断言。它的威力在于改变了举证责任:模型不能再用流利的措辞蒙混过关,它得拿出那一次 pytest 的真实退出码、那一行 Build succeeded。实践中,仅这一条就能大幅减少"假完成"——因为模型一旦被要求"贴出证据",就不得不真的去执行验证步骤。

1
2
3
4
5
6
# 反例(系统提示没约束时模型爱说的话)
"我已经修复了这个 bug,所有测试现在都通过了。" # 没有任何证据

# 期望(被自证约束后)
"运行 `pytest tests/auth -q`,输出 `12 passed in 0.8s`,
退出码 0;据此判定 auth 模块验收通过。" # 证据在手

验证不等于过度验证

需要提醒的反向陷阱:验证 harness 是为了抓真实风险,不是为了表演严谨。如果只是改了一行日志文案,没必要跑全量回归套件、没必要派验证者、更没必要为"万一以后日志格式要变"提前加一层抽象。验证的强度应当与改动的风险匹配——这也呼应了 effort 的思路:低风险改动用低强度验证(够信息就收手),高风险改动才动用完整的"对照规约 + 独立验证者 + 跑全套测试"。把简单 bugfix 顺手升级成大重构、为不会发生的场景预埋防御,本身就是一种应该被克制的过度工程。

一个可落地的最小 harness

把上面几块拼起来,一个朴素但管用的验证 harness 是:

  1. 开工前把验收标准固化成 spec.md(可被反复对照的事实源)。
  2. 执行中每隔若干步设检查点,跑 build + 相关单测。
  3. 关键 checkpoint 派 fresh-context 子 Agent 做独立验收。
  4. 系统提示写死"报告完成前先用工具结果自证"。
  5. 验证强度随改动风险伸缩,不搞一刀切。

这里还藏着一个常被忽视的细节:检查点之间,最好让 Agent 维持一个"最近的绿色状态"作为可回退的锚点。一旦某次检查失败,与其在脏状态上继续打补丁、越改越乱,不如回到最近一次全绿的快照,从那里重新出发。这等于把版本控制里"小步提交、随时回滚"的纪律内化进了 Agent 的执行循环——每个绿色检查点都是一次隐式的提交,错误最多波及一个检查点区间,而不会沿着整条执行链扩散。配合前面"报告前先自证"的约束,Agent 既不会谎称成功,也不会带着未被发现的回归一路狂奔。

让 Agent 自己跑测试、自己审自己的核心,不是多套一层流程,而是把"成功"重新定义为一件必须拿证据说话的事——一旦做到这点,编造的完成状态就失去了生存空间。