OpenCodeReview 新功能:让 AI 代码评审从"能审"到"审得准、接得住"
开源项目
OpenCodeReview(Apache 2.0):github.com/alibaba/open-code-review
一个星期前,我们介绍了 OpenCodeReview 的核心理念——将代码评审从"单次 prompt"升级为多阶段工程流水线:确定性骨架约束输出、跨文件取证保障上下文、行号精确匹配实现自动化闭环。这套架构已在阿里巴巴内部多个核心业务线落地验证。
但架构的正确只是起点。工具落地之后,更棘手的问题浮现:用户不知道工具在想什么。 哪些文件会被审查?用了什么规则?为什么某个文件被静默跳过?另一边,AI Coding Agent 正在重塑开发界面——当开发者的大部分时间花在 Claude Code 或 Cursor 的对话窗口里,一个独立的 CLI 工具如何成为这个新工作流的有机组成部分?
v1.1.1 到 v1.1.7 的七个版本迭代,核心围绕两条主线:可控性——让你看见并控制评审的全过程;可集成性——让 OCR 成为 AI 编码工作流的原住民。本文聚焦这些最有价值的新能力。
1. 看得见,才控得住:--preview 与 ocr rules check
可控性的第一步是可见。在消耗 API 费用之前,你需要确认工具要做的事符合你的预期。这不是锦上添花——对于需要为每次 LLM 调用付费的团队来说,这是成本控制的基本要求。
1.1 消失的评论
设想一个场景:你提交了 20 个文件的变更,运行 ocr review,等了五分钟,收到三条评论。你盯着屏幕,脑子里只有一个问题——另外 17 个文件呢? 是它们没问题,还是 OCR 根本没审?
答案是:你不知道。OCR 内部有一条四道门的过滤链:
1. 用户排除规则(你配的 exclude 模式) 2. 扩展名白名单(只审支持的文件类型) 3. Include 穿透(你明确指定的优先路径) 4. 默认路径排除(test 目录、生成代码等)
每条门都可能拦下一批文件。但在 v1.1.6 之前,这个过程是完全静默的。过滤链沉默地工作,用户只能在事后猜测。对于一个设计为"质量卡点"的工具,这种不透明本身就是一种质量问题。
1.2 --preview:花 token 之前先见全貌
--preview(-p)标志解决的就是这个问题。一条命令,零 LLM 调用:
ocr review --preview输出一目了然——哪些文件会被审查,哪些被跳过,以及跳过的原因:
Preview: 6 file(s) changed | +1297 -0
Excluded from review (6):
[M] CHANGELOG.md (unsupported_ext)
[M] deepdive.md (unsupported_ext)
[M] introducing-ocr-new-features.md (unsupported_ext)
[M] introducing-ocr.md (unsupported_ext)
[M] introducing-ocr2.md (unsupported_ext)
[M] introducing-ocr3.md (unsupported_ext)而当一个包含 Go 代码的 commit(如 --commit e71fa59)运行预览时,你会看到完全不同的结果——所有 .go 文件都将进入审查:
Preview: 5 file(s) changed | +226 -40
Will review (5):
[M] cmd/opencodereview/flags.go +7 -0
[M] cmd/opencodereview/output.go +65 -3
[M] cmd/opencodereview/review_cmd.go +22 -0
[M] internal/agent/agent.go +4 -37
[M] internal/agent/preview.go +128 -0关键设计决策:Preview 不是一套独立的"预估"逻辑。它复用了完整过滤算法——Preview() 方法遍历 diff、调用 whyExcluded() 对每个文件执行四道门判断——但不发起任何 LLM 请求。你看到的结果与真实评审时会发生的结果是确定性一致的。这不是 approximate,是 exact。
每个被排除的文件后面跟着具体的排除原因——unsupported_ext(扩展名不在白名单,比如 .md 文件)、default_path(test 目录或生成代码等系统默认排除路径)、user_exclude(你配的排除规则)——让沉默的过滤链变得可审计。
这在三种场景下尤其有用:
• 评审前确认范围:20 个文件只审 8 个?看一眼哪些被排除、为什么,确认没有重要文件被意外跳过。 • 调试过滤规则:配置了 include/exclude 后,预览确认规则生效是否符合预期。不必真正消耗 token 来验证配置。 • CI 前置检查:在 PR 流水线中加入 ocr review --preview步骤,让 CI 日志直观展示本次变更中有多少文件将进入评审、多少被排除。团队可以在 PR 页面直接检查审查范围是否合理,避免漏审重要文件。
1.3 ocr rules check:规则匹配的体检报告
文件进入了审查范围,下一个问题是:系统到底用了什么规则在审它?
不同语言、不同模块的审查重点截然不同——.java 文件关注 NPE 风险和线程安全,pom.xml 关注 SNAPSHOT 版本依赖,mapper.xml 关注 SQL 注入。这些规则由复杂的 glob 匹配引擎在背后驱动,但此前用户完全看不见匹配过程。
ocr rules check 把这条隐藏链路晒出来:
ocr rules check src/main/java/com/example/OrderService.java输出不是模糊的"使用了通用规则",而是一份精确的规则匹配报告。对 OCR 自己的 Go 源码运行检查:
File: cmd/opencodereview/main.go
Source: System built-in
Pattern: default
Rule:
────────────────────────────────────────
#### Correctness
Is the logic correct? Are there missing boundary conditions?
Are exceptions handled properly?
Is it thread-safe in concurrent scenarios?
#### Security
Are there security vulnerabilities such as SQL injection or XSS?
Is sensitive information handled correctly?
Is permission validation complete?
#### Performance
Are there obvious performance issues (e.g., N+1 queries, unnecessary loops)?
Are resources properly released?
#### Maintainability
Is the code clear and easy to understand?
Do names accurately express intent?
Does it follow the project's existing code style and architecture patterns?
#### Test Coverage
Do critical logic paths have corresponding test cases?
Do test cases cover boundary conditions?
────────────────────────────────────────当前系统内置规则尚未包含 Go 语言的专项规则,因此 Go 文件走到了 default(通用)规则。但如果用 --rule 标志指定自定义规则,输出立刻切换:
$ ocr rules check --rule /path/to/my-rules.json cmd/opencodereview/main.go
File: cmd/opencodereview/main.go
Source: Custom (--rule)
Pattern: **/*.go
Rule:
────────────────────────────────────────
测试自定义规则:检查 Go 文件的错误处理
────────────────────────────────────────而 Java 项目则会匹配到内置的专项规则——**/*.java 映射到一套中文编写的详尽检查清单,涵盖拼写错误识别、死代码、NPE 风险、线程安全问题、N+1 性能隐患等。pom.xml 走"禁止 SNAPSHOT 版本依赖",mapper.xml 走"SQL 注入风险检查"——这些规则文件现在以独立 Markdown 的形式放在 internal/config/rules/rule_docs/ 下,可直接阅读和贡献。
三个字段——Source(规则从哪来)、Pattern(匹配了哪个 glob)、Rule(完整规则文本)——回答了"用了什么规则、为什么匹配到这个规则、这个规则从哪来"。这是一份"规则匹配的体检报告"。
它的底层实现同样遵循"确定性计算而非 LLM 推断"的原则。ResolveDetail 接口返回的是 RuleDetail 结构体,其中 Source 被精确标注为四个枚举值之一——custom(--rule 标志)、project(项目级配置)、global(全局配置)、system(内置默认)——而非由模型根据上下文猜测。
在日常开发中的价值非常直接:自定义规则没生效?跑一下 ocr rules check,看看实际匹配了哪个模式。团队新人想知道某个模块的审查重点?一行命令,无需翻阅文档。
2. 规则系统重塑:四层解析器与文件过滤器
如果说 --preview 和 ocr rules check 解决了"看得见"的问题,那 v1.1.1 到 v1.1.5 的一系列重构解决的则是"控得住"——规则系统从能用,变成了可定制、可分层、可调试。
2.1 为什么我配置的规则没生效?
在 v1.1.1 之前,OCR 的规则系统只有一个层级:内置的系统规则。用户可以自定义规则文件,但加载语义模糊——没有明确的优先级,没有冲突解决策略,没有调试工具。
一个典型的困惑:团队在项目根目录的 .opencodereview/rule.json 里为 MyBatis mapper XML 写了 SQL 注入检查规则:
{
"rules": [
{
"path": "**/*mapper*.xml",
"rule": "检查所有 SQL 语句是否存在注入风险,确保参数使用 #{} 而非 ${}"
}
]
}跑完评审却发现,XML 文件依然用的系统默认通用规则。排查半小时,发现规则文件放在了错误的目录层级。但在当时,除了翻源码,你没有任何办法验证这一点。
规则解析是一道"黑箱"——你不知道规则从哪加载、按什么优先级合并、你的配置是否真的被用上了。对一个以"可审计"为核心卖点的工具,这道黑箱是它自己身上的盲点。
2.2 四层优先级:让规则各居其位
v1.1.1 引入的 composedResolver 把这道黑箱拆成了四条透明的查询链:
--rule 标志 (最高) → 项目 .opencodereview/rule.json → 全局 ~/.opencodereview/rule.json → 内置系统默认 (最低)每层 first-match-wins。对一个给定的文件路径,解析器从最高优先级开始逐层查询,找到第一条匹配的规则就返回,不再继续向下查找。
为什么是层次覆盖而不是跨层合并?这是一个刻意的设计选择。规则不是配置——规则是针对性指令。对同一个文件,把项目规则的"Java 性能检查"和全局规则的"安全检查"合并在一起,语义冲突的风险远大于收益。"更近的规则覆盖更远的"——这条直觉比任何合并算法都更容易理解和预测。
工程上的实现也很克制。每层的 ProjectRule 使用的是同一套 PathRule 匹配逻辑——glob 模式展开、大小写不敏感、doublestar.Match 匹配——差异仅在于规则文本的来源和加载路径。composedResolver.Resolve() 所做的就是顺序调用四层,不引入任何新的匹配语义:
func (c *composedResolver) Resolve(path string) string {
if rule := matchProjectRule(c.custom, path); rule != "" {
return rule
}
if rule := matchProjectRule(c.project, path); rule != "" {
return rule
}
if rule := matchProjectRule(c.global, path); rule != "" {
return rule
}
return c.system.Resolve(path)
}这种简单性本身就是一种设计品质——当你的规则没生效时,ocr rules check 一行命令就能告诉你是哪一层赢了、匹配了哪个模式、规则文本是什么。黑箱打开之后,调试成本从"翻源码"降为"一行命令"。
顺带提一句,v1.1.5 的 LLM 解析器也经历了类似的"去黑箱化"——自动裁剪模型名称中的版本后缀,让配置文件中的 LLM 设置优先级高于环境变量。这些不是大功能,但减少的是每次切换环境时"为什么连不上 LLM"的排查时间。对于每天要跑数十次评审的团队,这类微小摩擦的消除比新功能更有价值。
2.3 include/exclude:把审查边界划清楚
四层解析器解决的是"用什么规则审"的问题,v1.1.2 引入的 include/exclude 过滤器解决的是"审什么文件"的问题。
扩展名白名单和默认路径排除是系统级的粗粒度控制——.java 审、.xml 审,但 test 目录跳过、generated 目录跳过。这在大多数场景下够用,但在以下情况下捉襟见肘:
• 你只想审查 src/main/java/service下的业务逻辑,跳过src/main/java/dto下的纯数据传输对象• 你引入了一个第三方 vendor 目录,里面的代码不是你写的,但扩展名匹配了审查白名单 • 你接手了一个老项目, legacy/目录下的代码架构完全不同,不应用现代规则去审
rule.json 中新增的两个字段直接回应这些场景:
{
"include": ["src/main/java/**/*.java"],
"exclude": ["**/dto/**", "**/vendor/**", "**/legacy/**"],
"rules": [
{
"path": "**/*.java",
"rule": "所有 public 方法必须对参数进行非空校验..."
}
]
}优先级语义经过仔细设计:exclude 最优先——先剔除明确不要的,确保排除规则绝对生效。include 可以穿透默认路径排除——你可以"召回"被系统默认跳过的路径(不过仍受扩展名白名单限制,不能审查 OCR 不认识的文件类型)。
还有一个容易被忽视的边界条件处理:buildFileFilter 选择的是最高优先级且含有 include/exclude 配置的层——它不会跨层合并过滤规则。这意味着如果项目级 rule.json 配置了 exclude: ["**/vendor/**"],而全局 ~/.opencodereview/rule.json 配置了 include: ["**/vendor/**"],vendor 目录会被排除——项目级的规则赢了。这种"层间不合并"的设计避免了"项目排除了但全局又包含了"的优先级混乱。
从"工具决定审查什么"到"你决定审查什么"——这一转变把调试配置的时间从"折腾半天不知道为什么"压缩到了"看一眼 preview 就知道"。
3. 融入 AI 编码工作流:插件与 Agent Skill
规则系统让你控制了审查的"内容"和"标准"。但控制力的最后一环,是审查发生的"时机"和"界面"——不是"想起来的时候跑一下命令行",而是让它在正确的时刻、以正确的形态、自动出现在你已经在用的工具里。这是可控性的终极形态:工具不再要求你去找它,而是它来找到你。
3.1 CLI 的集成缝隙
2026 年,Claude Code、Cursor、Copilot 等 AI Coding Agent 已成为大量开发者的主要编程界面。但一个典型的 AI 辅助开发流存在一个质量和效率的断裂带:
Agent 写完代码 → 你切到终端 → 运行
ocr review→ 阅读结构化输出 → 切回 Agent → 手敲"请修复第 42 行的 NPE 风险和第 88 行的线程安全问题" → Agent 修复
这个流程里,你做的工作是协议转换——把 OCR 的结构化输出翻译成 Agent 能理解的指令。你是两个自动化系统之间的人工胶水。
理想流应该是什么样的?
Agent 写完代码 → Agent 自行调用 OCR 评审 → Agent 阅读结构化输出 → Agent 自行决定修复哪些 → 你只需要确认
整个回路在一次对话中完成。人不做协议转换,只做决策。
3.2 Claude Code 插件:对话框里的专业评审
OCR 通过标准的 Claude Code 插件机制注册自己。plugins/open-code-review/.claude-plugin/plugin.json 声明了插件元信息(名称、命令目录、版本),commands/review.md 定义了触发后执行的完整工作流。注册到 .claude-plugin/marketplace.json 后,用户可以通过 claude plugins install 发现并安装。
但更直接的方式是,将命令文件复制到项目中即可使用:
cp -r .claude /path/to/your-project/安装后,在对话中输入 /open-code-review,Claude Code 就会在后台执行一个精简但完整的三步工作流:
### Step 1: Run Code Review
ocr review --audience agent [user-args]
### Step 2: Filter and Evaluate
High: 明显的 bug、安全问题 → 必须保留
Medium: 上下文相关的建议 → 选择性保留
Low: 误报和 nitpicking → 直接丢弃
### Step 3: Fix
自动修复值得采纳的 High 和 Medium 问题--audience agent 是让这一切可行的关键参数。它抑制实时进度输出,只保留最终的评审总结——每条评论以结构化文本呈现,包含文件路径、行号范围、问题描述、修复建议。这不是给人边看边等的格式,是给 Agent 一次性解析后直接行动的格式。
你会发现,这个三步工作流和 SKILL.md 的前四个 Step 结构几乎完全相同——分类和修复逻辑都在里面。区别在于深度:插件的命令文件是一份精简版的行动清单,假设 Agent 已经掌握了代码评审的基础能力;而 Skill 是详细的教学文档,包含了分类的详细标准、输出格式模板、边界情况处理、踩坑记录。两者解决的是同一个工作流的不同粒度——插件是"做什么",Skill 是"怎么做以及为什么这样做"。
3.3 Agent Skill:教 AI 学会用 OCR
如果说插件告诉你"做什么",Skill 就是教你"怎么做以及为什么这样做"——skills/open-code-review/SKILL.md 是一份完整的教学文档,教会 AI Coding Agent 如何将 OCR 作为一个工具使用。它不是命令速查表,而是一个四阶段的决策工作流:
Step 1: 收集业务上下文。 分析评审目标(commit 信息、分支名称、关联需求),提取背景信息,通过 --background 传入 OCR。这让评审不只是看代码语法,而是判断代码是否正确地实现了意图。
Step 2: 执行评审。 始终使用 --audience agent 参数,确保输出格式适合 Agent 解析。
Step 3: 分类和报告。 这是 Skill 设计中最值得展开的一点。OCR 输出的每一条评论,Agent 不会全量呈现给用户——它会先做三级分类:
• High:明显的 bug、安全漏洞、逻辑错误,或定位精确且有明确修复方案的建议。必须报。 • Medium:有道理但依赖上下文判断、风格/性能建议、需要人工实现修复的建议。选择性报。 • Low:大概率误报、缺乏足够上下文、吹毛求疵、无意义的建议。直接丢弃。
为什么要分类而不是全量汇报?因为 AI 代码评审天然产生噪声。任何 LLM 都会在置信度不足时输出模糊的建议、过度谨慎的提醒、或者对代码风格的 nitpicking。如果 Agent 不加甄别地把所有评论抛给用户,信任会迅速耗尽——当你看到三条有价值的评论夹杂在七条"建议考虑使用更简洁的变量名"中时,整个评审的价值感知就会崩塌。
三级分类是一次信号提取。High 是信号,Medium 是需要上下文的半信号,Low 是噪声。让 Agent 替你过滤噪声,是对用户注意力的尊重。
Step 4: 修复。 这里有一个微妙但重要的设计区分:Skill 要求 Agent 判断用户是"要求评审并修复"还是"只评审"。前者自动应用 High 和 Medium 的建议,后者则先展示结果,询问是否修复。这种区分确保了 Agent 不会在用户期望"先看看再说"时擅自修改代码。
Skill 中还有一个容易被忽略但极其务实的部分——Gotchas(踩坑记录):
• --audience agentvs--audience human的区别——用错了会污染输出• untracked files 在 workspace 模式下也会被审查——需要用 staging 控制范围 • plan phase 在 diff 超过 50 行时触发——增加延迟但提升质量 • comment 语言可通过配置切换——默认中文
这些不是 API 文档。API 文档告诉你参数是什么,Gotchas 告诉你参数用错了会发生什么。它们是从真实使用中沉淀下来的运维知识。
但 Skill 最体现设计深度的地方,不是它教 Agent 怎么用 OCR——而是它教 Agent 怎么应对 OCR 的已知失败模式。
OCR 的行号定位基于 diff hunk + 代码片段匹配,并非 100% 准确。当定位失败时,评审评论的 start_line 和 end_line 会被置为 0。一个粗暴的 Skill 会直接丢弃这些评论,或者不加说明地呈现给用户。而 OCR 的 Skill 选择了一条更负责的路径——它教会 Agent 识别这个信号,然后自行补救:读文件内容、根据代码片段匹配实际位置、将修复应用到正确行号。
这十几行指令的设计价值在于:它承认了工具的不完美,并把这个认知编码进了 Agent 的行为规范里。Skill 不是一份"一切都会正常工作"的理想化文档,而是一份"当以下情况发生时,你应该这样做"的应急手册。这种对失败模式的坦诚,是一个工程化工具区别于 demo 的标记。
4. 权衡:你获得了控制权,也承接了决策的责任
这些能力加在一起,让 OCR 从一个"跑完出结果"的 CLI 工具,变成了一个可编程、可嵌入、可审计的质量组件。但灵活性的另一面是——每一个可以控制的维度,都是你必须做的决策。
v1.1.x 系列的所有新功能,其实在做同一件事:把隐式行为变成显式配置。 过滤不再沉默,规则不再黑箱,集成不再依赖人工协议转换。这条设计主线有一个贯穿始终的代价:你在获得控制权的同时,也承接了做决策的责任。
从"工具替你决定"到"你告诉工具该怎么做"。--preview 暴露了过滤链,但你得花时间看它;ocr rules check 展示了规则匹配,但你得理解四层优先级;include/exclude 允许精细控制审查范围,但你需要维护这份配置。控制力的另一面是认知负荷——每多一个可以控制的维度,就多一个需要做的决策。
这种代价是否值得? 答案是"取决于你的使用深度"。如果你只是偶尔跑一下 ocr review 看看结果,默认行为就够了——你可以完全忽略这些新功能。但如果你把 OCR 作为团队质量体系的一环——配了自定义规则、接了 CI 流水线、嵌入了 Agent 工作流——那这些显式配置就是必需品。OCR 的策略是:默认聪明,但允许你接管。
学习曲线的一次性成本。四层优先级、first-match-wins、跨层不合并 include/exclude——这些语义确实需要学习。但 ocr rules check 和 --preview 构成了一个即时反馈回路:你总可以用一条命令验证自己的理解。这种"文档即工具"的设计,把学习曲线的陡峭程度降低了一个数量级。
有意识的"不做"。 OCR 没有做"一键自动修复所有问题"的全自动模式。Skill 中的自动修复要求 Agent 动态判断置信度,且 High/Medium 的分界线由 Agent 在上下文中动态权衡。这不是技术上的做不到——在一个质量卡点工具中,涉及设计意图的自动修改可能引入比原始问题更严重的缺陷。把"是否修复"的决定权留给用户或上层 Agent,是一种负责任的克制。
5. 结语
从 v1.1.1 到 v1.1.7,七个版本的迭代在回答同一个问题:AI 代码评审工具怎样才能被真正用起来?
答案是两件事。第一,让它说清楚自己在做什么——--preview 告诉你哪些文件会被审、ocr rules check 告诉你会用什么规则审。黑箱被拆开后,沉默的过滤链变成了可审计的决策过程。第二,让它能嵌入你已经在用的工作流——Claude Code 插件让评审命令从终端搬进了对话窗口,Agent Skill 教会了 AI 如何解读和利用评审结果。工具不再是孤立的 CLI,而是一个可被 Agent 调度的能力组件。
OpenCodeReview 的架构优势在于让模型发挥更稳定——多阶段流水线、行号确定性定位、跨文件取证。而 v1.1.x 系列的新功能,让这种稳定性变得可感知、可控制、可调度。
一条命令,看一眼你的代码正在被怎样对待:
ocr review --preview
ocr rules check src/main/java/com/example/OrderService.java欢迎到 github.com/alibaba/open-code-review 试用和反馈。