@tvykruta:我在 Microsoft 和 Google 从事 C 和 C++ 工作多年,审查过数千次代码变更。我亲眼见证过优秀工程师如何……

X AI KOLs Timeline 新闻

摘要

一位拥有 Microsoft 和 Google C/C++ 经验的工程师,在真实 PR 上开展了 12 次 AI 代码审查实验,发现:不带任何共享上下文的全新审查 agent 能发现多得多的缺陷;要发现逻辑 bug,执行代码(而不只是阅读)是必不可少的;而模型的选择对性价比的影响,远大于对绝对能力的影响。

我在 Microsoft 和 Google 从事 C 和 C++ 工作多年,审查过数千次代码变更。我亲眼见过优秀的工程师发布糟糕的代码——因为审查者只是扫了一眼、点了通过,就去吃午饭了。 如今 AI 写代码的速度是人的 200 倍。谁来审查它? 如果你没有在每次变更上线前强制执行三名工程师的严格代码审查,那你根本没资格做软件。 开玩笑的。这话昨天还是对的。 今天我要回答的问题是:AI 能不能审查 AI 生成的代码?然后我会告诉你,如何用每次不到一美元的成本,得到一份不漏任何细节、达到资深工程师水准的出色审查。 把它收藏起来。完整的 REVIEW.md 放在第一条回复里,直接复制粘贴即可。 结果让我大吃一惊 1/ 我让写下这段代码的 AI 会话审查自己的作品。它在 23 个问题中只发现了 3 个,并建议直接合并。然后我在同一个模型上启动了一个全新的 agent,它完全不记得自己写过这段代码,我只把 diff 给它。它发现了 23 个中的 17 个。 2/ 只阅读代码的审查者,在那个功能 PR 的 8 个逻辑 bug 中一个都没发现。每一个逻辑 bug,都是由真正执行了代码的审查者发现的。 3/ 只往审查者的指令里加了一条规则,就把它的表现从 8 个逻辑 bug 发现 4 个提升到发现 7 个,还外加一个此前无人发现的 bug。同一个模型,成本从 $0.72 涨到 $0.74,速度却快了 27%。 4/ 最贵的模型并不等于最划算。Opus 在两个 PR 上的表现都追平或超过 Fable,价格却只有大约三分之一:每次审查不到 $1,而 Fable 要 $2 以上。 5/ 第二天,一个全新的审查 agent 在一个拥有 272 个通过测试、CI 全绿的 PR 中,发现了 4 个阻塞性 bug。 6/ "必须进行代码审查"被写进了 agent 的指令,而这个 agent 在第一个 PR 上就跳过了它。 这是一次惊人的突破。再也不会有 bug 蒙着绿色对勾溜过去了。 以下是我做到这一切的过程。 实验设置 • 来自一个生产代码库的 2 个真实 PR。一个是涉及 75 个文件的重构,另一个是涉及 23 个文件的功能开发。 • 共 12 次审查:4 次由已经了解这段工作的 AI 会话执行,8 次由全新的 agent 执行——它们只拿到 diff、代码仓库和一个规则文件。 • 3 个模型:Fable 5.1、Opus 5.5、Sonnet 5.5。 • 我自己在 PR 上以及当前最新的提交(head)上重新复现了每一个上报的缺陷,以便把仍在生效的 bug 和已修复的 bug 区分开。 • 成本根据各审查者的 token 数量按 API 牌价估算,没有使用缓存折扣,所以数字偏高。 然后我把这套东西用到一个真实的功能开发上。案例研究见下文。 1. 上下文才是盲区,而不是「谁写的代码」。 对于那个重构 PR: • 写代码的那个会话审查自己的作品(Fable):在 23 个问题中只发现 3 个 • 一个全新的 Fable agent,对这段工作毫无记忆:发现 23 个中的 17 个 • 一个全新的 Opus agent:发现 23 个中的 18 个 对于那个功能 PR,它由另一个会话在三天前写成: • 一个虽然没写过它、但已经在这个仓库里工作了一天的 Fable 会话:8 个逻辑 bug 一个都没发现 • 全新的 Fable、Opus 和 Sonnet agent:各自发现了 8 个中的 2 到 4 个 那个功能 PR 的审查者并没有写这段代码,却依然什么都没发现。一个与写作者共享同一套心智模型的审查者,会继承写作者的盲区——无论代码是谁写的。 2. 阅读只能发现风格问题。执行代码才能发现 bug。 每一个逻辑 bug 都来自这样一位审查者:它搭了一个临时数据库,用自己挑选的输入调用了变更后的代码,然后观察返回结果。那个已经了解上下文的会话什么都没运行。它只是读了代码,一个逻辑 bug 也没发现。 功能 PR 上那 8 个逻辑 bug,没有任何一个能仅凭 diff 文本看出来。但只要你运行代码,每一个都显而易见。 3. 规则文件对那位已有上下文的审查者几乎毫无作用。 给它一份书面检查清单,每个 PR 只多找到了一处问题。发现的逻辑 bug:依然是零。而用同一份文件,全新的 Fable 和 Opus agent 每个 PR 能找到 13 到 18 个问题。检查清单救不了一位内心已经认定代码正确的审查者。 4. 问题出在审查者的工作顺序上。 表现最好的全新审查者仍然漏掉了一半的逻辑 bug,所以我把它完整的工具调用轨迹拉出来,一步一步地过了一遍。 它确实打开过它漏掉的那三个 bug 背后的每一个函数,也理解了每一个。但它是在读完实现之后才写下测试输入的,于是每一个输入都遵循了实现自己的假设。它的边界情况,只是代码已经处理过的那些情况的变体。 代码实际上是自己出了考题,而它通过了考试。 修复只需要一条规则:"先写下你的测试输入,然后再去读实现。然后运行它们。"同一个模型,同一个 PR:召回率从 8 个中的 4 个提升到 7 个,还多发现了一个没人找到的 bug,代价只是多了两分钱。 5. 模型很重要,但没有流程重要。 Opus 5.5:每次审查 $0.72 到 $0.93,耗时 8 到 11 分钟。它在两个 PR 上发现的问题都最多:在彻底程度和成本之间取得了最佳平衡。 Fable 5.1:$2.11 到 $2.73,大约是 Opus 的 3 倍。每个 PR 都发现了一个其他人没抓到的 bug,但在测试全绿时跳过了与基础分支的比较。 Sonnet 5.5:$0.14 到 $0.21。单个发现的成本最低,但在完全相同的输入上,它第一次只找到 8 个中的 2 个,第二次找到 4 个。这种运行之间的波动,才是真正成本。 两个最强的审查者,在 20 个发现中只有 10 个是重合的。 一个令人意外的案例研究:一个真实功能,端到端审查 一个 agent 构建了一个真实的功能:37 个文件,5000 多行代码,272 个测试全部通过,CI 全绿。它没做任何审查就直接推送了。 我让它补上了审查。 11 个重大 bug。一个全新的 Opus 审查者发现了 11 个阻塞性 bug,外加数十个较小的问题。而所有这些,全都躲在绿色对勾后面。 8 次审查,6 轮修复。每个循环都是:审查 → 修复 → 再审查: • 功能本体:4 个阻塞项 → 修复 → 通过,但那个修复破坏了一个过滤器 → 修复 → 通过。共 3 次审查。 • 那个强制审查的工具:4 个阻塞项 → 修复 → 又冒出 2 个 → 修复 → 又 1 个 → 修复 → 通过 → 一次小修复 → 通过。共 5 次审查。 全程不到 40 分钟,每次审查成本不到一美元。 如果没有审查就上线,会发生什么: • 新功能的每一个页面都会在 2 月 29 日崩溃。 • 用户会看到被标上他们并不属于的高级类别的记录。 • 只要用户更改筛选条件,某个列表就会显示过期数据。 • 一个精心构造的链接可以往用户发出的邮件里塞入隐藏的收件人。 • 审查关卡本应强制执行审查,但它检查的是错误的仓库,并放行了真实的推送。 • 更糟糕的是,安装它会静默关闭已经运行在每一次提交上的每一项安全检查。 • 一个数据库文件会被提交进代码仓库。 这个案例教会了我: 1/ 272 个通过的测试,对这些 bug 毫无说明。其中任何一个都没有对应的测试。 2/ 第一次通过并不意味着结束。一个 bug 的修复制造了另一个 bug,只有第二次审查才发现它。 3/ 那个强制审查的工具经过 3 轮修改才正常工作。要审查的是关卡本身,而不只是代码。 AI 代码审查最佳实践 1/ 始终从一个全新的、对写代码毫无记忆的 agent 开始。只给它 diff、代码仓库和 REVIEW.md。在同一个模型上,这一步把召回率从 23 个中的 3 个提升到 17 个。 2/ 让它执行代码,而不只是阅读代码。阅读发现的逻辑 bug 为零;每一个逻辑 bug 都来自执行代码。 3/ 让它在读实现之前先写下测试输入。这一条规则,多花两分钱就把召回率从 8 个中的 4 个提升到 7 个。 4/ 每次都要把测试结果与基础分支做对比,即便一切全绿。 5/ 每一个修复提交都要重新审查。修复会制造新的 bug。 6/ 默认使用 Opus。再用另一个模型的第二位审查者来检查业务规则、阈值和关卡。两位最强审查者的发现只有半数重合。 7/ 用关卡来强制执行审查,而不是用提示词。使用 git 的 pre-push 钩子:该提交没有已批准的审查,就不允许推送。对规则本身的修改同样要接受审查。 8/ 让它逐条点名报告每个设计原则:关注点分离、意图式编程、封装、高内聚、低耦合。一条原则没被提及,就说明审查不完整。 9/ 检查的是诚实度,而不只是风格。不知道就保持不知道。UI 这
查看原文
查看缓存全文

缓存时间: 2026/10/03 19:03

我在 Microsoft 和 Google 用 C 和 C++ 工作了多年,审阅过成千上万次代码变更。我见过优秀的工程师因为评审者草草扫过代码、点批准然后去吃午饭,而发布糟糕的代码。

现在 AI 写代码的速度是以前的 200 倍。那谁来审这些代码?

如果你在每一次代码变更发布之前没有严格执行三名工程师的代码评审,你根本没有资格构建软件。

开玩笑的。这个说法昨天还是对的。

今天我要回答的问题是:AI 能不能审查 AI 生成的代码?然后我会告诉你如何在每次审查不到一美元的情况下,获得一次出色的、资深水平的、不遗漏细节的审查。

收藏这篇。完整的 REVIEW.md 在下面第一条评论中,直接可以粘贴使用。

结果让我大吃一惊

1/ 我让写这段代码的 AI 会话审查它自己的工作。它只发现了 23 个问题中的 3 个,并且建议合并。然后我在同一个模型上启动了一个全新的智能体,它对写代码这件事没有任何记忆,只给了它 diff。它发现了 23 个中的 17 个。

2/ 只读代码的评审者在功能 PR 中发现了 8 个逻辑错误中的零个。每一个逻辑错误都是由运行了代码的评审者发现的。

3/ 给评审者的指令中添加一条规则,使其从发现 8 个逻辑错误中的 4 个提升到发现 7 个,外加一个之前没人发现的。同一个模型,成本从 $0.72 变成 $0.74,速度快了 27%。

4/ 最贵的模型并不是最划算的选择。Opus 在两个 PR 上的表现与 Fable 持平或更好,而价格只有大约三分之一:每次审查不到 $1 vs. 超过 $2。

5/ 第二天,一个全新的评审智能体在一个有 272 个测试通过、CI 全绿的 PR 中发现了 4 个阻塞性 bug。

6/ “始终获取代码评审“这条指令被写入了智能体的配置中,但这个智能体在第一个 PR 上就跳过了它。

这是一次令人难以置信的解锁。再也不会有 bug 趁着绿色对勾溜进代码库了。

以下是我如何得出这些结论的。

实验设置

• 2 个来自生产代码库的真实 PR。一个是 75 个文件的重构,另一个是 23 个文件的功能开发。 • 共 12 次评审:4 次由已经拥有该工作上下文的 AI 会话完成,8 次由全新的智能体完成(它们只拥有 diff、仓库和一个规则文件)。 • 3 个模型:Fable 5.1、Opus 5.5、Sonnet 5.5。 • 我亲自重新运行了每一个报告的缺陷,分别在 PR 上和当前主干上,以区分仍然存在的 bug 和已被修复的 bug。 • 成本是根据每个评审者的 token 数量按 API 列表价格估算的,没有缓存折扣,所以成本偏高。

然后我把它用到了一个真实的功能开发上。案例研究如下。

  1. 盲点在于上下文,而不在于作者身份。

在重构 PR 上: • 写代码的会话审查自己的工作(Fable):只发现了 23 个问题中的 3 个 • 一个全新的 Fable 智能体,对该工作没有任何记忆:发现了 23 个中的 17 个 • 一个全新的 Opus 智能体:发现了 23 个中的 18 个

在功能 PR 上,由三天前的另一个会话编写: • 一个没有编写代码但在该仓库中工作了一天的 Fable 会话:发现了 8 个逻辑错误中的 0 个 • 全新的 Fable、Opus 和 Sonnet 智能体:各自发现了 8 个中的 2 到 4 个

功能 PR 的评审者并没有写代码,但仍然什么也没找到。与写代码者共享心智模型的评审者会继承写代码者的盲点,无论代码是谁写的。

  1. 读代码能发现风格问题。运行代码才能发现 bug。

每一个逻辑错误都来自一个评审者:它搭建了一个临时数据库,用自己选择的输入调用被修改的代码,并观察返回了什么。那个已经有上下文的会话什么也没运行。它只是读了代码,没有发现任何逻辑错误。

功能 PR 上的 8 个逻辑错误没有一个能仅从 diff 文本中看出。一旦你运行代码,每一个都是显而易见的。

  1. 对于已有上下文的评审者,规则文件几乎没用。

给它一份书面的检查清单,每个 PR 只增加了一个发现。发现的逻辑错误:仍然是零。用同一份文件,全新的 Fable 和 Opus 智能体在每个 PR 上发现了 13 到 18 个问题。检查清单无法修复一个已经相信代码是正确的评审者。

  1. 问题出在评审者的工作顺序上。

表现最好的全新评审者仍然遗漏了半数逻辑错误,所以我拉出了它完整的工具调用日志,一步一步地进行了分析。

它打开过它所遗漏的全部三个错误背后的函数,并且理解了每一个函数。但它是在阅读了实现代码之后才编写测试输入的,所以每个输入都遵循了实现代码自身的假设。它的边界情况只是代码已经处理过的情况的变体。

代码实际上已经自己写了考卷,然后自己通过了考试。

修复方法只有一条规则:“在阅读实现代码之前写下你的测试输入。然后运行它们。” 同一个模型,同一个 PR:召回率从 8 个中的 4 个提升到 8 个中的 7 个,外加一个之前没人发现的 bug,多花了两美分。

  1. 模型很重要,但不如流程重要。

Opus 5.5:每次审查 $0.72 到 $0.93,8 到 11 分钟。它在两个 PR 上都发现了最多的问题:在全面性和成本之间达到了最佳平衡。

Fable 5.1:$2.11 到 $2.73,大约是 Opus 的 3 倍。在每个 PR 上都发现了一个其他人没抓到的 bug,但在测试全绿时跳过了与基础分支的比较。

Sonnet 5.5:$0.14 到 $0.21。每个发现最便宜,但它在相同输入上先发现了 8 个中的 2 个,然后发现了 4 个。这种运行间的波动才是真正的成本。

两个最强的评审者在 20 个发现中只有 10 个是重合的。

一个令人惊讶的案例研究:一个真实功能的端到端评审

一个智能体构建了一个真实功能:37 个文件,5000 多行代码,272 个测试通过,CI 全绿。它没有经过评审就推送了代码。

我让它进行了评审。

11 个重大 bug。一个全新的 Opus 评审者发现了 11 个阻塞性 bug,外加几十个小问题。每一个都隐藏在绿色对勾后面。

8 次评审,6 轮修复。每个循环都是:评审 → 修复 → 再评审: • 功能本身:4 个阻塞问题 → 修复 → 通过,但修复破坏了一个过滤器 → 修复 → 通过。这是 3 次评审。 • 强制评审的工具:4 个阻塞问题 → 修复 → 又出现 2 个 → 修复 → 又出现 1 个 → 修复 → 通过 → 小修复 → 通过。这是 5 次评审。

全部在 40 分钟内完成,每次审查不到一美元。

如果没有评审,以下内容将会发布: • 新功能的每个页面都会在 2 月 29 日崩溃。 • 用户会看到被标记为不属于他们的高级类别的记录。 • 当用户更改筛选条件时,列表会显示过期数据。 • 一个精心构造的链接可以将隐藏的收件人插入用户发送的邮件中。 • 评审门禁并没有真正强制执行评审:它检查了错误的仓库,并让真正的推送通过了。 • 更糟糕的是,安装它会默默地关闭每个提交上已经运行的所有安全检查。 • 一个数据库文件会被提交到仓库中。

这个案例教会了我:

1/ 272 个通过的测试对这些 bug 什么都没说明。其中没有任何一个有对应的测试。

2/ 第一次批准并不是终点。一个 bug 的修复创建了另一个 bug,只有第二次评审才发现了它。

3/ 强制执行评审的工具需要 3 轮修改才能正常工作。要审查门禁本身,而不仅仅是代码。

AI 代码评审的最佳实践

1/ 始终启动一个全新的智能体,它对写代码这件事没有任何记忆。只给它 diff、仓库和 REVIEW.md。在同一个模型上,这使召回率从 23 个中的 3 个提升到 23 个中的 17 个。

2/ 让它运行代码,而不仅仅是阅读代码。阅读发现了零个逻辑错误;每个逻辑错误都是通过运行代码发现的。

3/ 让它在阅读实现代码之前写下测试输入。这条规则用两美分的额外成本将召回率从 8 个中的 4 个提升到 8 个中的 7 个。

4/ 每次都把测试结果与基础分支进行比较,即使一切全绿。

5/ 重新评审每一个修复提交。修复会创建 bug。

6/ 默认使用 Opus。对于业务规则、阈值和门禁,添加一个使用不同模型的第二个评审者。两个最强的评审者只有一半的发现是重合的。

7/ 用门禁来强制执行评审,而不是用提示词。使用 git pre-push 钩子:没有通过该确切提交的已批准评审,就不能推送。对规则本身的修改同样需要被评审。

8/ 让它按名称报告每一条设计原则:关注点分离、意图式编程、封装性、高内聚、低耦合。未提及的原则意味着这次评审是不完整的。

9/ 检查诚实性,而不仅仅是代码风格。未知就是未知。UI 绝不能凭空捏造数据。

10/ 要求一个明确的结论(APPROVE 或 CHANGES),并以“到目前为止发现了 K 个中的 N 个“的形式报告发现。bug 池永远不可能完整。

完整的 REVIEW.md 在下面的评论中。将其粘贴到你的仓库中,并将你的评审智能体指向它。

REVIEW.md:AI 代码评审规则。将其粘贴到你的仓库中。

  1. 谁来评审 • 评审者是一个没有编写过代码的全新智能体。它只会得到:diff、仓库、这个文件。 • 编写者的自我评审只是检查清单式的过一遍,不能算真正的评审。 • 默认模型:Opus。高风险变更(业务规则、阈值、评分、定价、校验门禁)需要在不同模型上使用第二个评审者。 • 每个修复提交都需要自己的评审。修复会创建 bug。

  2. 如何评审:执行,而不仅仅是阅读 • 在阅读实现代码之前,写下你将要测试的输入。在阅读代码之后写的输入会呈现代码的形状,从而错过代码所错过的东西。 • 在临时环境中使用这些输入运行被修改的代码。尽可能在真实数据的副本上进行只读探测。 • 每次都要将测试结果与基础提交进行比较,包括当头提交全绿时。

  3. 需要探测什么 • 输入的每一种替代形式,包括将其中两种组合起来的形式。 • 每一个单位,以及完全没有单位的输入。 • 一个输入中的两个或更多实体,按每一种顺序。 • 变更后的枚举、状态或常量的每一个消费者。 • 每一个原因或错误代码,追溯到用户实际看到的消息。 • 存储、计算或渲染同一事实的每一个位置(服务端和客户端)。它们是否仍然一致? • 边界情况:空、一个、正好在阈值上、超过上限。 • 日期:闰日、年界、时区。

  4. 诚实性 • 未知就是未知。UI 绝不能凭空编造、猜测或默默解析它没有的数据。 • 每一个标签都必须对它所显示的每一条记录都是真实的。

  5. 设计原则(对每一条都要报告) 对每一条:OK,或违反并附上 file:line。 • 关注点分离 • 意图式编程 • 封装性 • 高内聚 • 低耦合 阻塞问题:一个领域有两个所有者、重复的逻辑、模板或 UI 代码中的业务逻辑。 未提及的原则 = 一次不完整的评审。

  6. 门禁和工具 • 用它应该拒绝的输入攻击每一个新的检查、钩子或门禁,包括那些措辞不同以规避它的输入。 • 优先使用平台原语(例如 git pre-push),而不是巧妙地解析命令。 • 确认每一个豁免仍然指向存在的事物,并且没有豁免可以用来偷偷放行代码。 • 安装钩子绝不能禁用已有的钩子。 • 对规则本身的修改永远不能免于评审。

  7. 文档和重构 • 在任何查找替换或移动操作之后,重新阅读每一个编辑过的句子并问自己:这句话还是真的吗? • 有日期的文档保持其原有的路径和事实。

  8. 报告 • 发现分为 BLOCKING(阻塞)或 NIT(小问题)。阻塞问题必须修复;小问题是可选的。 • 每个发现都要附带复现步骤:输入、预期结果、实际结果。 • 报告“到目前为止发现了 K 个中的 N 个“。bug 池永远不可能完整。 • 通过的测试不是证据。要说明你执行了什么。 • 结尾必须恰好一行:VERDICT: APPROVE 或 VERDICT: CHANGES

这里有一个彩蛋。规则文件对我昨天写的那些 C++ 模式实施了更严格的合规要求:

附加材料:为了更强的合规性,请实施这种智能体代码评审:

是的。起初我发现不同的模型发现不同的缺陷这件事非常令人不安

更令人惊讶的是,Sonnet 对同一个 diff 运行多次会发现不同的缺陷。

一旦你检查了智能体的日志——原因就变得非常清楚,而且不再令人不安了。

和人类一样,LLM 必须决定深入研究哪些函数和文件。这是一个简单但极其庞大、复杂的决策树/搜索空间。这些决定是合理的、可以辩护的,即使结果看起来完全不相关。

我们构建的不是在宇宙飞船上运行的软件,所以我们不需要 100% 的覆盖率。

复杂的 bug 几乎总是架构糟糕的代码库导致的。

内聚性差、关注点分离不良。bug 不是根本原因,而是症状。

架构正确的系统不可能藏着复杂的 bug。

写好设计文档,并在开始实现之前评审它们。

我们的“肉计算机“速度太慢、IQ 太低,而且每天需要睡 8.0 小时。不可接受的瓶颈。

没有阻塞性问题,只是复盘。

我对我的 AI 代码 100% 进行 AI 代码评审,到目前为止 100% 的评审都发现了真实的缺陷。

永远不晚开始一次重大的重构。我在大规模重置方面取得了很大的成功。

Scott Bain——我听过最传奇的关于核设施重构的故事来源。

他的课程是我上过的最好的课程。

相似文章

Agentic Code Review(15分钟阅读)

TLDR AI

分析AI编码代理如何将瓶颈从编写代码转移到审查代码,数据显示代码变更量增加861%,缺陷率上升,使得代码审查成为软件工程中最具杠杆效应的技能。