上一次开这个开发杂谈话题已经是三年前聊的测试了。当时的 AI 编程还是 Kite 和 Tabnine 的时代,GH 的 Copilot 还只是一个刚起步的补全工具,ChatGPT 也还是只有单纯的聊天框。那时候的一些见解,今天来看其实还是比较精准地预测了未来,比如:
让 AI 根据测试去完成实际的实现 (也就是相对困难的部分) … 你至少必须花上很多时间去 review AI 写出的代码,去真正理解并维护这些代码 … 但是可能随着 AI 不断的学习和训练,这种情况会被彻底改变,也许在未来,AI 才是主驾,而人类开发者则变成副驾。如果那一天到来,那么编程就不再是程序员所需要掌握的核心技能了。
这些三年前的想法,很多已经远远以超越我想象的速度变成了现实。当时我的预测是,业界至少可能需要五年时间来把人类从编程任务中解放出来。但去年底以来,以 Opus 4.5 和 GPT-5.2-Codex 的智能飞跃为代表,所带来的 agent coding 的新时代,让这个进程加速了不少。
三年前的软件开发和今天的软件开发,显然已经不是同一个工种了,而相应地,一些以前被认为重要的技能和方法,放在当下也多少显得过时。不论是身经百战的老家伙还是刚刚入门的新朋友,当然也包括我自己,其实都正在这个进程中探索和挣扎。我觉得有必要重新把最近关于软件工程中的一些体会以及心得,特别是它们在 AI 新时代下的变化,再整理一下,并进行分享。
今天我想聚焦于代码审核 (code review),大致聊三个问题:AI 写的代码需要审核么?AI 的代码应该审核到什么程度?AI 的代码应该怎么审核?
AI 的代码需不需要审核
为什么要代码审核
在深入任何其他有关代码审核的问题前,我们有一个最基本的问题:软件工程里我们为什么需要代码审核。
很多企业开发者可能会说:我也不知道,但是公司就是要求“没有通过审核的代码不能合并”。企业开发中往往期望通过代码审核来寻找软件中的缺陷和错误,并在它们合并到主 branch 和提交给 QA 前就被低成本解决。但事实上,我们都知道在由人类主导的传统软件开发中,审核阶段想要找到所有问题是不可能的:并不是每一个 reviewer 都有条件、有时间以及有信念去对改动逐一验证,code diff 无法给你全貌,你也会有无数错过的细节和 edge case。对于企业来说,通过代码审核来抓到一些非常明显的问题,然后将其他的部分交给 QA 部门,可能是一种更加直接和经济的做法。
代码审核的另一个重要目的,就是找人背书和背锅,甚至培养备胎。当然,我们通常会把这件事情说好听一些,讲成“维护代码可读性”、“增进团队成员对他人代码的理解”或者“让所有人对整个代码库保持掌控”等。毕竟要是原来的开发者跑路了,新接手的人想要尽快顶上的话,已经通过 review 对代码库有一定理解的人,肯定是比从零开始的人更让人安心的。
对于个人开发者来说,上面的两条理由依然成立,唯一的区别是维度稍有不同。个人开发者依然需要对软件中的 bug 负责,而自己也会希望能对自己的代码库保持理解。虽然自己的代码自己 review 这件事情并非强制,但是自我审核的习惯往往也被认为是开发者的美德和最佳工程实践之一。
Agent Coding 的时代的变化
诚然,在人类主导开发的时代,上面两条理由足够站得住脚。但是当下 AI 主导的软件开发看起来把这两个代码审核的目标颠覆了。
Agent 可以不知疲倦地进行实际验证
和要吃要喝有脾气还会累的碳基生物不同,硅基生物们只要有电就能持续工作。最近的 agent 更是在“不达目的死不休”的训练激励下,动辄就能连续工作十几乃至几十小时。这直接摧毁了人类原本的“通过 review 来寻找代码缺陷”的前提。
为 agent 建立合理的验收标准,在 review 阶段不再纠结于代码这个中间产物,而是把 E2E 的测试提前并强制化,把验证交给 agent,让 review 下沉到最终产物,去在实际的验证中寻找缺陷,要更符合 agent 时代的 review 需求。
Agent 可以迅速理解整个仓库
Agent 拥有足够的查找能力,熟稔各种架构和技巧,它们能在几分钟甚至数十秒内,就理解代码库的情况。保持团队对代码库的理解这一传统 review 的另一个目的,在 agent 的能力下也略显过时和不必要。
以前模型能力不足时,我们可能还需要一些 code index 的方式帮助模型理解代码,但现在模型自主调用各类工具完成代码阅读以及结构化的理解,已经是基本能力了。相比于对代码库的理解,agent 更可能缺乏的是了解人类的决策过程:也就是一段代码是为什么发展到当前的样子的,它初始时想达成的目标,演化过程中随着需求的变化等信息,可能会难以追踪。为此,一种自然的想法是 让 review 上浮到需求和规格 (spec)。
AI 的代码需要非传统方式的审核
在架设好符合 agent 的 review 流程后,AI 将会有能力自主通过实际验证寻找 bug,也可以时刻保持对代码仓库的完整理解。最终人类 review 的作用就只剩背锅了。你喜欢背锅么?反正我不喜欢…
Review 在主观上已经不必要,再考虑到 AI 产出代码的速度,人类去 review 这些代码从客观上也变得不可能。传统的 review 已经完全不适应时代的发展,至少人类去 review AI 产出的代码,我认为已经完全没有意义。
虽然形式的 review 已经没有意义,但是 review 的形式依然还保有价值。我们接下来会仔细看看这么说的原因。
AI 的代码审核到什么程度
信息在人和 agent 之间的传播必然产生失真,或者我们可以将它叫做语义漂移 (semantic drift)。传统的软件开发,从 spec 到 review 的流程大概是:
1
开发者理解意图 → 意图在开发者脑中 → 开发者写代码 → reviewer 检查
而在 AI 软件开发时代,agent 往往工作的层级处于信息链的末端,这个链条其实变长了:
1
人的真实意图 → issue / spec → prompt / context → LLM 理解意图 → 代码 → reviewer 检查
每次信息的传递,都是一次转换,也都伴随漂移的可能。如果我们完全不对 AI 生成的代码进行核查,开发的结果必然发生偏移。这也正是 AI 代码审查的一个重要目的:尽可能消除信息差和语义漂移。
仔细看这条链条,人类能真正发挥作用的地方,其实只有两头。中间从 prompt 到代码的部分,是 agent 最擅长的地方,也是人类最难插手的地方:按 agent 的产出速度,几千行的 diff 人类根本看不过来,而且聪明如 agent 凝结的是数不胜数的优秀架构和神乎其技的 API 知识。在这些方面,放下人类尊严,虚心接受教育才是正道。
但链条的起点是只存在于人脑中的真实意图,而终点是能实际运行的产物,这两个位置恰好是 agent 无法替人类做判断的。所以前面提到的两个观点,其实就是“审核到什么程度”的答案:让 review 上浮到需求和规格,让 review 下沉到最终产物。至于中间的代码本身,人类不必再逐行审核,交给 agent 之间互相审核就好。
上浮
语义漂移有一个特点:越靠近源头,修正的代价越小。spec 里的一句含糊,到了代码里往往就是一整套错误的抽象和实现。更让人头疼的是,agent 必然会把这个错误实现得接近完美。实现自洽,架构优美,测试覆盖及其漂亮,光从代码里几乎看不出问题。除了它整个是错的以外,简直无懈可击。
所以 spec 是人类最应该花时间的地方,而且应该逐字逐句地认真审核。和动辄几千行的 diff 不同,一份好的 spec 通常只有一两百行,还是自然语言,这是人类完全能够应付,也应该花心思去琢磨的规模。在这个阶段,我一般会关心这些问题:
- 目标是不是我真正想要的?哪些事情明确不做?
- 有哪些备选方案,最终选择的理由是什么?
- 会动到哪些模块?有没有公共 API、数据迁移这类一旦出错就难以回头的改动?
- 做完以后,用什么来证明它做对了?
最后一条尤其重要:验收标准在 spec 阶段就定下来,后面 agent 实现和人类验收都以它为准,不用凭感觉。
spec 最好是讨论出来的:agent 先调查代码库、起草方案,人类挑毛病、提问题,agent 再修改,往返几轮才算定稿。如果对方案没有把握,还可以把同一份 spec 交给不同的模型交叉检查,不同模型的盲区通常不一样。在这个阶段多花一个小时,往往能省下后面一整天的返工。
定稿后的 spec 要留下来。它记录了这段代码为什么是现在这个样子,也就是前面说的 agent 最缺乏的决策信息。
下沉
链条的另一头是最终产物,代码被编译成了产品,review 时也不要再纠结于中间产物,而是直接去审核证据。
Agent 喜欢大本营战报似的宣布胜利已经是常态了:“已完成所有修改,测试全部通过”,但其中有多少测试有意义,有多少纯凭推断或者猜测,又有多少通过改了测试来迁就实现,完全是无法判断的。因此在产物审核时,原则只有一条:不要相信 agent 的结论,只相信 agent 给出的证据。具体来说:
- 自动化验证前置并强制化。单元测试、集成测试、E2E 测试,能自动跑的都让 agent 自己跑,并给出真实的命令和输出。修 bug 时,必须 TDD,先写红测观察失败,再修复并观察通过。先红后绿的过程本身就是证据:它证明了这个测试确实测到了东西。
- UI 和难以测试的部分,要截图或录屏。Agent 现在已经可以自己驱动模拟器和浏览器(我之前写的 sim-use 就是做这个的),那就让它把关键界面的截图和操作过程的录屏作为交付的一部分。最近的模型在 Computer Use 上也取得了长足进步,大多数情况下,GUI app 的验证也已经是被解决了的问题。一张截图或者一段录屏,要胜过一万句“我已确认 UI 正常”。
- 区分已验证和未验证。要求 agent 在报告中明确区分“实际运行并观察到的结果”和“推断或假设”,没有验证的部分也要老实写出来。
有了这些证据,人类最后要做的,就是对照 spec 里的验收标准检查证据。如有有空,最好也可以亲自上手用一用最终产物。这才是人类 review 时间最好的去处。
中间的代码呢
中间的代码交给 agent 互相审核:一个写,另一个(最好是不同的模型)审,修完再审,直到双方没有异议。人类不用逐行读,看审核的结论就够了。
AI 的代码怎么审核
下面以 Prowl 为例,说说我自己具体是怎么做的。
Prowl 是我最近在做的一个 Terminal app,我用它来运行和组织多 agent 协同环境,是我自己为自己开发的日常终端工具。如果有兴趣的话,你也可以下载试用。
用 docs-ai 讨论和记录 spec
Prowl 的仓库里有一个 docs-ai/ 目录,里面是按编号组织的设计记录。每个重要的功能,或者会影响后续决策的非平凡修复,都会有一个自己的文件夹:
1
2
3
4
docs-ai/NNN-<slug>/
000-plan.md # plan before implementation (RFC-like)
001-action.md # what was actually done, verified against the code
002-<topic>.md # amendments: follow-ups, corrections, or one record per slice
000-plan.md 就是 spec,必须在写代码之前完成。它包含背景 (Background)、目标与非目标 (Goals / Non-goals)、设计方案 (Design / Approach),以及备选方案和决策 (Alternatives & decisions)。前面说的 spec 审核时要关心的问题,在这个模板里基本都有对应的位置。我最看重的是最后一项:除了设计本身,还要写下做了哪些决策,为什么这么选。半年后不管是人类还是 agent 想知道“当时为什么没选另一种方案”,都可以在这里找到答案。
实现完成后,agent 需要写 001-action.md,记录实际做了什么、最终代码的状态,以及和计划不一致的地方 (Deviations from plan) 和尚未验证的疑问 (Open questions)。这两个小节是专门留给 agent “坦白”的:无论计划如何缜密,实现的时候一定会发现新的小问题。与其让 agent 悄悄地偏离计划,不如要求它把偏差摆到明面上。后续的修正和扩展以 002 之后的编号追加;如果方向发生了根本性的变化,则另开一个新的编号,并把旧计划标记为被取代 (Superseded)。
我把这套规则写成了一个 write-ai-doc skill,agent 开始较大的功能时会按它来起草计划。实际流程大概是:我用几句话描述需求,agent 调查代码库后起草 000-plan.md,我逐字读完,在对话里和它争论、修改,直到满意再开始实现。这是整个开发过程中我投入注意力最多的环节,可能占了开发流程的八成以上。
另外还有几条规则,是在实践中慢慢加上去的:
- 不是每个任务都要写。代码审核、日常调查、小修小补都不写,拿不准时也不写。否则这个目录很快就会变成 agent 的工作日志,噪声太多,反而没人看。
- 文档里出现的每个文件路径都必须真实存在;不能验证的内容只能放进 Open questions,不能写进正文。
- 没有实际跑过的构建和测试,不能写成已通过。
目前 Prowl 的 docs-ai 里已经有七十多个条目。新的 agent 碰到一段看起来奇怪的代码,读一读对应的条目就能知道它的来龙去脉,不用再去 git 历史里考古。我在其他一些更严肃的项目里也使用了类似的方法,效果很好。
Review Loop:让 agent 互相审核
Spec 定下来以后,实现和代码审核都交给 agent。审核这一步,我现在基本用 Prowl 内置的 Review Loop workflow 来跑。它的流程并不复杂:
- 负责实现的 agent(main)先写一份简报,说明这次改动的范围、对应的计划,以及已经做过的验证。
- Prowl 在旁边的分屏里启动一个独立的 reviewer agent。它阅读简报,检查实际的改动,给出问题列表(每个问题都带有编号、严重程度、代码位置和触发条件),以及
clean或issues的结论。 - main 逐条评估这些问题:确实存在的,先写一个会失败的回归测试,再修复;不认同的,用证据说明拒绝的理由。
- reviewer 进入下一轮:一边复核上一轮的问题是否真的修好,一边把当前完整的 diff 当作第一次看到一样重新审核。
- 循环往复,直到 reviewer 给出
clean并且 main 没有再做修改,或者达到轮数上限。最后由 main 写一份总结。
Prowl 的 workflow 提供了一个完整的多 agent 执行环境,整个 workflow 是用 YAML 描述和定义的,简化后的骨架大概是这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Simplified. Prompts, state updates, and delivery checks are omitted.
steps:
- id: brief
message: main # main writes the brief
- id: first_review
launch: reviewer # open the reviewer in a split
- id: rounds
while: state.round <= inputs.max_rounds
steps:
- id: assess
message: main # fix or reject each finding
- id: clean_exit
if: state.verdict == 'clean' && state.round >= inputs.min_rounds
then: [{id: stop, break: true}]
- id: next_review
message: reviewer
- id: summary
message: main
当然,实际的编排要比这个骨架“丰满”得多。这里面有几个小细节我觉得值得一提:
min_rounds默认为 2。即使 reviewer 第一轮就给出 clean,也不会直接结束,第一轮的 clean 往往只是看得不够仔细。- 双方都要拿出证据。reviewer 必须区分实际测试过的事实和推测,列出检查过和没检查过的文件,检查不完整时不能给 clean。main 拒绝一个问题时也要给出理由,而且被拒绝的问题不算 reviewer 认可的 clean。
- 修复时写的代码也要当作新代码审核。这条是吃过亏以后加的:早期版本里,后面几轮的 reviewer 几乎只在复核第一轮的问题,很少发现新问题,因为 prompt 把它的注意力锚定在了上一轮的报告上。现在每一轮都拆成“复核遗留问题”和“全新审核”两部分,两部分同样重要。
- 跑完不等于没问题。达到轮数上限时,总结里必须分清已解决的、未解决的,以及最后一轮修了但还没被再次审核的问题。
reviewer 可以使用任意的 agent profile,我一般会选和 main 不同的模型,理由和 spec 的交叉检查一样。两个 agent 都运行在真实的终端分屏里,我可以随时旁观和介入。在 review 期间 main 会在 PR 上留痕,而我基本只需要读最后那份总结,以及其中被拒绝和搁置的意见。
Review Loop 自己在开发时也是这样审过来的:第一轮 reviewer 找到三个实质性问题,全部复现并修复后,第二轮给出了 clean。过程记录在它的 docs-ai 条目里。如果你有兴趣,也可以在 Prowl 仓库里找到不少由 review loop 驱动的 PR review。
编排你自己的审核流程
Review Loop 只是 Prowl workflow 的一个内置例子。Prowl 的 workflow 就是一个描述“谁参与、按什么顺序做什么”的 YAML 文件:给某个 agent 发消息,用指定的 agent profile 启动新的 agent,根据交付结果的 verdict 做条件分支和循环,运行脚本,最后发出通知。参与者只通过 prowl CLI 交付结果,所以 Claude Code、Codex、Pi 或者其他任意 Prowl 能识别的 agent,都可以担任其中的任意角色。和单纯用一个 skill 之类的操作不同,Prowl workflow 提供了代码和状态机驱动的稳定工作流,我可以放心把任务交给任何模型,而不担心它们出错。
除了内置 workflow 外,自己写的 workflow 也可以放在 ~/.prowl/workflows/ 下个人使用,也可以放在仓库的 .prowl/workflows/ 里跟着项目走。想要什么样的审核流程,基本都可以自己编排出来,比如:
- 在实现之前,让两个不同的模型轮流审核
000-plan.md,把 spec 的交叉检查也自动化; - 要求 reviewer 的报告必须附带截图,没有截图的 UI 改动一律视为未验证;
- 同时启动多个 reviewer,分别关注正确性、性能和安全性,最后汇总。
这些 YAML 甚至都不用自己写:Prowl 自带了一个 prowl-workflow skill,直接告诉 agent“帮我写一个让两个 agent 互相审核的 Prowl workflow”,它就会帮你写好并完成验证。
其实不止审核,workflow 是一个通用的多 agent 在 Prowl 下协作的框架。只要涉及到 agent 任务编排(不止多 agent,也包括单 agent 的通用流程),你都可以使用 workflow 来轻易地实现和运行这个编排。如果你对这样的工作方式感兴趣,欢迎试试 Prowl(顺手点个星就更好了 :P )。
结语
回到开头的问题:AI 写的代码还需要审核吗?需要,但肯定不是逐行阅读 diff 的那种传统审核了。人类的注意力应该放在两头:在源头把意图讲清楚,在终点把证据看明白。中间的部分,我已经完全放手让 agent 们自己去吵。至于在企业开发中背锅这件事,大概还是逃不掉的,但至少在这套流程下,我能把这口锅背得明明白白。