1. 项目概述:这不是一个工具,而是一套可落地的开源代码评审工作流
“open-code-review”这个词乍一听像某个新发布的开源项目名,但其实它代表的是一种正在快速演进的工程实践范式——把代码评审(Code Review)这件事,从传统意义上依赖人工经验、主观判断、耗时耗力的“人肉流程”,转向由可复现、可审计、可扩展、可嵌入开发链路的自动化+智能化协同机制。我从去年开始在三个不同规模的团队里推动类似实践,不是简单地加个AI插件,而是从 Git 提交粒度出发,把 diff 分析、上下文理解、规范校验、风险识别、建议生成全部拆解成可配置、可验证、可回溯的模块。核心关键词open-code-review不是指某款特定产品,而是指整套设计原则:开放协议、开放规则、开放反馈、开放可追溯。它天然兼容CLI工具链,深度依赖git diffs作为输入源,同时以LLM Agent为智能增强层——注意,这里用的是 Agent,不是单纯的 LLM 调用。Agent 意味着它有记忆、有工具调用能力、有决策闭环,比如能自动查 Git Blame 获取作者历史风格,能调用 SonarQube API 获取该函数的历史缺陷密度,能读取 PR 描述里的 Jira ID 并拉取关联需求文档做语义对齐。这和直接丢一段代码给 ChatGPT 问“这段有没有 bug”有本质区别。很多人混淆LLM、Agent、Embedding这几个概念,简单类比:LLM 是一个超级语言处理器,像一台高配 CPU;Embedding 是把代码/文档变成向量的“翻译器”,相当于内存地址映射表;而 Agent 是装了操作系统、驱动程序、调度策略的完整计算机——它知道什么时候该查文档、什么时候该跑测试、什么时候该写注释、什么时候该沉默。像 DeepSeek、Qwen、CodeLlama 这些模型,属于底层 LLM;Codex CLI、ZCode CLI、Claude Code CLI 这些,是封装了特定 LLM + 工具链的命令行界面;而真正构成 open-code-review 骨架的,是背后那套可插拔的 Agent 编排逻辑。你不需要自己训练模型,但必须清楚每一步“谁在做什么、依据什么、输出什么、怎么验证”。这套东西适合三类人:一线开发者想减少低效重复评审、Tech Lead 想统一团队质量水位、Infra 工程师想把质量门禁嵌入 CI 流水线。它不替代人,但能让人的注意力真正聚焦在架构权衡、业务逻辑漏洞、跨模块耦合这些机器暂时无法替代的高价值判断上。
2. 整体设计思路:为什么必须绕开“一键 AI 审查”陷阱
2.1 传统 Code Review 的三大硬伤,决定了不能照搬 LLM 原始能力
我见过太多团队踩坑:花两周接入某款“AI Code Review”SaaS,结果上线后发现 80% 的建议是语法级噪音(比如“变量名建议用 camelCase”),剩下 20% 里又有 15% 是基于过时文档的错误推断(比如把已废弃的 internal API 当作当前标准)。问题出在哪?根本原因在于,原始 LLM 对代码的理解是静态快照式的,而真实开发场景是动态上下文驱动的。举个具体例子:一个 PR 修改了UserService.updateProfile()方法,LLM 看到 diff 后可能建议“增加空值校验”,但它不知道这个方法在上周刚被重构过,所有上游调用方已强制保证非空——这个信息藏在 Git commit message 里、藏在 Confluence 的重构纪要里、藏在 Slack 的 #backend 频道讨论中。纯 LLM 没法主动获取这些,它只能猜。所以 open-code-review 的第一设计原则就是:拒绝把 diff 直接喂给大模型,必须先做上下文编织(Context Weaving)。我们不是让模型“看代码”,而是让它“参与一次真实的工程师评审会”——会议材料包括:本次 diff 内容、最近三次相关文件的 commit log、该模块的 README.md 片段、Jira 中关联 ticket 的描述与评论、SonarQube 上该函数的历史技术债评分。这些材料不是简单拼接,而是按权重注入:diff 本身权重 1.0,commit log 权重 0.7,README 权重 0.5,Jira 描述权重 0.6。权重不是拍脑袋定的,而是通过 A/B 测试统计得出——我们曾用 200 个历史 PR 做对照实验,发现当 commit log 权重低于 0.5 时,模型对“为什么改这里”的归因准确率下降 37%。这就是为什么 open-code-review 必须是 CLI 驱动的:只有命令行才能精确控制输入源、权重、超参、缓存策略。图形界面或 Web UI 天然丢失这些精细控制能力。
2.2 Agent 架构 vs 单一 LLM 调用:关键差异在状态管理与工具调用
很多人问“Agent 和 LLM 有什么区别”,我用一个实际评审任务来说明:当检测到新增 SQL 查询时,open-code-review Agent 的执行路径是:
- 识别意图:从 diff 中提取出
SELECT * FROM users WHERE id = ?这行,触发 “SQL 安全检查” 子流程; - 调用工具:启动本地
sql-lint工具扫描是否含SELECT *、是否缺少索引提示; - 查证上下文:用
git blame查该 SQL 所在文件的最近修改者,并调用内部 API 查询此人过去三个月同类 SQL 的性能告警次数; - 生成建议:若
sql-lint报错且该开发者历史告警 > 2 次,则生成强提醒:“⚠️ 检测到未限定字段的 SELECT,结合您近期 3 次类似操作均引发慢查询,请补充字段列表并添加 USE INDEX 提示”; - 记录决策日志:将本次调用的工具返回值、上下文查询结果、最终建议原文全部写入
.review-log/2024-06-15_PR1234.json,供后续审计。
而单纯 LLM 调用只会做第 1 步和第 4 步,且第 4 步的依据仅来自 prompt 里塞进去的几行示例。Agent 的核心价值在于Tool Calling + Memory + Planning Loop。我们选型时明确排除了所有“黑盒 Agent 框架”,坚持用 LangChain 的自定义 AgentExecutor + 自研 Tool Registry,因为必须确保每个工具调用都能被拦截、被审计、被替换。比如git blame工具,在 macOS 和 Linux 下命令参数略有不同,我们就用 Python 封装一层,对外统一接口get_author_history(file_path, lines),内部自动适配系统。这种可控性,是任何开箱即用的“Codex CLI”或“Claude CLI”无法提供的——它们把工具链固化在二进制里,你没法知道它调用了什么、传了什么参数、返回值是否被篡改。open-code-review 的开放性,首先体现在工具层的完全透明。
2.3 为什么 Git Diffs 是不可替代的输入源,而非代码文件本身
有人提议“直接分析整个源文件”,这在工程上是灾难性的。我拿一个真实案例说明:某次 PR 修改了payment_service.go的第 45 行,增加了对 PayPal webhook 签名的二次校验。如果 Agent 分析整个文件,它会看到 800 行代码,其中 700 行是无关的支付网关抽象层。模型注意力必然被稀释,大概率忽略第 45 行这个关键变更。而git diff输入只包含:
+ if !isValidPayPalSignature(payload, signature) { + return errors.New("invalid PayPal signature") + }这才是评审需要聚焦的“信号”。我们实测对比过:用完整文件输入时,关键安全建议生成准确率仅 42%;用 diff 输入时,提升至 89%。更深层的原因是,diff 天然携带变更意图信号:+行表示“新增防御逻辑”,-行表示“移除过时校验”,@@ -23,5 +23,7 @@这样的 hunk header 告诉 Agent “这个改动影响范围很小,专注此处即可”。我们在 CLI 中强制要求输入必须是git diff格式,并内置预处理模块:自动过滤掉vendor/、node_modules/、*.min.js等无意义变更,对go.mod或package-lock.json这类依赖文件单独走语义解析(比如检测是否升级了有已知 CVE 的库版本)。这个设计直接决定了评审结果的信噪比。很多团队失败,就是因为第一步就错了——他们用 IDE 插件截取“当前打开的文件”,本质上还是在分析静态快照,而不是动态变更。
3. 核心细节解析:CLI 工具链的四大支柱模块
3.1 Diff 解析器:不止于文本分割,更要语义归因
open-code-review 的 CLI 入口叫ocr(open code review 的缩写),它的第一个子命令ocr diff-parse并不是简单地git diff --no-color,而是做了三层增强:
第一层:结构化 diff 提取
我们不用正则硬匹配+/-行,而是调用libgit2的绑定库(Go 用go-git,Python 用pygit2)解析二进制 diff,精准获取:
- 变更类型(add/modify/delete/rename)
- 文件路径(含 submodule 嵌套路径)
- 行号范围(old_start, old_count, new_start, new_count)
- 二进制变更标识(是否图片/字体等不可审内容)
这样做的好处是,当遇到git diff --binary输出时,能跳过*.png文件,避免 LLM 错误尝试“解读图片内容”。
第二层:变更语义标注
对每一处+行,运行轻量级规则引擎标注意图:
if !isValidPayPalSignature(...)→ 标签security:input_validationlog.Info("order created")→ 标签observability:loggingdb.Exec("INSERT ...")→ 标签data:persistence
规则库是 YAML 配置,支持正则+AST 匹配混合模式。例如检测 SQL 注入风险,既用正则r"db\.Exec\(\".*\{.*\}.*\"\)",也用 Go AST 解析ast.CallExpr的参数字符串是否含未转义变量。我们维护了 47 条高频变更模式,覆盖 92% 的日常 PR 场景。这个步骤耗时 < 200ms,但让后续 Agent 能直接按标签路由到对应专家工具(如 security 标签触发sqlmap扫描,observability 标签触发logcheck规则库)。
第三层:上下文锚点生成
为每个变更块生成三个锚点:
- Git 锚点:
git show HEAD~3:payment_service.go | sed -n '40,50p'(获取变更前 3 次提交的上下文) - 文档锚点:自动搜索
docs/payment.md中含 “PayPal webhook” 的段落 - 测试锚点:查找
*_test.go中调用isValidPayPalSignature的测试用例
这些锚点不是直接加载内容,而是生成 lazy-loading URI(如file://./docs/payment.md#L120),Agent 在需要时才解析。实测表明,预加载全部上下文会使平均响应时间增加 3.2 秒,而懒加载后稳定在 1.8 秒内,且内存占用降低 65%。
提示:
ocr diff-parse支持--debug-context参数,会输出所有生成的锚点 URI 列表,方便人工验证上下文相关性。这是调试 Agent 行为的第一步,务必养成习惯。
3.2 Agent 编排器:状态机驱动的评审决策流
Agent 不是“一个大模型+一堆 prompt”,而是一个带状态迁移的有限自动机。ocr agent-run的核心是ReviewStateMachine,它定义了 7 个状态和 12 条迁移边:
| 状态 | 触发条件 | 执行动作 | 输出 |
|---|---|---|---|
INIT | 接收 diff 解析结果 | 加载全局规则库,初始化内存 | {context: {}, findings: []} |
CONTEXT_FETCH | 检测到security标签 | 并行调用git blame+jira api+sonar api | 新增author_history,jira_desc字段 |
RULE_CHECK | 上下文就绪 | 运行本地规则引擎(如禁止SELECT *) | 新增rule_violations数组 |
LLM_ENHANCE | 规则检查完成且需语义推理 | 构建带权重的 context prompt,调用 LLM API | 新增llm_suggestions数组 |
CONSOLIDATE | 所有子任务完成 | 合并 rule + LLM 结果,去重加权 | {final_findings: [...]} |
FORMAT_OUTPUT | consolidation 完成 | 生成 Markdown 报告,插入 diff 行号引用 | report.md |
PERSIST_LOG | 格式化完成 | 写入.review-log/,更新 Git note | 日志哈希值 |
关键设计点在于状态可中断、可重入、可审计。比如CONTEXT_FETCH状态若超时(默认 8s),自动降级为只用git blame,跳过 Jira/Sonar;下次运行时,状态机会从CONTEXT_FETCH继续,而非重头开始。所有状态迁移都记录到内存 trace 中,ocr agent-run --trace可输出完整决策路径。我们曾用此功能定位到一个 Bug:某次 LLM 返回格式错误 JSON,导致CONSOLIDATE状态崩溃,trace 显示它卡在LLM_ENHANCE的第 3 次重试,从而快速修复了 prompt 的 schema 约束。
3.3 规则引擎:可热加载的领域知识库
open-code-review 的灵魂不在 LLM,而在规则引擎。它由三部分组成:
1. 内置规则集(Built-in Rules)
用 YAML 定义,存于rules/builtin/目录,包含:
security.yaml: 检测硬编码密钥、SQL 拼接、XSS 风险模板performance.yaml: 识别 N+1 查询、大对象序列化、阻塞式 I/Omaintainability.yaml: 检查圈复杂度 > 10、重复代码块、缺失单元测试覆盖率
每条规则含id,severity(critical/high/medium/low),pattern(AST 或正则),message,fix_suggestion。例如security.yaml中一条规则:
id: "SEC-003" severity: critical pattern: ast: "CallExpr[Fun->Ident.Name=='os.OpenFile'][Args[2]->IntLit.Value=='0600']" message: "文件权限设置为 0600,但当前用户可能无权访问" fix_suggestion: "使用 os.Stat 检查父目录权限,或改用 0644"2. 团队自定义规则(Team Rules)
存于项目根目录ocr-rules.yaml,优先级高于内置规则。支持继承:
extends: ["builtin/security.yaml"] rules: - id: "TEAM-DB-001" pattern: "CallExpr[Fun->Ident.Name=='db.Query'][Args[0]->StringLit.Value~'SELECT.*FROM.*WHERE.*=.*']" message: "原始 SQL 查询,应使用参数化查询"3. 动态规则(Dynamic Rules)
通过 HTTP API 注册,用于临时策略。比如发布前夜,PM 要求“所有涉及用户手机号的字段必须打码”,运维可 POST 一条规则到http://localhost:8080/rules,Agent 下次运行自动加载,无需重启。
规则引擎启动时编译所有 YAML 为内存索引,AST 规则用go/ast或tree-sitter解析,正则规则用regexp.Compile预编译。实测 500 条规则下,单文件扫描耗时 < 150ms。这是 open-code-review 可控性的基石——你可以随时关闭某条规则,或调整其 severity,而 LLM 的“幻觉”无法被如此精确干预。
3.4 输出生成器:人机协同的报告格式设计
ocr report-gen不生成“AI 审查报告”,而是生成工程师可直接 copy-paste 到 GitHub PR comment 的 Markdown 片段。格式严格遵循团队约定:
### 🔍 自动评审发现(由 open-code-review v0.4.2 生成) #### ⚠️ 高风险建议(2 条) - **[security]** `payment_service.go:47` 检测到 PayPal 签名校验新增,但未验证 webhook body 的完整性(缺少 HMAC-SHA256 校验)。 ✅ 建议:参考 `docs/security/webhook.md#L88` 添加 `verifyWebhookBody(payload)` 调用。 📜 上下文:该逻辑在 Jira TICKET-1234 中明确要求双因子校验。 #### 💡 优化建议(1 条) - **[maintainability]** `payment_service.go:120` `processRefund()` 函数圈复杂度为 12,超过阈值 10。 ✅ 建议:将退款状态校验逻辑拆分为独立函数 `validateRefundEligibility()`。 📊 数据:SonarQube 历史评分为 3.2/10(低于团队均值 4.7)。 > 💬 此报告由自动化流程生成,**不替代人工评审**。请重点关注标有 ⚠️ 的高风险项。关键设计:
- 行号精确绑定:所有建议都带
file:line锚点,点击可跳转到 GitHub PR 的对应 diff 行。 - 证据链显式呈现:每条建议后跟
✅ 建议、📜 上下文、📊 数据三段式说明,杜绝“我觉得有问题”式模糊表述。 - 责任归属清晰:底部声明强调“不替代人工”,规避法律与流程风险。
- 可配置模板:通过
--template pr-comment或--template slack切换输出格式,Slack 模板会自动 @ 相关 reviewer。
我们曾对比过:纯 LLM 生成的报告平均被开发者忽略率 68%,而这种结构化报告的采纳率提升至 89%。因为工程师一眼就能看到“证据在哪、依据是什么、怎么改”,而不是在一段 AI 生成的散文里找重点。
4. 实操过程:从零搭建一个可运行的 open-code-review 环境
4.1 环境准备:最小可行依赖与版本锁定
不要试图用最新版所有工具。我们经过 17 次 CI 环境重建测试,确定以下组合最稳定:
| 组件 | 版本 | 选择理由 |
|---|---|---|
| Go | 1.21.6 | go-git在 1.22+ 有内存泄漏,1.21.6 是最后一个 LTS |
| Python | 3.10.12 | LangChain 0.1.14 与 PyTorch 2.1.2 兼容性最佳 |
| Git | 2.39.2 | 支持git diff --submodule=diff,对 monorepo 关键 |
| LLM API | OpenRouter (DeepSeek-Coder 33B) | 免费 tier 足够小团队,响应稳定,支持 function calling |
安装命令(macOS/Linux):
# 1. 安装 Go 1.21.6(避免 brew install go,默认是最新版) wget https://go.dev/dl/go1.21.6.darwin-arm64.tar.gz sudo rm -rf /usr/local/go sudo tar -C /usr/local -xzf go1.21.6.darwin-arm64.tar.gz # 2. 创建专用 Python 环境 python3 -m venv ~/venv/ocr-env source ~/venv/ocr-env/bin/activate pip install "langchain==0.1.14" "pygit2==1.12.1" "tree-sitter==0.20.4" # 3. 安装 Git 2.39.2(Ubuntu 示例) sudo apt-get install -y software-properties-common sudo add-apt-repository ppa:git-core/ppa sudo apt-get update sudo apt-get install -y git=1:2.39.2-0ubuntu0.22.04.1注意:
pygit2必须与系统 Git 版本匹配,否则git blame调用会 segfault。我们曾因此浪费 3 天排查时间——错误日志只显示Segmentation fault (core dumped),没有更多线索。解决方案是pip uninstall pygit2 && pip install pygit2==1.12.1,该版本专为 Git 2.39 编译。
4.2 初始化项目:五步创建可评审的仓库
假设你要为一个 Go 项目启用 open-code-review,按顺序执行:
Step 1:初始化 ocr 配置
# 在项目根目录运行 ocr init --team-rules ./ocr-rules.yaml生成.ocr/config.yaml:
llm: provider: "openrouter" model: "deepseek-coder:33b" api_key: "${OPENROUTER_API_KEY}" # 从环境变量读取 rules: builtin_dir: "./rules/builtin" team_file: "./ocr-rules.yaml" dynamic_url: "http://localhost:8080/rules" output: template: "pr-comment" max_findings: 10Step 2:配置 Git Hook(可选但强烈推荐)
# 创建 pre-push hook,每次推送前自动评审 cat > .git/hooks/pre-push << 'EOF' #!/bin/bash echo "🔍 Running open-code-review before push..." if ! ocr agent-run --diff "$(git diff origin/main...HEAD)" --format markdown; then echo "❌ open-code-review failed. Fix issues or skip with 'git push --no-verify'" exit 1 fi EOF chmod +x .git/hooks/pre-pushStep 3:编写第一条团队规则在ocr-rules.yaml中添加:
extends: ["builtin/security.yaml"] rules: - id: "TEAM-PAYPAL-001" pattern: ast: "CallExpr[Fun->Ident.Name=='isValidPayPalSignature'][Args[0]->Ident.Name=='payload']" message: "PayPal 签名校验必须配合 body 完整性校验" fix_suggestion: "在调用 isValidPayPalSignature 前,添加 verifyWebhookBody(payload)" severity: criticalStep 4:测试单文件评审
# 模拟一个变更 echo "func handlePayPalWebhook(payload []byte, signature string) error { if !isValidPayPalSignature(payload, signature) { return errors.New(\"invalid signature\") } // process... }" > test_webhook.go # 运行评审(不走 Git,直接测试) git add test_webhook.go git commit -m "add paypal webhook handler" ocr agent-run --diff "$(git diff HEAD~1)" --debug你会看到输出中TEAM-PAYPAL-001规则被触发,并生成带行号的建议。
Step 5:集成到 GitHub Actions在.github/workflows/ocr.yml中:
name: Open Code Review on: [pull_request] jobs: ocr: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # 必须,否则 git blame 失败 - name: Setup Go uses: actions/setup-go@v4 with: go-version: '1.21.6' - name: Install ocr CLI run: | go install github.com/your-org/ocr-cli@latest - name: Run open-code-review env: OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} run: ocr agent-run --pr-number ${{ github.event.number }} --format markdown4.3 CLI 核心命令详解与参数调优
ocrCLI 有 7 个主命令,日常使用频率最高的是这 4 个:
ocr diff-parse—— 理解你的变更
# 基础用法:解析当前分支相对于 main 的 diff ocr diff-parse --base-ref main # 进阶:只分析特定文件类型,跳过测试 ocr diff-parse --base-ref main --include "*.go" --exclude "*_test.go" # 调试:输出所有生成的上下文锚点 ocr diff-parse --base-ref main --debug-context关键参数:
--base-ref:指定比较基准(默认origin/main),支持HEAD~3、v1.2.0等任意 Git ref--hunk-threshold:设置最小变更块大小(默认 3 行),小于阈值的微小变更会被合并到邻近块,避免碎片化评审
ocr agent-run—— 执行智能评审
# 最简运行(使用默认配置) ocr agent-run --diff "$(git diff origin/main)" # 指定 LLM 模型和温度 ocr agent-run --diff "$DIFF" --model "qwen2-72b" --temperature 0.3 # 限制只运行特定规则组 ocr agent-run --diff "$DIFF" --rules "security,performance"关键参数:
--timeout:全局超时(默认 30s),CONTEXT_FETCH状态超时后自动降级--max-llm-calls:防止 LLM 陷入循环,单次运行最多调用 3 次(默认)--dry-run:不生成报告,只输出决策 trace,用于调试 Agent 流程
ocr report-gen—— 生成人类可读报告
# 生成 GitHub PR comment 格式 ocr report-gen --input ./review-result.json --format pr-comment # 生成 Slack 格式,自动 @ reviewer ocr report-gen --input ./review-result.json --format slack --reviewer "@alice" # 导出为 HTML 供离线审阅 ocr report-gen --input ./review-result.json --format html --output report.html关键参数:
--template-file:指定自定义 Markdown 模板路径,支持 Jinja2 语法--hide-low-severity:过滤掉low级别建议,减少噪音
ocr rules-list—— 管理规则库
# 查看当前激活的所有规则(含来源) ocr rules-list # 启用/禁用某条规则 ocr rules-toggle --id "SEC-003" --disable # 从远程 URL 加载规则(用于跨团队共享) ocr rules-import --url "https://raw.githubusercontent.com/org/rules/main/go-security.yaml"4.4 实战案例:一次真实 PR 的全流程评审记录
我们以一个真实 PR(#1234)为例,展示 open-code-review 如何工作:
PR 描述:
“修复 PayPal webhook 签名校验漏洞。之前只校验 header signature,未校验 body 完整性。参考 Jira TICKET-1234。”
Diff 内容节选:
func handlePayPalWebhook(payload []byte, signature string) error { + if !verifyWebhookBody(payload) { + return errors.New("webhook body tampered") + } if !isValidPayPalSignature(payload, signature) { return errors.New("invalid signature") }ocr agent-run执行过程:
diff-parse识别出两处+行,标注security:input_validation标签;CONTEXT_FETCH状态并行调用:git blame:确认handlePayPalWebhook由dev-alex于 3 天前创建;jira api:拉取 TICKET-1234 描述,含“必须校验 body HMAC”要求;sonar api:该函数历史技术债评分为 2.1/10(低分);
RULE_CHECK运行TEAM-PAYPAL-001规则,匹配成功;LLM_ENHANCE构建 prompt,包含 Jira 要求原文和 Sonar 评分,LLM 生成建议:“✅ 建议补充verifyWebhookBody的异常处理,参考docs/security/webhook.md#L88的重试策略”;CONSOLIDATE合并规则建议(critical)和 LLM 建议(high),去重后保留两条;FORMAT_OUTPUT生成 Markdown,精确绑定到handlePayPalWebhook函数起始行;PERSIST_LOG写入./.review-log/2024-06-15_PR1234.json,含完整 trace。
最终报告效果:
GitHub PR 页面自动显示机器人评论,包含:
- 一条 ⚠️ 高风险项(规则触发)
- 一条 💡 优化建议(LLM 增强)
- 所有建议均带
docs/security/webhook.md#L88链接和Jira TICKET-1234引用 - 底部注明 “由 open-code-review v0.4.2 生成,执行耗时 2.3s”
开发者dev-alex点击链接直达文档,10 分钟内补全了异常处理逻辑,并回复 “✅ 已按建议修复”,评审闭环完成。整个过程无人工介入,但每一步都可追溯、可验证、可解释。
5. 常见问题与排查技巧实录:那些文档里不会写的坑
5.1 LLM API 调用失败:不是网络问题,而是上下文溢出
现象:ocr agent-run报错HTTP 400: {"error": "context_length_exceeded"},但你确认 diff 很小。
真相:问题出在上下文锚点加载。我们曾遇到一个 PR,diff 只有 5 行,但CONTEXT_FETCH状态拉取了 Jira ticket 的全部评论(含 200 条历史讨论),总 token 超过模型上限。
排查步骤:
- 运行
ocr diff-parse --base-ref main --debug-context,查看输出的锚点 URI; - 对每个 URI 手动 curl,检查内容长度(
curl -s URL | wc -c); - 发现
jira/api/issue/TICKET-1234?fields=comment返回了 12KB 的 JSON;
解决方案:
- 在
config.yaml中配置jira.max_comment_chars: 2000,限制只取最新 3 条评论; - 或改用
jira/api/issue/TICKET-1234?fields=summary,description,只拉核心字段; - 绝对不要在 prompt 中 dump 整个 JSON,而是用 LLM 先 summarize 再注入。
实操心得:我们给所有外部 API 调用加了
--max-content-size参数,超过阈值自动 truncation 并 log warning。这是 open-code-review 可靠性的底线——宁可信息不全,也不能因单个 API 失败导致整个评审中断。
5.2 规则不生效:90% 是 AST 解析器没匹配上
现象:你写了规则检测db.Query("SELECT * FROM ..."),但实际 diff 中的db.Query("SELECT * FROM users")没被触发。
根本原因:db.Query是函数调用,但SELECT * FROM users是字符串字面量,AST 中它属于CallExpr.Args[0].StringLit.Value,不是CallExpr.Fun。你的 pattern 写成了匹配函数名,而非参数值。
正确写法(Go AST):
pattern: ast: "CallExpr[Fun->Ident.Name=='db.Query'][Args[0]->StringLit.Value~'SELECT.*FROM']"验证方法:
- 用
ocr diff-parse --debug-ast test.go输出 AST JSON; - 在 https://astexplorer.net/ 粘贴,交互式查找节点路径;
- 确认
StringLit.Value的实际值是否含转义(如"SELECT * FROM users"在 AST 中是SELECT * FROM users,无引号)。
注意:不同语言 AST 结构差异巨大。Python 用
ast.parse(),JS 用acorn,Go 用go/ast。不要跨语言复用 pattern,必须针对目标语言重写。
5.3 Git Blame 失败:不是权限问题,而是 ref 深度不足
现象:ocr agent-run在 CI 中报错git blame failed: fatal: no such commit 'HEAD~3'。
原因:GitHub Actions 默认fetch-depth: 1,只拉取最新一次 commit,git blame无法追溯历史。