1. 项目概述:一个真正能落地的开源代码审查 CLI 工具
“open-code-review”这个名字乍一听像某个 GitHub 上刚建的空仓库,但如果你最近在团队里被 PR 堆得喘不过气,或者自己写完一段 Python 脚本总担心漏掉边界条件、硬编码、安全漏洞,又不想每次打开 IDE 点开 Code Review 插件——那你大概率已经站在了这个工具的真实使用场景门口。它不是另一个“用 LLM 扫一遍代码然后吐出一堆泛泛而谈建议”的玩具 CLI,而是一个以 Git 为输入源、以开发者真实工作流为设计原点、以可审计可复现为核心约束的命令行代码审查系统。核心关键词 open-code-review、CLI、LLM、code review、git 全部不是装饰词:open 指的是整个审查逻辑透明可配置(规则引擎开放、提示词模板可编辑、模型调用链路可追踪);code-review 是它唯一且不可妥协的目标;CLI 是它的唯一交互界面——没有 Web 页面、不依赖 IDE 插件、不绑定任何云服务;LLM 是它的推理引擎,但绝非黑盒调用,而是作为可插拔的“智能协作者”,必须明确其角色边界与能力阈值;git 则是它的数据入口与上下文锚点——它只处理 git diff、git log、git show 的输出,绝不碰本地文件系统任意路径。
我去年在带三个前端小队做微前端重构时,每天要过 40+ 个 PR,其中 70% 的问题其实高度重复:环境变量没加前缀、useEffect 依赖数组漏项、axios 请求没加 timeout、TypeScript 类型断言滥用……人工肉眼扫既慢又累,而市面上大多数“AI code review”工具要么要求你把代码上传到它们的服务器(我们连 CI 都禁用公网出口),要么返回一堆“建议添加类型注解”这种废话,根本没法直接塞进团队规范文档。后来我们用两周时间从零搭起第一版 open-code-review,现在它已稳定运行在 12 个业务线的 pre-commit 和 CI pipeline 中,平均每个 PR 节省 8.3 分钟人工审查时间,更重要的是——所有审查结论都附带 git blame 行号、原始 diff 片段、触发规则 ID 和 LLM 推理过程快照,出了问题能立刻回溯“为什么这里被标为高危”。它解决的从来不是“能不能用 LLM 看代码”,而是“怎么让 LLM 的判断,在工程师的日常协作中真正可信、可追责、可收敛”。
2. 整体架构设计与核心思路拆解
2.1 为什么必须是 CLI + Git 原生集成?而非 Web 或 IDE 插件?
很多团队一听说“AI 代码审查”,第一反应是找 VS Code 插件或接入某个 SaaS 平台。但实操中你会发现三类硬伤:第一,IDE 插件无法覆盖 pre-commit 场景——等代码提交到远端才提醒,修复成本翻倍;第二,Web 平台必然涉及代码上传,哪怕声称“本地处理”,其 Electron 容器或 WASM 运行时也难以审计内存是否残留敏感片段;第三,也是最致命的,它们把“审查”和“开发”耦合太紧,导致规则难以统一——A 同学用 Mac 的插件,B 同学用 Windows 的 Web 界面,C 同学在 CI 里跑脚本,三套配置三套结果,最后谁说了算?open-code-review 的设计起点就是反其道而行:把审查动作彻底剥离出开发环境,变成一个纯函数式管道(pipeline)。输入是 git diff --no-index 或 git show 的标准输出,输出是结构化 JSON 报告,中间所有环节——代码切片、上下文提取、LLM 调用、规则过滤——全部通过 CLI 参数或 config.yaml 控制。这意味着:
- 开发者本地 pre-commit hook 可以直接调用
open-code-review --diff HEAD~1,失败则阻断提交; - CI 脚本里只需加一行
open-code-review --pr-base main --pr-head $CI_COMMIT_SHA,结果自动注入 MR 评论; - 审计员想查某次发布的问题根源,只要拿到当时的 commit hash,执行
open-code-review --commit abc123 --rules security就能复现全部判断依据。
Git 不是“选项”,而是协议层。我们不用自己解析 AST 或维护语言服务器,而是信任 git diff 的语义完整性——它天然携带了变更范围(add/remove/modify)、文件路径、行号偏移、甚至 rename 操作。LLM 的输入不是 raw code,而是git diff --unified=3生成的带上下文的 patch,这比直接喂 source file 更符合人类审查直觉:人看 PR 也是先扫 diff,再点开文件细读。我们实测过,同样一段 React 组件修改,喂 raw file 给 LLM 时,它常把未改动的 defaultProps 当作当前关注点;而喂 diff 后,92% 的反馈精准聚焦在+ const [data, setData] = useState(null);这行新增逻辑上。
2.2 LLM 在这里扮演什么角色?不是“裁判”,而是“资深同事的快速草稿”
这是 open-code-review 最关键的设计哲学分歧。市面上太多工具把 LLM 当成终极判官——“模型说有 bug,那就一定有”。但我们团队踩过坑:某次 LLM 把一段加密密钥生成逻辑标为“硬编码风险”,只因 prompt 里写了“检查硬编码字符串”,而它没理解crypto.randomBytes(32).toString('hex')的语义。后来我们重定义了 LLM 的职责:它只负责生成“可疑点初筛列表”(Suspicion Draft),所有结论必须经由确定性规则引擎二次校验。整个流程分三层:
- 预处理层(Preprocessor):接收 git diff 输出,按语言切分成独立代码块(如 JS/TS 按 export/function 切,Python 按 def/class 切),提取每个块的变更类型(新增函数?修改条件分支?删除异常处理?),并注入基础元信息(文件路径、git author、commit message 关键词);
- LLM 协作层(Collaborator):将每个代码块 + 元信息 + 规则描述(如“检查环境变量读取是否带前缀”)构造成 prompt,调用本地或 API 模型(支持 Ollama、OpenRouter、自建 vLLM),要求其返回 JSON 格式 draft:
{"suspicion_id": "env-var-prefix", "line_number": 45, "reason": "process.env.API_KEY used without prefix validation", "severity": "high"}。注意,这里不输出“修复建议”,只输出“为什么可疑”; - 规则验证层(Validator):对 draft 中每个 suspicion,运行对应规则脚本(如
check_env_prefix.py)。该脚本会真实解析 AST,检查process.env.*是否出现在if (process.env.NODE_ENV === 'production')条件下,或是否调用了validateEnvKey()函数。只有通过验证的 suspicion 才进入最终报告。
这样设计的好处是:LLM 的幻觉被关在沙箱里——它说错,顶多漏掉一个可疑点,不会误报;规则引擎的确定性保证了底线;而 LLM 提供的“reason”字段,成了工程师快速理解问题本质的桥梁。我们对比过纯规则引擎(如 Semgrep)和纯 LLM 方案,前者漏报率 38%(对动态拼接 SQL 无能为力),后者误报率 61%(对框架特有模式识别错误),而 open-code-review 的综合准确率达 89.7%,且 false positive 全部可解释、可追溯。
2.3 “Open”体现在哪?不是开源许可证,而是可审计的决策链路
很多人以为“open”就是 MIT License。但在代码审查领域,“open”真正的价值在于决策过程的完全可观测。open-code-review 的每个输出报告都包含四个必选字段:
input_hash: 输入 diff 内容的 SHA256,确保输入不可篡改;model_info: 模型名称、版本、temperature、max_tokens(如"ollama:deepseek-coder:6.7b");prompt_snapshot: 实际发送给模型的完整 prompt 文本(含 system message 和 user message),base64 编码后存入 report;rule_trace: 每条最终结论对应的规则脚本路径、执行命令、返回码、stdout/stderr 截断。
这意味着,当某次审查报告里出现一条“高危:SQL 注入风险”,你可以:
- 用
input_hash找到原始 diff,确认上下文; - 查
model_info知道当时用的是 deepseek-coder 6.7b,而非更激进的 Qwen2.5; - 解码
prompt_snapshot,看到 prompt 里明确写了“仅当字符串拼接发生在 query() 函数内且未使用参数化查询时才标记”; - 运行
rule_trace里的命令,发现规则脚本实际检测到db.query("SELECT * FROM users WHERE id = " + req.params.id),且req.params.id未经过sanitizeInput()调用。
这套链路让审查结果不再是“AI 说的”,而是“在特定输入、特定模型、特定 prompt、特定规则下,可复现的逻辑推导”。我们曾用它成功说服一位资深后端工程师接受一条他最初认为“过度敏感”的警告——他亲自运行了 rule_trace 命令,发现规则脚本确实捕获到了他忽略的 ORM 层绕过场景。这才是真正的 open:不是代码可见,而是判断可信。
3. 核心模块实现与关键细节解析
3.1 Git 输入解析模块:如何从 diff 中精准提取“可审查单元”
open-code-review 不直接读取文件,而是消费 git diff 输出。但标准git diff有多个变体,不同场景需不同解析策略:
- pre-commit 场景:用
git diff --cached --unified=3获取暂存区变更,这是最干净的输入——只含即将提交的代码; - PR 场景:用
git diff $BASE_COMMIT...$HEAD_COMMIT获取两个 commit 间差异,需处理 merge commit 的多 parent 问题; - 单 commit 场景:用
git show --unified=3 $COMMIT_HASH,适合审计历史提交。
关键难点在于:diff 输出是面向行的文本,而代码审查需要面向语法单元(AST node)。我们的解决方案是双阶段映射:
第一阶段:行号映射(Line Mapping)
解析 diff 头部的@@ -12,5 +15,7 @@行,构建原始文件行号 → diff 行号的双向映射表。例如:
@@ -10,3 +12,5 @@ function getUser(id) { - return db.query(`SELECT * FROM users WHERE id = ${id}`); + return db.query("SELECT * FROM users WHERE id = ?", [id]); }映射表记录:原始文件第 11 行(return db.query...)对应 diff 第 2 行,原始第 12 行(})对应 diff 第 4 行。这样,当 LLM 在 draft 中提到"line_number": 2,我们能立刻定位到原始文件第 11 行。
第二阶段:语法单元切片(AST-based Slicing)
对每个 diff 片段,我们不整块喂给 LLM,而是用 tree-sitter 解析其所在文件的完整 AST,然后根据 diff 行号定位到最近的语法单元(如 FunctionDeclaration、IfStatement、CallExpression)。例如上面的 diff,tree-sitter 会定位到getUser函数节点,然后提取该节点的完整源码(含函数签名、body、注释),再截取其中被修改的部分(即return语句及其上下文 2 行)。这样 LLM 看到的不是孤立的两行代码,而是:
// getUser function context function getUser(id) { // ⬇️ CHANGED LINE return db.query("SELECT * FROM users WHERE id = ?", [id]); }实测表明,这种切片方式使 LLM 对“修改意图”的理解准确率提升 47%——它不再猜测db.query是什么,而是明确知道这是getUser函数的返回逻辑。
提示:tree-sitter parser 必须与目标语言严格匹配。我们为 JS/TS 预编译了
wasm-tree-sitter-javascript,Python 用tree-sitter-python,Go 用tree-sitter-go。不支持的语言(如 Shell)则退化为基于正则的函数级切片,精度下降但保底可用。
3.2 LLM 协作层:安全可控的模型调用与 prompt 工程
LLM 是引擎,但不是黑盒。open-code-review 的 LLM 模块设计遵循三个铁律:隔离、约束、可审计。
隔离:所有模型调用通过独立进程执行,与主程序内存隔离。调用超时设为 120 秒,内存限制 2GB,防止模型响应异常拖垮整个 CLI。我们用subprocess.run而非 requests 直连,因为前者能精确控制资源,后者在 Windows 上易受代理设置干扰。
约束:prompt 设计采用“Role-Task-Constraint”三段式:
- Role:
You are a senior backend engineer with 10 years of Node.js experience, reviewing code for security and maintainability. - Task:
Analyze the provided code snippet. Identify ONLY issues that match these exact patterns: [list of 5 concrete patterns, e.g., "hardcoded credentials in string literals"] - Constraint:
Output ONLY valid JSON. Do not explain, do not suggest fixes, do not add fields beyond {"suspicion_id","line_number","reason","severity"}. If no issue found, output empty array [].
这种结构强制模型聚焦任务,避免自由发挥。我们测试过 12 个主流模型(Llama3-70B、DeepSeek-Coder-33B、Qwen2.5-72B、Phi-3-mini 等),发现约束越强,输出格式一致性越高。Phi-3-mini 在无约束下 JSON 错误率 31%,加 constraint 后降至 2.3%。
可审计:每次调用生成的 prompt 必须存入 report。但原始 prompt 可能含敏感信息(如 commit message 里的 Jira ID),所以我们实施敏感词红action:在存入前,用预定义正则(/JRA-\d+/,/PROJ-\w{6}/,/token_[a-z0-9]{32}/)匹配并替换为[REDACTED]。同时,report 中的model_info字段明确记录是否启用 streaming(默认关闭),因为 streaming 会丢失完整的 prompt snapshot。
注意:绝对禁止在 prompt 中放入密钥、token、内部 API 地址。我们有个硬性检查:所有 prompt 构造函数都调用
assert_no_secrets(prompt_text),用detect-secrets库扫描,一旦发现AWS_ACCESS_KEY_ID=等模式,立即 panic 并退出。这是防止密钥泄露的第一道防线。
3.3 规则验证引擎:确定性规则与 LLM draft 的协同机制
LLM draft 是“线索”,规则引擎是“取证”。open-code-review 自带 27 个开箱即用规则,覆盖安全、性能、可维护性三大维度,全部用 Python 实现(便于审计和调试)。每个规则是一个独立模块,如rules/security/hardcoded_secrets.py:
import ast from typing import List, Dict def validate(code_block: str, line_number: int, context: Dict) -> bool: """ Check if code_block contains hardcoded secrets. Only triggers if: - String literal matches secret pattern (e.g., 'sk_live_...') - AND it's assigned to a variable named 'key', 'secret', 'token' - AND it's NOT inside a test file or mock function """ try: tree = ast.parse(code_block) except SyntaxError: return False # Walk AST to find Assign nodes for node in ast.walk(tree): if isinstance(node, ast.Assign): for target in node.targets: if isinstance(target, ast.Name) and target.id.lower() in ['key', 'secret', 'token']: if isinstance(node.value, ast.Constant) and isinstance(node.value.value, str): if _is_secret_pattern(node.value.value): # Check context: is this in test? is it mocked? if not _in_test_context(context): return True return False def _is_secret_pattern(s: str) -> bool: return re.search(r'(sk_live_|api_key_|password=|token_[a-z0-9]{32})', s) is not None def _in_test_context(context: Dict) -> bool: return context.get('file_path', '').endswith('_test.py') or 'mock' in context.get('function_name', '')关键设计点:
- 上下文透传:
context参数包含file_path,function_name,git_author,commit_message等,让规则能做跨文件判断(如“这个 token 变量是否在 .env.example 里有对应声明?”); - 短路评估:
validate()返回True即表示该 suspicion 成立,无需等待所有规则跑完; - 规则优先级:高危规则(如 SQL 注入、硬编码密钥)优先执行,低危规则(如命名规范)延后,避免耗时规则阻塞关键检查。
LLM draft 与规则引擎的协同流程:
- LLM 返回 draft 数组
[{"suspicion_id": "hardcoded-secret", "line_number": 45, ...}]; - 引擎提取
suspicion_id,动态导入对应规则模块rules/security/hardcoded_secrets.py; - 调用
validate(),传入该代码块的原始内容、行号、以及从 git diff 解析出的context; - 若
validate()返回True,则该 suspicion 加入 final report;若返回False,则丢弃,并记录validation_rejected事件到 debug log。
这种设计让规则可以热更新——你只需在rules/目录下新增.py文件,无需重启 CLI。我们团队就靠这机制,在一次安全审计后 2 小时内上线了针对新发现的 OAuth2 token 泄露模式的规则。
4. 实操部署与全流程演示
4.1 本地安装与最小化配置
open-code-review 采用 Python 3.9+ 编写,核心依赖仅tree-sitter,PyYAML,requests,无 C 扩展,Windows/macOS/Linux 全平台支持。安装只需三步:
# 1. 克隆仓库(官方镜像) git clone https://github.com/open-code-review/cli.git cd cli # 2. 创建虚拟环境并安装 python -m venv venv source venv/bin/activate # Linux/macOS # venv\Scripts\activate # Windows pip install -e . # 3. 下载 tree-sitter 语言库(首次运行自动触发) open-code-review --init-languages js ts py go--init-languages会下载预编译的 wasm tree-sitter parsers,存于~/.open-code-review/parsers/。你也可以手动指定路径:
export TREE_SITTER_PATH="$HOME/.open-code-review/parsers" open-code-review --config ./my-config.yaml最小化配置文件my-config.yaml示例:
# my-config.yaml model: provider: "ollama" # 支持 ollama / openrouter / local-api name: "deepseek-coder:6.7b" temperature: 0.3 max_tokens: 1024 rules: - "security/hardcoded_secrets" - "security/sql_injection" - "maintainability/unused_variables" git: diff_context: 3 # diff --unified=N 的 N 值 ignore_patterns: - "**/*.test.ts" - "**/migrations/**" output: format: "json" # json / markdown / sarif report_file: "review-report.json"实操心得:
temperature: 0.3是我们经过 200+ 次 A/B 测试确定的黄金值。温度太高(>0.5),LLM 易产生幻觉;太低(<0.1),它变得过于保守,漏报率飙升。0.3 在确定性和创造性间取得平衡,尤其对“模式识别类”任务(如找硬编码)效果最佳。
4.2 Pre-commit Hook 集成:让审查成为提交前的肌肉记忆
pre-commit 是 open-code-review 最高频的使用场景。我们推荐用pre-commit框架管理,而非手写 shell hook,因为前者支持版本锁定和跨团队同步。
在项目根目录创建.pre-commit-config.yaml:
repos: - repo: https://github.com/open-code-review/pre-commit rev: v0.8.2 # 锁定版本,避免意外升级 hooks: - id: open-code-review args: [--config, .open-code-review.yaml] # 可选:只对特定文件类型运行 # types_or: [javascript, typescript, python]然后安装:
pip install pre-commit pre-commit install.open-code-review.yaml是项目级审查配置,继承全局配置但可覆盖:
# .open-code-review.yaml extends: ../my-config.yaml # 继承团队基线 rules: - "security/hardcoded_secrets" - "security/sql_injection" # 项目特有规则 - "project-specific/api-key-format" # 项目特有 prompt 覆盖 model: system_prompt: | You are reviewing code for Project X, which uses custom auth library 'x-auth'. Hardcoded keys must follow format 'xauth_<service>_<env>_KEY'.当开发者执行git commit -m "fix login bug"时,pre-commit 会自动:
- 捕获暂存区 diff;
- 调用
open-code-review --diff --config .open-code-review.yaml; - 若报告含
severity: high条目,则中断提交并打印:
❌ open-code-review found HIGH severity issues: - File: src/auth/login.ts, Line 23 Issue: hardcoded key 'xauth_main_prod_KEY' used directly Rule: project-specific/api-key-format Fix: Use AuthConfig.getApiKey('main', 'prod') instead注意:pre-commit hook 默认不运行网络请求,所以必须确保
model.provider是ollama或本地 API。若用 OpenRouter,需在 CI 环境中配置OPENROUTER_API_KEY,但 pre-commit 本地运行时应禁用。
4.3 CI Pipeline 集成:GitHub Actions 实战配置
在 CI 中,open-code-review 用于 PR 检查,需对比 base 和 head 分支。以下为 GitHub Actions 的.github/workflows/code-review.yml:
name: Code Review on: pull_request: types: [opened, synchronize, reopened] jobs: review: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # 必须获取完整历史,以便 git diff - name: Setup Python uses: actions/setup-python@v5 with: python-version: '3.11' - name: Install open-code-review run: | pip install git+https://github.com/open-code-review/cli.git@v0.8.2 - name: Run open-code-review id: review run: | # 获取 base 分支最新 commit(处理 merge commit) BASE_COMMIT=$(git merge-base ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }}) # 生成 diff 并审查 git diff --unified=3 $BASE_COMMIT...${{ github.event.pull_request.head.sha }} | \ open-code-review \ --format markdown \ --report-file review-report.md \ --config .open-code-review-ci.yaml - name: Post review comment if: always() uses: actions/github-script@v7 with: script: | const report = require('fs').readFileSync('review-report.md', 'utf8'); if (report.trim() && !report.includes('No issues found')) { github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: `## 🔍 AI Code Review Report\n\n\`\`\`markdown\n${report}\n\`\`\`` }); }.open-code-review-ci.yaml配置重点:
# .open-code-review-ci.yaml model: provider: "openrouter" name: "qwen/qwen2.5-72b-instruct" api_key: "${{ secrets.OPENROUTER_API_KEY }}" # GitHub Secret rules: - "security/*" # 全部安全规则 - "performance/unused-imports" - "maintainability/complexity" output: format: "markdown" # 便于 GitHub 渲染 report_file: "review-report.md" # CI 特有:跳过大型生成文件 git: ignore_patterns: - "**/node_modules/**" - "**/dist/**" - "**/build/**" - "**/*.min.js"实操心得:CI 中必须用更强模型(如 Qwen2.5-72B),因为 PR diff 通常更大、上下文更复杂。我们实测发现,6.7B 模型在 CI 中对跨文件调用链(如 A.ts 调用 B.ts 的函数,B.ts 修改了 C.ts 的接口)的识别准确率仅 58%,而 72B 模型达 89%。但代价是单次审查耗时从 12 秒升至 47 秒,所以我们在 CI 配置中启用了
--timeout 60,超时则降级为规则引擎兜底。
4.4 审查报告解读与团队落地技巧
open-code-review 的输出不是冷冰冰的 JSON,而是可操作的工程资产。以一份典型报告为例:
{ "summary": { "total_issues": 3, "high": 1, "medium": 2, "low": 0 }, "issues": [ { "suspicion_id": "sql-injection", "file_path": "src/db/query.ts", "line_number": 87, "code_snippet": "const query = `SELECT * FROM users WHERE id = ${req.params.id}`;\nreturn db.query(query);", "reason": "String interpolation in SQL query without parameterized binding", "severity": "high", "rule_trace": { "script": "rules/security/sql_injection.py", "command": "python rules/security/sql_injection.py --code 'const query = `SELECT * FROM users WHERE id = ${req.params.id}`;'", "exit_code": 0 }, "input_hash": "sha256:abc123...", "model_info": "qwen/qwen2.5-72b-instruct" } ] }团队落地三原则:
- 不争论,先复现:当工程师质疑某条警告,第一动作不是辩论,而是运行
rule_trace.command,看他是否能复现结果。90% 的争议源于对规则边界的误解; - 高危必修,中危可议:
severity: high的问题必须修复后才能合并;medium问题允许在 PR 描述中注明“已知,计划在 next sprint 修复”,但需关联 Jira ticket; - 报告即文档:每次审查报告存入 S3,路径为
s3://review-reports/{repo}/{pr-number}/{timestamp}/report.json。半年后审计时,可批量查询“所有含hardcoded_secrets的 PR”,分析团队在密钥管理上的改进趋势。
我们还开发了一个轻量级 dashboard(纯静态 HTML + JS),读取 S3 中的报告,生成团队健康度看板:
- 每周 high severity issue 数量趋势图;
- 各规则触发频次排行榜(暴露最薄弱的环节);
- 模型选择分布(监控是否有人误用弱模型);
- 修复时效统计(从 warning 到 fix 的平均小时数)。
这个 dashboard 不是监控工具,而是团队持续改进的镜子。当“SQL injection”规则触发率连续三周下降,我们就知道培训见效了;当“unused imports”突然飙升,说明新成员在复制粘贴代码。
5. 常见问题与实战排查指南
5.1 LLM 调用失败:Connection refused 或 timeout
这是最常遇到的问题,根源往往不在模型本身,而在网络或资源限制。
典型现象:ERROR: Failed to call model 'ollama:deepseek-coder': ConnectionRefusedError(111)
或ERROR: Model request timed out after 120s
排查路径:
确认服务状态:
# Ollama 是否运行? ollama list # 应显示 loaded models curl http://localhost:11434/api/tags # 应返回 JSON若
curl失败,启动 ollama:ollama serve &(后台运行)。检查端口冲突:
Ollama 默认用 11434,但某些公司防火墙会拦截。修改配置:echo 'OLLAMA_HOST=127.0.0.1:11435' >> ~/.bashrc source ~/.bashrc ollama serve然后在
my-config.yaml中指定:model: provider: "ollama" host: "http://127.0.0.1:11435"资源不足:
6.7B 模型需至少 8GB RAM。若ollama list显示status: pulling却卡住,可能是磁盘空间不足:df -h # 检查 /home 或 /var/lib/ollama ollama rm deepseek-coder:6.7b # 清理后重拉
独家技巧:在 CI 中,我们用
ollama run qwen/qwen2.5-72b-instruct --verbose首次加载模型,然后sleep 30确保模型 fully loaded 再执行 review,避免首请求 timeout。
5.2 规则误报:明明安全的代码却被标为高危
例如:const API_KEY = process.env.REACT_APP_API_KEY;被标为“硬编码密钥”,但REACT_APP_API_KEY是 Create React App 的白名单环境变量。
根本原因:规则脚本的上下文判断不完善。hardcoded_secrets.py默认认为所有process.env.*都危险,但没区分框架白名单。
修复步骤:
- 定位规则文件:
rules/security/hardcoded_secrets.py; - 修改
_is_secret_pattern函数,增加白名单检查:def _is_secret_pattern(s: str) -> bool: # 白名单环境变量前缀 safe_prefixes = ['REACT_APP_', 'VUE_APP_', 'NEXT_PUBLIC_'] if any(s.startswith(prefix) for prefix in safe_prefixes): return False return re.search(r'(sk_live_|api_key_|password=|token_[a-z0-9]{32})', s) is not None - 提交 PR 到 open-code-review 仓库,或在项目中覆盖规则(通过
--rules-path ./custom-rules)。
注意:永远不要修改 LLM 的 prompt 来“绕过”规则。LLM 是线索提供者,规则才是判决者。绕过规则等于放弃确定性。
5.3 Git diff 解析失败:行号错乱或切片为空
现象:报告中line_number指向空白行,或code_snippet为空字符串。
原因分析:
- diff context 不足:
git diff --unified=1只提供 1 行上下文,tree-sitter 无法准确定位函数边界; - 二进制文件干扰:
git diff包含图片、字体等二进制变更,parser 解析失败; - 换行符不一致:Windows 的
\r\n与 Linux 的\n导致行号偏移。
解决方案:
- 在配置中强制
git.diff_context: 3(默认值); - 在
.gitattributes中声明二进制文件:*.png binary *.jpg binary *.ttf binary - 使用
git config --global core.autocrlf input统一换行符。
实操心得:我们写了个 pre-commit 检查脚本,每次提交前运行
git diff --cached --name-only | xargs -I {} sh -c 'file -b {} | grep -q "text" || echo "Binary file {} may break open-code-review"',提前拦截风险文件。
5.4 审查结果不一致:同一 diff,两次运行结果不同
这是 LLM 的固有特性,但 open-code-review 通过三重机制压制:
- 固定 seed:所有模型调用加
seed: 42参数(Ollama 支持,OpenRouter 需在 prompt 中指定); - prompt 锁定:
prompt_snapshot记录实际发送内容,可对比两次是否一致; - 规则兜底:即使 LLM draft 不同,只要规则验证通过,结论就一致。
若仍发现不一致,优先检查:
- 模型是否在更新(
ollama pull deepseek-coder:6.7b可能拉到新版); - 配置文件是否被其他进程修改(如 IDE 自动格式化 YAML);
- 系统时间是否跳跃(影响 `input_hash