自研代码审查机器人把 PR 合并时间砍掉一半:一次工程复盘
自研代码审查机器人把 PR 合并时间砍掉一半:一次工程复盘
原文:Vapi Blog《How an in-house review bot cut our PR merge time in half》(https://vapi.ai/blog/ai-code-review-bot)
起点是一个以「天」计的评审队列
Lat Ware 开始做 Pedantic Review Bot 的时候,Vapi 的一个 PR 从打开到合并平均要 130 多小时,也就是五天以上。Vapi 已经为两个 AI 评审工具付了钱,但在他们自己的代码库上,这两个工具都没抓到 Lat 早期提示词抓到的问题。
关键不在「自研更聪明」,而在于自研把两件事放到一起权衡:一次评审的成本,和它发现的问题。用他的话说,他当时是在和以「天」计的评审时间竞争,所以可以允许机器人一次评审花 20 分钟——商业产品做不到这一点。
机器人现在审查 Vapi 工程师开的每一个 PR,平均合并时间降了一半,到 65 小时。
一次评审是怎么跑的
- PR 打开即触发,草稿也算;作者可以主动 @ 机器人再跑一遍。
- 已经审过的 commit 会被跳过,重复事件不会触发第二次付费评审。
- 每条行内发现以一句加粗总结开头,带一个严重度标记,从绿色(吹毛求疵)到红色(阻塞合并)。
- 第二个机器人 Pedantic Architecture Review Bot 专门找设计问题,它是在一个设计缺陷漏过原版机器人之后建的。
那个缺陷的特征是:单看任何一行代码都看不出来的设计问题。架构机器人用的提示词几乎一样,只是把「可能的问题」换成「可能的高层设计模式问题」,并且需要更高的推理强度。它写报告,只有和具体位置绑定的发现才留行内评论。它还有一个复杂度过滤器:2026 年 9 月 2 日到 10 月 2 日之间,763 次架构工作流运行只产出了 33 份报告。
踩坑一:第一版话太多
第一版留下的评论多到 Lat 把它命名为 Pedantic。其中很多针对的是这个 PR 本来就碰到的、早就存在的问题。团队的说法是:它的评论读起来像草率的 AI 输出,而且他们说得有道理。
他反复改提示词,让评论更短、更容易照着做。抱怨随着工程师开始细读评论、并发现发现本身是准确的而慢慢消失。修掉「正在改动的代码里既有的问题」也就成了团队维护工作的一部分——既然不可能一次修完,机器人就把这类工作限制在当前正在改的代码上。
踩坑二:让机器人先证明自己的结论
机器人不会在没核实过的情况下提出异议。分析 job 会先检出这个 PR,在报告一条发现之前,智能体可以去追调用方、去跑它怀疑会失败的测试,或者写一个小复现。
这里有一个值得抄的架构决定:分析与发评论是两个 job。分析会话被剥掉了 Git 凭据,只把发现写进一个 JSON 文件;第二个 job 校验这个文件并发出评论,不执行智能体写下的任何东西。它能评论,但不能批准、不能推送,由工程师决定要处理哪些。
// 示意:分析 job 写下的发现文件(字段为示意,非官方格式)
{
"pr": 4821,
"findings": [
{
"file": "src/router.ts",
"line": 118,
"severity": "blocker",
"summary": "流量切换与发布比例的应用顺序反了",
"verified_by": "trace_callers"
}
]
}
一个月里它抓到了什么
2026 年 9 月 2 日到 10 月 2 日,机器人审了 1694 个 PR,在 276 个里面提出了 363 条阻塞级发现。其中一半以上涉及构建、测试、CI 或部署,其余涉及运行时行为和计费。
有几条测试类的发现,是绿色的 CI 勾根本盖不住的:
- 有一次改动,把测试断言换成了测试运行器永远不会收集的 Storybook story。
- 有一个 benchmark 声称成功,实际上一个测试都没跑。
- 在一个流量切换改动里,架构机器人抓到两次写入的顺序错了:后端先把流量切到新版本,然后才应用「发布比例 0%」。在那个时间窗里,一个本来不该有任何流量的发布,吃到了全部流量。
101 版提示词的实验,以及一次被推翻的结论
Lat 为这个机器人试过接近 200 条提示词。有一次被记录下来的实验是:在同一个 PR 上测试 101 版开场指令,其它一切保持不变——代码库上下文、diff、作用域规则、JSON 输出格式。
101 次运行产出 803 条原始发现,归并成 57 个不同的问题。一个 AI 裁判在不知道某个问题被多少次运行报过的情况下逐条判定:32 个真、24 个假、1 个无法验证。没有任何一次运行找到超过 10 个真问题。
报告一开始把更激进的那版提示词判为赢家。Lat 拒绝了这个结论,因为计分没有充分惩罚假阳性——报得越多的提示词得分越高,但每多报一条,无论真假,工程师都要多查一条。按他的本意把假阳性加权之后,赢家变回原来那条极简提示词:
Please aggressively review [PR link] for possible problems.
Post what you find as inline comments.
Don't sacrifice on making the problem easy to solve.
Give a one-sentence summary in bold at the top of each comment.
Assign a severity rating from the range 🟢 🟡 🟠 🔴,
with 🟢 being nitpicks and 🔴 being blockers.
两个可迁移的结论:关于「检查什么、怎么报告」的指令,比「你是什么角色」的人设更有效;把严重度符号原样写死,能让输出格式更一致。
多次便宜的运行,胜过一次最贵的运行
既然没有哪一次运行能找到全部问题,Lat 就想知道固定预算该怎么花。他还在自己的代码库上比较了不同模型——理由是公开榜单测的是供应商挑的任务,那些任务可能和他们的代码库长得很不一样。
实验对象是一次真实重构:它把一个终止保护从服务入口挪走了,但仓库里的 lint 测试仍然要求这个保护留在入口,所以这次改动会挂掉 lint 门禁。
| 配置 | 成本 | 严重度加权得分 |
|---|---|---|
| 代码评审:3 次 extra-high 强度 | $12.32 | 244(合计) |
| 代码评审:1 次 maximum 强度 | $12.30 | 35.5(平均) |
| 设计评审:3 次 extra-high 强度 | $6.36 | 668(合计) |
| 设计评审:1 次 maximum 强度 | $11.20 | 51(平均) |
两种比较里,三次 extra-high 都胜过一次 maximum,成本持平或更低。原因也合理:每一次额外运行会重复更多前面已经找到的东西,边际收益递减。
而那个已知的阻塞问题非常难抓:194 次代码评审运行里只有 8 次发现了那个 lint 失败;195 次设计评审运行里只有 2 次。
踩坑三:什么算「在范围内」
几个 GPT-6 配置在原文提示词下几乎什么都不报,理由是父提交里已经存在的问题不在本次评审范围内。Lat 加了一句话:Include errors already present in the parent.
在代码评审实验里,GPT-6 Astra 每轮的严重度加权分从 0.3 涨到 24.2。但这句话会过度纠正:在设计实验里,GPT-6.1 Sol 拿到的分全部来自既有问题,没有一分和这个 PR 自己的设计有关。另外要注意,其它模型没收到这句指令,所以它们的分数不能和它直接比较。
一条有用的发现,应该说明问题是被这个 PR 引入的,还是本来就存在。工程师可能两种都想修,但需要这个区分来决定要不要合并。
还没测到的部分,以及六条可迁移经验
作者自己把边界写得很清楚:101 版实验里每条提示词只在一个 PR 上跑过一次,判定还是 AI 做的;模型对比也只用了一个 PR,都需要在更多 PR 上重复才能推广。月度数据里有些架构报告是刻意对几个月前已合并的 PR 重跑,不算「合并前抓到」。活动报告也还没跟踪工程师最终采纳或驳回了哪些发现。
所以下面这六条,比那些具体分数更值得带走:
- 你的评审队列决定了延迟预算。人在等几天,机器人花几分钟就已经是改善。
- 让评审者跑代码,而不是只读代码。
- 让「读不可信代码的智能体」远离「能发评论的那个 token」。
- 把假阳性当成本算。Lat 的实验里,正是这一条让极简提示词胜出。
- 先试几次便宜的运行,再决定要不要买那一次最贵的。
- 明确告诉评审者:既有问题算不算在范围内。
最后是那个更普遍的方法:与其相信公开榜单,不如在自己的代码库上跑实验——用你自己的代码、你自己的成本口径去做 A/B。Lat 的原话是,实验才是真相浮出来的地方。
模型几个月就会换代,他也打算在新模型出来后重跑这些实验;但这些结论应该能活得更久一些。文中涉及的具体模型、工具与配置,以官方文档为准,未验证最新版本。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)