资讯动态

基于大模型的代码审查工具open-code-review设计与落地实践

发布时间:2026/9/18 4:35:08 来源:尧图企业网站定制
在研发流程里代码审查一直是保证质量最重要也最容易被敷衍的环节。忙起来的时候PR 描述就一句话Reviewer 点个“看起来没问题”就合入结果问题全留到测试和线上才冒头。我做后端开发快十年对这种场景再熟悉不过也正是因为踩过太多这类坑才花业余时间搞了一个叫 open-code-review 的开放性代码审查小项目。它严格来说不是一个开箱即用的商业产品而是一套可自行组装、可定制审查规则的方案——核心是调用大模型对代码 Diff 做自动化评审把审查门槛降低让团队在代码合入前多一道机器视角的关卡。这篇文章我就完整拆解一下这个项目的设计思路、核心实现、接入方式和我在实际落地中遇到的典型问题。如果你想自己在团队里搭一套基于大模型的代码审查服务或者想深入理解这种工具的原理和边界这篇内容可以直接当作参考方案来用。1. 整体设计思路为什么自研而不是直接买现成工具1.1 现有工具的痛点市面上其实已经有不少 AI 代码审查工具比如 CodeRabbit、GitHub Copilot 自带的 review 功能、还有一些商业平台的机器人。这些工具质量总体不错但我实际用下来有几个绕不开的痛点。第一是规则不可控。商业工具的审查逻辑是黑盒你只能接受它的风格和侧重。有的工具对代码风格过于敏感PR 里全是格式类评论有的对业务逻辑又完全不管形同虚设。团队想要自定义一套“符合自身规范”的审查标准基本做不到。第二是数据安全顾虑。代码是公司最核心的资产直接发送到第三方平台很多团队在合规上就过不去。尤其金融、政务类项目别说代码了连依赖清单都要求内网隔离。第三是成本。按 PR 数量收费的 SaaS 服务对大型团队来说是一笔不小且持续的开销。我今天看后台数据一个中型团队 20 个人一个月几千条审查请求按条计费的话轻松破万。自己做 open-code-review 的初衷很简单用一个可完全掌控的脚本通过调用大模型 API 来审查代码 Diff并且把 Prompt 和审查规则全部开放出来让团队按需改。钱可控、规则可控、数据可控。1.2 两条技术路线的选择在设计这套方案时我首先明确了核心链路拿到代码变更内容交给大模型分析输出审查意见。但这里有一个关键分叉——代码内容怎么传给模型方案 A直接把整个文件的所有代码塞给模型让模型自己找问题。方案 B只把代码变更的 Diff 内容传给模型聚焦审查变更点。我一开始想得太简单选了方案 A结果非常糟糕。一是 Token 消耗大大文件一次要好几万 Token费用直线上升二是上下文太长之后模型注意力分散反而抓不住变更里的真正问题三是没有变更范围的约束模型会把你原有的老代码也评一遍噪音极大。后来果断切到方案 B只取git diff的结果作为输入。这里有三个好处输入量小、聚焦精准、和团队现有 Git 工作流天然契合。事实上现在主流 AI 审查工具都是基于 Diff 的这已经是业内默认做法。1.3 核心架构地图整个 open-code-review 的架构非常简单主要由四部分构成变更获取层执行git diff命令解析出文件级别和行级别的变更内容。审查触发层脚本入口支持本地手动跑、Git 钩子自动跑、CI 里跑。模型交互层把 Diff 内容按规则包装成 Prompt调用大模型接口拿到结构化 JSON 审查结果。结果输出层把 JSON 解析成人类可读的 Markdown 报告输出到命令行或作为 GitHub PR 评论。没有引入复杂框架没有搞微服务就是一个 Python 脚本为核心加上 GitHub Action 和本地 CLI 两种使用方式。这么做的好处是任何团队都能在半小时内读懂全部代码并按自己的需求去改。2. 核心实现细节与实操要点2.1 获取代码 Diff 的正确姿势获取 Diff 是整个工具的地基。我在实现中发现这里有不少容易被忽略的细节。最基本的命令是git diff origin/main...HEAD这能拿到当前分支相对主分支的所有变更内容。但直接拿输出当 Prompt 会有问题如果分支是老分支离 main 太远Diff 会非常大甚至超过模型上下文窗口。解决方法是限制文件大小和单文件 Diff 行数超过阈值就拆分批次或者干脆跳过对超大文件的审查逻辑。# 只统计变更文件列表不展示具体内容 git diff --name-only origin/main...HEAD # 获取指定文件的 diff 内容 git diff origin/main...HEAD -- app/services/user_service.py还有一个很关键的点必须加上--unified20。默认 Git Diff 只显示上下文 3 行这对代码审查来说远远不够。一个函数改动往往涉及到调用方的逻辑变化如果上下文太少模型看不到函数签名和调用方给出的意见就会很浅。我实测下来20 行上下文是比较均衡的值既能保持输入量可接受又能让模型把握住局部逻辑。另外要注意git diff和git diff --cached的区别。前者是工作区相对暂存区的差异后者才是已暂存内容相对上次提交的差异。在 CI 里跑审查时我们用两个提交点之间的 Diff即origin/main...HEAD这样所有已推送的 commit 变更都在范围内和本地文件状态无关结果更稳定。2.2 Prompt 设计是决定质量的核心环节Prompt 写得好不好直接决定审查意见的质量。我一开始用的 Prompt 特别简单就一句话“请审查以下代码差异指出问题。”结果模型输出空泛、语气含糊、还经常说“建议考虑是否可能需要优化”这类废话完全不可用。后来我参考了几家商业化 AI 审查工具的思路把 Prompt 拆成了四个层次角色设定告诉模型你是资深研发负责人正在做代码评审。输入说明明确输入内容是代码 Diff逐文件展示变更。审查要求要求输出结构化的 JSON格式必须符合给定 Schema。质量约束明确哪些不算问题、哪些是重点、防止模型为了找问题而找问题。比如质量约束这部分定义了几条硬规则代码风格问题缩进、命名个人偏好、格式化不报告。对没有实际影响的理论性缺陷不报告。应当聚焦于逻辑错误、空指针风险、并发安全隐患、资源泄漏、明显的性能瓶颈。每条建议必须附带对应的文件和代码行号。这些规则看起来简单但实际上能省掉一半以上的噪音评论。如果你自己搭这类工具我强烈建议多在质量约束上做文章模型非常乖巧你约束得越具体它输出得越精准。2.3 结构化输出与解析大模型的响应天然是自然语言但对程序来说我们需要的是结构化数据。这里我选了 JSON 作为中间格式定义如下{ summary: 本次变更的总体评价300字以内, issues: [ { file: app/services/order_service.py, line: 152, severity: BLOCKER, type: NULL_POINTER_SAFETY, message: order 对象可能为 null需要先判空再访问属性 } ] }为了强制模型输出这个格式我在 Prompt 里给出了明确要求并用response_format参数设定为 JSON 模式。实测下来只要模型版本够新JSON 格式的稳定性还是不错的但偶尔也会出现字段缺失或 JSON 中夹杂解释性文字的问题。所以解析时不能太脆弱最好加一层容错处理先尝试json.loads失败的话用正则把 JSON 块从文本中抠出来再解析。2.4 审查规则扩展严重级别与问题类型为了让审查结果更贴近团队实际需求我设计了严重级别和问题类型两个维度。严重级别分为三档BLOCKER必须修复才能合入比如明显的空指针、严重的并发问题、敏感信息泄露。WARNING应该修复但不阻塞合入比如边界情况未处理、潜在性能风险。SUGGESTION供参考的改进建议比如代码可读性、设计模式上的优化。问题类型我目前内置了以下几种枚举NULL_POINTER_SAFETY、RESOURCE_LEAK、CONCURRENCY_ISSUE、SECURITY_RISK、PERFORMANCE_ISSUE、LOGIC_ERROR、CODE_SMELL。这块团队可以自己扩展你只需要修改 Prompt 里的枚举定义让模型按约定的类型输出然后解析端完善一下类型映射就行。3. 实操过程从零搭一套可用的审查服务3.1 环境准备与依赖我用的技术栈比较简单Python 3.10主要依赖只有一个 OpenAI SDK——如果你用的是兼容 OpenAI 接口的国产模型厂也完全没区别。pip install openai另外还需要代码托管平台提供的令牌用于提交审查评论。我默认适配的是 GitHub用 GitHub Token如果你用 GitLab 或 Gitea只需要换 API 调用部分即可整体流程完全一致。3.2 最小可用版本实现我先贴一个最简版的核心审查函数这个函数接收 Diff 文本返回审查报告。这基本就是整个 open-code-review 的最小内核。from openai import OpenAI client OpenAI(api_keyyour-api-key) def review_diff(diff_text: str, model: str gpt-4o-mini) - dict: prompt f 你是一名资深后端工程师正在执行代码评审任务。 请审查以下代码变更内容diff找出真正有价值的问题。 要求 1. 只关注逻辑、安全、并发、性能、资源管理等问题。 2. 忽略纯风格问题。 3. 输出 JSON 格式包含 summary 和 issues 两个字段。 4. issues 数组中每个元素包含 file、line、severity、type、message。 diff 内容如下 {diff_text} response client.chat.completions.create( modelmodel, messages[{role: user, content: prompt}], response_format{type: json_object}, temperature0.3, ) # 容错解析 content response.choices[0].message.content try: return json.loads(content) except json.JSONDecodeError: import re match re.search(r\{.*\}, content, re.S) if match: return json.loads(match.group()) return {summary: 解析失败, issues: []}这个函数的逻辑很直白但已经具备了一条最简可用的审查链路。实际项目中我在此基础上做了几个增强加日志埋点方便排查模型返回异常。加超时限制避免模型长时间无响应。加失败重试个别大模型接口偶尔会 5xx。3.3 增量 diff 处理拆分与合并当 PR 变更的文件很多时把所有 Diff 塞进一个 Prompt 是不现实的。我采用的策略是“按文件拆分审查按 PR 合并汇总”。具体实现思路拿到变更文件列表。按文件名逐文件取 Diff。每个文件单独调用一次模型进行审查。把所有文件的结果合并成一份总报告。这个策略的好处是显然的Token 消耗可控单文件审查更有深度就算某个文件审查失败也不会拖垮整个流程。但随之而来的问题是调用次数增加。如果一个 PR 改了 20 个文件就要调用 20 次模型 API。为了控制成本我加了一个过滤规则只审查“重要文件”比如src目录下的.java、.py、.go文件跳过test、docs、*.lock这些低价值文件。这又是一个减少噪音和成本的关键决策。3.4 接入 GitHub Action 自动触发本地手动跑只是第一步真正实用还是要让它在推代码时自动执行。我写了一个 GitHub Action在每次推送到 Pull Request 时自动运行审查脚本然后把结果作为评论发到 PR 下。name: open-code-review on: pull_request: types: [opened, synchronize] jobs: code-review: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 with: fetch-depth: 0 - uses: actions/setup-pythonv5 with: python-version: 3.11 - name: Install deps run: pip install openai - name: Run review env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} DIFF_BASE: origin/${{ github.event.pull_request.base.ref }} run: | python review.py --github --repo ${{ github.repository }} --pr ${{ github.event.pull_request.number }} --base $DIFF_BASE有几个易踩的坑必须提一下actions/checkout必须设置fetch-depth: 0否则 GitHub Action 默认只拉取最后一次提交的浅克隆根本没有完整历史git diff会失败或者拿到错误的对比结果。基准分支不能写死main应该从github.event.pull_request.base.ref动态取否则分支名不标准的工作流会白跑。API Key 一律走 GitHub Secrets绝不能直接写在 YAML 里。3.5 在本地手动运行线上自动审查跑通了但本地调试时也需要能手动调用。我实现了 CLI 执行入口支持可选参数来指定 Diff 来源。# 审查当前分支相对 main 的改动 python review.py --local --base main # 输出详细 JSON python review.py --local --base main --output json # 审查指定文件 python review.py --local --base main --file src/auth/login.py这个命令行入口对日常开发非常有用。我经常在提交之前先跑一遍本地审查把明显的问题在 push 之前就修掉了。尤其是写新功能的时候模型对边界条件的敏感度往往比我高能提前揪出很多隐藏问题。4. 常见问题与排查技巧实录4.1 模型返回 JSON 不稳定我用的模型是 gpt-4o-mini这个模型本身对 JSON 模式支持很好但如果你使用的是其他模型尤其是开源部署的本地模型JSON 格式稳定性会差很多。常见的问题是返回内容里夹杂解释文字或者 JSON 结构缺失字段。解决方案分三层在 Prompt 里使用“只输出 JSON 格式不要包含任何解释性文字”的强约束表达。解析时做好容错用正则把 JSON 片段提取出来。解析失败时降级为不报错仅记录日志避免整个 CI 流程被一个小问题卡死。4.2 忽略文件列表我踩过一个坑测试文件被修改后模型经常给出一些无意义的建议比如“测试断言不够严格”“测试命名不符合项目规范”这些评论在团队里根本没人在意。后来我在审查前加了一道过滤流程专门定义了一个“低价值文件”列表所有测试相关目录test、tests、spec配置文件.yaml、.yml、.json、.ini、.toml自动生成文件*.lock、go.sum、package-lock.json纯前端打包产物dist、build这些文件要么是机器生成的要么对审查价值不高跳过它们专注审查核心源代码质量和效率都直线提升。这个思路和 Code Review 时人工优先看src目录是一个道理。4.3 Token 超限与费用控制一个大文件可能轻松超过 3 万 Token如果按文件拆分一个 PR 有十个大文件就是 30 万 Token 的消耗。费用上即使是便宜的模型一天几十个 PR 也是非常可观的数字。控制方案的优先级排序先过滤掉低价值文件。对超大文件设置阈值超过 500 行 Diff 直接跳过只提示“文件过大未审查”。对同一文件的多次 commit 做合并 Diff避免重复审查。缓存同文件同 Diff 的审查结果PR 更新后只审查新增部分。在实际使用中单文件 500 行的阈值是一个不错的平衡点。超过这个值的内容模型的表现也会显著下降审查价值不大。4.4 审查意见太浅甚至废话连篇有时候模型给出的意见就是“建议使用更清晰的命名”“可以考虑拆分函数”这些话不能说错但毫无价值。团队如果收到这种评论多了很容易对整个工具产生厌恶感变得根本不看机器人的评论。我的对策是在 Prompt 的“质量约束”部分明确写避免无意义建议。如果某条问题没有具体的上下文依据就不要输出。每条建议必须指出明确的风险后果而不是泛泛而谈。经过这轮调整报告质量显著提升。现在团队的 PR 评论里机器人的意见基本集中在真正的逻辑和安全性问题上偶尔也会发现人手没注意到的隐蔽问题——比如一次 concurrency 的竞态条件在代码评审时被模型先揪出来了这类时刻就是工具价值感最强的时候。5. 进一步扩展的方向open-code-review 目前已经能稳定内部使用了但我心里清楚它还只是个雏形还有几个值得继续深挖的方向。一个是把团队自己的规范沉淀成规则库。每个团队都有一些历史遗留的坑把这些典型反例整理成语料加进 Prompt模型就能更好地识别并避免同类问题。这相当于把团队的技术债变成了训练材料。另一个方向是接入更多代码托管平台。目前适配了 GitHub但国内团队用 Gitee、自建 GitLab 的比例很高把这些平台接好工具的实际覆盖范围会大很多。还有一个值得思考的方向是“自动修复”。现在工具只负责发现问题后续能不能让模型直接生成修复补丁人类 Review 一键确认合入这是 AI 辅助开发里最接近“生产力质变”的落点。虽然目前还只是探索阶段但这个方向已经能看到明显的价值和空间。回到最开始那句话代码审查的意义从来不只是找 bug而是把团队对代码质量的共识沉淀下来变成一种可执行的机制。open-code-review 这个项目如果能帮团队把审查这件事变得不那么“看心情”它的价值就实现了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价