1. 项目概述:这不是一个“工具”,而是一套可落地的代码审查新范式
open-code-review 这个名字乍看像某个开源项目仓库,但实际它代表的是一种正在快速成型的工程实践范式——用开源精神重构代码审查(Code Review)的整个生命周期。它不是指“把代码审查流程开源”,而是指审查过程本身对开发者透明、可审计、可复现、可协作演进。核心关键词里反复出现的 CLI、LLM、Git,已经勾勒出它的技术骨架:一个命令行驱动的、深度集成大语言模型能力的、与 Git 工作流无缝咬合的自动化审查系统。我从 2022 年底开始在团队内部搭建第一版原型,当时只是想解决“PR 描述写得像天书”“新人不敢提意见”“资深工程师总在重复指出同一个空指针问题”这三类高频痛点。两年下来,我们发现 open-code-review 的价值远不止于“省时间”。它让代码审查从一种“人对人的主观评判”,变成了“人+模型+规则+历史数据”的协同决策过程。比如,当某次提交引入了一个新的 JSON 解析逻辑,系统不仅能调用 LLM 分析潜在的反序列化漏洞,还能自动拉取过去三年内所有同类 PR 的审查记录,告诉你“这个模式在 2023 年 Q2 被 7 位同事标记过风险,其中 4 次最终导致了线上告警”。这种基于真实工程上下文的反馈,是传统静态扫描工具永远给不了的。它适合三类人:一线开发工程师(想减少无效沟通)、技术负责人(需要可量化的质量基线)、以及正在构建内部研发平台的基建团队(需要可插拔、可审计的审查模块)。它不替代人工判断,而是把人工最该花精力的地方——比如“这个业务逻辑是否符合领域规范”——从一堆语法错误和格式噪音中解放出来。
2. 核心设计思路:为什么必须是 CLI + Git 原生集成,而不是 Web UI 或 IDE 插件
2.1 选择 CLI 作为唯一入口,是工程效率的硬性约束
很多人第一反应是:“做个漂亮的 Web 界面不更直观?” 我们试过。2023 年初上线过一个基于 React 的轻量 Web 控制台,结果三个月后就下线了。根本原因在于审查行为发生的上下文永远在终端里。一个典型的开发流是:git add .→git commit -m "feat: add user profile cache"→git push origin feat/profile-cache。此时开发者注意力完全在终端输出、Git 日志、甚至正在跑的单元测试上。如果审查要跳转到浏览器,哪怕只多一次cmd+tab切换,就会打断思维流。更致命的是权限和环境隔离问题。Web 服务要访问本地 Git 仓库、读取.env配置、调用本地 LLM 模型(比如 Ollama 的qwen2:7b),就必须做复杂的代理和沙箱,而这些恰恰是安全审计的雷区。CLI 则天然规避了这一切:它运行在用户当前 shell 环境下,拥有与开发者完全一致的文件系统权限、环境变量和网络配置。我们要求open-code-review的核心命令必须能在git commit的--hook阶段直接触发,这意味着它必须是一个零依赖、秒级启动的二进制。我们最终选用了 Rust 编译的cargo-bin方案,主程序体积压到 8.2MB,冷启动耗时稳定在 120ms 内(实测 MacBook Pro M1)。这个数字不是为了炫技,而是为了确保它能嵌入到pre-commit钩子中而不拖慢日常提交。如果你看到某个方案号称“支持 Web 和 CLI 双模式”,那它的 CLI 版本大概率是 Web 后端的一个薄客户端,本质还是网络请求——这在离线开发、内网环境或处理敏感代码时就是不可接受的单点故障。
2.2 Git 原生集成不是“适配”,而是“共生”
open-code-review 的 Git 集成深度远超git review这类简单封装。它的核心设计哲学是:Git 的每一个对象(commit、tree、blob)都是审查的天然输入源,而审查结果必须以 Git 对象的形式沉淀下来。这意味着它不生成独立的 HTML 报告或数据库记录,而是将审查结论直接写入 Git 注解(Git Notes)或作为特殊 commit 的 metadata 存储。举个具体例子:当你运行ocr review --since HEAD~3,它会解析这三次提交的 diff,调用 LLM 生成结构化建议(如{"severity":"high","file":"src/auth/jwt.go","line":47,"suggestion":"使用 jwt.ParseWithClaims 替代 jwt.Parse,显式指定 signing method"}),然后将这个 JSON 作为一条 Git Note 附加到对应 commit 上。好处是什么?第一,审查历史随代码库永久保存,git log --show-notes就能回溯;第二,任何 Git 工具(包括 GitHub/GitLab 的 Web UI)都能原生显示这些注解,无需额外插件;第三,它天然支持分支合并时的审查继承——当feat/login合并到main,其上的所有审查注解会自动成为main分支的一部分。我们曾对比过将审查结果存入独立数据库的方案,结果发现:一旦数据库宕机或网络延迟,git push就会卡住(因为 hook 等待确认),而 Git Notes 是本地操作,完全无感。这种“把审查变成 Git 的一部分”的思路,才是 open-code-review 区别于其他 LLM 代码工具的本质。
2.3 LLM 的角色定位:不是“裁判”,而是“协作者”与“记忆体”
网络热词里频繁出现的 “codex cli”、“zcode cli”、“trae cli”,本质上都在尝试把 LLM 当作一个黑盒 API 调用。open-code-review 的关键突破在于重新定义了 LLM 在审查链路中的位置。它不追求“让 LLM 一次性给出完美修复建议”,而是将其拆解为三个可验证、可审计的原子能力:
- 上下文理解器(Context Parser):负责将 Git diff、相关函数签名、最近 5 次修改该文件的 commit message,结构化为 LLM 的 prompt。这里的关键是避免信息过载——我们实测发现,超过 120 行的 diff 输入会让 LLM 的准确率断崖式下跌。因此系统会自动进行 diff 分片(chunking),每片只包含一个逻辑单元(如一个函数变更),并注入该单元的调用链快照。
- 模式识别器(Pattern Matcher):不依赖 LLM 的“自由发挥”,而是预置了 87 个经过验证的代码模式规则库(如 “SQL 字符串拼接”、“硬编码密钥”、“未处理的 panic”)。LLM 的任务只是判断当前 diff 是否匹配这些模式,并返回匹配 ID 和置信度。这大幅降低了幻觉风险,也使得审查结果可复现——换一个 LLM 模型,只要 pattern ID 一致,结论就不变。
- 知识检索器(Knowledge Retriever):这才是 LLM 最不可替代的价值。当检测到一个模糊的、需要领域知识的判断(例如,“这个缓存失效策略是否会导致库存超卖?”),系统会从团队 Wiki、过往 Jira ticket、甚至 Slack 历史消息中,用 embedding 检索出最相关的 3 条上下文,再喂给 LLM 做最终推理。这个过程全程可审计:检索到的原始文档链接、embedding 向量距离、LLM 的思考链(Chain-of-Thought)都会作为审查注解的一部分写入 Git Notes。所以,open-code-review 的 LLM 不是“代替你思考”,而是“帮你想起你忘了的那些事”。
3. 核心实现细节:从零搭建一个可运行的 open-code-review 环境
3.1 环境准备:最小可行依赖与安全边界设定
搭建 open-code-review 的第一步,不是装模型,而是划定安全红线。我们团队的红线有三条:绝不上传源码到公网 API、绝不读取.env以外的任意文件、绝不执行任何生成的代码。这意味着所有 LLM 推理必须在本地完成。目前最成熟的选择是 Ollama + 本地模型。我们推荐qwen2:7b(通义千问 2 的 7B 版本),理由很实在:它在 16GB 内存的 M1 Mac 上能稳定运行,对 Go/Python/Java 的代码理解准确率比同等参数的 Llama3 高 11.3%(基于我们自建的 2000 条代码审查 benchmark)。安装步骤极其精简:
# macOS 下一键安装 Ollama curl -fsSL https://ollama.com/install.sh | sh # 拉取模型(注意:这是唯一需要联网的步骤) ollama pull qwen2:7b # 验证模型可用性 echo "func main() { fmt.Println(\"hello\") }" | ollama run qwen2:7b "请分析这段 Go 代码是否有潜在问题?"提示:不要用
llama3:8b或phi3:3.8b。它们在代码理解任务上存在系统性偏差——对错误语法的容忍度过高,容易漏报严重 bug。我们的 benchmark 显示,qwen2:7b在“识别未关闭的 HTTP 连接”这一项上的召回率是 92%,而llama3:8b只有 63%。
Git 的配置是第二关键环节。open-code-review 依赖 Git 的notesRef功能,必须启用。在你的项目根目录下执行:
# 启用 notes 功能,使用专用 ref 避免污染主命名空间 git config core.notesRef refs/notes/open-code-review # 创建初始 notes ref(否则第一次 review 会失败) git notes --ref=open-code-review append -m "open-code-review initialized" HEAD注意:
refs/notes/open-code-review这个 ref 名称不能改。它是整个审查历史的锚点。如果后续你想在 CI 中自动运行审查,CI 脚本必须显式 fetch 这个 ref:git fetch origin refs/notes/open-code-review:refs/notes/open-code-review。
3.2 核心 CLI 工具链:自己动手写一个最小可用版
官方open-code-reviewCLI 尚未发布,但我们可以用 200 行 Bash 脚本搭出 MVP。重点不是功能多全,而是验证核心链路是否通畅。以下脚本命名为ocr,放在$PATH下即可使用:
#!/bin/bash # 文件名: ocr (open-code-review) # 用法: ocr review --since HEAD~1 set -e # 1. 解析参数 SINCE="HEAD~1" while [[ $# -gt 0 ]]; do case $1 in --since) SINCE="$2" shift 2 ;; *) echo "Usage: ocr review --since <commit>" exit 1 ;; esac done # 2. 获取 diff(只取 changed files,排除 test 和 vendor) DIFF=$(git diff --no-color --unified=0 "$SINCE" | grep -E '^(diff|---|\+\+\+|@@)' | grep -v '/test' | grep -v '/vendor') # 3. 如果 diff 为空,退出 if [ -z "$DIFF" ]; then echo "No changes found since $SINCE" exit 0 fi # 4. 构建 prompt:强调“只输出 JSON,不要解释” PROMPT="你是一个资深的代码审查助手。请严格按以下 JSON 格式输出,不要任何额外文本: { \"issues\": [ { \"file\": \"string\", \"line\": number, \"severity\": \"low|medium|high\", \"message\": \"string\", \"suggestion\": \"string\" } ] } 以下是 Git diff: $DIFF" # 5. 调用本地 LLM(Ollama) RESULT=$(echo "$PROMPT" | ollama run qwen2:7b --format=json 2>/dev/null || echo "{}") # 6. 解析 JSON 并写入 Git Notes if echo "$RESULT" | jq -e '.issues' > /dev/null 2>&1; then ISSUES=$(echo "$RESULT" | jq -r '.issues[] | "\(.file):\(.line) \(.severity) \(.message)"') if [ -n "$ISSUES" ]; then # 将每个 issue 作为一条 note 附加到对应 commit git log --oneline --format="%H" "$SINCE" | while read COMMIT; do echo "$ISSUES" | while IFS= read -r ISSUE; do if [ -n "$ISSUE" ]; then git notes --ref=open-code-review append -m "$ISSUE" "$COMMIT" 2>/dev/null || true fi done done echo "✅ Review completed. Found $(echo "$RESULT" | jq -r '.issues | length') issues." else echo "🔍 No issues detected by LLM." fi else echo "⚠️ LLM output invalid. Raw response:" echo "$RESULT" | head -n 10 fi这个脚本的价值不在于生产可用,而在于让你亲手触摸到 open-code-review 的心跳。它强制你面对三个真实问题:
- Diff 处理的脆弱性:
git diff输出格式会因 Git 版本、配置(如core.autocrlf)而变化,脚本里用grep粗筛只是权宜之计,生产环境必须用libgit2绑定解析。 - LLM 输出的不可靠性:
ollama run --format=json并不保证输出是合法 JSON,所以必须有 fallback 机制(脚本里用|| echo "{}"处理)。 - Git Notes 的并发风险:多个
git notes append同时执行可能冲突,生产版必须加锁或用git notes merge。
实操心得:我第一次运行这个脚本时,在一个含 12 个文件的 PR 上,LLM 返回了 37 条建议,其中 21 条是误报(比如把日志语句当成“硬编码”)。但关键在于,这 21 条误报全部被写入了 Git Notes,我可以用
git notes show --ref=open-code-review HEAD~1精确看到它们。这让我意识到:open-code-review 的价值不是“零误报”,而是“所有判断都留痕、可追溯、可修正”。后来我们加了一条简单规则:任何被人工驳回的 LLM 建议,都会自动加入 pattern matcher 的负样本库,下次遇到类似 diff 就会跳过。
3.3 审查规则引擎:如何让 LLM 的“主观判断”变成可复现的“客观标准”
LLM 的最大风险是“每次回答都不一样”。open-code-review 的破局点在于构建一个分层的规则引擎,把 LLM 的能力框定在可验证的轨道上。这个引擎有三层:
第一层:硬规则(Hard Rules)—— 由正则和 AST 解析器驱动
这是 100% 确定性的。例如检测 Go 代码中的os/exec.Command调用是否拼接了用户输入:
// 触发规则的代码 cmd := exec.Command("sh", "-c", userInput) // ❌ 危险 // 不触发的代码 cmd := exec.Command("ls", "/tmp") // ✅ 安全我们用go/ast解析器遍历 AST,检查CallExpr.Fun是否为exec.Command,且CallExpr.Args[1](即第二个参数)是否为Ident或BasicLit类型。如果是BinaryExpr(字符串拼接),则标记为 high severity。这类规则 200+ 条,覆盖了 OWASP Top 10 中 83% 的代码级漏洞。它们不依赖 LLM,执行速度是微秒级。
第二层:软规则(Soft Rules)—— LLM 驱动的模式匹配
这是 open-code-review 的核心创新。我们不给 LLM 一段代码让它“自由评论”,而是给它一个明确的分类任务:
请判断以下 diff 是否属于以下模式之一: PATTERN_001: SQL 字符串拼接 PATTERN_002: 未校验的用户输入直接用于文件路径 PATTERN_003: JWT token 未验证签名 ... 输出格式:PATTERN_002,0.92LLM 只需返回模式 ID 和置信度。我们用qwen2:7b微调了一个专用的 classifier 模型,专门做这个任务。在 5000 条标注数据上,它的 top-1 准确率是 94.7%,远高于通用模型的 78.2%。关键是,这个 classifier 的输出是离散的、可枚举的,可以和硬规则的结果做逻辑与/或组合。例如,只有当硬规则检测到os/exec.Command,且 soft rule 置信度 > 0.85 时,才触发 high severity 告警。
第三层:上下文规则(Context Rules)—— 基于团队知识的动态增强
这是让审查真正“懂业务”的一层。我们维护一个context-rules.yaml文件,里面是团队特有的约定:
- id: "auth-token-expiry" trigger: "jwt.NewWithClaims" context: | 所有 JWT token 必须设置 15 分钟过期,且必须使用 RSA256 签名。 参考:https://wiki.internal/auth/jwt-best-practices - id: "cache-key-format" trigger: "redis.Set" context: | 缓存 key 必须包含 service_name:version:key_pattern 格式。 例如:user-service:v2:user:12345当 soft rule 匹配到jwt.NewWithClaims时,系统会自动加载auth-token-expiry的 context,并将其中的约束作为 prompt 的一部分再次调用 LLM:“请检查此 JWT 生成代码是否满足:1) 过期时间 ≤ 15 分钟;2) 使用 RSA256 签名”。这样,LLM 的判断就不再是凭空猜测,而是基于团队共识的精准校验。
4. 实战场景拆解:一个真实 PR 的 open-code-review 全流程
4.1 场景还原:电商订单服务新增优惠券核销接口
假设你正在开发一个电商后端,刚提交了一个 PR,内容是新增/api/v1/orders/{id}/apply-coupon接口。这个 PR 包含 3 个文件变更:handler.go(HTTP handler)、service.go(业务逻辑)、coupon_test.go(单元测试)。现在,你执行:
ocr review --since HEAD~1系统会按以下顺序工作:
Step 1:Diff 解析与分片git diff HEAD~1输出约 280 行。系统自动识别出三个逻辑单元:
handler.go的ApplyCouponHandler函数(42 行)service.go的ApplyCoupon方法(87 行)coupon_test.go的新测试用例(31 行)
每个单元被单独送入审查流水线。测试文件会被优先处理,因为它的变更往往暗示了业务逻辑的重点。
Step 2:硬规则扫描
对service.go的ApplyCoupon方法,硬规则引擎立刻捕获两个问题:
- 第 53 行:
db.QueryRow("SELECT * FROM coupons WHERE code = '" + couponCode + "'")—— 触发PATTERN_001(SQL 字符串拼接),severity: high - 第 78 行:
log.Printf("Coupon applied: %s", couponCode)—— 触发PATTERN_012(日志中打印敏感字段),severity: medium
这些结果直接写入 Git Notes,不经过 LLM。
Step 3:软规则匹配
对handler.go的ApplyCouponHandler,soft rule classifier 返回:PATTERN_045,0.96(HTTP handler 未校验用户权限)PATTERN_022,0.88(未处理并发请求下的优惠券超领)
注意,classifier 没有说“你错了”,而是说“这个模式匹配度很高”。这为后续的人工判断提供了明确焦点。
Step 4:上下文规则注入
因为PATTERN_045被触发,系统加载context-rules.yaml中的auth-check-required规则:
所有 /api/v1/orders/* 接口必须在 handler 开头调用 auth.CheckPermission(ctx, "ORDER_WRITE")于是,系统构造新 prompt:“请检查 ApplyCouponHandler 是否在开头调用了 auth.CheckPermission(ctx, "ORDER_WRITE")?如果不是,请指出缺失位置。” LLM 分析后返回:
{"missing": true, "suggestion": "在第 22 行 handler 函数开头添加:if !auth.CheckPermission(ctx, \"ORDER_WRITE\") { return errors.New(\"permission denied\") }"}Step 5:审查结果聚合与呈现
最终,git notes show --ref=open-code-review HEAD输出如下(已格式化):
[open-code-review] hard-rule: PATTERN_001 (SQL injection) in service.go:53 hard-rule: PATTERN_012 (sensitive log) in service.go:78 soft-rule: PATTERN_045 (auth check missing) confidence: 0.96 soft-rule: PATTERN_022 (concurrency risk) confidence: 0.88 context-rule: auth-check-required violation at handler.go:22这个输出不是一份报告,而是一个行动清单。你可以直接复制context-rule的建议,粘贴到代码里修复。更重要的是,所有这些结论都附着在HEAD这个 commit 上,未来任何人git show HEAD都能看到完整的审查上下文。
4.2 团队协作模式:如何让 open-code-review 成为团队的“共同记忆”
open-code-review 的终极价值,是把分散在 Slack、Confluence、个人脑中的隐性知识,固化为代码库的显性资产。我们团队的做法是:
每日晨会同步:每天站会前 5 分钟,所有人运行
ocr summary --last 24h,这个命令会扫描过去 24 小时所有 commit 的 notes,生成一个汇总:🔍 Last 24h review summary: - High severity: 3 (all SQL injection) - Medium severity: 12 (mostly logging & error handling) - New pattern detected: PATTERN_099 (Redis pipeline misuse) — 2 occurrencesPattern 库共建:当
PATTERN_099被多次触发,团队会开一个 30 分钟的短会,由第一个发现者讲解问题本质,然后一起把它写入patterns/099-redis-pipeline.md,并更新 classifier 的训练数据。这个过程,就是团队知识的沉淀。新人 onboarding:新成员入职第一天,不是看文档,而是运行
ocr learn --from 2023-01-01 --to 2023-12-31。这个命令会回放过去一年所有审查记录,按 severity 和 file 分组展示。他看到的不是抽象规则,而是“去年 3 月,张三在payment.go第 142 行犯过的同样错误,被李四用PATTERN_033捕获”。这种基于真实代码的 learning,效果远超阅读 100 页 PDF。
踩过的坑:我们最初允许 LLM 直接生成修复代码(如
git apply补丁),结果导致两次线上事故——LLM 错误地修改了第三方 SDK 的 vendor 文件。现在,所有suggestion字段只提供文字描述,绝不包含代码块。修复必须由人手写,这是不可逾越的红线。
5. 常见问题排查与避坑指南:来自 127 次真实故障的总结
5.1 LLM 返回空或乱码:不是模型问题,而是 prompt 工程缺陷
现象:ocr review命令执行后,终端只输出null或一串乱码(如UUU),Git Notes 无内容。
根源分析:这不是 Ollama 或模型坏了,而是 prompt 中的 diff 内容触发了 LLM 的 tokenizer 边界错误。我们统计了 127 次同类故障,92% 的原因是 diff 中包含了非 UTF-8 字符(如 Windows 的\r\n、某些编辑器插入的 BOM 头、或二进制文件的 diff 块)。
解决方案:在调用 LLM 前,对 diff 做严格清洗:
# 清洗 diff 的 Bash 片段 CLEAN_DIFF=$(echo "$DIFF" | \ iconv -f utf-8 -t utf-8//IGNORE 2>/dev/null | \ sed 's/\r$//' | \ grep -v '^Binary files' | \ grep -v '^diff --git' | \ head -n 500) # 强制截断,防爆内存关键技巧:
iconv -f utf-8 -t utf-8//IGNORE是救命命令。它会丢弃所有无法转换的字节,而不是报错退出。head -n 500是另一重保险——LLM 的 context window 是有限的,强行喂入 2000 行 diff,只会让模型“忘记”前面的内容。
5.2 Git Notes 冲突:多人同时 review 同一个 commit 的正确应对
现象:git notes append报错fatal: cannot lock ref 'refs/notes/open-code-review'。
这是 Git 的乐观锁机制在起作用。当两个开发者几乎同时对HEAD运行ocr review,Git 会拒绝第二个写入,防止数据覆盖。
标准解法是使用git notes merge:
# 在发生冲突时,先 fetch 最新 notes git fetch origin refs/notes/open-code-review:refs/notes/open-code-review # 然后 merge(自动解决大多数冲突) git notes merge --strategy=ours origin/refs/notes/open-code-review但更优雅的方案是:让每个开发者使用自己的 notes ref。在~/.gitconfig中添加:
[notes] ref = refs/notes/open-code-review-$(USER)这样,张三的 review 写入refs/notes/open-code-review-zhangsan,李四的写入refs/notes/open-code-review-lisi,完全隔离。CI 系统则统一读取refs/notes/open-code-review-ci。这种“分片 notes”模式,是我们团队在 50 人规模下稳定运行的关键。
5.3 审查结果“过于严格”:如何平衡 LLM 的谨慎与开发者的效率
现象:LLM 对每个fmt.Println都标记为mediumseverity,认为“生产环境不应有调试日志”,导致 PR 被淹没在 50+ 条低优先级建议中。
这不是 LLM 的错,而是规则权重没调好。open-code-review 的设计原则是:LLM 的输出必须经过 severity 映射层才能落地。我们在config/severity-mapping.yaml中定义:
# 默认映射 default: PATTERN_001: high PATTERN_012: medium PATTERN_045: high # 例外规则:在 test 文件中,PATTERN_012 降级为 low exceptions: - file_pattern: ".*_test\.go$" pattern_id: "PATTERN_012" severity: "low"当ocr review发现coupon_test.go中的日志时,会查表发现这是例外,自动降级。这个映射表是团队持续迭代的产物,每周回顾会上,大家会投票调整哪些 pattern 该升/降级。
实操心得:我们曾把
PATTERN_022(并发风险)默认设为high,结果导致大量 false positive。后来改为medium,并加了一条业务规则:“只有当函数名包含Apply或Deduct时,才升级为 high”。这个细粒度控制,让审查的信噪比提升了 3 倍。
5.4 模型响应缓慢:本地 LLM 性能优化的 3 个硬核技巧
现象:ocr review命令卡住 15 秒以上,开发者失去耐心,直接跳过审查。
根本原因:qwen2:7b在 CPU 模式下推理太慢。但我们不推荐买 GPU——成本太高。三个零成本优化方案:
量化模型:用
ollama create重新打包模型,启用q4_k_m量化:# 创建量化版模型 echo 'FROM qwen2:7b PARAMETER num_ctx 4096 PARAMETER num_gpu 0 ' | ollama create qwen2:7b-quant -f -量化后模型体积从 4.2GB 降到 2.1GB,推理速度提升 2.3 倍。
Prompt 缓存:对重复出现的 pattern,提前生成 prompt embedding 并缓存。我们用
chroma做本地向量库,当 classifier 识别出PATTERN_045,直接从缓存中取出最优 prompt,跳过实时构造。异步审查:
ocr review默认同步执行,但可加--async参数:ocr review --since HEAD~1 --async # 立即返回,后台进程继续运行 # 结果会写入 .ocr-async-result.json开发者可以继续工作,5 分钟后
cat .ocr-async-result.json查看结果。这个模式在大型 monorepo 中效果显著。
6. 进阶扩展:从个人工具到团队基础设施
6.1 CI/CD 集成:让 open-code-review 成为代码入库的“守门员”
把ocr review嵌入 CI 流水线,不是简单加一行script: ocr review,而是要设计一套分级拦截策略。我们采用三段式:
Stage 1:Pre-merge Check(PR 创建时)
在 GitHub Actions 的pull_requesttrigger 中运行,只检查本次 PR 的 diff。如果 hard rule 触发highseverity,直接exit 1,阻止合并。这是底线。Stage 2:Post-merge Audit(合并到 main 后)
在pushtomain的 trigger 中运行,扫描整个 commit range。它不阻断流程,但会:- 将所有 review notes 推送到
refs/notes/open-code-review-ci - 生成 weekly report,发送给 Tech Lead
- 如果发现新 pattern(如
PATTERN_099首次出现),自动创建 Jira ticket “Investigate new pattern 099”
- 将所有 review notes 推送到
Stage 3:Historical Scan(每月一次)
用ocr scan --from 2023-01-01 --to 2023-12-31全量扫描历史代码,目的是:- 更新 classifier 的训练数据(用新发现的 pattern 正负样本)
- 生成技术债地图:
grep "PATTERN_001" .git/refs/notes/open-code-review-ci | wc -l统计 SQL 注入遗留数量
关键配置:CI 中必须显式设置
OLLAMA_HOST=http://host.docker.internal:11434,否则容器内无法访问宿主机的 Ollama 服务。这是 Docker 网络的经典坑。
6.2 多模型协同:为什么不用一个“全能”模型,而要组合 3 个专用模型
网络热词里常讨论“哪个 LLM 最适合代码”,但 open-code-review 的实践证明:没有全能模型,只有合适分工。我们生产环境部署了三个模型:
qwen2:7b:主模型,负责 soft rule classification 和 context-aware suggestion。优势是中文理解强,对国内常用框架(如 Gin、Beego)的 API 熟悉度高。starcoder2:3b:专用于 hard rule 的 AST 生成。当需要“把这段 Go 代码转成 AST JSON”时,starcoder2的准确率比qwen2高 22%,因为它在 The Stack 数据集上做过专项微调。tinyllama:1.1b:极简模型,只做一件事——判断一条建议是否该被人工驳回。输入是 LLM 的原始 suggestion 和开发者git blame查到的作者信息,输出是{"reject": true, "reason": "author is security expert"}。这个模型小到可以编译进 CLI 二进制,毫秒级响应。
这种“模型即服务(MaaS)”架构,让每个模型只做自己最擅长的事,整体准确率和稳定性远超单一大模型。
6.3 安全红线:如何彻底杜绝密钥泄露风险
热词中反复出现的“使用 LLM 时如何防止密钥泄露”,是 open-code-review 的生命线。我们的方案是“三不原则”:
不读取:CLI 启动时,会扫描当前目录及父目录,如果发现
.env、.secrets、aws-credentials等敏感文件,立即退出并报错:“Sensitive file detected. Aborting.”。这个扫描用find+stat实现,不打开文件内容,纯靠文件名和权限位判断。不上传:所有 LLM 调用都通过本地 Ollama,网络请求只限
localhost:11434。我们在~/.ollama/config.json中禁用所有远程 registry: