ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

LLM辅助代码评审:AI初筛+人工复核如何重构Code Review流程

LLM辅助代码评审:AI初筛+人工复核如何重构Code Review流程 代码评审流程一旦开始由 LLM 参与开发者首先感受到的不是“评审变强了”而是“评审这件事被拆开了”。Hacker News 上有一个讨论帖标题就是 “Ask HN: What happens to code review process when using LLMs?”问的正是这个问题当模型能读懂 diff、能写评论、能指 bug 之后原本属于人类的评审环节还剩多少价值流程应该怎么重新组织。这个问题没有标准答案但过去一年的开源工具和团队实践已经给出几条相对清晰的路径。LLM 最擅长的是在 PR 进入人工评审之前完成一轮机械性、规格性的初筛缩进对不对、命名规不规范、有没有明显的空指针、配置项是不是写死了、测试有没有覆盖新增分支。它做不了的事情同样明显没法真正理解一个跨服务调用的业务正确性没法判断这次改动是否符合团队很久以前口头约定过的架构约束也没法为数据安全这种高风险变更拍板。所以更稳妥的判断是LLM 不会取消代码评审而是把评审分层。AI 负责初筛和提示人类评审员的注意力从“看每一行”转移到“看 AI 筛过之后的重点、风险和我自己的专业知识能覆盖的盲区”。这篇文章就从工程落地的角度把这件事拆开讲清楚LLM 代码评审适合什么场景、怎么接入现有 Git 和 CI/CD 流程、怎么验证模型审得准不准、批量审多个文件要怎么设计以及最容易踩的坑。1. LLM 代码评审核心能力速览能力项说明定位辅助评审不替代人类决策核心能力diff 审查、风格检查、常见缺陷检测、测试缺口提示、提交信息把关不擅长领域深层架构权衡、跨服务数据流验证、业务正确性、组织隐性规范接入方式IDE 插件、本地 CLI、CI Action、直接调用模型 API前置条件可访问的 LLM 服务云端 API 或本地推理、Git 仓库、diff 提取能力资源成本以 Token 计费本地模型需考虑显存和推理速度适合场景PR 初筛、新人代码指导、风格类问题、安全配置提示、跨团队协作时减少低水平往返不适合场景关键模块未经人工复核直接自动合并、需要完整理解业务语义的强制把关先给结论LLM 如果只被当成“自动写评论的机器人”价值不大如果被当成“第一轮评审员”把人类的注意力集中到模型覆盖不了的问题上价值立刻不一样。从当前主流实践看模型能承担得比较好的检查项集中在几类代码规范与命名、明显的空引用和未捕获异常、资源未关闭、日志打印敏感信息、测试是否覆盖新增分支、配置文件是否存在硬编码。这些检查的共同点是规则相对固定、局部就能判断不需要依赖整条业务链路。反过来凡是需要“知道这次改动为什么存在”的问题比如这个接口为什么设计成这样、性能瓶颈是不是在这里、服务之间的事务边界有没有被破坏模型在没有充分上下文的情况下只能给出泛泛建议这类问题还是要靠人。2. 适用场景与使用边界2.1 适合谁用第一类是 PR 量大的中大型团队。每天十几个 PR人工评审排队开发等待合并时间变长。用一个 LLM 先跑一轮把明显问题提前打回去能显著减少“小问题来回改”的轮次。第二类是个人开发者和小团队。没有专职 reviewer代码质量主要靠自觉。LLM 能扮演一个永不疲倦的初评者至少在提交前帮你检查一遍基本问题。第三类是规范化要求高的项目比如对外 SDK、支付相关逻辑、涉及数据导出的模块。这类项目最需要的是稳定一致的检查清单LLM 配合固定 prompt 可以充当执行标准检查的自动化环节但最终授权仍然取决于人。2.2 能解决什么问题缩短评审周期AI 初筛在前人工复核在后减少等待。降低低级错误密度空指针、未关闭连接、异常吞掉、敏感信息打印这类问题模型检出率稳定。提升评审反馈质量模型给出的建议通常带文件位置和修改方向开发者收到的是可执行的反馈而不是“感觉这里不太对”。缓解团队评审疲劳人只需要看模型标出的中高等级问题而不是逐行通读。2.3 不适合什么场景不允许代码出内网的场景。把代码明文发送给外部 API 之前必须确认公司数据政策。没有授权就不要用云端模型处理核心业务代码。需要绝对准确裁决的场景。LLM 的评审结果天然带概率性误报和漏报都存在不能作为质量门禁的唯一依据。业务语义强的模块。模型不知道这次 PR 对应的需求背景、用户场景和事故历史不能代替业务负责人判断“这么做对不对”。大型历史仓库全量扫描。Token 成本和上下文窗口都是硬约束上来就跑全量扫描通常只会得到一堆噪音。2.4 合规与安全边界代码本身是公司资产里面还可能包含密钥、用户数据处理逻辑、内部架构信息。接外部模型前要过三道检查第一数据出境和数据隐私要求是否允许第二是否需要对代码做变量名替换、删除注释、局部截取第三模型服务商的协议是否写明不利用你的输入做训练。内部部署一个开源模型通过 Ollama、vLLM、llama.cpp 等方式是更可控的选择代价是需要自己管推理资源。另外要明确AI 评审建议不构成最终质量结论。涉及人身安全、资金交易、隐私数据的代码必须由具备资质的工程师复核签字不能把决策责任转嫁给模型。3. 接入前置与环境准备接入 LLM 代码评审不需要特殊硬件如果你用云端 API一台普通开发机加一个 Git 仓库就够了。要做的事情分成四块。第一块是代码仓库。GitHub、GitLab、Gitea 都可以统一要求能拿到 PR 或 MR 的 diff。Git 本身已经提供了git diff能力后续脚本基本都建立在“提取两个分支之间的变更内容”这个操作上。第二块是模型访问方式。推荐先走兼容 OpenAI API 格式的服务无论你用的是 OpenAI、Azure OpenAI、国内大模型平台还是本地部署的模型接口路径大多是/chat/completions带上Authorization头就能调用。环境变量建议统一设置export OPENAI_API_KEYyour-api-key export OPENAI_BASE_URLhttps://api.openai.com/v1 export REVIEW_MODELgpt-4o-mini本地推理则常用 Ollama 启动一个兼容接口模型名按实际拉取的模型填写。需要注意不同模型对代码评审的指令遵循能力差异很大小模型容易把“找出问题”理解成“每行都夸一遍”所以模型选型很关键。第三块是运行环境。一个简单的 Python 脚本加requests库就能跑通如果要在 CI 里运行准备好能执行 shell 命令的 Runner以及存放密钥的 Secrets 管理。本地验证时建议准备一个测试仓库专门放几段故意写错的代码。第四块是评审范围。先在配置文件里明确哪些目录跳过评审例如vendor、dist、node_modules、生成的 protobuf 代码、锁文件。这些文件要么不是人写的要么体积大且无评审价值过滤掉可以节省大量 Token。4. 把 LLM 接入代码评审流程4.1 第一级IDE 内提示最轻量的接入方式是在 IDE 里安装支持自定义 prompt 的 AI 插件选中有问题的方法或文件让模型在提交前先做一轮“自检”。这个方式的优点是没有流程改造缺点是评审结果不沉淀、不强制全凭开发者自觉。适合个人使用不适合团队质量门禁。4.2 第二级本地 CLI 评审脚本在本地提交前手动跑一遍是最容易实现也能立刻见效的方式。核心逻辑就是两条命令先用git diff拿到变更再喂给模型。git diff origin/main...HEAD /tmp/change.diff python scripts/review_diff.py /tmp/change.diffreview_diff.py的核心部分如下import os import sys import requests def read_diff(path): with open(path, r, encodingutf-8) as f: return f.read() def call_review(diff_text): api_key os.environ[OPENAI_API_KEY] base_url os.environ.get(OPENAI_BASE_URL, https://api.openai.com/v1) model os.environ.get(REVIEW_MODEL, gpt-4o-mini) system_prompt ( 你是一位资深代码评审工程师。请针对下面的 diff 输出评审意见。\n 要求\n 1. 每条意见必须指出文件路径、行号、问题等级critical/warning/nit。\n 2. 只报告确定存在的问题不要泛泛而谈。\n 3. 如果没有严重问题直接回复“未发现明确问题”。\n ) resp requests.post( f{base_url}/chat/completions, headers{Authorization: fBearer {api_key}}, json{ model: model, messages: [ {role: system, content: system_prompt}, {role: user, content: f请评审以下代码 diff\n\n{diff_text}}, ], temperature: 0.2, }, timeout180, ) resp.raise_for_status() return resp.json()[choices][0][message][content] if __name__ __main__: diff read_diff(sys.argv[1]) output call_review(diff) print(output)这里有两个细节值得注意。第一temperature要调低评审类任务希望输出稳定太高会出现“编造问题”的情况。第二prompt 里明确要求“只报告确定存在的问题”否则模型会用“建议考虑优化一下”这类空话填满输出。这个脚本跑通之后可以包装成 Git 钩子在pre-push阶段自动执行。这样每次推送前都会先过一轮 AI 初筛。4.3 第三级CI 自动评审更进一步是把评审放进 Pull Request 流程每当有人提交 PR自动触发一次模型评审并把结果回写到 PR 评论区。这样评审记录会沉淀在 PR 上下文里后续人工评审可以直接引用。GitHub Actions 的典型配置如下name: llm-code-review on: pull_request: types: [opened, synchronize] jobs: review: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 with: fetch-depth: 0 - name: 提取变更 diff run: | git diff origin/${{ github.event.pull_request.base.ref }}...origin/${{ github.event.pull_request.head.ref }} /tmp/change.diff wc -l /tmp/change.diff - name: 运行 LLM 评审 env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }} run: python scripts/review_diff.py /tmp/change.diff跑完之后评审结果默认只在 CI 日志里显示。如果要自动评论到 PR 上需要额外一步把脚本输出写入 GitHub API。更简单的办法是选择现成的 AI 评审 Action它们通常已经实现了评论回写、文件过滤和按严重级别展示。自己写脚本的好处是可控坏处是要处理评论接口、限流和重试适合有一定自动化经验的团队。4.4 本地模型接入方式如果代码不能出内网可以用 Ollama 跑一个本地模型然后让原来的脚本指向本地地址。只需要改一个环境变量export OPENAI_BASE_URLhttp://127.0.0.1:11434/v1 export REVIEW_MODELqwen2.5-coder:7b-instruct优点是完全不出网缺点是模型能力上限受硬件约束评审质量和云端大模型有明显差距。常见的情况是7B 级别模型能稳定识别空指针和命名问题但遇到跨文件逻辑、复杂边界条件时基本靠猜。实际占用的显存要按模型量化版本实测不能只看参数数字。5. 功能测试与效果验证接入之后第一件事不是全面上线而是拿一组历史 PR 做回测。验证维度包括模型能不能发现真问题、会不会大量误报、输出是不是可执行。下面给出一套通用测试流程。5.1 缺陷检测测试准备一段故意写错的代码观察模型能否给出准确指认。def get_user_name(user_id): user load_user(user_id) return user.name # 当 user_id 不存在时user 可能为 None预期结果模型应指出user可能为空并建议增加判空处理同时给出对应行号。判断标准是意见是否准确、是否包含修改方向。如果模型只输出“请确保代码健壮”说明 prompt 约束不够或者模型没有真正理解代码逻辑。5.2 风格与规范测试输入一段命名混乱、未使用变量的代码看模型是否给出风格类意见。这类检查模型通常做得很好但要注意控制噪音。许多模型会把“变量名可以更好”也列为问题导致 PR 上出现过多低价值评论。建议在 prompt 里把问题分级风格类问题统一归到nit低于warning让开发者可以选择忽略。5.3 安全与配置泄漏测试测试模型能否识别密钥提交、敏感日志输出、SQL 拼接。这部分如果评审结果可靠价值非常大因为人工评审很容易漏掉散落在大量文件里的密钥。def connect(): password abc123 conn mysql.connect(userroot, passwordpassword) logger.info(password is %s, password)预期结果模型应至少提示两点一是密码硬编码二是将密码写入日志。如果模型没有发现密钥问题可以考虑在 prompt 中补充安全审查专项说明或者换一个更强的模型。5.4 大 diff 与多文件测试拿一个改动十几个文件的 PR 测试。重点观察三点模型是否忽略部分文件是否存在上下文窗口截断意见是否集中在少数几个文件里。大 diff 是本方案最容易翻车的地方。处理思路是不要让一次请求吞掉全部 diff而是按文件分组每个文件单独请求最后汇总。这样既避免超长输入也方便定位问题属于哪个文件。5.5 评审质量量化评估建议建立一张简单的评估表对每个测试 PR 记录指标统计方式目标检出真问题数模型意见中被人工确认有效的问题数越多越好误报数模型意见中实际不存在的问题数越少越好覆盖率模型检出的问题占全部应发现问题比例结合人工评审统计平均耗时从提交 diff 到返回意见的耗时控制在可接受范围Token 成本每个 PR 评审消耗的输入和输出 Token按预算控制这套指标要连续跑一段时间才能有结论。最忌讳的做法是测一个 PR 觉得“挺准”就上线因为单个样本的偶合性太强。6. 接口 API 与批量任务设计6.1 直接调用模型 API如果你的目标是自建评审服务而不是依赖现成 Action核心接口就是模型提供商的/chat/completions。下面是一个 curl 调用示例curl -X POST https://api.openai.com/v1/chat/completions \ -H Authorization: Bearer $OPENAI_API_KEY \ -H Content-Type: application/json \ -d { model: gpt-4o-mini, messages: [ { role: system, content: 你是资深代码评审工程师。只报告确定存在的问题输出 JSON 数组每项包含 file、line、severity、comment。 }, { role: user, content: 请评审以下 diff\n -1,5 1,6 \n def foo():\n- return None\n return load_bar()\n } ], temperature: 0.2 }返回结果里最重要的字段是choices[0].message.content模型会按要求输出 JSON 或纯文本取决于 prompt 里怎么约束。6.2 让模型输出结构化数据评审建议如果不结构化没法直接回写 PR、没法做统计分析。推荐在 prompt 里要求输出 JSON 数组每条意见包含以下字段{ file: src/main.py, line: 28, severity: warning, comment: user 可能为 None建议加判空处理, suggestion: if user is None: raise UserNotFoundError() }拿到结构化结果后后续做评论回写、汇总报告、按严重度过滤都很方便。6.3 批量任务与并发控制批量审一个 PR 的全部文件时最简单的方案是串行循环改造成本低但耗时随文件数线性增长。更高效的方案是控制并发数量同时处理多个文件。下面是一个并发的批量评审思路from concurrent.futures import ThreadPoolExecutor def review_one_file(file_path, file_diff): # 组装该文件的局部 prompt result call_review(file_diff) return {file: file_path, result: result} with ThreadPoolExecutor(max_workers4) as executor: futures [ executor.submit(review_one_file, path, diff_text) for path, diff_text in file_diffs.items() ] for future in futures: results.append(future.result())注意三点并发数过高会触发模型服务限流需要根据实际接口配额调整失败的任务要记录并重试通常采用指数退避批量任务要写日志至少记录每个文件耗时、成功失败状态否则大面积失败时只能瞎猜。6.4 评审服务的整体流程一个稍完整的批量评审服务设计如下接收参数base 分支、head 分支、可选的目录过滤规则。提取 diff按文件拆分。过滤无评审价值的文件。并发调用模型限定 max_workers。解析结构化结果写入报告文件。将结果回写到 PR 评论区或发送到消息服务。这个流程已经足够替换掉市面上一些轻量 AI 评审工具的底层逻辑缺点是维护成本在你这边优点是模型选择、prompt、成本完全自由。7. 资源消耗与成本观察LLM 代码评审的主要资源不是 CPU 和内存而是 Token。一个 PR 的 diff 会被整体计算为输入 Token模型的回帖内容是输出 Token。成本估算公式是一次评审成本 (输入Token数 × 输入单价 输出Token数 × 输出单价) / 1000具体单价以你选择的模型服务商为准不同模型差别很大。控制成本的实践有几个方向。第一只审真正的代码文件。过滤锁文件、二进制文件、生成的代码和超长测试数据一次 PR 的输入 Token 能省三分之一甚至更多。第二按文件切片而不是整体提交。一个 20 个文件的大 PR如果整体拼进一次请求很可能突破上下文窗口或超出单次请求限制。按文件分组后每次调用都是“局部上下文”成本稳定而且某个文件失败不会拖垮整个评审。第三用更小、更便宜的模型做常规检查只对关键模块用大模型二次评审。常见组合是用速度快的小模型跑风格和明显缺陷当文件涉及安全、支付、数据导出等目录时再路由到大模型。第四设置 Token 预算上限。在脚本里对每个 PR 的 diff 总行数做限制超过阈值就只评审改动量最大的前 N 个文件避免一次异常 PR 产生过高成本。显存方面如果走云端 API本地不需要 GPU。如果本地部署模型显存取决于模型规模和量化方式实际占用需要按你的模型版本和推理框架实测。本地 7B 级别量化模型在普通消费级显卡上可以运行但推理速度对大型 diff 的实时评审来说可能偏慢更适合离线批量扫描。要观察本地推理资源可以用nvidia-smi看显存占用用推理框架自带的请求日志看单次调用耗时。延迟是另一个要关注的点。云端 API 单个请求通常几秒到几十秒不等取决于 diff 长度和模型负载。CI 里串行评审会导致 Pipeline 明显变慢所以大 PR 一定要并发或异步化。8. 常见问题与排查方法问题现象可能原因排查方式解决方案评审意见全是空话没有具体问题prompt 缺少“只报告确定问题”的约束检查系统提示词和 temperature重写 prompt要求输出文件路径和行号明显 bug 没被发现模型只看到局部 diff缺少函数上下文检查输入中是否包含相关上下文将相邻函数或相关文件片段一并送入误报太多开发者不再看模型能力不足或过度生成统计误报率换更强模型调低 temperature提高“存疑不报”的约束API 返回 401API Key 错误或未配置检查环境变量确认 Key 权限和过期时间API 返回 429请求超过速率限制查看服务商限流文档降低并发数增加指数退避重试大 PR 结果被截断超出上下文窗口查看报错信息和 diff 行数按文件切片、按量截断、只审关键文件敏感代码被发送外部没有配置目录过滤和授权评估检查脚本传入的 diff 内容内部部署模型或做变量脱敏后再发送CI 评审耗时过长串行调用或模型过大查看每步耗时日志并发调用小模型跑常规检查大模型只跑关键文件模型返回格式无法解析模型输出不符合 JSON 要求打印原始返回内容增加 JSON 格式示例用response_format固定输出排查时有一条核心原则先确认输入是什么再判断输出为什么不对。把每次请求的 diff 截断内容、模型 raw 输出全部记录到本地日志里很多问题一眼就能看出来。9. 最佳实践与使用建议9.1 固定评审标准不要每次评审都现场写 prompt。把评审标准沉淀为一份固定的系统提示词模板包含问题分级、输出格式、必查项空指针、资源泄漏、密钥硬编码、日志敏感信息、测试覆盖。团队内部可以像维护规范文档一样维护这份模板。9.2 分层评审不要把 AI 当最终裁决最稳妥的分层方式模型初筛出所有疑似问题打上等级低级问题自动反馈给提交者提醒修改中等级问题进入人工评审队列高级问题必须由人工专门处理模型意见只作为参考。默认不要做“无人工干预自动 approve”否则一旦模型漏掉关键问题责任归属会非常模糊。9.3 用历史 PR 做回测上线前至少准备 20 个历史 PR其中有已知缺陷也有正常改动。让模型对这 20 个 PR 跑一遍评审对比人工当时发现的问题计算检出率和误报率再决定是否扩大范围。这个步骤能筛掉大量“看起来能用但实际全是噪音”的模型和 prompt 组合。9.4 数据安全与合规涉及公司核心代码、密钥、客户数据的仓库必须先确认代码能不能发送到外部模型服务。不能确认的时候就用内部部署的开源模型或者对 diff 做脱敏处理替换字符串字面量、删除注释、只发送结构和逻辑骨架。同时要提醒开发者模型可能会把代码片段保留在服务端日志里这本身就是一种数据风险。9.5 评审记录要沉淀模型的每一次评审建议都应该记录下来和 PR 关联。这样后续可以做两件事一是评估模型每周检出的问题趋势二是当模型建议和人工判断冲突时把冲突作为 prompt 迭代和模型升级的依据。没有记录的评审工具只是一个聊天窗口不是质量体系。9.6 别让噪音淹没信号LLM 评审最大的失败模式不是漏报而是误报。一旦模型反复在 PR 里提“这里建议优化一下”这类无效建议开发者会养成“AI 评论不看”的习惯真正有用的建议也会被忽略。宁可设置更保守的 prompt让模型只报确定的问题也不要让它刷存在感。10. 总结与下一步回到最开始的问题LLM 进入代码评审流程之后发生了什么答案是流程从“一个 PR 等一个人看”变成了“一个 PR 先由模型初筛再由人处理模型筛出的重点”。人类评审的角色上移从逐行检查变成架构判断、业务正确性把握和最终授权。这个转变不是自动化替代人而是把人的时间重新分配到价值更高的地方。最值得先尝试的做法是写一个类似上文review_diff.py的脚本把自己近期的历史 PR 拉出来跑一轮回测看看模型在实际代码里能检出什么。这一步能让你立刻判断这个方案值不值得继续投入。最容易踩的坑有两个。一个是把模型评价当真理忽略误报和漏报直接让它决定代码能不能合并另一个是不控制成本把所有文件所有历史一次性灌给模型Token 账单出来才发现比请人还贵。如果你的团队已经跑通“AI 初筛 人工复核”下一步可以往两个方向扩展第一把评审结果量化成指标接进质量看板让“评审检出率”“误报率”“评审等待时长”变成每天可见的数据第二把模型评审从“事后提示”变成“提交前建议”在pre-push阶段就拦住明显问题。时间长了你会发现真正有价值的不是模型找到的那个 bug而是流程因为模型介入而重新变得高效这件事本身。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进