资讯详情

基于CLI与LLM Agent的本地代码审查工具链实战

📅 2026/10/19 8:29:27 | 华诺云谱 👁 阅读
基于CLI与LLM Agent的本地代码审查工具链实战
1. 为什么我要自己搭一套 open-code-review代码审查这件事干过几年的人都有体会它重要但没人愿意干。团队里 PR 堆成山reviewer 点开 diff 扫两眼回一句 LGTM 就合了。不是不负责任是真没时间。一个中等规模的项目一天十几个 PR每个 PR 几百行改动你让谁逐行看都不现实。我最初的想法很简单能不能让机器先过一遍把明显的问题挑出来人只看机器拿不准的部分。市面上现成的 SaaS 工具有不少但把公司代码推到第三方平台这件事在很多团队里根本过不了合规。于是就有了open-code-review这个项目——一套跑在自己机器上、基于 CLI 和 LLM Agent 的代码审查工具链。它做的事情用一句话说清楚在本地或 CI 里用命令行拉起一个大模型 Agent让它读 git diff按你定义的规则输出审查意见结果直接回写到 PR 或者终端。核心关键词就是这几个open-code-review、code review、CLI、LLM Agent、Git。适合谁看适合那些团队里有 git 基础、想引入 AI 辅助审查、又不想把代码交出去的工程师。你不需要是 AI 专家但得会用命令行得懂 git 的基本操作。我踩过的坑不少从 git worktree 的隔离问题到 Agent 每次都要确认的交互阻塞再到模型选型的反复横跳。这篇就把整套东西拆开讲包括为什么这么设计、每一步怎么落地、哪些地方容易翻车。2. 整体设计思路与方案选型2.1 为什么是 CLI 而不是插件或 Web 服务一开始我也想过做成 IDE 插件毕竟 VS Code 生态成熟用户装个扩展就能用。但很快放弃了原因有三个。第一审查的触发点不在编辑器里。代码审查发生在 PR 阶段而不是写代码的时候。你在编辑器里看到的 diff 是工作区的而真正要审的是已经提交、准备合并的那部分。插件要拿到这个上下文得额外做一堆 git 操作反而绕远了。第二CI 集成是刚需。团队希望审查能自动跑在流水线上PR 一开就触发。CLI 天然适合这种场景一行命令塞进 pipeline 就行。插件做不到这一点Web 服务又太重。第三CLI 的调试成本最低。出问题的时候我直接在终端里复现加个--verbose就能看到 Agent 每一步的输入输出。插件和服务都得打日志、翻日志效率差一个量级。所以最终定下来核心是一个 CLI 工具输入是 git 的 diff输出是结构化的审查结果。至于结果怎么用——打到终端、写成文件、还是回写 PR 评论——交给下游处理。2.2 LLM Agent 在这里扮演什么角色很多人把 LLM 和 Agent 混着说其实差别很大。LLM 是模型本身Agent 是围绕模型搭的一套执行循环。你问 DeepSeek 一个问题它答一段那是 LLM你让它先读文件、再跑命令、根据结果决定下一步那是 Agent。open-code-review 里用的是 Agent 模式原因在于审查不是一次问答能搞定的。一个真实的审查流程是这样的先拿到 diff判断改了哪些文件对每个文件可能需要读完整文件而不只是 diff 片段才能理解上下文有些改动涉及调用关系得去搜一下这个函数在别处怎么用的综合以上信息才能给出靠谱的意见这套流程里第 2、3 步都需要 Agent 主动去取信息而不是被动等模型回答。所以架构上我给 Agent 配了几个工具读文件、搜代码、跑 git 命令。模型负责决策调哪个工具工具负责返回结果循环直到 Agent 认为信息够了输出审查意见。提示Agent 的工具集不要贪多。我一开始给了七八个工具结果模型经常选错效率反而低。后来砍到三个核心工具准确率明显上升。2.3 模型选型的取舍模型这块我试过好几家。结论是审查任务对模型的代码理解能力要求高对创造力要求低。所以选型时优先看代码能力而不是通用对话能力。具体来说我关注三个指标一是能不能准确理解 diff 的语义不是逐行翻译而是知道这行改动意味着什么二是长上下文能力因为有些文件几千行得整个塞进去三是输出格式的稳定性Agent 需要模型按固定结构返回格式一乱整个流程就崩。实测下来代码专精的模型在这类任务上明显更稳。通用模型也能用但经常在这行改动有没有问题上给出模棱两可的回答反而增加人工复核成本。我的建议是先用你手头能访问的最强代码模型跑一遍建立基线再考虑降本。别一上来就图便宜用小模型审查质量差到没法用省下的钱不够填坑的。2.4 Git 集成的几个关键决策Git 这块有几个设计点值得说。第一用 diff 而不是全量文件。审查的对象是这次改了什么不是这个文件长什么样。所以核心输入是git diff的输出。但前面说了Agent 可能需要读完整文件这是按需触发的不是默认行为。第二支持 git worktree 做隔离。这是个容易被忽略的点。如果审查过程需要 checkout 到某个分支去读文件直接在主工作区操作会污染你当前的开发状态。用git worktree开一个临时工作区审完就删干净利落。我在这上面栽过跟头——有次审查脚本把主工作区的未提交改动给覆盖了从那以后所有涉及 checkout 的操作一律走 worktree。第三diff 的生成参数要固定。你注意过热词里那条git -c diff.mnemonicprefixfalse -c core.quotepathfalse --no-optional-locks吗这不是随便写的。diff.mnemonicprefixfalse保证 diff 里的路径前缀是标准的a/b/而不是i/w/这种随上下文变化的core.quotepathfalse让中文路径正常显示而不是转义成八进制--no-optional-locks避免在 CI 里因为锁文件冲突导致命令失败。这三个参数不加Agent 解析 diff 时就会出各种莫名其妙的错。3. 核心细节解析与实操要点3.1 diff 解析别小看这一步很多人以为 diff 解析就是按行读其实坑很多。一个标准的 unified diff 长这样diff --git a/src/auth.py b/src/auth.py index 1a2b3c4..5d6e7f8 100644 --- a/src/auth.py b/src/auth.py -10,7 10,9 def login(user, pwd): if not user: return None - token generate_token(user) if not verify_password(user, pwd): return None token generate_token(user) return token解析时要注意几点hunk header 里的行号范围 -10,7 10,9 决定了这段改动在原文件和新文件里的位置Agent 要读完整文件时得靠这个定位上下文行和改动行的区分空格开头是上下文-是删除是新增多 hunk 的情况一个文件可能有好几处不连续的改动得分别处理。我的做法是解析成一个结构化对象每个文件一个条目里面包含文件路径、改动类型新增/修改/删除、以及一个 hunk 列表。每个 hunk 记录起始行号、上下文行、增删行。这样 Agent 拿到的不是一坨文本而是有结构的数据理解起来准确得多。注意二进制文件的 diff 是没法解析的遇到就跳过。图片、编译产物这类改动让 Agent 审也没意义。3.2 Agent 的提示词设计提示词这块我改了十几版总结出几条经验。第一角色要具体。别写你是一个代码审查助手要写你是一个有十年经验的 Python 后端工程师正在审查一个 Web 服务的 PR。角色越具体模型的输出越贴近真实审查场景。第二输出格式要死板。我要求 Agent 按固定 JSON 返回每个问题包含文件路径、行号、严重级别blocker/warning/info、问题描述、修改建议。格式固定了下游才能自动化处理。这里有个技巧在提示词里给一个完整的输出示例比描述十遍格式都管用。第三明确什么该报什么不该报。模型有个毛病什么都想说两句。我在提示词里明确列了审查范围逻辑错误、安全漏洞、性能问题、边界条件。至于代码风格、命名规范这些交给 linter不让 Agent 管。不然它会在变量名上给你写一堆废话。第四要求给出理由。每条意见必须说明为什么这是问题而不是建议改成 XXX。有理由的意见人工复核时能快速判断对不对没理由的意见reviewer 还得自己去想反而增加负担。3.3 工具集的设计与实现Agent 能调用的工具我最终定了三个工具名功能触发场景read_file读取指定文件的指定行范围需要看 diff 之外的上下文search_code在代码库里搜索字符串或正则需要确认某个函数/变量的使用情况run_git执行只读的 git 命令需要看提交历史、blame 信息每个工具的实现都要注意安全和效率。read_file要限制最大读取行数防止 Agent 一口气读个几万行的文件把上下文撑爆search_code要限制结果数量并且优先返回相关度高的run_git只允许白名单命令log、blame、show可以push、reset这种一律拒绝。提示工具返回结果时加上明确的边界标记比如file_content.../file_content。模型对边界标记很敏感能有效减少它把工具输出和提示词混淆的情况。3.4 交互确认的绕过热词里有个问题很典型claude code cli 怎么避开每次确认的动作。这个痛点我太懂了。Agent 每调一个工具就弹一次确认审查一个 PR 要点几十次根本没法用。我的处理方式是分级授权。只读操作全部自动放行读文件、搜代码、看 git 历史这些不会改变任何状态没必要确认。写操作才需要确认但审查场景里本来就不该有写操作。所以实际上整个审查流程可以做到零确认。具体实现上我在工具定义里加了一个readonly标记Agent 调度时只读工具直接执行非只读工具走确认流程。这样既保证了安全又不影响效率。如果你用的是现成的 CLI 工具找找有没有类似的自动批准只读操作配置项一般都有。4. 完整实操流程与关键环节4.1 环境准备git 和 CLI 工具链先把基础环境搭好。git 的安装不复杂但有几个配置项必须设对。Windows 上装 git去官网下安装包一路下一步就行。装完打开 git bash先配身份git config --global user.name 你的名字 git config --global user.email 你的邮箱然后是我强烈建议加上的几个配置# 保证 diff 路径前缀标准Agent 解析不会出错 git config --global diff.mnemonicprefix false # 中文路径正常显示 git config --global core.quotepath false # 换行符处理跨平台协作必备 git config --global core.autocrlf inputcore.autocrlf这个特别说一下。Windows 用 CRLFLinux/Mac 用 LF不统一的话 diff 里全是换行符改动Agent 会被这些噪音干扰。设成input表示提交时转成 LF检出时不转适合大多数场景。CLI 工具这边你需要一个能跑 Agent 的命令行环境。我用的是 Node.js 生态的工具装好 Node 之后全局安装对应的 CLI 包就行。装完跑一下--version确认能正常输出版本号。如果报 unable to locate the codex cli binary 这类错八成是 PATH 没配好检查一下全局安装目录在不在环境变量里。4.2 用 git worktree 做审查隔离这是整个流程里我最想强调的一步。审查脚本需要 checkout 到目标分支去读文件如果直接在主工作区操作会打断你正在做的事。git worktree就是干这个的。# 在临时目录创建一个关联到目标分支的工作区 git worktree add /tmp/review-workspace feature-branch # 审查完成后清理 git worktree remove /tmp/review-workspacegit worktree的好处是它和主仓库共享.git目录不占额外空间但工作区是独立的。你在里面怎么折腾都不影响主工作区。审查脚本进来先建 worktree审完删掉全程隔离。注意worktree 创建后如果脚本异常退出可能留下残留。建议在脚本里加个 trap无论正常结束还是报错都执行清理。不然攒多了git worktree list会列出一堆僵尸工作区。4.3 生成 diff 并喂给 Agentdiff 的生成命令要固定参数前面提过的那三个git -c diff.mnemonicprefixfalse -c core.quotepathfalse --no-optional-locks diff main...HEADmain...HEAD这个三点语法表示从 main 和 HEAD 的共同祖先到 HEAD 的改动也就是这个分支相对 main 引入的所有变化。比main..HEAD更准确后者在 main 有新提交时会包含不属于本分支的改动。拿到 diff 后先做解析转成结构化数据再拼进提示词。提示词的大致结构是[角色定义] [审查范围说明] [输出格式要求 示例] [结构化 diff 数据] [可用工具说明]这里有个细节diff 数据放在提示词靠后的位置。模型对提示词末尾的内容注意力更高把最需要它仔细看的东西放最后效果更好。4.4 Agent 执行循环与结果收集Agent 跑起来之后就是一个循环模型输出决策 → 执行工具 → 结果回填 → 模型继续决策。直到模型输出最终的审查结果循环结束。这个循环要设几个保护最大轮次限制防止模型陷入死循环一直调工具单次工具调用的超时防止某个命令卡死总 token 预算防止上下文无限增长把费用跑爆。我一般设最大 15 轮单次工具超时 30 秒总预算根据模型定价来定。结果收集就是解析模型最后输出的 JSON。解析失败的话做一次重试把解析错误信息回填给模型让它重新输出。重试还失败就降级处理把原始输出存下来人工看。4.5 结果回写与展示审查结果有几个去处。终端展示是最简单的按严重级别排序blocker 用醒目颜色标出来。写文件适合留档每次审查生成一个 markdown 报告。回写 PR 评论需要调用对应平台的 API这块因平台而异但核心逻辑一样把每条意见转成评论定位到对应的文件和行号。行号定位这里有个坑diff 里的行号是新文件的行号但 PR 评论 API 通常要求的是 diff 中的位置。这两个不是一回事。你得维护一个映射把新文件行号转成 diff 位置。我一开始没注意评论全贴错地方了尴尬得很。5. 常见问题与排查技巧实录5.1 Agent 输出格式错乱怎么办这是最高频的问题。表现是模型返回的 JSON 缺字段、多字段、或者干脆返回一段自然语言。原因通常是提示词里的格式说明不够强或者模型能力不够。排查思路先看提示词里有没有给完整的输出示例。示例比描述管用得多。如果给了示例还错检查是不是 diff 太长把格式说明挤到上下文边缘了试试把格式要求放在提示词最末尾。还不行就换模型有些模型在结构化输出上确实弱。我自己的经验是在提示词里加一句只输出 JSON不要有任何其他文字能解决大部分问题。模型有时候会忍不住加个好的以下是我的审查结果这句话就能把它摁住。5.2 审查意见太多或太少太多的话通常是审查范围没界定清楚。模型把风格问题、命名问题全报上来了。解决办法是在提示词里明确排除这些并且要求每条意见必须关联到具体的逻辑或安全问题。太少的话可能是 diff 太长导致模型注意力分散或者模型太保守不敢报。前者可以试试分段审查把大 PR 拆成几个小 diff 分别审后者可以在提示词里鼓励模型宁可多报也不要漏报关键问题但要注意别矫枉过正。5.3 git 相关报错速查报错信息原因解决unable to locate the codex cli binaryCLI 没装或 PATH 不对检查全局安装目录是否在 PATHlogin failed. check api token凭证没配或过期重新配置 API tokenworktree already exists上次残留没清理git worktree prune后重试diff 里中文显示为八进制quotepath 没关加-c core.quotepathfalse换行符导致全文件 diffautocrlf 配置不一致统一设为 input5.4 成本和速度的平衡Agent 模式比单次问答贵因为要跑多轮。控制成本的关键是减少不必要的工具调用。我观察到Agent 经常为了确认一个显而易见的事情去读文件比如 diff 里明明写了函数签名它还要去读完整文件确认。解决办法是在提示词里说明diff 中已包含的信息不要重复获取。速度方面工具调用的串行执行是瓶颈。如果模型一次决策要调多个工具可以考虑并行执行。但要注意有些工具调用之间有依赖不能盲目并行。我的做法是让模型在输出里标明哪些调用可以并行然后按标记执行。5.5 几个我踩过的坑坑一diff 太大导致超上下文。有个 PR 改了 3000 行直接塞进去模型直接报超限。后来改成按文件切分每个文件单独审最后合并结果。虽然丢失了跨文件的关联分析但至少能跑。坑二Agent 把测试文件也审了。测试代码的审查标准和业务代码不一样混在一起模型会给出奇怪的意见。后来在 diff 解析阶段就把测试文件过滤掉单独处理。坑三模型对删除的代码过度关注。diff 里删除的行模型有时候会问为什么删了这个但删除本身往往就是重构的一部分不需要解释。在提示词里说明删除的代码除非涉及功能移除否则不需要评论能减少这类噪音。坑四并发审查时的资源竞争。同时审多个 PR 时如果都用同一个 worktree 路径会冲突。解决办法是给每个审查任务生成唯一的临时目录名用 PR 号或随机字符串区分。6. 我个人的一些使用体会这套东西跑了大半年最大的感受是AI 审查不能替代人但能极大提升人的效率。以前一个 PR 我要花二十分钟逐行看现在 Agent 先过一遍把明显的问题标出来我只看它拿不准的部分五分钟搞定。省下的时间不是一点半点。另一个体会是提示词的迭代是无止境的。每次遇到新的误报或漏报就回去改提示词。改到现在我的提示词已经有两千多字了但值得。好的提示词能让同一个模型的表现提升一个档次。最后分享一个小技巧把 Agent 的审查结果和人工审查结果做对比找出差异针对性优化。我每周会抽几个 PR人工审完之后再跑一遍 Agent看看它漏了什么、多报了什么。这个反馈循环是提升审查质量最有效的手段没有之一。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑