最近在技术社区里经常看到一个问题:引入 LLM 之后,代码评审(Code Review)流程到底变成了什么样?团队里有人用 AI 辅助查漏,有人直接让 AI 生成评审意见,也有人担心评审标准被“AI 口味”带偏。这确实是个值得认真梳理的话题。本文会从流程演变、工具落地、风险控制和工程实践出发,完整拆解 LLM 参与代码评审的现状与做法,同时提供一套可落地的辅助工具示例和排查思路,适合正在尝试或已经接入 AI 评审的开发者、技术负责人阅读。
1. 背景与核心概念
1.1 从“人工评审”到“人机协同评审”
传统的代码评审通常是这样:开发者提交 Pull Request,由一到多名熟悉业务的工程师逐文件查看变更,在关键逻辑、边界条件、异常处理、命名规范、兼容性等方面提出意见,然后开发者修改后再评审,循环往复直到通过。这个过程能保证代码质量,但也存在几个老问题:
- 评审速度慢:大 PR 动辄数百行甚至上千行,评审者需要大量时间逐行阅读。
- 评审质量不稳定:评审者的经验、状态、对业务的熟悉程度都会影响意见质量。
- 低级问题占用人工精力:比如未使用的变量、明显的边界遗漏、日志格式不一致等,完全可以由工具自动发现。
LLM(大语言模型)介入后,评审过程的基本目标没有变,但执行方式发生了变化。现在比较常见的工作方式不是“AI 替代人”,而是“AI 先审一遍,人再审关键点”。LLM 可以快速读取 diff(代码差异),从风格、逻辑、潜在缺陷、测试覆盖等角度生成评审意见,人工评审者再对 AI 意见进行确认、补充和最终裁决。
这种方式把“逐行阅读”的体力活交给了模型,把“理解业务意图和合理性”的脑力活留给人。重点已经不是“要不要用 LLM”,而是“怎么设计一套流程,让 LLM 的输出真正对质量有帮助,而不是制造噪音”。
1.2 LLM 评审的常见形态
目前团队里用 LLM 做代码评审,大致有几种形态:
| 形态 | 说明 | 适合团队 |
|---|---|---|
| 开发者本地使用 | 在 IDE 插件或命令行工具中粘贴 diff,获取评审建议 | 个人学习、小团队尝试 |
| CI 机器人自动评审 | PR 触发流水线后自动调用 LLM API,将结果以评论形式发回 PR | 对自动化要求较高的团队 |
| 企业内部评审平台 | 在开源评审工具或自研评审系统里集成 LLM 服务 | 对数据安全和合规要求高的团队 |
| 人工借力模式 | 评审者自己把 diff 发给 LLM,作为“第二意见”参考 | 几乎所有团队都可使用 |
后面第三节和第四节会分别展开。
1.3 需要先区分的概念
这里有几个容易混淆的概念,建议先理清楚:
- 静态检查(Lint):如 ESLint、Checkstyle、SonarQube,基于规则引擎做检查,结论确定、可解释,但没有语义理解能力。
- 代码评审(Code Review):人在代码变更上下文中进行的合理性判断,既看风格,也看逻辑、设计、业务一致性。
- LLM 辅助评审:由模型生成技术意见,属于“建议型”输出,不具备确定性,也不能被当成不可置疑的权威。
这三者不应该互相替代,更合理的做法是叠加:先跑 Lint 扫掉低级问题,再用 LLM 做逻辑层面的第一轮检查,最后人工做业务裁决。这也是本文推荐的基本框架。
2. 评审流程在 LLM 时代的变化
2.1 一条典型的“LLM 辅助评审”流程
如果团队决定把 LLM 正式接入评审流程,一个比较稳妥的流程模型如下:
- 开发者提交 PR,触发 CI。
- 静态检查工具先运行,比如 SonarQube、ESLint、Checkstyle。
- 脚本提取 PR 的 diff 数据,调用 LLM API,生成初步评审意见。
- LLM 意见作为 PR 评论或附件提交,供人工评审者参考。
- 人工评审者结合业务上下文确认意见,必要时向 LLM 追问细节。
- 开发者根据确认后的意见修改代码。
- 完成后重新评审,直到满足团队自定义的合入门槛。
这套流程的关键在于“LLM 意见仅供参考,不自动阻塞合入”。否则,模型产生误报时,开发者会被无效意见反复打断,最终导致团队对 AI 评审失去信任。
2.2 评审角色分工的变化
引入 LLM 后,原本由“评审人”承担的工作被拆分成了三部分:
- 自动工具层:负责识别格式错误、无用代码、明显的空指针风险等。这层建议用传统静态检查工具,因为规则可控、输出稳定。
- LLM 语义层:负责从代码逻辑、函数职责、异常处理是否完整、命名是否达意等角度提出建议。这一层能发现很多静态检查发现不了的问题,但也可能产生误报。
- 人工决策层:负责结合需求文档、业务场景、历史惯例来最终裁定。这个角色目前无法被替代。
也就是说,评审者不再需要把时间花在“找问题”上,而可以把更多精力放在“判断哪些问题值得改、怎么改更合适”上。这其实是评审价值的回归。
2.3 评审标准和质量的定义变得更重要
以前评审质量依赖个人经验,标准往往是“团队内部约定俗成”。LLM 介入后,因为模型每次输出的风格和标准都相对一致,反而倒逼团队把评审标准显性化。比如:
- 团队是否要求所有公开方法必须有注释?
- 是否允许嵌套三层以上的 if?
- 新增异常是吞掉还是向上抛?
- SQL 是否必须走索引?
- 接口变更是否需要同步更新文档?
这些规则如果不先定义好,LLM 就只能按自己学到的“通用最佳实践”来评,未必符合团队实际情况。因此,现在越来越多的团队会在系统提示词(Prompt)中内置评审规范,把团队约定交给模型,让它的输出方向与团队节奏保持一致。
另外,LLM 评审结果也应该像人工评审一样可追溯。每一条意见最好附上涉及的文件、代码片段、建议级别(阻塞 / 建议 / 可选),这样开发者才能判断优先级。
3. 环境准备与最小工具链搭建
3.1 自建一套评审辅助工具需要准备什么
如果你想先在本地实验,而不是直接上企业方案,最低限度需要准备以下内容:
- 操作系统:Windows、macOS 或 Linux 均可,关键是能跑 Python 3 脚本。
- Python 版本:建议 3.9 及以上。
- LLM 访问方式:可以是有 API Key 的在线大模型服务,也可以是本地部署的推理服务。版本和接口取决于你实际可用的模型,本文示例逻辑可以通用。
- Git 命令行工具:用于生成 diff。
- 一个简单的 HTTP 请求库,比如 Python 的
requests。
需要注意,不同模型、不同版本的 API 格式可能有差异,如果你使用的是企业内部私有化部署的模型,请以自己环境的接口文档为准。
3.2 目录结构规划
为了便于后续扩展,建议把工具设计成一个小型 Python 项目:
llm-review-helper/ ├── config.py # 配置文件,放置 API 地址、Key、模型名 ├── review_helper.py # 主入口脚本,负责生成 diff 并调用模型 ├── prompts.py # 系统提示词和用户提示词 └── requirements.txt # 依赖清单这里不追求做成完整平台,而是先跑通“提取 diff -> 构造提示词 -> 获取评审结果”这条链路。
requirements.txt内容如下:
requests>=2.28.0如果你的环境没有安装requests,先执行:
pip install -r requirements.txt3.3 配置文件示例
config.py内容如下:
# 文件路径:llm-review-helper/config.py # 模型服务的接口地址,需要根据你的实际环境调整 API_URL = "http://your-model-api-endpoint/v1/chat/completions" # 从环境变量读取密钥,避免直接写在代码里 import os API_KEY = os.getenv("LLM_API_KEY", "your-api-key") # 使用的模型名称,例如 gpt-4o-mini、qwen-max、deepseek-chat 等 MODEL_NAME = "your-model-name" # 评审提示词语言,建议按团队习惯设置 REVIEW_LANGUAGE = "中文"这里需要注意,不要把 API Key 硬编码到代码仓库里,更不要把配置提交到 Git。推荐的做法是通过环境变量传入,或者在本地使用.env文件管理。
4. 完整实战:编写一个 LLM 评审辅助脚本
这一节会给出一个可以直接运行的 Python 脚本,实现两个核心能力:
- 读取指定 Git 提交的 diff。
- 将 diff 传递给 LLM,生成结构化的评审意见。
这个脚本只是演示“LLM 辅助评审”的最小闭环。生产环境还需要接入 PR 平台接口、处理鉴权、做结果持久化,但核心思路是一样的。
4.1 编写系统提示词
prompts.py内容如下:
# 文件路径:llm-review-helper/prompts.py def build_system_prompt(team_rules: str = "") -> str: """ 构建系统提示词。 team_rules 可以传入团队自定义的评审规范。 """ prompt = """你是一名资深代码评审专家。 请从以下几个维度对代码变更进行分析: 1. 逻辑正确性:是否存在边界条件遗漏、并发问题、异常处理缺失。 2. 代码可读性:命名是否达意,逻辑是否清晰,复杂度是否过高。 3. 工程规范:是否符合团队约定的代码风格、目录分层、依赖管理原则。 4. 测试建议:哪些高风险逻辑应该补充单元测试或集成测试。 评审要求: - 对每条意见标明严重级别,分别使用 BLOCKER、MAJOR、MINOR。 - 如果没有问题,可以写“未发现明显问题”,不要无中生有。 - 给出的建议要具体,最好能结合当前 diff 中的代码说明修改方向。 """ if team_rules: prompt += f"\n以下是团队额外评审规范,请一并遵守:\n{team_rules}\n" return prompt def build_user_prompt(repo_path: str, commit_id: str) -> str: """ 构建用户提示词,包含仓库路径和提交号。 """ return f"""请评审仓库 {repo_path} 中提交 {commit_id} 的代码变更。 请给出中文评审意见,并且使用清晰的 Markdown 结构。"""系统提示词的目的是为模型划定评审范围。实际使用时,可以把团队规范、历史评审教训都放进这个提示词里。
4.2 编写 diff 提取函数
review_helper.py中需要先实现一个函数,用来执行 Git 命令并获取 diff:
# 文件路径:llm-review-helper/review_helper.py import subprocess def get_git_diff(repo_path: str, commit_id: str) -> str: """ 获取指定提交的代码变更内容。 repo_path:本地仓库路径 commit_id:Git 提交号,也可以是分支名或 HEAD """ cmd = ["git", "-C", repo_path, "show", commit_id, "--no-ext-diff"] result = subprocess.run( cmd, capture_output=True, text=True, encoding="utf-8", errors="replace", ) if result.returncode != 0: raise RuntimeError(f"获取 diff 失败:{result.stderr}") return result.stdout补充说明:
git show能查看某一次提交的完整差异,比多个文件分开处理更省事。--no-ext-diff禁止调用外部 diff 工具,确保输出纯文本。encoding="utf-8"是为了避免 Windows 下中文路径或注释出现乱码。
如果你希望评审“暂存区变更”,可以改用下面的命令:
git -C repo_path diff --cached如果你希望评审“工作区未暂存变更”,则用:
git -C repo_path diff在 IDE 中使用时,也可以直接把选中的代码内容传给模型,不一定要走 Git。
4.3 编写 LLM 调用函数
继续完善review_helper.py:
# 文件路径:llm-review-helper/review_helper.py import requests import config import prompts def call_llm(system_prompt: str, user_prompt: str) -> str: """ 调用模型接口,返回评审结果文本。 """ headers = { "Authorization": f"Bearer {config.API_KEY}", "Content-Type": "application/json", } payload = { "model": config.MODEL_NAME, "messages": [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ], "temperature": 0.3, # 不同模型的参数名可能不同,部分接口使用 max_tokens "max_tokens": 2000, } resp = requests.post(config.API_URL, headers=headers, json=payload, timeout=60) resp.raise_for_status() data = resp.json() # 不同模型的返回结构可能不同,按实际接口调整 return data["choices"][0]["message"]["content"]这里有两个点需要注意:
temperature设置为 0.3,是为了减少随机性,让评审结果更稳定。如果你希望每次结果更具发散性,可以调高到 0.7 左右,但对评审场景来说,稳定比创意更重要。timeout=60是防止模型接口异常时脚本一直卡住。
如果使用的是 OpenAI 兼容接口,一般返回结构就是data["choices"][0]["message"]["content"]。如果使用的是其他厂商接口,字段可能会有差异,请以官方文档为准。
4.4 编写主流程
最后把整个流程串起来:
# 文件路径:llm-review-helper/review_helper.py def main(): repo_path = input("请输入仓库路径:").strip() commit_id = input("请输入提交号(默认为 HEAD):").strip() or "HEAD" print("正在提取代码变更...") diff_text = get_git_diff(repo_path, commit_id) if not diff_text.strip(): print("没有获取到任何变更内容,请检查提交号。") return print("正在调用模型进行评审...") system_prompt = prompts.build_system_prompt( team_rules="禁止使用 TODO 注释;新方法必须包含返回值说明;涉及删除数据必须二次确认。" ) user_prompt = prompts.build_user_prompt(repo_path, commit_id) review_result = call_llm(system_prompt, user_prompt) print("\n========== 评审结果 ==========\n") print(review_result) if __name__ == "__main__": main()这样,一个最简单的“本地 LLM 评审辅助工具”就完成了。运行方式如下:
python review_helper.py输入仓库路径和提交号后,脚本会输出模型的评审意见。
4.5 一个更贴近 CI 的变体:评审未提交变更
如果是在 CI 场景下,你通常拿到的不是本地提交号,而是“目标分支”和“当前分支”之间的差异。下面给出一个更实用的变体:
# 文件路径:llm-review-helper/review_helper.py # 功能:评审两个分支之间的代码差异 def get_diff_between_branches(repo_path: str, base_branch: str, head_branch: str) -> str: cmd = [ "git", "-C", repo_path, "diff", base_branch, head_branch, "--no-ext-diff", ] result = subprocess.run( cmd, capture_output=True, text=True, encoding="utf-8", errors="replace", ) if result.returncode != 0: raise RuntimeError(f"获取分支差异失败:{result.stderr}") return result.stdout你可以把上一节main()函数中的get_git_diff调用替换成这个函数,从而让脚本适配 GitHub/GitLab MR 的 CI 场景。
4.6 用 GitHub Actions 触发自动评审
假设团队使用 GitHub,并且已经把 LLM 评审脚本封装成了可执行命令,可以添加一个 Workflow 文件来自动触发。
.github/workflows/llm-review.yml示例:
name: LLM Code Review on: pull_request: types: [opened, synchronize] jobs: llm-review: runs-on: ubuntu-latest steps: - name: 检出代码 uses: actions/checkout@v3 with: fetch-depth: 0 - name: 设置 Python uses: actions/setup-python@v4 with: python-version: "3.11" - name: 安装依赖 run: | pip install requests - name: 执行 LLM 评审 env: LLM_API_KEY: ${{ secrets.LLM_API_KEY }} run: | python review_helper.py \ --base "${{ github.event.pull_request.base.sha }}" \ --head "${{ github.event.pull_request.head.sha }}"需要注意,on中的事件类型是 YAML 的一部分,不能省略。实际使用时,如果仓库有严格的 CI 资源限制,也可以设置成手动触发(workflow_dispatch),避免每个 PR 都消耗模型费用。
CI 自动评审的意义在于,它把原本分散在每个人本地的操作统一化了:每个 PR 都有一套相同标准的“AI 预审意见”,人工评审者可以在完全了解代码之前先看一遍机器意见,效率会提升很多。
5. LLM 评审带来的问题和排查思路
5.1 常见问题清单
把 LLM 接入评审之后,团队一定会遇到下面这些情况。这不是个例,而是通用挑战。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| AI 频繁给出无意义的建议 | 提示词中没有说明“未发现明显问题时不要乱提意见” | 在系统提示词中明确要求低噪声输出,并增加严重级别过滤 |
| AI 评审结果不稳定 | 模型温度过高,或者每个 PR 没有使用固定模板 | 将温度降低到 0.2~0.4;固定系统提示词 |
| AI 只关注风格,忽略逻辑 | 提示词缺少逻辑检查维度 | 在系统提示词中显式列出“边界条件、并发、异常处理”等维度 |
| 评审结果太长,没人看 | 没有做结果摘要和级别过滤 | 在提示词中要求“先给结论摘要,再列详细意见” |
| 误报导致开发者反感 | 团队过度信任 AI 意见,没有人工二次裁决 | 设定“AI 意见默认不阻塞合入,需人工确认”规则 |
| 模型无法理解业务上下文 | 其他服务、数据库结构、需求文档没有提供给模型 | 把关键业务约束和模块说明写入提示词,或使用 RAG 补充上下文 |
| API 调用超时或失败 | 网络不稳定、模型服务压力大 | 增加重试机制;超时时间加长;失败时跳过 AI 环节,不阻塞 CI |
5.2 一个典型的排查流程
如果遇到“AI 评审意见质量非常差”的情况,建议按以下顺序排查:
- 先看提示词是否准确描述了团队规范。如果团队规范为空,模型只会按照通用最佳实践来评,自然脱离实际。
- 再看输入给模型的 diff 是否完整。如果 diff 只是片段,模型很容易误判。
- 检查模型版本。不同模型的能力差异很大,有的模型在长文本理解上明显更强。
- 检查温度参数。评审场景不建议使用默认的高随机性参数。
- 对历史结果做抽样统计。随机挑选 20 条 AI 意见,区分“有效”“无效”,看命中率是否值得投入。
这种排查思路也适用于企业内部自研模型服务:先确认输入质量,再确认模型参数,最后考虑是否需要更换模型或增加上下文。
5.3 “噪声”到底怎么定义
很多团队最终放弃 AI 评审,就是被“噪声”劝退的。所谓噪声,指的是那些看起来正确、但对当前业务没有价值甚至错误的建议。比如:
- 建议拆分的函数只有 3 行,拆分后反而增加阅读负担。
- 建议使用某个设计模式,但当前场景根本用不到。
- 误报空指针、误报未使用变量。
- 强行要求增加测试,但该方法属于一次性脚本。
要减少噪声,核心在于:让模型知道自己不懂业务,遇到不确定的情况要主动说“需要人工确认”,而不是强行生成建议。
同时,可以把“噪声率”作为团队每周复盘的质量指标之一。如果连续数周的噪声率都很高,优先调整提示词,而不是继续堆 Prompt 技巧。
6. 最佳实践:把 LLM 评审做成“真有用”的工程能力
6.1 先分级别,再决定是否阻塞
强烈建议把评审意见分为三个级别:
BLOCKER:必须修复,否则会影响功能实现、安全性或数据一致性。MAJOR:建议修复,属于逻辑或设计层面的明显问题。MINOR:可选修复,包括风格微调、注释补充、命名建议。
CI 只有遇到BLOCKER级别的错误才考虑阻塞合入,MAJOR和MINOR建议由人工判断。这样可以保持 AI 评审对开发的友好度。
在提示词中可以直接要求:
每条意见必须以 `BLOCKER`、`MAJOR`、`MINOR` 开头,并且说明涉及的文件与行号。6.2 上下文裁剪:不能把整个仓库喂给模型
很多团队在接入 LLM 评审时容易犯一个错误:把整个 PR 的所有文件、甚至整个 README 一起发给模型。这样做不仅浪费 token,而且会稀释模型对核心逻辑的注意力。
更稳妥的上下文策略是:
- 只发送变更的 diff。
- 如果某个函数是本次修改的核心,可以把该函数的前后 10 到 20 行一并放进去。
- 如果涉及跨文件接口,可以在提示词中补充接口定义片段。
- 必要时提供业务背景摘要,但不要超过 200 字。
6.3 安全与合规边界
LLM 评审存在数据外发风险,尤其当代码包含内部算法、客户信息、密钥时。这一点必须明确:
- 优先使用企业内部私有化部署的模型服务。
- 如果使用外部模型 API,必须对代码做脱敏处理,比如替换真实 IP、真实密码、真实姓名。
- 在提示词中加入“如果代码中包含疑似密钥或敏感信息,请只提示位置,不展示内容”。
- 涉及删除、权限变更、SQL 批量操作等高风险变更时,人工评审必须二次确认,AI 意见只能做参考。
这一条怎么强调都不过分。AI 评审工具是提效手段,不是绕过安全审查的借口。
6.4 定期更新评审规范
团队的评审规范不是一成不变的。引入 LLM 后,可以把过去半年人工评审中反复出现的意见类型整理出来,沉淀成“团队评审知识库”。
比如:
- 老系统代码禁止擅自重构。
- 新增接口必须考虑幂等性。
- 日志必须打印请求 ID。
- 数据库操作必须使用事务。
这些规范写进系统提示词后,模型在后续评审中会更容易命中团队真正关心的问题,减少“通用正确然而无用”的评价。
6.5 保留人工评审的完整记录
即使 AI 参与评审,人工评审的痕迹仍然要保留。建议在 PR 描述中增加「人工评审确认」段落,由最终合入人填写:
人工评审确认: - [ ] 已阅读 AI 评审意见 - [ ] 已检查业务逻辑 - [ ] 已确认无 BLOCKER 级别问题 - [ ] 已确认敏感信息未出现在变更中这种做法能防止 AI 评审意见成为“免责清单”,也便于事后追溯合入决策。
7. 总结:LLM 评审时代,真正的瓶颈在哪
回到最开始的问题:LLM 参与评审后,流程到底发生了什么变化?答案是:流程的骨架并没有变,仍然是“提交、检查、评审、修改、合入”,但每个环节的界面都变了。开发者需要学会阅读和屏蔽不必要的 AI 意见;评审者需要从“找问题的人”变成“做判断的人”;团队需要把过去模糊的评审标准变成可读、可配置、可进化的规范文本。
如果让我给正在犹豫是否接入的团队一句最直白的建议,那就是:不要在短时间内指望 LLM 把评审质量从 70 分提升到 95 分,但可以快速用它把低级问题消灭在人工评审之前,让人把力气花在真正值得花的地方。把提示词、级别过滤、安全边界和人工确认机制先定好,再逐步扩大 AI 评审的覆盖范围,这条路径目前在工程实践里已经被验证是走得通的。
这篇内容没有给出“AI 会自动帮你维护代码质量”的许诺,因为那是一个常见的误区。真正可靠的 AI 评审,永远建立在清晰的人工标准和流程之上。