尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于LLM Agent的本地代码评审工具:open-code-review实战

基于LLM Agent的本地代码评审工具:open-code-review实战 1. 为什么我要自己动手做一个 open-code-review 工具团队里代码评审这件事说多了都是泪。项目一多人一忙PR 挂三天没人理是常态好不容易有人看了留一句“这里再优化下”就完事具体怎么优化、为什么有问题全靠猜。更麻烦的是有些低级问题——变量命名不规范、异常没捕获、日志打太多敏感信息——每次都要人肉去盯评审者累提交者也烦。市面上的商业代码评审服务不是没有但要么按人头收费贵得离谱要么必须把代码推到第三方平台公司安全规范那一关就过不去。所以我一直想找一个能跑在自己机器上、能接自己的模型、还能跟 Git 工作流无缝衔接的方案。open-code-review这个项目就是在这个背景下折腾出来的一个基于 CLI 的代码评审工具底层用 LLM Agent 驱动直接读取 Git diff把评审意见按文件、按行号输出全程本地可控。它解决的问题很具体把重复性的、规则明确的代码检查交给模型把人从“找茬”里解放出来专注在架构和业务逻辑的讨论上。适合谁用我觉得三类人最合适——一是中小团队里没有专职代码评审角色的开发者二是想给自己项目加一道自动检查的个人开发者三是想研究 LLM Agent 怎么落地到研发流程里的技术爱好者。哪怕你之前只听过git commit跟着下面的思路也能把整套流程跑起来。2. 整体设计与技术选型拆解2.1 为什么是 CLI 而不是 Web 服务一开始我也想过做个 Web 界面点一下按钮就出评审报告多直观。但真动手时发现几个问题Web 服务要部署、要维护、要处理鉴权团队里每个人还得记一个地址而代码评审这个动作天然发生在开发者本地——写完代码、git add之后、git commit之前这个时间点最合适。CLI 工具可以直接挂在 Git 的 pre-commit 钩子上或者手动敲一行命令零部署成本。另一个考虑是数据边界。CLI 跑在本地diff 内容只在本地进程和模型 API 之间流转不经过任何中间服务器。对于代码这种敏感资产少一个中转就少一份风险。这也是我坚持不做 Web 端的核心原因。2.2 LLM Agent 在这里扮演什么角色很多人把 LLM 和 Agent 混着说其实差别挺大。LLM 是“大脑”你给它一段文本它给你一段回复Agent 是“大脑加手脚”它会自己决定要不要读文件、要不要执行命令、要不要再问一轮。在open-code-review里Agent 的价值在于多轮工具调用它先拿到 diff发现某个函数被改了但没看到上下文就会主动去读原文件发现改动涉及配置文件就会去检查有没有对应的环境变量说明。这种“自己找信息”的能力是单纯把 diff 丢给模型聊天窗口做不到的。至于模型选型我用的是兼容 OpenAI 接口的通用模型服务DeepSeek、通义、智谱这些都能接只要改一下base_url和api_key就行。这里不绑定任何一家是因为模型迭代太快今天好用的明天可能就贵了保持可替换性比选一个“最好”的更重要。2.3 和 Git 的集成方式工具的核心输入是git diff。具体来说我用了git diff --cached拿暂存区的改动这样评审的是“即将提交的内容”而不是工作区里还没整理完的草稿。命令里带了几个参数git -c diff.mnemonicprefixfalse -c core.quotepathfalse --no-optional-locks diff --cached --unified5diff.mnemonicprefixfalse让 diff 头部的路径前缀统一成a/b/方便后续解析不然不同 Git 版本前缀不一样正则匹配容易翻车。core.quotepathfalse中文文件名不会被转义成八进制直接显示原文评审报告里可读性好很多。--no-optional-locks避免在 CI 环境里因为锁文件冲突导致命令卡住。--unified5上下文给 5 行比默认的 3 行多一点模型判断改动意图时更准。这几个参数看着琐碎但都是踩过坑之后定下来的后面讲排查问题时还会细说。3. 核心细节解析与实操要点3.1 diff 解析把补丁变成模型能懂的结构git diff的输出是给人看的模型直接读也能读但容易漏掉行号信息。所以我做了一层解析把每个文件的改动拆成结构化数据{ file: src/service/user.py, hunks: [ { old_start: 42, new_start: 42, lines: [ {type: context, content: def get_user(uid):}, {type: add, content: if uid is None:}, {type: add, content: raise ValueError(uid required)}, {type: del, content: return db.query(uid)} ] } ] }这样模型拿到的不是一坨文本而是带类型标记的行列表它能明确知道哪行是新增、哪行是删除、哪行是上下文。实测下来结构化输入比纯文本输入的评审准确率高不少尤其是涉及行号引用的时候。注意解析 hunk 头的时候要小心 -old_start,old_count new_start,new_count 这个格式有些 diff 会省略 count表示 1正则里要把这个情况覆盖到不然会解析出错。3.2 Prompt 设计让模型说人话、说具体话Prompt 是这类工具的灵魂。我试过好几版最后稳定下来的结构是这样的角色设定明确告诉模型“你是一名资深代码评审者关注正确性、可维护性、安全性不纠结个人风格偏好”。输入说明解释 diff 的结构告诉它add是新增、del是删除。输出格式要求按文件分组每条意见包含行号、严重级别blocker/warning/suggestion、问题描述、修改建议。约束条件明确“不要对没改动的代码提意见”“不要重复提同一个问题”“建议要给出具体代码片段”。这里有个经验严重级别一定要让模型自己判断但要在 prompt 里给出判断标准。比如“blocker 指会导致运行时错误或安全问题”“warning 指可能引发 bug 或维护困难”“suggestion 指可读性和风格改进”。不给标准的话模型会把所有问题都标成 blocker报告就没法看了。3.3 工具调用让 Agent 自己去看上下文前面提到 Agent 会主动读文件具体实现是给它注册几个工具函数read_file(path, start_line, end_line)读指定文件的指定行范围。search_symbol(name)在仓库里搜索某个函数或变量的定义位置。list_changed_files()列出本次改动涉及的所有文件。模型在评审时如果觉得“这个函数被调用了但定义没看到”就会自己调search_symbol去找。这个能力对跨文件改动的评审特别有用比如改了一个接口签名Agent 会去检查所有调用点有没有同步更新。实操心得工具调用要设上限我一般限制单个文件最多 5 次调用整个评审最多 30 次。不设限的话模型可能陷入“读文件—发现新问题—再读文件”的循环token 消耗飞快。3.4 输出渲染终端里也能看得舒服评审结果最终要打在终端里所以格式很重要。我用的是带颜色的分级输出blocker 用红色背景一眼就能看到。warning 用黄色。suggestion 用灰色不抢注意力。每个文件一个区块文件路径加粗意见按行号排序。如果终端支持超链接大部分现代终端都支持文件路径会做成可点击的直接跳到编辑器对应位置。这个细节虽小但用起来顺手很多。4. 完整实操流程与关键环节实现4.1 环境准备Git 和 Python 一个都不能少先把基础环境搭好。Git 的安装不用多说Windows 上直接下安装包一路下一步Mac 用brew install gitLinux 用包管理器装就行。装完验证一下git --version # git version 2.43.0Python 建议 3.10 以上因为用了一些新语法。依赖管理我用的是uv比 pip 快很多uv venv uv pip install openai rich clickopenai是模型调用 SDKrich负责终端渲染click做命令行参数解析。这三个是核心依赖其他都是可选的。4.2 配置模型接入在项目根目录建一个.env文件OCR_API_KEYyour_api_key_here OCR_BASE_URLhttps://api.your-provider.com/v1 OCR_MODELdeepseek-chat这里OCR_是 open-code-review 的缩写前缀避免和其他工具的变量冲突。base_url要填兼容 OpenAI 接口的地址大部分国内模型服务都支持。model填具体的模型名不同服务商命名不一样填之前查一下文档。注意.env一定要加进.gitignoreAPI key 泄露的后果不用我多说。我见过有人把 key 提交到公开仓库半小时就被刷了几百万 token。4.3 核心命令跑起来工具的主命令设计得很简单ocr review默认评审暂存区的改动。如果想评审某个分支和主分支的差异ocr review --base main --head feature/login如果想评审某个具体的 commitocr review --commit abc1234内部逻辑是先把 diff 拉出来解析成结构化数据然后按文件分批送给 Agent。为什么要分批因为一次性把整个 diff 塞进去token 容易超限而且模型注意力会分散。我一般按文件切分单个文件超过 500 行改动就再按 hunk 切。4.4 参数计算token 预算怎么估这里有个实际的计算过程。假设一个文件改了 100 行每行平均 40 个字符diff 本身大约 4000 字符。加上 prompt 模板 1500 字符、上下文文件读取预留 3000 字符单文件输入大约 8500 字符。按 1 token 约等于 4 个英文字符、1.5 个中文字符估算大约 2500 token。输出按输入的 30% 算约 750 token。单文件总消耗约 3250 token。一个中等规模的 PR 改 10 个文件总消耗约 32500 token。按主流模型价格一次评审成本在几毛钱到一块钱之间。这个成本比人工评审便宜太多而且随时能跑。4.5 挂到 Git 钩子上想让评审自动化可以挂 pre-commit 钩子。在.git/hooks/pre-commit里写#!/bin/bash ocr review --staged if [ $? -ne 0 ]; then echo 代码评审未通过请处理后重新提交 exit 1 fi这样每次git commit都会先跑评审有 blocker 级别问题就阻止提交。不过我不建议一上来就开这个先手动跑一段时间等 prompt 调稳定了再挂钩子不然误报会让人想砸键盘。5. 常见问题与排查技巧实录5.1 模型返回格式不对怎么办最常见的问题是模型不按要求的 JSON 格式输出夹杂一堆解释性文字。我的处理方式是双重保险一是在 prompt 里明确“只输出 JSON不要任何其他内容”二是在解析时做容错先用正则提取 JSON 块提取失败就退化成纯文本展示至少不丢信息。如果频繁出现格式问题可以在 prompt 里加一个 few-shot 示例给一组输入输出样例模型模仿能力很强给了例子之后格式稳定性会明显提升。5.2 diff 解析出错怎么排查diff 解析出错通常有几个原因现象可能原因解决方法文件名乱码core.quotepath没关加-c core.quotepathfalsehunk 头解析失败count 被省略正则里把 count 设为可选行号对不上用了--unified0改成--unified5二进制文件报错diff 包含二进制解析时跳过Binary files开头的行我一般会在解析后打印一下统计信息比如“解析到 8 个文件23 个 hunk”数字对不上就说明有问题方便快速定位。5.3 模型“幻觉”出不存在的问题这是 LLM 的通病它会“脑补”一些代码里没有的问题。应对方法有两个一是要求模型引用具体行号和代码片段如果它引用的行号在 diff 里不存在这条意见就丢弃二是在 prompt 里强调“只评审 diff 中出现的改动”明确禁止对未改动代码提意见。实测下来加了行号校验之后幻觉率能降一大半。剩下的一小部分靠人工快速扫一眼就能过滤掉。5.4 大 PR 跑得慢怎么优化大 PR 的瓶颈通常在模型调用串行处理 20 个文件要等好几分钟。优化思路是并发调用用asyncio把不同文件的评审并行发出去。但要注意两点一是并发数别开太大一般 3 到 5 个就够了太多容易触发服务商的限流二是要处理部分失败的情况某个文件评审失败不能影响其他文件最后汇总时把失败的文件列出来让用户重试。5.5 评审意见质量不稳定怎么调质量不稳定通常和 prompt 有关。我的经验是把评审规则拆细不要笼统地说“找问题”而是分维度正确性、安全性、性能、可维护性、测试覆盖。每个维度给一两条判断标准模型有了明确的检查清单输出会稳定很多。另外温度参数调低也有帮助。我一般设 0.2 到 0.3太低会死板太高会发散。这个值可以按团队偏好微调。6. 一些踩坑之后的个人体会做这个工具最大的感受是LLM 评审不是替代人而是把人从重复劳动里捞出来。它最擅长的是发现那些“规则明确但人容易忽略”的问题比如空指针、资源没释放、日志泄露敏感信息。而架构合不合理、业务逻辑对不对还是得人来判断。另一个体会是prompt 要持续迭代。我前后改了十几版每改一版都拿同一个 PR 跑一遍对比看哪些意见是新增的、哪些是消失的。这个过程有点像调参急不得但每调一次质量都会往上走一点。最后分享一个小技巧把每次评审的结果存下来定期回顾哪些意见被采纳、哪些被忽略。被忽略多的规则就从 prompt 里删掉被采纳多的就加强。这样工具会越用越贴合团队的实际需求而不是变成一个只会说废话的“评审机器人”。
返回列表