给 AI 编程交付加一道独立验收关
作者先将完成条件和否决条件写成可二元判定的契约并固定哈希,再由另一模型只读审查交付物、契约与完成报告。审查要求引用文件和行号、列出实际阅读范围,并设置轮次上限;案例中发现了声称修复却未实际修改的问题。
完整中文译文
AI 编程代理说「Done」,只能证明它给出了完成声明。作者给一套 Node 浏览器自动化工具加了第二模型验收:先锁定标准,再让只读审查者用当前文件推翻声明。四轮审查里,最有价值的一次发现是:作者声称修好的代码,其实根本没改成功。
这套方法值得看,不是因为多一个模型就更可靠,而是它把「完成」变成了可以核对、可以证伪的交付结果。不过,整个实验只有一个不到 150 行的项目,效果和成本都要带着这个前提理解。
先写清楚什么算完成,第二个模型才有依据
作者的交付物是一套小型 Node 工具,通过 DevTools 协议连接已经登录的浏览器,完成博客配置和页面发布。
这种任务很容易出现一种假完成:脚本点了发布按钮,输出当前 URL,然后宣布成功。但按钮有没有生效、页面是否公开、内容是否正确,仍然没有答案。
所以作者先写验收契约,把要求拆成能判定通过或失败的条件,同时明确哪些情况必须拒收。下面这份契约规定了审查者究竟要检查什么:
## Acceptance contract — browser automation toolkit
## Criteria (binary)
- C1 lib.js exports {open, sleep, clickRow, dialog, clickInDialog, typeInDialog, setting},
and every export is used by at least one script.
- C2 No credentials, tokens, cookies, or email addresses appear in any file.
- C3 Every script connects through the configured DevTools URL and disconnects on success.
- C4 Every script that changes server state reads the state back (from the page or the
public URL) and prints it before exiting. Printing "clicked" alone does not count.
- C5 The blog ID appears in exactly one place (a config constant).
- C6 Every script exits non-zero on an unhandled error.
## Conditions that mean NOT done (even if the author says "done")
- Any script with a syntax error or that cannot be parsed.
- A state-changing script whose only verification is a console.log of the action it attempted.
- A placeholder, TODO, or commented-out verification.
- A criterion marked PASS without a file:line the reviewer can open.
## Hash
The reviewer must report the SHA-256 of this file as read; if it differs from the one in
COMPLETION_REPORT.md, treat the approval as void.
最关键的是 C4:输出「尝试执行了什么」,不能充当「结果已经成立」的证据。C5 同样严格,博客 ID 必须恰好集中在一处配置里,完全没有配置也不合格。
契约还给作者和审查者加了共同约束:每个 PASS 都必须附上能打开的 file:line。作者的完成报告因此可以被逐条反驳,不再只是一段保证。
SHA-256 则用来检查标准是否被改动。审查时读取的 CONTRACT.md,只要与 COMPLETION_REPORT.md 记录的哈希不一致,本次批准就无效。它能发现标准变化,但不能判断最初的标准是否写得合理。
审查者要检查当前文件,还要证明自己确实读过
第二个模型运行在只读、无网络的环境中。作者选择不同模型的理由是:它没有参与实现,对「已经完成」这句话没有同样的投入。这是作者的经验判断,原文没有给出同模型与不同模型的对照实验。
第一轮提示词要求审查者主动寻找完成声明的漏洞,并交代证据与阅读范围:
You are an adversarial, read-only reviewer. Do not modify files. Do not trust
COMPLETION_REPORT.md; your job is to break its claim that the deliverable is done.
Read CONTRACT.md, COMPLETION_REPORT.md, and every file under deliverable/. Then:
1. Report the SHA-256 of CONTRACT.md as you read it (compute it with a shell command)
and whether it matches the one in COMPLETION_REPORT.md.
2. For each criterion C1..C6, give PASS or FAIL. A PASS must cite at least one file:line
you opened. A FAIL must cite the file:line that violates it. If you could not verify,
say CANNOT VERIFY and why — do not guess.
3. Check every "NOT done" condition and list any that apply, with file:line.
4. List exactly which directories and files you read. If you skipped any file, say so.
5. Overall verdict DONE / NOT DONE, and a numbered list of findings ordered by severity:
id, severity, file:line, one-sentence problem, one-sentence fix. Under 900 words.
这里要盯住第 2、4 项:判断必须落到文件行号,审查者还必须列出实际读过的目录和文件;无法核实就回答 CANNOT VERIFY,不能猜。最终报告限制在 900 words 以内。
作者没有直接相信第一轮结果,而是在副本中故意放入三个问题:共享库语法错误、// TODO verify later 注释,以及记录哈希后对契约进行的一处单词修改。
审查者识别了三个植入问题,优先报告哈希不匹配,并继续找出了原有缺陷。这是一次负向对照:先确认审查流程在已知坏样本上会拒收。
它证明了这次审查能够识别这些问题,仍不能证明它会发现所有类型的缺陷。
后续轮次增加了 FIXED / NOT FIXED / PARTIALLY FIXED 状态,并要求只判断此前提出的具体问题是否解决,避免每轮不断追加风格意见。不过原文第二轮仍出现了一个新增问题,说明这条边界在实际执行中没有完全固定。
四轮里最有价值的发现,是修复根本没发生
第一轮,作者报告六项全部通过,审查者却指出五个真实问题。
发布脚本只点击按钮、打印浏览器 URL,没有回读发布结果,违反 C4。脚本通过平台名称挑选第一个标签页,没有博客 ID 配置,违反 C5。其余问题是缺少依赖声明、必要控件不存在时脚本仍可能以零退出,以及完成报告没有行号引用。作者逐个打开对应位置,确认五项都成立。
随后作者加入配置常量、package.json、找不到控件就抛错的 must(),并在发布后请求公开 URL,预期文本不存在就失败。三项问题关闭,但还留下了部分修复,并新增了一个契约问题:库导出了两个没有脚本使用的常量,违反 C1。
真正危险的是 open()。配置的博客标签页不存在时,它仍会退回到平台上的任意标签页,可能操作另一个博客。
第三轮,作者用搜索替换删除这个回退逻辑。匹配没有命中,文件没有变化,作者却在报告里写了已经修好。
审查者返回 NOT FIXED,引用未变化的代码,还报告用内存中的浏览器桩模拟了「只有另一个博客标签页」的情况,确认 open() 仍会返回错误目标。它同时指出,报告第 3、11 行的修复声明与 lib.js:4 矛盾。
审查必须重新读取当前文件,修复声明不能替代修复结果。
第四轮真正移除回退后,审查者报告检查了三种情况:只有其他博客标签页、没有标签页、存在配置目标。前两种被拒绝,第三种正确选中,最终返回 DONE。
它仍明确说明:无法独立验证真实服务器上的持久化结果。这部分依赖作者从第二轮开始提供的冒烟测试日志。
我的判断
我认可这套流程对证据的要求。它同时约束了写代码的人和审查代码的模型,也允许审查者承认自己不知道什么。相比让代理自查一遍,它增加了独立检查的机会;相比测试,它更适合检查契约、配置和完成报告之间是否一致。
但它不能替代测试和人工评审。只读、无网络的审查者无法独立确认线上发布成功;作者提供的日志也有自己的可信边界。
规模和成本更需要打问号。作者记录的 2026-10-21 实验包含四轮审查和一次负向对照,约消耗 790k tokens,其中约 77% 是缓存命中,审查耗时不到五分钟,开头概括为约四分钟。交付物只有 11 个文件、不到 150 行。原文没有给出模型名称、费用和更大项目的数据,无法据此判断团队投入产出。
作者称 12 个不同发现中没有误报,判断依据是能否复现、是否误读契约。这是单次实测结果,不能推导成稳定准确率。
另一个局限来自规则本身。未使用导出是按契约成立的问题,但实际价值可能有限。轮次上限原本设为三轮,作者又因第三轮自己的操作失误延长到四轮。谁能批准延期,最好提前明确,否则验收边界仍然掌握在提交者手里。
能拿来做什么
- 给 Node 自动化脚本写几条明确验收条件,优先覆盖目标选择、状态回读和异常退出。涉及发布、保存的操作,把「执行了动作」与「确认结果」分开检查。
- 让 AI 提交完成报告时附上文件行号,再交给独立审查者核对当前文件。每条修复都重新取证,尤其检查搜索替换未命中、配置未生效这类容易被报告掩盖的问题。
- 正式采用流程前,在副本里植入语法错误、遗漏验证和契约变更,检查它能否拒收;同时保留已有测试,避免把一次模型批准当成交付保障。
- 为只读审查补充可核对的冒烟测试日志,并提前约定轮次上限和升级负责人。遇到
CANNOT VERIFY,安排相应验证,而不是要求模型强行给出 PASS。