Open Code Review 深度拆解:阿里如何用「确定性工程 + LLM Agent」混合架构终结 AI 代码审查的三大顽疾——从 70 条静态规则到 Agent 工具调用的全链路架构哲学
AI 写代码已经越来越成熟,但 AI 审代码的落地之路却远比想象中崎岖。当 Claude Code、Cursor 等通用 Agent 被寄予厚望时,它们在代码审查场景暴露出了三个致命缺陷:覆盖不全、位置漂移、质量不稳定。阿里巴巴内部用了两年时间打磨出的 Open Code Review(OCR),用一种「确定性工程 + LLM Agent」的混合架构,以 1/9 的 token 消耗实现了比通用 Agent 更高的 F1 分数。本文从源码级拆解这条全链路的七步执行链路,剖析为什么「工程管线」比「提示词」更重要。
一、背景:AI 代码审查为什么「说起来美好,落地起来痛苦」?
1.1 三大顽疾
如果你试过用 Claude Code 或类似通用 AI Agent 做代码审查,大概率遇到过以下问题:
覆盖不全——改动文件较多时,Agent 容易「偷懒」,只审查部分文件,遗漏重要变更。一个包含 20 个文件改动的 PR,Agent 可能只认真看了前 5 个就给出了「整体没问题」的结论。
位置漂移——报告的问题行号与实际代码位置不符。Agent 说「第 42 行存在空指针风险」,但开发者打开文件发现第 42 行是一个空行。这种错误的精确度比不精确更糟糕,因为它浪费了开发者定位问题的时间。
质量不稳定——纯自然语言驱动的 Skills 难以调试,审查质量随 prompt 微调剧烈波动。今天审查质量不错,明天换个模型版本就全面退化。
这三个问题的根因是什么?纯语言驱动的架构缺少对审查流程的硬性约束。 模型可以「理解」代码,但它无法保证一定审查所有文件、一定准确定位行号、一定稳定输出质量。
1.2 阿里的解法:把「不能出错」的交给工程,把「需要判断」的交给 Agent
阿里集团内部从 2024 年开始部署 AI 代码审查助手,经过两年多、数万名开发者、数百万个代码缺陷的验证,沉淀出了 Open Code Review 这个开源项目。2026 年 6 月正式开源后,一周内 GitHub 新增 4,700+ stars,目前已累计超过 17,000。
OCR 的核心设计理念可以用一句话概括:将「绝对不能出错」的部分交给确定性工程,将「需要动态决策」的部分交给 LLM Agent。
这不是一个学术概念,而是从大规模生产数据中蒸馏出的工程约束。用一句 OCR 团队的话说:「真正影响落地效果的,不只是模型最终说了什么,而是审查过程能不能被规则约束、被工具支撑、被系统消费。」
二、架构全景:从 npm 命令到结构化评论的七步链路
OCR 的执行链路可以拆解为七个节点。理解这条链路,是理解整个混合架构的关键。
npm 入口 → 配置归一化 → Diff 解析与审查队列 → 运行上下文补齐 → 并发分发 → 单文件 Agent 审查 → 汇总输出
接下来我们逐步拆解每一步。
2.1 第一步:npm 命令进入 Go CLI
OCR 的安装方式是 npm:
npm install -g @alibaba-group/open-code-review
但核心审查逻辑并不运行在 Node.js 中。npm 包只是负责安装和暴露 ocr 命令,实际的审查引擎是 Go 编写的 CLI 程序。npm 包把用户输入的参数转交给当前平台的 Go 可执行文件。
这个设计选择是有意义的:Go 的并发模型天然适合处理多文件并发审查任务,而 npm 只是利用其分发生态优势。
2.2 第二步:归一化审查配置
进入 review 子命令后,程序不会立刻调用模型。它先整理本次审查的配置——把命令行参数、项目配置(.opencodereview/rule.json)、用户全局配置(~/.opencodereview/rule.json)和内置默认值合并成一份可执行的审查配置。
这一步的产物是审查范围、规则来源、文件过滤器、输出格式、并发度和可选的需求背景。规则怎么注入、文件怎么筛选、prompt 怎么填充,都在后续节点展开。
规则优先级设计:
| 优先级 | 来源 | 说明 |
|---|---|---|
| 1(最高) | 命令行 --rule | 临时验证规则 |
| 2 | 项目级 .opencodereview/rule.json | 提交到仓库,团队共享 |
| 3 | 用户全局 ~/.opencodereview/rule.json | 个人偏好 |
| 4(最低) | OCR 内置规则 | 按文件类型匹配 |
关键设计点:自定义规则不会整体替换内置规则。规则按文件路径逐层匹配,只有命中自定义规则时才使用,否则继续查找下一层。这意味着你可以在自定义规则中只关注特定路径,其他文件仍然受到内置规则的保护。
2.3 第三步:根据 Git diff 生成审查队列
这一步是整个链路中「确定性工程」最密集的环节。
程序读取本次代码差异(支持三种来源:工作区变更、分支比较、单个提交),整理成按文件拆分的 diff 列表,然后注入只读的 DiffMap 供后续工具按路径查询。
接下来是关键的筛选逻辑——不是所有 diff 文件都适合作为主审对象:
| 文件 | 是否进入审查队列 | 原因 |
|---|---|---|
src/pay/service.go | ✅ | 业务代码变更 |
src/pay/coupon.go | ✅ | 业务代码变更 |
src/pay/service_test.go | ❌ | 测试文件默认不作为主审对象 |
src/pay/generated/client.go | ❌ | 命中用户排除路径 |
assets/logo.png | ❌ | 二进制文件 |
这个筛选过程是确定性的——不依赖模型判断,而是用工程逻辑确保不会遗漏重要变更,也不会浪费 token 在无意义的文件上。
筛选完成后,每个进入队列的文件形成一个 ReviewTask:
// 审查任务结构(简化示意)
type ReviewTask struct {
File string // 文件路径
Diff string // 该文件的 diff 内容
Rules []string // 匹配到的审查规则
}
2.4 第四步:补齐 Review Agent 运行上下文
审查队列确定后,程序围绕队列里的文件逐个展开审查。每个文件进入审查前,程序都会拼装一份完整的运行上下文:
- 当前文件路径
- 当前文件 diff
- 过滤后仍在审查队列里的其他变更文件列表
- 需求背景(可选)
- 可用工具定义
- 当前文件对应的审查规则
审查规则不是提前固定的一段文本,而是按当前文件路径和文件类型动态匹配的。同一次审查里的 Go 文件、前端文件、XML 文件可以拿到不同的审查重点。
拼装前的 Review Agent prompt 结构(简化示意):
System prompt:
你是代码审查助手。
只审查当前文件 diff 中新增或修改的代码。
如果需要更多上下文,可以调用工具。
如果确认发现问题,必须通过 code_comment 提交结构化评论。
如果当前文件已经审查完成,调用 task_done 结束任务。
User prompt:
其他变更文件:{{change_files}}
当前文件:{{current_file_path}}
当前文件 diff:{{diff}}
需求背景:{{requirement_background}}
审查规则:{{system_rule}}
审查计划:{{plan_guidance}}(可选)
请审查当前文件 diff。
这段模板中的占位符,要到单文件审查阶段才会被替换成真实内容。这种延迟绑定的设计,让同一个 Agent 模板可以适配不同文件、不同规则、不同上下文。
2.5 第五步:按并发度分发文件子任务
加载 diff 后,Agent 手里已经有一个审查队列。并发分发节点做的事情很单纯:按 --concurrency 控制同时运行的文件任务数量,把队列里的文件分发出去。
分发前还会做一次大 diff 预过滤——如果单个文件的 diff 超过阈值,直接跳过并记录 warning,避免撑爆后面的 prompt。
这个设计体现了 OCR 的一个核心理念:确定性地拒绝超大输入,比让模型尝试处理然后失败更可靠。
2.6 第六步:单文件 Agent 审查——两阶段执行
这是整个链路中唯一由 LLM 驱动的环节。OCR 将单文件审查拆分为两个阶段:
阶段一:Plan(审查计划)
对于变更较大的文件,OCR 会先执行 Plan 阶段。Plan 的职责是分析当前文件 diff,识别潜在风险点,并为每个风险点规划需要读取的上下文。
Plan system prompt:
你是代码审查计划助手。
职责是分析当前文件 diff,识别潜在风险点,并为每个风险点规划需要读取的上下文。
可用工具:file_read, file_read_diff, code_search, file_find
输出要求:
只输出 JSON,不输出额外解释。
JSON 字段包括 change_summary、issues、severity、description、tool_guidance。
Plan 的输出示例:
{
"change_summary": "支付前根据 CouponID 调整扣款金额",
"issues": [
{
"severity": "medium",
"description": "需要确认优惠券抵扣后的金额边界,避免出现负数或零金额扣款",
"tool_guidance": [
{
"name": "file_read",
"reason": "查看 coupon.Apply 的返回值约束",
"arguments": "src/pay/coupon.go"
}
]
}
]
}
关键设计:Plan 只规划,不实际调用工具。 它输出的是一个「审查路线图」,告诉后续的 Review Agent 应该优先读取哪些文件、查询哪些 diff。这类似于人类审查者在正式 review 前先快速浏览一遍变更概览。
阶段二:Review Agent(正式审查)
Review Agent 拿到 Plan 输出后,进入正式审查循环。模型每一轮都会拿到当前 messages 和工具定义,可以做三类动作:
| 动作 | 说明 | 典型场景 |
|---|---|---|
| 读取上下文 | 调用 file_read / file_read_diff / code_search | Plan 建议确认相关文件变更 |
| 提交评论 | 调用 code_comment | 已确认问题,需要记录 |
| 结束任务 | 调用 task_done | 当前文件没有更多问题 |
工具调用是 OCR 的核心差异化能力。 相比通用 Agent 的自由工具调用,OCR 的工具集是从大规模生产数据中提炼的专用工具:
| 工具 | 作用 | 为什么需要 |
|---|---|---|
file_read | 读取文件完整内容 | diff 只显示变更部分,需要上下文 |
file_read_diff | 查看其他变更文件的 diff | 跨文件问题判断 |
code_search | 搜索代码库中的调用点 | 确认接口使用方式 |
file_find | 按文件名线索查找文件 | 不知道准确路径时 |
code_comment | 提交结构化审查评论 | 最终输出 |
task_done | 结束当前文件审查 | 流程控制 |
只有通过 code_comment 提交的结构化评论才会进入最终结果。 模型的普通 assistant 文本输出不会直接影响审查结果。这保证了输出的可控性——模型的「思考过程」不会污染最终的审查意见。
审查结束后,还会做一次评论过滤:删除那些仅凭当前 diff 就能证明错误的评论。例如模型说「新增变量没有被使用」,但当前 diff 后续新增代码已经使用了这个变量,这类评论就会被过滤掉。
2.7 第七步:汇总结构化评论并输出
并发分发启动的多个文件子任务完成后,汇总输出节点负责全局收尾:
- 等待所有文件审查结束,确保评论都已写入
- 如果所有文件都审查失败,本次审查直接失败
- 从评论收集器中取出评论列表
- 根据代码变更位置补齐缺失的评论行号
- 统计审查耗时、文件数、评论数和模型用量
- 根据
--format选择 text 或 json 输出
# 文本输出(面向终端)
ocr review --format text
# JSON 输出(面向 CI/Agent)
ocr review --format json
JSON 输出结构包含 status、summary、comments、warnings 和统计信息,可以直接被 CI 流水线或其他 Agent 消费。
三、为什么混合架构优于纯 LLM?——从 Token 消耗到审查质量的全维度分析
3.1 官方基准测试数据
OCR 使用一个名为 AACR-Bench 的评测集进行基准测试。该评测集从 50 个热门开源仓库中精选 200 个真实的 Pull Request,覆盖 10 种编程语言,由 80+ 位资深工程师交叉标注验证,共标注 1,505 个缺陷。
| 指标 | OCR | Claude Code(同模型) | 分析 |
|---|---|---|---|
| F1 分数 | 更高 | 较低 | 综合质量更好 |
| Precision(准确率) | 更高 | 较低 | 误报更少 |
| Recall(召回率) | 较低 | 更高 | 有意取舍,优先减少噪音 |
| 平均耗时 | 更短 | 更长 | CI 延迟更低 |
| Token 消耗 | ~1/9 | 基准 | 成本节省 89% |
3.2 实测对比:OCR vs CodeRabbit vs 传统 CI
来自第三方实测数据(基于约 50K 行 Python 代码的历史 PR):
| 指标 | OCR | CodeRabbit | 传统 CI |
|---|---|---|---|
| 平均发现数 | 3.0 | 5.0 | 1.7 |
| 误报率 | 11% | 33% | 0% |
| 漏报率 | 0% | 0% | 67% |
| 平均执行时间 | 2 分钟 | 3.3 分钟 | 22 秒 |
| 每 PR 成本 | ~1.5K token | ~4K token | 0 |
3.3 关键洞察
误报是 AI 代码审查的头号杀手。 CodeRabbit 的误报率 33% 意味着每发现 3 个问题就有 1 个是误报。在实际开发中,高频误报会导致开发者不再看审查意见,失去工具的价值。OCR 的 11% 误报率显著优于纯 LLM 方案。
为什么 OCR 的误报率更低? 因为确定性工程层做了两件事:
- 精准文件选择——只把真正需要审查的文件交给模型,减少信息噪声
- 规则匹配聚焦——根据文件特征匹配审查规则,让模型注意力高度聚焦
纯 LLM 方案的误报主要来自「模型不了解项目上下文」——它基于通用规范判断,但不知道这个接口的请求量很低、不需要优化,或者这个「安全隐患」在当前场景下不可能被触发。
混合方案的成本优势。 OCR 的静态规则层过滤掉了约 70% 的基础问题(空指针、SQL 注入、线程安全等),只有需要语义理解的 30% 才调用 LLM。这就是为什么 OCR 能用 1/9 的 token 消耗实现更好的审查质量。
四、深度剖析:确定性工程层的四大机制
4.1 机制一:精准文件选择
OCR 不会把所有 diff 文件都丢给模型。它用工程逻辑筛选出真正需要审查的文件:
- 排除二进制文件:图片、编译产物等
- 排除生成代码:protobuf 生成文件、OpenAPI 生成客户端等
- 排除测试文件(默认行为,可通过 include 覆盖)
- 排除用户明确排除的路径
- 排除纯删除文件:只有删除没有新增的文件不作为主审对象
这个筛选过程是确定性的、可配置的。你可以在 .opencodereview/rule.json 中精确控制哪些文件进入审查、哪些文件排除:
{
"include": ["tests/**/*.py"],
"exclude": ["**/generated/**", "vendor/**"]
}
4.2 机制二:智能文件分组
OCR 会将相关文件打包为审查单元。例如 message_en.properties 和 message_zh.properties 会被分为一组,由同一个子 Agent 审查。
这种分组不是随意的,而是基于文件间的语义关联性——同一个功能模块的多语言配置、同一个模块的代码和测试、同一组 API 的请求和响应定义等。
4.3 机制三:细粒度规则匹配
OCR 内置了一套按文件类型匹配的审查规则体系。以 TypeScript 为例,.ts、.tsx、.js、.jsx 文件会命中同一组前端规则,覆盖:
- TypeScript 类型安全
- React Hooks 规范
- 副作用处理
- 异步错误处理
- 常见安全问题(XSS 等)
Go 文件则关注并发安全、错误处理、接口设计等。Mapper XML 文件关注 SQL 注入、参数错误。
自定义规则会叠加在内置规则之上——如果自定义规则命中了某个文件路径,该文件使用自定义规则;如果没有命中,继续使用内置规则。这种分层设计让团队可以在不丢失内置审查能力的前提下,针对特定模块加强审查力度。
4.4 机制四:外部定位与反思模块
OCR 有一个独立的位置定位模块,用于系统性提高 AI 反馈的行号准确度。这不是简单的行号映射,而是在模型输出评论后,根据实际的代码变更位置重新校准评论的行号。
这个机制直接解决了通用 Agent 的「位置漂移」问题。模型可能说「第 42 行有空指针风险」,但实际问题可能在第 45 行——定位模块会根据 diff 的真实变更位置,将评论重新锚定到正确的行号上。
五、Agent 层设计哲学:为什么「工具调用」比「提示词」更重要
5.1 场景化 Prompt 优化
OCR 的 System Prompt 不是一个通用的「你是代码审查助手」,而是从大规模生产数据中提炼的专用模板。核心约束包括:
- 只审查 diff 中新增或修改的代码——不审查删除代码,因为删除代码的审查逻辑完全不同
- 必须通过 code_comment 提交评论——普通文本输出不进入最终结果
- 必须调用 task_done 结束任务——明确的流程控制,避免模型无限循环
5.2 场景化工具集
OCR 的工具集是精心设计的,每个工具都有明确的使用场景:
// 工具定义(简化示意)
var tools = []Tool{
{
Name: "file_read",
Description: "读取变更后的文件完整内容",
Parameters: map[string]string{
"path": "文件路径",
},
},
{
Name: "file_read_diff",
Description: "查看本次变更里其他文件的 diff",
Parameters: map[string]string{
"path": "文件路径",
},
},
{
Name: "code_search",
Description: "在代码库中搜索文本或正则",
Parameters: map[string]string{
"query": "搜索关键词",
},
},
{
Name: "code_comment",
Description: "提交结构化审查评论",
Parameters: map[string]string{
"severity": "严重级别: high/medium/low",
"description": "问题描述",
"suggestion": "修复建议",
},
},
}
5.3 对比:通用 Agent 的工具调用为什么「看起来能用,实际不够好」
通用 Agent(如 Claude Code)的工具调用是开放式的——它可以读文件、写文件、执行命令、搜索代码库。但在代码审查场景下,这种开放性反而成了问题:
- 工具选择不可预测——模型可能选择用
shell命令读文件,而不是用file_read - 工具调用可能出错——执行
grep搜索时可能因为路径问题返回空结果 - 工具调用浪费 token——通用 Agent 会调用很多与审查无关的工具
OCR 的工具集是封闭的、确定性的——只有 6 个专用工具,每个工具的输入输出都是确定的。模型只需要决定「调用哪个工具」,而不需要关心「工具怎么实现」。
六、实战:从安装到 CI 集成
6.1 快速上手
# 安装
npm install -g @alibaba-group/open-code-review
# 配置 LLM
ocr config provider # 交互式选择提供商
ocr config model # 选择模型
# 审查当前工作区变更
ocr review
# 审查分支差异
ocr review --from main --to feature-branch
# 审查单个提交
ocr review --commit abc123
# 全文件扫描(不依赖 Git diff)
ocr scan
6.2 自定义审查规则
在项目根目录创建 .opencodereview/rule.json:
{
"rules": [
{
"path": "src/pay/**/*.go",
"rule": "重点检查金额计算精度、错误处理完整性、并发安全、事务边界"
},
{
"path": "**/*mapper*.xml",
"rule": "检查 SQL 注入风险、参数类型错误、缺少闭合标签"
},
{
"path": "src/api/**/*.ts",
"rule": "检查接口参数校验、错误码规范、类型安全、防重放攻击"
}
],
"exclude": ["**/generated/**", "vendor/**", "**/*.pb.go"]
}
6.3 CI/CD 集成
GitHub Actions 示例:
name: AI Code Review
on:
pull_request:
branches: [main]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install OCR
run: npm install -g @alibaba-group/open-code-review
- name: Run Review
run: ocr review --from main --to HEAD --format json > review.json
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Post Review Comments
if: always()
run: |
# 解析 JSON 输出,发布到 PR
node .github/scripts/post-review.js review.json
GitLab CI 示例:
ai-review:
stage: review
script:
- npm install -g @alibaba-group/open-code-review
- ocr review --from $CI_MERGE_REQUEST_TARGET_BRANCH_SHA --to $CI_COMMIT_SHA --format json > review.json
artifacts:
paths:
- review.json
only:
- merge_requests
6.4 委托模式:让 Agent 自己执行审查
OCR 支持「委托模式」——文件选择和规则解析由 OCR 负责,实际审查由你已经安装的 AI Agent(如 Claude Code)执行:
# 预览委托审查范围
ocr delegate preview
# 对指定文件执行委托审查
ocr delegate rule src/main.go src/handler.go
这种模式适合已经在使用 Claude Code、Cursor 等工具的团队——OCR 负责确定性部分(文件选择、规则匹配),你的 Agent 负责语义部分(代码审查)。
七、架构哲学:从「工具」到「基础设施」的范式跃迁
7.1 为什么 OCR 不是又一个「Prompt 技巧」?
市面上有很多 AI 代码审查工具,它们的共同特点是:写一个精心设计的 prompt,然后把 diff 丢给 LLM,把输出直接贴到 PR 里。
OCR 的本质区别在于:它不是一个工具,而是一条流水线。
- 流水线的输入是确定的(Git diff + 规则配置)
- 流水线的处理是可配置的(文件筛选、分组、规则匹配)
- 流水线的输出是结构化的(JSON + 文本)
- 流水线的失败是可追溯的(session 管理 + warning 记录)
7.2 从大规模生产中蒸馏的工程约束
OCR 的每一个设计决策都可以追溯到生产环境中的真实问题:
- 为什么要有 Plan 阶段? 因为大文件 diff 直接丢给模型时,模型经常遗漏关键信息。先做一个「路线图」再执行审查,显著提高了审查质量。
- 为什么只有 code_comment 的输出才算数? 因为模型的思考过程经常会包含错误的中间结论。如果把这些也作为审查结果输出,会产生大量噪音。
- 为什么要做评论过滤? 因为模型偶尔会输出「新增变量没有被使用」这类错误评论。用 diff 信息交叉验证可以过滤掉这类误报。
- 为什么要有会话管理? 因为大规模审查可能中断(网络问题、token 限制)。会话恢复能力让审查不会白费。
7.3 专用 AI 工具的崛起
OCR 代表了一个重要趋势:专用 AI 工具正在超越通用 Agent。
通用 Agent 像一个全栈工程师——什么都能做,但每个领域都做不到最好。专用工具像一个领域专家——只做一件事,但把这件事做到极致。
在代码审查这个场景下,OCR 用工程约束弥补了 LLM 的不确定性,用领域知识(内置规则、场景化 Prompt)提高了审查质量,用专用工具集降低了 token 消耗。最终效果是:用 1/9 的成本,实现了比通用 Agent 更好的审查质量。
八、局限性与最佳实践
8.1 有意的 Recall 取舍
OCR 的 Recall(召回率)低于通用 Agent。这是有意的设计取舍——OCR 优先减少噪音(误报),而不是追求发现所有可能的问题。
对于需要极高召回率的场景(如安全审计),建议将 OCR 与传统静态分析工具结合使用。
8.2 语言支持现状
OCR 目前原生支持 Python 和 Java,计划扩展到 Go 和 TypeScript。其他语言可以通过委托模式使用。
8.3 数据隐私考虑
OCR 的审查过程需要将代码变更发送到 LLM 提供商的 API。如果你的代码涉及敏感信息,建议:
- 使用私有化部署的 LLM
- 使用委托模式(OCR 不调用 LLM,由你本地的 Agent 执行)
- 通过
exclude配置排除敏感文件
8.4 最佳实践总结
- 项目级 rule.json 必须提交到仓库——确保团队所有人使用相同的审查标准
- CI 集成使用 JSON 输出——结构化输出更容易被流水线消费
- 大规模审查使用会话恢复——避免因中断导致审查白费
- 结合传统 CI 工具——OCR 负责语义审查,传统工具负责形式化检查
- 定期更新内置规则——OCR 的内置规则会随版本更新,保持最新版本可以获得更好的审查效果
九、总结:AI 代码审查的正确打开方式
Open Code Review 的核心启示是:AI 代码审查的关键不在于模型多强,而在于工程管线多可靠。
纯 LLM 方案的问题不是模型不够聪明,而是缺少对审查流程的硬性约束。OCR 用确定性工程层(文件选择、规则匹配、并发控制、位置校准、评论过滤)包裹住 LLM 的不确定性,在发挥模型理解能力的同时,通过工程化手段保证审查的稳定性和覆盖率。
对于不同团队的选型建议:
| 团队规模 | 推荐方案 | 理由 |
|---|---|---|
| 大型团队(20人以上) | OCR + 自定义规则 | 内部编码规范可以通过规则引擎覆盖 |
| 中小团队 | OCR 委托模式 / CodeRabbit | OCR 委托模式零 LLM 成本,CodeRabbit 即用即走 |
| 开源项目 | OCR + 传统 CI | 复用已有基础设施,边际成本最低 |
最终,AI 代码审查应该是一个可接入流水线的工程任务,而不是一次临时问答。OCR 用两年大规模生产验证证明了这一点。
项目地址:github.com/alibaba/open-code-review
许可证:Apache-2.0
官方网站:open-codereview.ai