找到评审漏掉的东西的那次评审:面向大改动的可选深度评审
一个评审者看一遍 diff 会放行的东西,几个对抗式评审者加反驳者不会放行。这篇文章讲一次多代理评审在常规评审已经通过的 176 次提交上找到了什么、它花多少钱、为什么它是可选的,以及哪种循环形状能防止它吃光你的预算。
上周,一叠 21 个 pull request、大约 176 次提交,走完了每个 Coograph 项目都在跑的常规评审:一个只读评审者看一遍 diff,每条发现都有一段来自新读取文件的引用做支撑。通过了。然后它走了另一种评审,那一次找到了一把被释放了两次的锁、一个去掉修复也能通过的测试,以及一个可能覆盖客户文件的队列任务。这三个,之前都被批准了。
这篇文章讲的就是第二种评审。它随 Coograph 1.6.0 发布,叫 /coograph-ultra-review,刻意做成可选,并附带一条什么时候该停的规则——因为用它的第一周教会了我们没有这条规则会发生什么。
一个评审者漏掉什么
常规评审代理读 diff,读文件,过一遍清单,出报告。它擅长的正是一个细心的人擅长的:约定、明显的逻辑错误、一个没处理的 null。它不擅长的也正是细心的人不擅长的:一处改动和一个不在屏幕上的进程之间的相互作用。
上面三条发现全是这一类。被释放两次的锁,是一个 goroutine 先显式释放了一个占用,然后在 defer 里又释放了一次——只要中间没有第二个请求拿走这个占用,就没事。去掉修复也能通过的测试,是它的准备步骤跑在被测代码重新读取那一行之前,于是它本该钉住的那件事从未发生。队列任务则是在一次二十分钟的等待之前检查过一次自己还拥有这台服务器,之后再也没检查;客户在等待期间装了别的东西,就被覆盖了。
这些在 diff 里都看不到。它们只在有人问一个很窄的问题——重投递会对这里做什么?——然后另一个人试图证明答案是错的时候,才会现身。
深度评审换了什么做法
/coograph-ultra-review 就是这个,以 Claude Code 里一个保存好的工作流的形式。
一个侦察代理把 pull request 或提交列表解析成文件和改动的行,并向代码图查询被改内容的调用方和导入方。然后每个维度一个评审者,在被评审的提交上读代码,脑子里只装一个问题:状态与并发;数据丢失路径;契约与兼容性;怎么都能通过的测试。每人返回发现,附原文引用、文件和行号,以及一个具体的失败场景——一边是输入和状态,另一边是错误结果。没有引用,就不算发现。
然后是让它不一样的那部分。每条 Critical 和 Warning 都交给反驳者——一群独立代理,唯一的任务是从一次新的读取出发证明这条发现是错的,每人一个不同的视角:引用的那行存在吗、真的做了声明里说的事吗;场景真的会发生,还是有守卫拦住了它;有没有现成测试与这个解读矛盾。只有没人能反驳,发现才算成立。被反驳的发现仍然留在报告里,附反驳者的理由,因为反驳者有时也会错。
一份报告,每个提交一个结论,页脚写着它花了多少。
成本是最该放在前面说的诚实数字:一次完整运行是 120 万到 160 万 token,大约二十分钟。一个 33 个提交的 pull request 花了 310 万、39 分钟。所以代理被要求的是提议它——当改动超过十五个文件、当它要交给人审批、当它碰到队列任务、锁、状态流转、迁移或两个服务之间的契约时——而绝不自己运行。
吃掉一天的循环
先说我们最初做错了什么,你就不必再错一次。
用这种工具最顺手的方式是:跑一次,修它找到的,再对修复跑一次,重复到干净为止。我们就是这么做的。一个修复跑了四次完整运行,大约 500 万 token,每一轮都找到真问题。第一轮找到了那个没有真正测到修复的测试。第二轮找到七个问题,全在第一轮的修复新加的代码边缘。第三轮在第二轮的修复里找到一个 Critical。它收敛了,但很慢,而且第二、三轮找到的大部分东西都在原始改动根本没碰的代码里。
要盯的就是这个模式:一个对每次修复都全幅度评审的评审,会永远找到上一次修复的边缘,而且每一条都真实到值得修。工具没错。错的是循环。
所以发布版有三种模式和一条停止规则。
| 模式 | 什么时候 | 成本 |
|---|---|---|
plan | 在写会碰生命周期或并发的代码之前 | 和一次完整运行差不多的价钱,但它在代码存在之前运行 |
full | 一批改动的第一次评审,以及所有修复到位后对整个 PR 的那一次 | 120 万–160 万 token |
delta | 一批修复,对照上次运行的发现检查:修好了吗,改动的行又弄坏了什么 | 约 50 万 |
落在改动行之外的发现作为后续项返回,不是阻塞项。停止规则:修 diff 里的,记录其余的,最多两轮 delta,对整个 PR 跑一次 full,然后交给人。同样几行上出现第三轮机器发现,不是跑第四轮的理由;它是设计需要被看一眼的信号,而 plan 模式是看这一眼最便宜的地方。
我们在已经建好的设计上测了 plan 模式,用的是第三轮之前它的原样记录。它仅凭方案文本、在任何代码之前,就找到了第三轮的那个 Critical,外加两件代码评审漏掉的事。它也花了一次完整运行的钱。方案评审的价值在时机,不在价格。
为什么它是可选的
因为大多数改动不需要它,而一个每次要花 100 万 token 的工具必须挣回每一次。常规评审仍是默认。当改动符合条件时,代理会提一句深度评审——一句话,带着成本——然后把决定留给你。衡量评审是否发生过的 retro 把它算作一次评审;其他什么都不变。
如果你跑它,就按停止规则说的方式跑,然后把 pull request 交给人。这仍然是最后一步,而用了它一周之后,这一步我们信得更多而不是更少:交到人手上的,是一份生命周期问题已经被书面问过、答过的 diff。
1.6.0 里还有
一位用户寄来的两个修复,如果你在跑代码图,都值得知道。
增量图更新一直在删除早先运行已解析出的每一条调用边:链接器把节点 id 当成名字重新解析,找不到匹配,就把它们丢了。如果你的图在一次提交之后丢了 calls 边,就是这个原因。拉取,用 --build 重建一次,之后的更新会保留它们。
另外,uv run 会按机器的默认 Python 解析。在默认版本低于 3.10 的机器上,MCP 服务器启动时就死于 your requirements are unsatisfiable,宿主报成 CONNECTION_CLOSED,真正的原因躺在没人读的日志里。现在 Coograph 写出的每个 uv run 都带 -p 3.12;uv 会按需下载解释器。同步脚本也不再整个覆盖项目的 .mcp.json;它只合并 code-graph 条目,文件其余部分不动。
试试
Coograph 1.6.0 在 Claude 插件市场和初始化器里都有。在一个 Coograph 项目里,/coograph-ultra-review(插件里是 /coograph:coograph-ultra-review)加上一个 PR 编号或一个提交列表;skill 会在任何东西运行之前先提出模型方案和成本。工作流文件是 .github/skills/coograph-ultra-review/ 里的普通 JavaScript,维度随你改。参数、模式和停止规则在文档里。
削减你的 AI 编程账单 67–78%。Coograph 采用 MIT 许可、永久免费。Pro 提供定制服务。