代码评审
前面的评审与验证章把验证分成三层:确定性检查、AI 评审、人工验收。本章把其中最难、最容易翻车的一层(AI 评审)拆成可以照着执行的步骤,并补上它两侧的衔接:哪些判断该下沉成确定性检查,哪些必须留给人。
Agent 写代码的速度远超人读代码的速度。有团队的主干单日落地改动在短期内成倍增长,评审者如果仍按原来的方式逐行阅读,很快就会不堪重负,最后只剩扫一眼便批准。核心变化是把问题从”有没有人批准过”换成”有什么证据证明这次改动行为正确”:能自动判断的交给机器,有限的人力留给只有人能做的判断。
本章不限定某种语言或团队必须使用哪套工具。每个主题都按”原则 → 怎么做 → 什么时候不这么做”展开;工具只按职能举例,换语言或团队时,替换成对应的同类工具即可。
一次评审的主线是:评审前把能机器化的检查沉下去(14.1),备齐送审材料(14.3),用独立评审或双模型互审找问题(14.2、14.4),对意见分级处置(14.6)并控制评审收敛(14.5),再按改动的风险决定要不要人来把关(14.7、14.8)。后面各节就按这条线展开。
15.1 评审前把能机器化的沉下去
评审方的注意力有限。凡是机器能精确、重复判断的事项,就不该占用它。进入评审前,先完成并通过确定性检查:格式、lint、类型、与改动相关的测试、密钥与安全扫描。这些检查给出可重复的结论,不消耗人的判断力。
| 职能 | 这类工具举例 | 接入点 |
|---|---|---|
| 格式与快速 lint | 各语言的格式化器与 linter(如 JS/TS 的 Biome、Oxlint,Python 的 ruff) | 提交钩子 + CI |
| 类型与构建 | 项目自带的类型检查与构建命令 | 提交钩子 + CI |
| 密钥扫描 | gitleaks、trufflehog 这类跨语言工具 | 提交钩子 + CI 双挂 |
| 多语言规则 / 自定义模式 | ast-grep、Semgrep 这类结构匹配工具 | CI |
评审里反复出现的意见,不应一直靠人提醒。同一条意见第三次出现,可以启动机器化评估;能够稳定判断、误报和成本可控的,再写成 lint 或 pattern。需要理解意图或取舍的意见仍由评审处理。
把这些检查挂到提交钩子上,能在代码离开本地前就拦住一部分问题。钩子框架的选择跟着团队的语言构成走,没有唯一答案:
- 纯 JS/TS 项目可以用绑定 Node 的 husky;但多语言团队要避免它,往非 JS 项目里塞 package.json 会污染工程(冒出 node_modules、CI 被绊)。
- 语言无关的框架更适合多语言团队,常见的是两类:lefthook 是单二进制、无运行时依赖、支持并行;pre-commit 自带各语言的隔离环境和现成的 hook 清单,列出需要的检查就能自动拉取,但本身依赖 Python。
- 两者之间按三点取舍:团队是否已经依赖 Python(是则 pre-commit 不增加负担);钩子的瓶颈是多个独立的慢任务(lefthook 并行才有收益)还是单个慢工具主导(并行帮不上忙);要不要 pre-commit 那样现成的 hook 生态省去自己编写。
- 需求很简单时,Git 原生的
core.hooksPath加一个提交进仓库的脚本目录就够了,零依赖,代价是没有现成生态、全靠自己维护。
提交钩子能减少遗漏,但不能充当最终边界:它要在每个克隆仓库里单独安装,也能被绕过。合并前真正必须通过的检查还要在 CI 里重跑一遍(上一章讲过这条边界)。
什么时候不这么做:一次性脚本、原型和注定要丢弃的探路代码,不必先补齐全套门禁。门禁强度取决于改动是否会长期保留、是否会被其他代码依赖。
15.2 独立评审与同源盲区
让写代码的同一个 Agent 在同一个会话里回头评审自己,几乎没有价值。有研究发现,多数被测的开源模型改不动自己刚犯的错,却能改对被标成”别人写的”同一个错误,因为模型会为自己的判断背书。这就是同源盲区:写、测、审出自同一来源时,盲区会在每一步重复,评审也就失去了独立性。
独立评审至少满足两个硬条件:
- 新上下文:评审方不继承实现过程。即使由实现方自审,也要重新开启会话,以陌生视角检查自己的产出。
- 只读:评审方只读代码和材料,不直接改工作区。改动由作者根据意见去做,评审和修复分开。
在这两个硬条件之上,有两个增强手段:
- 独立身份或凭据:给评审方独立的账号或令牌。Agent 从提交历史推断出”这是我写的 PR”时会有所偏袒,独立身份能减少这种因作者身份产生的偏袒;但它只解决偏袒,不替代认知独立。认知独立仍要靠新上下文和只读这两个硬条件。
- 换模型:更换厂商或模型可以带来不同视角。但换模型不是独立性的必要条件,真正起作用的是新上下文和只读权限;即使换了模型,如果仍带入实现会话的上下文,也会继承原来的盲区。
什么时候不这么做:改动极小且可以一键回退时,一次新上下文中的只读评审已经足够,不必动用独立身份或多个模型。评审强度要与风险匹配,不必每次都用最高强度。
15.3 送审材料
评审方需要足以判断这次改动的材料,而不是把整个仓库翻一遍:意图与验收条件、当前 diff 与受影响的调用链、项目规则与安全基线、测试基线与运行结果。上一章已经列过这份清单,这里只补一条 AI 评审特有的纪律:评审顺序要固定。
评审顺序:先看规格和验收条件(解决的是不是正确的问题),再看正确性、回归与安全风险,最后才看可维护性。顺序反了,评审方容易先陷进命名和风格,放过最该拦住的方向性问题。
15.4 双模型互审的一次完整循环
这是独立评审的一种落地形态,适用于跨模块、边界多或回归影响大的改动。两个评审方各自完成评审,再把两边的发现合成一份,最后完成修复。
第 1 步 · 两方并行独立评审。 对未合并的改动,让两个评审方各出一份问题清单。例如,重新开启一个会话(清除作者上下文)作为一方,再用不同模型或不同身份开启另一个会话作为另一方。双方独立评审,互不查看对方的结果。两方需要统一的是评审基线、严重程度定义和输出格式,这样两份清单才能对等合并;检查重点不必完全相同,各有侧重反而能找到互补的问题。独立性靠新上下文和不同模型或身份来保证,不靠谁审得更狠。给每一方的指令要同时管住三件事:审什么(检查项)、怎么判断(忽略这段代码是谁写的,只按代码和规格本身判断,避免它认出是自己的产出而偏袒)、输出什么格式。一份可抄的指令:
忽略这段代码是谁写的,只按代码和规格本身判断。你是只认规矩的资深评审者。
逐项检查:规格是否落实、正确性、边界情况、回归风险、安全基线、可维护性。
只报真能导致缺陷、安全漏洞或线上故障的问题,不要用命名、空格这类小毛病凑数。
每条写明严重程度、文件和行号、成立依据、建议动作。
安全基线逐条对照:任何违反都是阻断项,必须修完才放行。
第 2 步 · 交叉去重合并。 把一方的清单交给另一方比对:
对比下面这份发现和你自己的发现,去重合并成一份;你认为不成立的,写明反证再划掉。
最终得到一份合并后的清单。双方有分歧时,以能拿出证据(代码或文档出处)的一方为准;仍无法达成一致的,交给人裁决。
第 3 步 · 修复后再审一轮。 作者按清单修复;完成后由评审方复核原问题,以及修复本身影响到的范围,避免改动一处又破坏相邻代码。
第 4 步 · 结束这一轮。 一轮循环结束的判据是:每条意见都落在四个状态之一,即已修复、判定不成立、已接受风险、转成后续任务,没有悬而未决的。如果改到后面意见反而越来越多、迟迟收不了口,不要无限审下去,按 14.5 的收敛规则止损。
通过标准:两方都在新上下文、只读的条件下独立审过并完成合并;安全基线无违反或已修;每条意见都有证据、等级和最终处置;送审材料对应的是修复后的当前版本。
什么时候不这么做:低风险、可回退的小改动,一次独立评审足够,不必进行两轮双模型互审。双模型互审只用于值得投入相应成本的改动。
15.5 让评审收敛
Agent 评审 Agent 很容易陷入来回拉扯。有一个 PR 曾反复往返多轮都收不了口;事后追溯版本发现,大部分被反复标记的问题在第一轮评审前就已存在。评审方很少找到新的 bug,只是每轮多翻出几条旧问题。不设限制,评审就会空转。
下面几条收敛规则可以直接写进流程或钩子:
- 轮次上限:给互审设置轮数上限(例如三轮)。超过上限后,让作者回头重审方案,而不是继续修补。
- 不重扫已关闭项:不再翻出已经关闭的问题,但仍要审本轮新改的 diff、原问题是否真修好,以及修复牵连到的相邻代码。收敛是不重复做已完成的检查,不是把范围缩到只看新 diff。
- 一类一次:同一类问题标注一次、一次性全修,不要每轮翻出同类的另一个实例。
- 换检查重点:如果需要评审第三轮,就更换检查重点(例如前两轮查正确性,第三轮专查可维护性),否则只会重复付出成本,却得到相同的意见。
- 已提发现不重复:已经记录过的发现不再重复提。
意见反复出现却没有新证据时,交给人裁决;修改持续引出新问题时,缩小改动范围或回到方案阶段重做。
什么时候不这么做:高风险、难回退的改动可以放宽轮次上限,但要调整评审重点,不能让同一套检查反复空转。
15.6 意见分级与处置
意见仍按阻断、重要、建议三档处理,安全基线违反一律按阻断。这套分级和处置上一章已经定义,这里只讲 AI 评审层独有的一个问题:它偏向挑刺,要主动降噪。
AI 评审偏向挑刺多半和它被使用的方式有关:评审方被要求”找问题”,产出一长串意见才像在干活,说一句”没问题”得不到正反馈;它也不容易拿捏风格偏好和真正缺陷各该占多大权重,于是常把命名、空格这类小事和安全漏洞混在一起报。结果是它很少直接判断代码没有问题,不加约束时清单会越堆越长,时间一久人会被训练到整体忽视它,连其中真正的阻断项也一起错过。这不是靠”让它少说点”能解决的,要靠几个机制把信号从噪音里滤出来:
- 分级加证据:每条意见都要带等级、文件行号和成立依据,不能只写”这里可能有问题”。依据不足的当场关闭,不让它留在清单里。
- 多方投票:多方都认定的,优先处理。单方提出但证据充分的,保留并复核;单方发现、证据却充分的严重问题,正是多开一个评审方的价值所在,不能因为只有一方提出就丢弃。单方提出、拿不出证据又只是风格偏好的,关闭。证据互相冲突的,交给人裁决。
- 信任期:新引入一个 AI 评审方时,先让它只评论、不拦截合并;等它的意见被证明值得采纳,再给它阻断的权力。一上来就让没调好的评审方拦合并,只会逼人学会无视它。
- 风格下沉:能写成确定性规则的风格和格式问题,交给 lint 和格式化器去管(14.1),别让 AI 用自然语言反复提:机器一次管全、不带情绪,也不会把它和真正的正确性问题混在一起。
15.7 按可逆性分档
上一章按风险给出了合并前的最低要求,这里补一个更本质的判断标准:可逆性优先于改动大小。能够一键回退、不改数据的变更,即使误批也容易恢复,门槛可以较低;涉及数据、计费、认证或数据库结构的变更,无论 diff 多小都要由人检查。
两个比模型自评更可靠的信号:
- 反向依赖数(爆炸半径):只改一行但被半数模块依赖的共享配置,爆炸半径大于修改几百行的孤立脚本。反向依赖数是成本较低的客观信号,不受 Agent 自我评价的影响。
- 路径清单:认证、数据库结构、CI 配置这类敏感路径,一旦发生改动就进入人工评审,与模型给出的风险分无关。要检查的对象正是 Agent,不能让它自行决定检查强度。
什么时候不这么做:这套分档约束的是最低强度。项目可以提高某类改动的要求,但不要让所有改动都机械地走最重流程;流程过重时,检查更容易被跳过,质量却未必提高。
15.8 人类留在环里的判断
有限的人力应当用在机器做不好的事情上。有五件事无论工具多强都需要人来判断:
- 要不要做:方向、范围、品味。
- 高风险代码:计费、认证、自动更新、CI 配置、数据库结构等出错代价高的部分。
- 会被复制的架构:一旦确定,其他人会继续复用的模式。
- 读证据:测试输出、实际执行的命令及结果、截图、评审发现。说不清这些检查可能漏掉什么,就不要急着批准。
- 对结果负责:交付者承担责任,无论代码是谁编写的。
人工验收怎么做、介入深浅如何随风险变化、人退出处证据强度不许下降这条底线,上一章已经讲过,这里不再展开。要强调的只有一点:上面这五件事不随工具变强而消失,它们是 AI 评审层之上必须由人保留的判断。
15.9 不建这套流程的情形
这个领域还没有标准答案,把它写成”只有一条正确路径”本身就是错误的。动手前需要考虑几种严肃的反对立场:
- 限制产出,而不是加强审查。有开源项目直接收紧了 AI 贡献,理由不是 AI 代码质量差,而是审查的意义被削弱:评审人写的 PR 可以培养未来的维护者,评审机器产出则没有这项长期收益,而且 Agent 不能对结果负责。对小型开源项目,答案可能是”限制 AI 大量产出”,而不是”搭一整套重流水线”。
- 自动化门禁可能制造虚假信心。有了门禁和 AI 摘要,人反而更容易放心地不再评审:几十个文件的大 PR 转眼就被批准,大家默认”模型生成并通过测试就可以”。工具可能在掩盖风险,而不是减少风险。无论是否进行评审,人仍要对交付到生产的代码负责。
- 重编排是规模问题,不是所有团队都需要。确定性门禁这一层(lint、CI、密钥扫描、分支保护)成本较低、争议较少,适合优先建立。多 Agent 投票、风险分类器和复杂互审编排属于规模扩大后才值得采用的上层机制;小团队直接照搬大厂的整套重设置,很可能造成过度工程。
落地顺序建议:先建立确定性门禁(第一层);再引入 AI 评审,起步阶段按 14.6 的信任期办法只评论、不阻断;把反复出现的意见转成确定性规则;最后再按需采用独立身份、双模型互审等更重的手段。先建立基础,再逐步增加更复杂的机制。
本章小结
评审的核心是用证据替代批准动作。能由机器判断的事项应沉淀为确定性门禁;AI 评审通过新上下文和只读权限减少同源盲区,再用分级、收敛规则和证据要求控制噪音与反复评审。风险按可逆性分档,高风险和用户可见行为交给人,人的介入减少时,证据强度不能随之下降。工具按职能选择,语言或团队变化时换成同类工具;流程强度则取决于改动的风险和存活期。
参考与出处
- 从批准动作转向行为证据,以及三层验证模型:Anthropic: How the Claude Code team does code review
- 大部分人工评论可自动化、AI 代码不比人写代码更难审、先建确定性地板的落地顺序:Trellis: Is Increasing AI Usage Increasing Code Review Burden?
- 削弱 CI 的改动应停止合并、红旗清单、追踪一条关键路径:GitHub: Agent pull requests are everywhere—here’s how to review them
- 同一会话自审改不动自己的错:arXiv 2603.12123
- 跨模型评审与同模型全新会话评审的比较:arXiv 2610.01471
- 限制 AI 产出的反对立场:Godot 2026 贡献政策
- 独立工具选型基准:Martian Code Review Bench
- 双模型互审的简短话术(竞争找真问题、忽略作者身份、别拿小毛病凑数):Reddit: cheeky code review prompt 讨论
- 提交钩子框架的多语言选型判据(husky 绑 Node、lefthook 并行、pre-commit 隔离环境):Andy Madge: Git Hook Frameworks Comparison