尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

基于大模型的自动代码评审工具open-code-review实践

发布时间:2026/9/26 14:52:04

资讯中心
01
ARTICLE

基于大模型的自动代码评审工具open-code-review实践

基于大模型的自动代码评审工具open-code-review实践
1. 我为什么会做 open-code-review 这个项目先交代一下背景。我所在的团队大概从两年前开始做微服务拆分代码仓库从个位数涨到了三十多个每次合并请求的评审压力肉眼可见地增大。不是不想认真 review是真的看不过来——一个后端服务改动动辄几百行涉及多个模块光是把上下文理清楚就得花不少时间。期间试过让组内轮流做代码评审官也试过定 weekly review 专场效果都一般最后往往变成看一眼有没有明显 bug有没有留下 console.log这种级别的表面功夫。后来我就想能不能做一个工具让机器先把该检查的检查掉把人力的注意力留给真正需要人肉判断的地方。open-code-review 这个项目就是在这个诉求下开始动手的。它的定位很直白一个跑在 CI 流程里的自动代码评审工具拉取合并请求的差异内容调用大模型做静态分析把发现的问题按严重程度列出来直接回写到代码托管平台的 PR 评论里。这不新鲜市面上一堆商业产品在做类似的事。但我的目标是做一个足够轻、足够透明的版本——用 GitHub Actions 就能跑配置简单分析逻辑可以自己改模型也可以自己换。这样不管团队用的是 GitHub 还是 GitLab只要网络环境允许接入大模型接口就能以很低的成本获得一个 7x24 小时在线的初级评审员。如果你也是被代码评审折磨的开发者、Tech Lead或者单纯想给自己仓库加一道自动检查那这个项目的思路应该能给你不少启发。2. 核心设计一次不依赖外部平台的评审任务是怎么搭起来的2.1 我需要先把评审这件事拆成几个动作动手之前我把人工 review 时做的事拆解了一下大概有这么几类看 diff找到每一处改动的上下文。对照已知的代码规范检查命名、格式、明显的反模式。根据经验判断潜在 bug、边界条件、并发问题。给出改进建议而不是只贴一行不建议这样写。前两步其实非常有规律机器完全能做甚至比人做得更稳定。第三步是难点但大模型处理得也不错尤其是那种这里要检查数组越界这个条件判断少了空值处理级别的逻辑模型往往一眼就能看出来。第四步输出格式需要约束一下不然会得到一堆废话。于是 open-code-review 的核心设计就定下来了从代码托管平台的 API 拉取当前合并请求的 diff。把 diff 按文件拆分、按 hunk 切片分批发给大模型。用一段精心设计的 prompt 约束模型只输出结构化的问题列表。把模型返回的结果解析成评论回写到 PR 下方。这样整个流程不依赖任何第三方代码评审平台所有数据只经过托管平台 API 和模型 API逻辑透明、可控日志记录也方便排查。2.2 为什么选择 GitHub Actions 而不是自建服务我之前也被问过为什么不搞一个常驻服务监听 webhook毕竟那看起来更专业。但我一开始就否决了这个方案原因很现实GitHub Actions 对开源仓库免费额度足够一个小团队内部仓库用 private runner 成本也不高。最关键是它天然解决了任务从哪触发的问题每次 push、开 PR 都能自动触发不需要自己去维护一个服务端的生命周期。另一个考虑是安全性。自建一个 webhook 接收服务意味着你还要处理地址暴露、密钥管理、服务可用性这些事而 GitHub Actions 的工作流本身就是隔离的secrets 机制也比较成熟模型 API key 放仓库 secret 里就行。open-code-review 的入口就是一个code-review.ymlworkflow 文件。开发者只需要把这个文件复制到自己的仓库.github/workflows/目录下填上 API key 和相关的环境变量就能跑起来。不需要额外部署任何后端服务。2.3 diff 的处理方式整段塞给模型是最蠢的做法一开始我也踩过坑试图把整个 PR 的 diff 文本一股脑做成一个 prompt 发给模型。后果很明显单个 PR 改动大时token 会超限。模型注意力被无关内容分散容易漏掉真正的关键问题。如果某个 hunk 有问题模型给出的建议无法精确关联到具体行回写评论时会非常痛苦。所以我很快改成按文件、按 hunk 切分的策略。具体逻辑是先用代码托管平台的 API 拉取合并请求的files列表对每个文件再获取对应的 diff 内容然后把按行号切出的 hunk 作为独立的审查单元。这样单次请求的 token 消耗可控每个 hunk 里的代码量也刚好能让模型看得清上下文。这样做还有额外的好处如果模型某轮请求因为网络问题失败了只需要重试那一个 hunk不用整个 PR 重新过一遍。2.4 不同编程语言的差异化提示词只靠一套规则远远不够做了一个初版之后第一轮测试就发现用中文注释的 Python 项目和用英文注释的 Java 项目模型的评价风格完全是两个方向。前者容易强行夸奖后者则容易表现得过于挑剔。这说明固化的一套 prompt 不可能通吃所有项目。于是我引入了语言适配层为每种主流语言保存了一段独立的评审规则提示词。在 Python 里提醒模型关注类型注解和上下文管理器是否正确使用在 Java 里提醒它注意空指针和并发问题在 Go 里提醒错误处理是否被吞掉在 TypeScript 里则偏重类型安全和可能的 undefined。这套规则不复杂但效果提升非常明显。关键点在于给模型的约束要具体不能泛泛地说请检查代码质量而是把该语言里最容易踩的坑直接列出来引导模型往那个方向想。3. 具体实现从拉取 diff 到回写评论的完整链路3.1 拉取合并请求数据时容易被忽略的坑整个链路的第一步是拿到 PR 的所有改动。我刚开始直接用了 GitHub 的GET /repos/{owner}/{repo}/pulls/{pull_number}/files这个接口拿到的响应里就包含了每个文件的filename、patch和status看起来够用了。但后来发现有个重要的参数per_page。GitHub 默认一页返回 30 条文件记录如果一个 PR 改了超过 30 个文件实际上并不罕见后面的文件就全被静默丢弃了。这个 bug 一度导致我误以为模型漏检实际上是数据源根本没取全。修复方法是显式遍历分页。我在脚本里加了一个循环只要当前页的记录数等于per_page就继续请求下一页直到拿完为止。这算是一个典型的小参数、大坑写代码的人如果没实际遇到大量文件的 PR基本不会注意到。3.2 精确到行号的评论定位实现模型返回一个问题描述时如果不带着行号那这条评论的价值就大打折扣——阅读者还得自己回 diff 里找位置。所以我在 prompt 里要求模型必须输出file_path、start_line和end_line然后回写时通过 GitHub API 的position参数来定位。但这里又有一个坑GitHub 的 review 评论 API 里position指的是 diff 中相对于该 hunk 头部的偏移量不是源码里的绝对行号。模型给出行号后我需要通过解析 diff hunk 头部那行 -开始行,行数 开始行,行数 来完成映射。除非你直接用系统自带的解析方法否则这个偏移很容易算错。我的解决方案是先用简单的正则解析出每个 hunk 的旧文件起始行号和新文件起始行号然后建立新文件行号 - diff hunk 内位置的映射表最后将模型输出的行号转换到对应的映射位置。由于新文件行号在 hunk 内是递增的计算起来并不复杂但省掉了评论错位这种最影响体验的问题。3.3 多轮对话提高准确性但不要过度依赖第一版我是单轮请求prompt 里给规则、给代码、要求输出 JSON。模型返回结果有时会漏掉一些隐含 bug尤其是有多个问题集中在同一段代码时。后来我加了确认轮的逻辑模型先输出第一轮的问题列表然后我再追加一段上下文要求它针对第一轮的结果做一次自查——比如上一轮你说这里可能空指针请结合完整函数判断是否确实可能发生。这个策略有点类似让模型做第二轮思考能过滤掉一部分误报。但也要克制不要让模型无限修订因为每一轮都意味着额外的时间开销和 token 成本。通常两轮足够第一轮产生候选问题第二轮用于汇总、去重、并按严重程度排序。4. 让模型输出直接可用的 JSON全靠 prompt 约束技巧4.1 我踩过的输出格式坑模型输出格式不稳定别提多烦了第一次试验时我在 prompt 里写请将代码问题输出为 JSON模型确实输出了一坨 JSON但结构非常不稳定有时字段名是line_number有时是line有时用数组包着多个对象有时只输出一个对象最离谱的一次它在一个 JSON 字符串里夹杂了 Markdown 表格。这种情况很难用传统的json.loads来稳定解析我被迫写了一套容错解析器先把代码块去掉、再尝试截取第一个大括号或中括号到最后的大括号/中括号再尝试修复未闭合引号。后来我意识到与其靠解析器兜底不如从一开始就把输出格式约束得更严格。我最终的方案是在 prompt 中明确声明只输出 JSON不要包含任何其他文本不要使用 Markdown 代码块并且在最后给出一个符合预期的 JSON 示例。同时用temperature0.2这样的低随机度参数尽可能减少模型即兴发挥的空间。4.2 few-shot 示例的正确打开方式few-shot 是约束模型输出的最有效手段但示例不能随便写。我见过很多教程用完美输出作为示例这在复杂任务里反而容易出问题因为模型容易照着示例的完美结构套一旦实际场景里问题数量不一样输出结构就乱了。所以我提供了两条示例一条展示发现了一个阻断性问题 两个普通问题的输出另一条展示没有发现问题的输出。这样模型能学会如何表达没有异常而不是硬编一条无意义的评论。示例里的字段命名也是经过精心选择的。我用severityblocker / warning / suggestion来代表问题的严重程度用category来标记问题类型比如bug、performance、security、style。这个分类在后端回写评论时能决定标签和颜色也方便开发者在海量评论中快速过滤。4.3 解析 JSON 之后的兜底策略即使模型还是胡来也要优雅降级即便做了这么多约束仍然不能指望每次输出都完美。我的兜底策略是如果解析失败就把模型返回的原始文本封装成一条warning级评论挂到 PR 底部并注释备注该评论由自动分析生成请人工复核内容。这样的好处是分析链路不会中断也不会因为某一次的格式错误导致整次评审任务失败。日志里我会把解析失败时的原始输出完整记录下来形成一个小样本库定期分析并补充到 prompt 的 few-shot 示例里形成一个正向循环。5. 实践中的效果与翻车记录哪些建议可信哪些纯属噪音5.1 高价值发现空指针、资源未关闭、并发竞争在跑了差不多 30 个真实 PR 之后我统计了模型反馈中最有价值的问题类型。排在第一位的是空指针和未判空就调用方法的问题。这类问题在 Java 和 Go 项目里非常常见而且代码评审时人眼很容易漏掉——因为它们只会在特定的参数组合下才触发review 时靠脑内模拟很难察觉。 其次是资源未关闭的问题。比如 Python 里文件句柄没走with语句、Go 里 HTTP 响应体没有关闭。这类问题虽然模型只是基于代码模式识别并非真的运行了代码但大多数情况下判断是准确的。并发相关的建议也值得一提。有一次模型在 Go 代码里发现了一个共享 map 在多个 goroutine 中并发读写的隐患还建议加锁或用sync.Map。这个发现让我很意外因为那不是一段新代码而是一段被移动位置的历史代码人工评审时根本没意识到它现在被并发调用了。5.2 翻车记录模型的强行找茬与误报当然误报也不少。最典型的问题是模型对既有代码风格过度敏感比如把一个符合项目规范的var变量建议改为const或者把一段已经加了空判断的代码继续提示可能的空指针。还有一类翻车是模型强行找茬。当 diff 里的代码很规范、没什么可提的点时模型为了不显示得无所事事会在style类别里硬写几条建议。为此我在代码评审 prompt 里加了一句若未发现问题请如实输出空数组不要生成低质量建议。实测下来确实抑制了一部分噪音但并不能完全根除。所以我设计了严重程度过滤机制在回写评论前允许仓库维护者通过配置文件设定阈值比如blocker和warning级别的评论才被写入 PRsuggestion级别的默认只在日志里展示。这样既保留模型的分析能力又避免噪音淹没真实问题。5.3 模型能力边界哪些代码需要绝对避免让模型做决策使用一段时间后我也清楚了这套工具的边界。模型在判断代码风格是否统一这类整体性、规范性问题时能力有限。比如一个项目使用Prettier统一格式但模型看到旧代码没有格式化它可能会建议修改而这些修改在真实提交中会引入巨大的 diff 噪音。更危险的是模型不能理解业务逻辑。它可能会建议把一段看起来冗余但实际上是先扣款再退款的流程简化一旦开发者盲目采纳就会引入严重业务 bug。所以我在项目文档里特意用一个醒目区块写明自动评审只能作为辅助手段不能替代人工评审。规则类问题相信它逻辑类问题建议只把它当成提醒不要直接采纳它的修改建议。6. 接入 CI 之后的工作流优化从触发到评论的整个流程6.1 基于 GitHub Actions 的触发时机与并发控制我设计了三种触发场景pull_request打开或更新时全量评审pull_request_review_comment触发时增量评审被评论那段代码以及手动通过workflow_dispatch触发全仓库扫描用于定期体检。其中增量评审是最费心思的一块。如果每次 PR 更新都做全量评审模型调用量和时间成本都很高而且经常重复分析没有改动的代码。我的做法是先通过 GitHub API 比较该 PR 的 base 分支和 head 分支之间的差异提取出本次提交相对上次评审提交的增量部分仅对这部分增量做模型分析。这个方案实现起来也不算复杂——每次评审完成后把当前 head commit SHA 存到一个标记文件里下一次任务开始时读取这个 SHA做一次compare请求即可。这样既保证了效率也保证了评审的连贯性。6.2 回写评论时的消息排版与去重如果模型一次返回 30 个问题全部塞进一条评论里会显得很杂乱阅读体验很差。我的方案是按严重程度分组生成一张 Markdown 表格每条问题包含文件:行号、问题类别和简要说明。去重也很关键因为同一个问题可能同时在多条评论里被模型提到。我维护了一个简单的指纹表将文件路径 行号 问题类别拼接成字符串通过哈希判断是否已经报告过。已报告过的问题直接丢弃避免开发者看到 5 条一模一样的内容。7. 总结一下open-code-review 实际能帮你省下多少时间现在回到最初的问题这工具到底值不值得搭。我的个人体会是它不能帮你完全摆脱代码评审但它能把评审里最有体力活属性的部分干掉——比如看一下有没有明显的空指针看一下有没有资源泄漏看一下命名是否符合规范。这些事过去需要人花十几分钟逐行扫现在模型几秒钟就能完成而且不会累。真正省下的时间更多是在沟通环节。过去我们开评审会时大量时间花在解释这段代码风险在哪上现在模型先把候选问题列出来评审者只需要逐条确认并给出最终判断沟通成本降了一个量级。如果你也想尝试建议先从一个相对小的仓库开始把误报率调到自己能接受的阈值再逐步扩大范围。别一上来就希望它替代所有人工评审那超出了当前模型的能力范围也会让你很快失望。最后再分享一个小技巧模型每次分析完你都可以积累一批高质量的正例和误报案例定期把误报案例加到 prompt 的负面示例里比如以下场景不算问题模型的表现会越来越好。自动评审这件事本质上是个持续调参的过程而不是一次性配置完就再也不管的黑盒。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。