代码审查这件事,做久了的人都有一个共识:真正耗时间的从来不是写代码,而是等 review。本地改完一堆文件,push 上去,然后就是漫长的等待——等同事有空、等 CI 跑完、等有人在评论区敲下第一句"这里是不是有问题"。如果碰上跨时区协作,一个来回就是一天。所以当我第一次看到open-code-review这个方向的时候,第一反应不是"又一个工具",而是"终于有人把这件事挪到本地来了"。
这篇要聊的,就是怎么用 CLI 加 LLM Agent 的方式,在代码离开你机器之前就完成一轮高质量的审查。核心关键词包括open-code-review、code review、CLI、LLM Agent、Git。它解决的是"审查延迟"和"审查质量不稳定"这两个老问题,适合所有还在用人工肉眼 diff 的开发者,也适合想把 review 环节前移到本地工作流里的团队。下面我会从设计动机、环境搭建、核心机制、实操流程、踩坑经验几个角度,把这件事讲透。
1. 为什么要把 code review 从远端拉回本地
1.1 远端 review 的三个结构性延迟
先说清楚问题在哪。传统的 code review 流程是:本地开发 → commit → push → 开 PR/MR → 等人审 → 改 → 再 push → 再等。这个链条里有三段延迟是结构性的,不是靠"大家勤快点"能解决的。
第一段是人的延迟。reviewer 有自己的任务,你的 PR 排在他队列里,什么时候被看到完全不可控。第二段是上下文的延迟。reviewer 打开 diff 的时候,他看到的是一堆加了红绿标记的行,但他不知道你为什么要这么改,不知道你试过哪些方案,不知道这段代码上游依赖了什么。他得重新建立上下文,这个成本极高。第三段是反馈的延迟。他提了意见,你改完再 push,他又得重新看一遍,来回几轮,一个小改动能耗掉半天。
open-code-review这个思路的价值就在于,它把前两段延迟直接消掉了。代码还在你本地工作区的时候,LLM Agent 就已经读完了 diff、读完了相关文件、给出了意见。你改完再让它看一遍,秒级反馈。等到真正 push 的时候,PR 上剩下的应该只是"这个设计决策需要人拍板"这类真正需要人类判断的问题。
1.2 LLM Agent 和普通 lint 工具的本质区别
很多人会问:这不就是 ESLint、Pylint 干的事吗?完全不是一回事。Lint 工具是基于规则的,它只能检查语法层面、风格层面、已知模式的问题。它能告诉你"这个变量没用到",但它没法告诉你"你这个函数把业务逻辑和 IO 混在一起了,后面测试会很难写"。
LLM Agent 的区别在于三点。一是语义理解,它能读懂代码在干什么,而不只是匹配模式。二是上下文关联,它能同时看多个文件,理解一个改动对其他地方的影响。三是可对话,你可以追问"为什么你觉得这里有问题",它会给出推理过程,而不是甩你一个错误码。
这里要澄清一组经常被混淆的概念,因为热词里反复出现:Agent、LLM、AI 模型到底什么关系。LLM(Large Language Model)是底层的大语言模型,比如 DeepSeek、GPT 系列、Claude 系列,它们本质上是"给定输入预测输出"的模型。AI 模型是个更大的范畴,LLM 只是其中一类。而 Agent 是在 LLM 之上加了一层"循环 + 工具调用"的架构——它能让模型自己决定"我要读哪个文件""我要跑哪条命令""我看完结果后下一步干什么"。所以 DeepSeek 属于 LLM,也属于 AI 模型,但它本身不是 Agent;只有把它接进一个能调工具的框架里,才变成 Agent。搞清这个层级,后面配置的时候就不会晕。
1.3 本地审查能覆盖和不能覆盖的边界
得把预期摆正。本地 LLM 审查能覆盖的是:明显的逻辑漏洞、边界条件遗漏、命名和可读性问题、潜在的资源泄漏、重复代码、与项目既有模式不一致的写法。这些占了日常 review 意见的很大一部分。
它覆盖不了的是:涉及业务语义的决策("这个折扣规则到底该不该这么算")、架构层面的取舍("该不该引入这个新依赖")、以及需要跨团队共识的接口变更。这些必须留给人。所以正确的用法不是"用 AI 替代 reviewer",而是"用 AI 把 reviewer 从琐碎意见里解放出来,让他专注在真正需要判断的地方"。这个定位想清楚了,工具才用得对。
2. 把 CLI 环境搭起来:Git 与运行时的准备
2.1 Git 的安装与最小必要配置
open-code-review依赖 Git 来获取 diff,所以第一步是把 Git 装好配好。Windows 用户直接去官网下安装包,安装过程中有一个选项要注意:"Adjusting your PATH environment"这一步,选第二项 "Git from the command line and also from 3rd-party software",这样 Git 命令才能在任意终端里用。很多人装完发现git命令找不到,就是这一步选错了。
装完之后做最小配置,这两条是必须的:
git config --global user.name "你的名字" git config --global user.email "你的邮箱"没有这两条,commit 会直接失败。另外建议顺手配一下换行符处理,跨平台协作时能省很多事:
# Windows 上建议 git config --global core.autocrlf true # macOS / Linux 上建议 git config --global core.autocrlf input提示:如果你用的是 Gitee 这类国内平台,SSH 密钥的配置和 GitHub 基本一致,生成密钥后把公钥贴到平台的 SSH 设置里即可。私钥文件权限在类 Unix 系统上要设成 600,否则会被拒绝使用。
验证安装是否成功,跑git --version看到版本号就对了。如果是在 Windows Terminal 里装了 CLI 工具却提示找不到命令,八成是 PATH 没刷新,重开一个终端窗口通常就好了。
2.2 运行时与 CLI 工具的安装路径问题
LLM Agent 类的 CLI 工具通常需要 Node.js 或 Python 运行时。以 Node 生态为例,安装方式一般是全局安装:
npm install -g <cli-package-name>装完之后用<cli-name> --version验证。这里有个高频坑:版本能查到,但实际调用时报 "unable to locate the binary"。这个错误的根因通常是全局安装目录不在系统 PATH 里,或者终端会话缓存了旧的 PATH。排查顺序是:先npm config get prefix看全局目录在哪,再确认这个目录在 PATH 里,最后重开终端。
还有一种情况是工具本身依赖某个外部二进制(比如某些 CLI 会去调另一个命令行程序),这时候报错信息里会明确说找不到哪个 binary。解决办法是单独把那个依赖装上,或者用工具提供的配置项指定它的绝对路径。
2.3 认证与权限:让 Agent 能读到你的代码
CLI 工具第一次运行一般要配置 API Token 或登录。这一步的常见报错是login failed. check api token。排查思路很直接:Token 是不是复制的时候带了空格、Token 是不是过期了、网络能不能通到服务端。逐项确认基本都能解决。
另一个绕不开的话题是权限。Agent 要读文件、要跑 git 命令,有些工具默认每次操作都问你"是否允许",用起来很烦。大多数 CLI 都提供了"完全访问"或"自动批准"的模式,开启后它就不再逐条确认。我的建议是:在受控的本地仓库里可以开,但在包含敏感配置的目录里要谨慎。更稳妥的做法是配置一个允许列表,只放行读文件和 git 只读命令,写操作仍然手动确认。
3. open-code-review 的核心工作流拆解
3.1 从工作区 diff 到结构化审查意见
这个工具的核心动作其实就一句话:把当前工作区相对某个基准的 diff 喂给 LLM,让它输出结构化的审查意见。基准可以是 HEAD(已提交但未 push 的改动),也可以是某个分支(比如相对 main 的所有改动)。
为什么用 diff 而不是整个代码库?因为 diff 是"变更的意图"最直接的表达。整个代码库太大,塞不进上下文窗口,而且大部分没改的代码跟这次审查无关。diff 加上被改动文件的完整内容,这个组合既控制了体积,又保留了理解改动所需的上下文。
输出的结构化是关键。好的实现会把意见分成几类:必须修复的(bug、安全问题)、建议修改的(可读性、性能)、仅供参考的(风格偏好)。这样你扫一眼就知道哪些必须处理,哪些可以忽略。如果工具只给你一大段自然语言,那价值会打不少折扣。
3.2 让 Agent 理解"这次改动想干什么"
单纯看 diff 有个问题:Agent 不知道你的意图。比如你删了一段看起来有用的代码,它可能会报警,但实际上那段代码是死代码。解决办法是在调用时给它一点上下文——commit message、或者你手动写一句"这次改动是为了修复 XX 问题"。
更聪明的做法是让 Agent 自己去读 commit 历史和相关文件。这就是 Agent 相比纯 LLM 调用的优势:它可以先git log看看最近的提交,再git diff看具体改动,然后决定还要读哪些文件来补全理解。这个"自主决定读什么"的过程,就是 Agent 循环的体现。
3.3 审查意见的置信度与误报处理
必须承认,LLM 审查会有误报。它可能把一段正确的代码判成有问题,也可能漏掉真正的问题。降低误报的几个实操手段:
一是给它明确的审查清单。与其让它自由发挥,不如告诉它"重点看空指针、边界条件、资源释放、并发安全"。范围收窄,准确率会明显上升。二是要求它给出理由和代码位置。如果它说"这里有问题"但指不出具体行号、说不出为什么,这条意见的可信度就要打折。三是分级处理。把它的输出当成"待验证的线索",而不是"最终结论",逐条自己判断。
我个人的经验是,把误报当成正常成本。哪怕十条意见里只有三条有价值,只要那三条是你自己容易忽略的,这轮审查就赚了。关键是别被误报带偏,看到明显不对的意见直接跳过,不要跟它较劲。
4. 实操:一次完整的本地审查流程
4.1 准备一个可复现的测试仓库
要真正跑通,先准备一个干净的小仓库。这样出问题好排查,也不会污染你正在做的项目。
mkdir ocr-demo && cd ocr-demo git init建两个文件,一个模拟业务逻辑,一个模拟工具函数,随便写点有改进空间的代码。比如故意留一个没处理的边界条件、一个命名很差的变量。然后提交一次作为基准:
git add . git commit -m "init: baseline"接着做一次"有问题"的改动——比如加一个函数,里面有个数组越界的隐患。先不要 commit,让改动停留在工作区。这一步很关键,因为我们要审查的就是"未提交的改动"。
4.2 触发审查并读懂输出
在仓库根目录运行审查命令(具体命令名以你所用工具为准,通常是ocr或类似形式)。工具会做几件事:读取git diff、读取被改文件的完整内容、把这两部分拼成 prompt 发给 LLM、把返回结果格式化输出。
拿到输出后,重点看三样东西:问题定位(文件 + 行号)、问题类型(bug / 风格 / 性能)、修复建议。如果它指出了你故意埋的那个越界隐患,说明链路是通的。如果它没指出来,先别急着否定工具——检查一下你的 prompt 是不是太宽泛,或者 diff 是不是没被正确读取。
4.3 把审查接进日常提交习惯
跑通一次不难,难的是让它变成习惯。我的做法是把它挂在 commit 之前。Git 提供了 hooks 机制,可以在.git/hooks/pre-commit里写脚本,每次 commit 前自动跑一遍审查。如果发现严重问题就中断提交,让你先处理。
#!/bin/sh # .git/hooks/pre-commit <review-command> --severity high if [ $? -ne 0 ]; then echo "发现高优先级问题,请先处理再提交" exit 1 fi注意这个脚本要有可执行权限(chmod +x)。另外别把审查设得太严格,否则每次提交都被拦,用两天你就想把它关了。只拦"高优先级"这一类,其余的输出到终端让你自己看,这个平衡点比较舒服。
5. 踩过的坑与排查链路
5.1 diff 为空导致的"假通过"
最常见的坑:工具跑完显示"没有发现问题",你以为代码很干净,实际上是 diff 为空。原因可能是你的改动已经 commit 了,而工具默认只看工作区未提交的改动。这时候要显式指定基准,比如--base HEAD~1或者直接指定分支。
排查方法很简单:先手动跑git diff和git diff HEAD~1,看看哪个有输出。工具读的是哪个,你就得保证那个有内容。这个坑我踩过不止一次,尤其是改完顺手 commit 了再想起来要审查的时候。
5.2 大改动超出上下文窗口
一次改了几十个文件、上千行,直接喂给 LLM 会超上下文,工具要么报错要么截断,截断之后审查质量断崖式下跌。解决办法有两个:一是分批审查,按目录或按文件分组,一组一组来;二是只审查关键文件,把那些纯格式化、纯重命名的改动排除掉。
排除的方式通常是用路径过滤,比如只审查src/下的改动,忽略dist/、*.lock、生成代码。这些文件本来也不该人工 review,交给工具更是浪费 token。
5.3 工具报"找不到 binary"的完整排查
前面提过这个错误,这里给一个完整的排查链路,因为它在不同环境下表现不一样:
| 现象 | 可能原因 | 排查动作 |
|---|---|---|
| 命令能查到版本但调用失败 | PATH 未刷新 | 重开终端,echo $PATH确认 |
| 提示找不到某个依赖 binary | 依赖未安装 | 单独安装该依赖 |
| 全局安装后仍找不到 | 全局目录不在 PATH | npm config get prefix后手动加入 |
| Windows Terminal 里失败但 CMD 里正常 | 终端环境差异 | 检查终端是否加载了完整环境变量 |
按这个顺序走,基本都能定位。核心思路是:先确认命令本身在不在,再确认它依赖的东西在不在,最后确认环境变量对不对。
5.4 审查意见与项目规范冲突
LLM 不知道你们团队的规范。它可能建议你用某种它认为"更好"的写法,但你们项目约定用另一种。这时候别盲从。解决办法是在项目根目录放一个约定文件(比如REVIEW.md),把团队的规范写进去,调用审查时让它读这个文件。这样它的建议就会向你们的规范靠拢。
我一般会在里面写清楚:命名约定、错误处理方式、日志规范、禁止使用的 API。写一次,后面每次审查都受益。这比每次手动纠正它的建议省事得多。
6. 把审查能力扩展到更多场景
6.1 用 worktree 并行处理多个审查任务
git worktree是个被低估的功能。它允许你在同一个仓库上挂多个工作目录,每个目录对应不同的分支。这对审查场景特别有用:你可以在一个 worktree 里正常开发,在另一个 worktree 里 checkout 待审查的分支,让 Agent 去审,互不干扰。
git worktree add ../review-branch feature/xxx这样你就不用为了审查而 stash 当前改动、切分支、再切回来。审查完直接git worktree remove清理掉。对于需要频繁审查不同分支的场景,这个操作能省下大量上下文切换的时间。
6.2 审查结果的沉淀与复用
审查意见如果只是看一眼就没了,价值有限。更好的做法是把它存下来。可以按日期或分支命名,存成 markdown 文件放进一个专门的目录。时间长了你会发现,某些类型的问题反复出现——那就是你个人的"高频错误清单",针对性改进比泛泛地"我要写更好的代码"有效得多。
团队层面也可以做类似的事:把大家的高频问题汇总,形成一份团队级的审查清单,反过来喂给 Agent 当 prompt。这样工具会越来越懂你们的项目,误报越来越少。
6.3 与现有工具链的衔接思路
本地审查不是孤立的。它可以和格式化工具、静态检查、单元测试串成一条链:格式化 → 静态检查 → 本地 LLM 审查 → 单元测试 → commit。前面几步是确定性的、快速的,先把机械问题清掉;LLM 审查处理语义层面的问题;测试兜底。这个顺序的好处是,LLM 不用浪费精力在格式问题上,专注在它擅长的语义分析上。
至于要不要把它接到远端 CI,我的看法是:本地审查为主,CI 上可以跑一个轻量版本作为兜底,但别指望 CI 上的 LLM 审查能替代人工。CI 环境拿不到你本地的完整上下文,效果会打折扣。
7. 关于成本和效果的一点实测体会
最后说点实在的。跑本地 LLM 审查是有成本的,主要是 token 消耗。一次中等规模的改动(十几个文件、几百行 diff),消耗的 token 量取决于你喂进去多少上下文。控制成本的关键是别把无关文件塞进去,以及别对每个小改动都跑全量审查。
效果方面,我的实测感受是:对于逻辑类问题,它的命中率相当可观,尤其是边界条件和异常处理这类容易被忽略的地方;对于架构和业务语义,它基本帮不上忙,也不该指望它。把它定位成"一个不知疲倦、但需要你复核的初级 reviewer",这个预期最接近实际。
还有一点:prompt 的质量直接决定输出质量。同样一段代码,你问"这段代码有问题吗",和问"重点检查数组访问是否越界、资源是否正确释放、异常是否被吞掉",得到的答案完全不是一个层次。花十分钟打磨你的审查 prompt,比换一个更强的模型带来的提升可能还大。
这套流程我用了几个月,最大的改变不是发现了多少 bug,而是提交前的心理负担小了很多。以前 push 之前总有点忐忑,怕漏了什么被人指出来;现在本地先过一轮,心里有底,push 出去也从容。工具的价值有时候不在于它多强,而在于它让你对自己的产出更有信心。