返回博客

AI 代码审查已经崩了。这是缺失的那一环。

AI 代码审查已经崩了。这是缺失的那一环。

我们来对一件大多数人不会说出口的事情坦诚相待:我们几乎不审查 AI 生成的代码。

Diff 有 600 行。Agent 动了 14 个文件。你打开 pull request,滚动浏览变更,眯着眼看了几个函数,然后 merge。也许先跑了测试。也许没有。代码看起来还行。Agent 说做完了。上线吧。

这不是偷懒。这是一个结构性问题。传统的代码审查是为这样一个世界设计的:人类写了代码,被问到时能解释自己的推理。Diff 只有 50-200 行,因为这是一个人在专注工作时能写出的量。PR 描述会说"我选择方案 X 是因为 Y",你可以信赖这个上下文。

AI agent 不是这样工作的。Claude Code 能在两分钟内产出 500 行可运行的代码。PR 描述往往只是"实现功能 X"。Diff 告诉你改了什么,但关于为什么什么都没说。没有记录 agent 考虑了哪些替代方案。没有解释它做了哪些取舍。没有证据表明它实际测试了什么。你在审查一个黑盒的输出,而审查工具只给你看输出。

这篇文章分析了为什么基于 diff 的审查对 agent 输出不起作用,真正缺失的层是什么,以及一个具体的模式,让 AI 生成的代码变得可审查而不会让你的审查时间翻三倍。

审查断层

开发者在私下对此很坦诚。在社区帖子里,模式不断重复:"我基本上只是扫一眼 agent 的 diff。""检查测试通过就 merge。""看起来大差不差就批准。"

在现有约束下这是理性行为。当一个人类同事提交 PR 时,你有上下文:你知道他在做什么,看过 ticket,可能在站会上讨论过方案。Diff 是补充材料。真正的审查通过共享上下文完成了。

对 agent 来说,这些你一样都没有。Agent 接了一个任务,沉默了三分钟,产出了一个 diff。唯一的上下文是 agent 在 PR 上留下的一行描述。从零开始审查那个 diff,没有关于意图或推理的上下文,比审查同样大小的人类 PR 要多花 5-10 倍时间。所以人们不做。他们抽查几个关键区域就批准了。

结果可以预见。有上下文本可以发现的 bug 溜过去了。Agent 做出的小决定不断累积,造成架构漂移。代码质量以微妙的方式退化:没坏,但也不完全对。某个文件里不一致的错误处理。一个能用但扩展性差的数据库查询。一个新的工具函数复制了已有的,因为 agent 不知道它的存在。

这些失败在 diff 审查中都看不到。只有当你理解 agent 想做什么,并能将其意图与执行进行对比时,才能看到。

为什么 diff 不够

Diff 是文本变更的记录。就这样。对于人类代码审查,diff 有用是因为审查者可以从模式识别和共享上下文推断意图。你看到同事添加了 try/catch 块,知道他在处理上周 bug 报告中的错误情况。看到他重命名了函数,知道他在遵循团队约定的命名规范。

对于 agent 生成的代码,你无法推断意图,因为你不是推理过程的参与者。以下是 500 行 agent diff 实际告诉你的:

  • 哪些文件被修改了
  • 哪些行被添加、修改或删除了
  • 新代码的语法结构

以下是它没有告诉你的:

为什么选了这个方案。 Agent 可能考虑了三种不同的实现。它选了一种。你不知道为什么。也许选的那个是最优的。也许只是它第一个试的,效果还行。从 diff 看不出来。

哪些替代方案被放弃了。 如果 agent 选择了轮询策略而不是 WebSocket 订阅,这是深思熟虑的架构决定还是偶然?Diff 不会告诉你。

实现是否符合 spec。 你需要在一个窗口打开 spec,另一个窗口打开 diff,手动交叉验证每个验收标准。大多数人不会这么做。

测了什么以及怎么测的。 Diff 可能包含新的测试文件。但 agent 运行了吗?通过了吗?覆盖了 spec 中的边界情况吗?要知道的话你得 checkout 分支自己跑。

Agent 是否守住了范围。 也许任务是"修复登录 bug",但 agent 还重构了 auth 中间件、重命名了两个工具函数、更新了配置 schema。这些改动单独看都没问题。但它们没有被要求,没有被规定,也没有针对原始验收标准进行测试。

这不是某个特定 diff 工具的问题。GitHub 的审查界面、GitLab 的 merge request、Gerrit、终端里的 git diff。它们都给你看同样的东西:什么变了。对于 agent 输出,"什么变了"是最不重要的问题。重要的问题是:这个改动做了它应该做的事,而且没做别的吗?

缺失的层:实现叙事

审查者真正需要的是 agent 的推理轨迹。不是 diff。实现的故事:agent 计划做什么,实际做了什么,以及如何验证结果。把它叫做实现叙事。

一个好的实现叙事回答五个问题:

  1. 计划是什么? 写代码之前,agent 打算做什么?哪些文件,什么方案,什么顺序?
  2. 实现过程中发生了什么? 计划在接触代码库后还成立吗?有意外、转向或范围变更吗?
  3. 最终结果是什么? 不是 diff。用白话总结什么变了以及为什么。
  4. 如何验证的? Agent 为确认实现可用所采取的具体步骤。不是"测试通过"而是"我通过执行 X 验证了验收标准 #3,观察到 Y。"
  5. 审查者应该检查什么? Agent 自己关于什么值得人类关注的建议。也许有一个可以两种方向走的设计决策,或者一个值得听听第二意见的性能取舍。

这些在标准 PR 工作流中都不存在。PR 描述字段是没人强制的自由文本。Agent PR 默认使用最简描述,因为 agent 被指示去实现,不是去记录推理。

差距不在工具。在流程。审查基础设施已经存在。缺的是一个结构化的 agent 意图记录,让审查者可以拿来和 diff 对照。

这就是 Beadbox 要解决的问题。

实时查看整个 agent 队列正在做什么。

Beta 期间免费试用 →

Plan-Comment-Done 模式

这是一个不增加显著开销就能弥补差距的模式。它有三部分:agent 在写代码前评论一个 plan,实现,然后在完成时评论一个结构化的 done 报告。

步骤 1:Plan 评论

在 agent 打开任何文件之前,它写出打算做什么。编号步骤,要动的文件,以及要采取的方案。

PLAN: Fix WebSocket reconnection dropping messages during
server restart.

1. Add a message buffer to hooks/use-websocket.ts that queues
   outbound messages while the connection is in CONNECTING state
2. On successful reconnection, flush the buffer in order
3. Add a 30-second timeout: if reconnection hasn't succeeded,
   surface an error to the user via the toast system
4. Update the existing reconnection test to verify buffer
   behavior

Files: hooks/use-websocket.ts, components/connection-status.tsx

Test: Unit test for buffer queueing/flushing, manual test by
killing the WS server mid-operation and verifying no messages
are lost on reconnect.

这花 agent 大约 30 秒。审查者不到 2 分钟就能读完。在这 2 分钟里,你能发现在 diff 审查中要花 20 分钟才能找到的问题:

  • 范围对吗?(计划说 2 个文件。如果 diff 动了 6 个,说明偏了。)
  • 方案合理吗?(带 flush-on-reconnect 的消息缓冲区是合理的。如果 agent 提议从服务器重新加载全部状态,你会想先讨论一下。)
  • 有遗漏步骤吗?(超时窗口期间发送的消息怎么办?丢弃还是重试?)

计划是一个轻量级检查点。审查它,批准它,agent 带着对意图的共同理解继续前进。

步骤 2:实现

Agent 写代码。这里什么都没变。但因为计划存在,agent 有一个可以遵循的路线图,而不是走一步看一步做临时决定。

步骤 3:Done 评论

Agent 完成后,发布一个结构化的完成报告。不是"完了"。不是"实现了功能"。而是实际做了什么的详细说明。

DONE: WebSocket reconnection now buffers outbound messages
during CONNECTING state and flushes on successful reconnect.

Changes:
- hooks/use-websocket.ts: Added messageBuffer ref, queue
  messages when readyState !== OPEN, flush in FIFO order on
  'open' event. Added 30s reconnection timeout.
- components/connection-status.tsx: Shows "Reconnecting..."
  badge during buffer state, switches to error toast after
  timeout.

QA Verification:
1. Start dev server and WebSocket server
2. Open app, verify connection dot is green
3. Kill WebSocket server process
4. Trigger an action that sends a WS message (e.g., update
   a bead status)
5. Restart WebSocket server within 30 seconds
6. Verify: the buffered message is delivered, bead status
   updates in the UI
7. Repeat step 3, but wait >30 seconds before restart
8. Verify: error toast appears after timeout

Acceptance criteria:
- [x] Messages sent during reconnection are not lost (step 6)
- [x] Timeout surfaces user-visible error (step 8)
- [x] No behavior change when connection is stable (step 2)

Commit: f4e2a1b

现在审查者有了需要的一切。读计划(意图是什么),读 done 评论(实际构建了什么以及如何验证的),然后带着完整上下文看 diff。Diff 审查从"这都是什么?"变成"让我确认这和 agent 说的一致。"

Agent 输出的审查清单

即使有了实现叙事,你仍需要系统性的方法。这是我审查 Claude Code 输出时使用的清单。每次审查花 5-10 分钟,能捕获单靠 diff 会遗漏的那些类别的 bug。

Spec 对齐:

  • 实现是否覆盖了 spec 中每个验收标准?
  • 是否有超出 spec 要求的改动?
  • Done 评论是否将每个标准映射到验证步骤?

范围控制:

  • Agent 是否只修改了计划中列出的文件?
  • 如果动了额外文件,是否有说明原因?
  • 是否有不属于任务的"清理"改动(重命名、重格式化、重构)?

测试覆盖:

  • 新行为有对应的新测试吗?
  • 测试确实在测正确的东西吗?(Agent 有时会写出因为测的是 mock 而不是实现所以无意义通过的测试。)
  • Agent 声称运行了测试吗?有证据吗?

架构一致性:

  • 改动是否遵循代码库中现有的模式?
  • 是否有重复现有抽象的新抽象?
  • 错误处理策略是否与项目其他部分一致?

依赖意识:

  • 如果 agent 添加了依赖,是否合理?
  • 改动是否破坏了现有功能?(检查导入了被修改模块的文件。)
  • 如果任务依赖其他任务,那些依赖是否已解决?

这个清单适用于任何代码审查工具。打印在便签上,放进 PR 模板,或者内置到 CLAUDE.md 里让 agent 知道它会被什么标准衡量。重点不是具体条目。是用结构化的协议取代"看起来不错"。

Beads 作为审查界面

Plan-comment-done 模式需要一个居所。如果计划和 done 评论散布在 Slack 消息、PR 描述和终端输出中,你就丢失了 spec、计划、实现和验证之间的联系。

这就是 beads 解决的问题。Beads 是一个开源的 Git 原生 issue tracker,每个"bead"承载一个任务的完整生命周期:描述即 spec,评论即 agent 计划,评论即 done 报告,评论即 QA 结果。全部附着在一个实体上,可搜索,永久保存。

使用 bd CLI 的审查工作流:

创建带 spec 的任务:

bd create --title "Fix WebSocket reconnection message loss" \
  --description "## Problem
Messages sent during WebSocket reconnection are silently
dropped...

## Acceptance Criteria
1. Messages queued during CONNECTING state are delivered
   on reconnect
2. 30-second timeout surfaces error to user
3. No behavior change when connection is stable" \
  --type bug --priority p1

Agent 认领工作并发布计划:

bd update bb-f4e2 --claim --actor eng1
bd comments add bb-f4e2 --author eng1 "PLAN: Add message
buffer to WebSocket hook...

1. Queue outbound messages when readyState !== OPEN
2. Flush buffer in FIFO order on 'open' event
3. Add 30s timeout with error toast
4. Update reconnection test

Files: hooks/use-websocket.ts, components/connection-status.tsx"

你在 2 分钟内审查计划:

bd show bb-f4e2  # 读 spec + plan 评论

如果计划看起来对,agent 继续。如果不对,你在写任何代码之前就评论修正。

Agent 完成并发布 done 报告:

bd comments add bb-f4e2 --author eng1 "DONE: WebSocket
reconnection now buffers outbound messages...

QA Verification:
1. Kill WS server, trigger action, restart within 30s...

Acceptance criteria:
- [x] Buffered messages delivered on reconnect
- [x] Timeout error visible
- [x] No regression on stable connection

Commit: f4e2a1b"

bd update bb-f4e2 --status ready_for_qa

QA 独立验证:

bd show bb-f4e2  # 读 done 评论中的验证步骤
# 执行每个步骤
bd comments add bb-f4e2 --author qa1 "QA PASS: All 3 criteria
verified. Buffer flushes correctly, timeout fires at 30s,
stable connections unaffected."

整个审查轨迹在一个地方。六个月后,当有人问"WebSocket 为什么在重连期间缓冲消息?",答案就在 bead 里:spec 解释问题,计划解释方案,done 评论解释构建了什么,QA 评论确认它能用。

当终端审查触及天花板

对一个任务运行 bd show 能给你一切。但当你审查多个 agent 跨多个并行工作流的输出时,CLI 工作流线性扩展:每个任务一个 bd show,看哪些准备好审查需要一个 bd list,每个需要批准的计划一个 bd show

这就是 Beadbox 的位置。Beadbox 是一个实时仪表板,显示工作区中每个任务的当前状态、最新评论和在审查流水线中的位置。你看到哪些 agent 发布了需要你批准的计划。哪些发布了等待审查的 done 报告。哪些还在进行中。随着 agent 通过 bd CLI 写评论和改状态,一切实时更新。

使用 plan-comment-done 模式不需要 Beadbox。CLI 处理整个工作流。但当五个 agent 同时产出可审查的输出时,能一眼看到审查队列而不是逐个查询每个任务,会改变你通过流水线的速度。

Beadbox 在 beta 期间免费,它运行的 beads CLI 是开源的

审查问题不会自己解决

AI 生成的代码增长速度超过了我们审查它的能力。我们现有的工具是为不同的规模和不同的工作流构建的。GitHub PR、IDE diff、甚至精密的静态分析:它们都没有解决根本问题,即不知道作者意图的情况下审查代码比知道意图时难得多。

解决方案不是更好的 diff 工具。是结构化的意图:agent 计划做什么、实际做了什么、以及如何验证结果的记录。Plan-comment-done 模式在不增加显著开销的情况下给你这个记录。Agent 花 30 秒写计划。你花 2 分钟审查。Agent 花 60 秒写 done 报告。你带着完整上下文而不是从零开始审查 diff。

五个带走的原则:

  1. 要求代码前先有计划。 30 秒的 plan 评论能省下 20 分钟的审查会议。如果计划错了,在代码存在之前就修正。

  2. 要求结构化的 done 报告。 "完了"不是 done 报告。验证步骤、验收标准映射和 commit hash 才是 done 报告。

  3. 对照 spec 审查,而不是 diff。 Diff 显示什么变了。Spec 说什么应该变。交叉对照。

  4. 强制范围边界。 如果 agent 动了计划外的文件,这是警告信号。未计划的改动就是未审查的改动。

  5. 把审查当作协议而不是判断。 清单比直觉能抓到更多 bug。"看起来不错"不是审查。

Agent 会继续变快。Diff 会继续变大。问题是你的审查流程是否跟得上,还是你仍在眯着眼看 600 行的 diff 祈祷一切顺利。

如果你正在构建这样的工作流,给 Beadbox 在 GitHub 上加个星。

Like what you read?

Beadbox is a real-time dashboard for AI agent coordination. Free during the beta.

Share