第一种,是直接让大模型 review PR diff。

优点很明显,几乎零成本。

但稳定性并不好。同一个 PR 连跑两次,评论数量和内容都可能差很多。

更重要的是,模型很容易把注意力放到一些「显眼但价值不高」的地方,例如:

一段中文字符串
一个 catch {}
一个 magic number
真正值得关注的语义问题,反而容易被淹没。

第二种,是把 ESLint、TypeScript 检查收紧。

它们确实能很好地解决规范问题。

例如:

未使用变量
空 catch
类型错误
风格一致性
但是像 race condition、stale closure、path traversal 这类语义 Bug,它们天然发现不了。

第三种,就是继续人工 Review。

准确率最高,但吞吐始终上不去,而且 reviewer 很容易疲劳。

后来我们逐渐意识到,我们真正需要的其实不是一个更聪明的 AI,而是一种职责划分:

LLM 负责发现语义 Bug
Linter 负责规范检查
人只关注 AI 和静态分析都解决不了的问题
于是,我们开始尝试阿里的AI驱动的代码审查 CLI 工具—— Open Code Review(OCR)。

OCR 比直接让 LLM Review,多了一层什么?
刚开始,我也以为 OCR 的价值只是「把 prompt 封装好了」。

真正用下来以后发现,它更多解决的是工程化问题。

例如:

规则可以版本化。

团队把规则写进 .opencodereview/rule.json,随仓库一起维护。规则修改可以走 PR,可以 Git blame,不再依赖某个人脑子里的 prompt。

会主动补上下文。

OCR 不只是把 diff 丢给模型,而是会按需读取相关代码、类型定义、调用关系,让模型拥有比单纯 diff 更多的信息。

很多误报,其实都是因为上下文不足。

会做事实核查。

OCR 内置了 REVIEW_FILTER_TASK,会检查评论是否能够被 diff 直接反证。

例如评论说:

文件里存在 XXX

但 diff 根本没有 XXX。

这种评论会直接被过滤。

还能直接集成 GitHub PR。

评论最终直接落到对应代码行,reviewer 不需要切换工具。

整个思路其实很合理:

ESLint 负责规范。
OCR 负责语义。
人负责架构和业务。
可惜,现实并没有这么理想。

第一个坑:LLM 太喜欢"认真工作"
OCR 上线第一周,我就把它关掉了。

不是因为它不准。

而是因为它太能说。

一次 PR,大概三四十条评论。

其中大部分都是:

这里硬编码了中文字符串
这里 catch 是空的
建议抽一个 helper
建议去掉 magic number
建议不要嵌套三元表达式
这些问题有没有道理?

都有。

问题在于,它们本来就是 ESLint 的职责。

Reviewer 已经看过 ESLint,再看一遍 AI 的重复提醒,没有任何增益。

真正重要的问题反而被埋没了。

第二个坑:我以为问题在 Workflow
我的第一反应,是做后处理。

在 workflow 里加了一层过滤:

正则过滤低价值评论
path + line + body 去重
真实 PR 跑下来以后,结果很尴尬。

regex:

0 命中。

因为 OCR 的评论写得非常正式。

它不会说:

Looks good.

也不会说:

This is fine.

而是:

Hardcoded Chinese strings detected…

这些完全匹配不到。

去重也没什么效果。

真正重复的评论,LLM 会重新组织语言。

如果按内容去重,去不掉。

如果按位置去重,又可能把同一行两个真正不同的问题一起删掉。

折腾了一晚上,我发现自己一直在错误的地方用力。

第三个坑:读完源码,我才找到真正的杠杆
后来,我把 OCR 的源码 clone 下来读了一遍。

读完以后发现,之前很多判断都是错的。

真相一:rule.json 不是硬规则
我原来以为:

rule.json 是规则。

实际上:

它只是作为 Review Checklist 放进用户提示词。

如果 diff 里某个 token 特别显眼,例如:

catch {}
或者:

‘上传失败’
模型的注意力很容易被这些 token 吸走。

仅仅写一句:

不要报告硬编码字符串。

作用并不明显。

后来我们改成:

把 NEVER REPORT 放到最前面
给具体 token 示例
效果才开始稳定下来。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐