资讯动态

Rust项目如何用LLM规则守护人工代码审查

发布时间:2026/8/27 5:24:57 来源:尧图企业网站定制
最近这段时间越来越多技术团队开始把 LLM 引入代码审查流程。不过大家很快发现一个问题如果让 LLM 直接点评、直接改、甚至直接合并代码很容易出现“AI 说得热闹人工 reviewer 反而被淹没”的情况。尤其是 Rust 这种编译规则严格、所有权模型复杂、unsafe 代码需要人为判断的语言纯靠 LLM 全量审查并不现实。近期 Five Rust teams adopt LLM rules to protect human code review 这件事引起了我的注意。它有价值的地方不在于“用 LLM 审查代码”这个动作而在于“用规则限定 LLM 的审查范围反过来保护人类 code review 的最终决策权”。本文不打算做新闻复述而是围绕这个思路整理一套可以在 Rust 项目里落地的 LLM 辅助代码审查方案包含规则设计、配置示例、工作流说明和排错清单。如果你正在做 Rust 项目或者所在团队正准备引入 AI 辅助 code review这篇文章会比较适合你。读完后你可以照着设计一套“LLM 提供建议、人类掌握决策”的审查流程而不是简单地把代码丢给大模型。1. LLM 代码审查为什么需要规则约束1.1 什么是 LLM Rules先把概念说清楚。这里的“LLM rules”并不是指 Cargo 或 Clippy 的规则而是指团队在接入 LLM 审查代码时预先定义的一组行为边界和输出约束。这些规则通常包括LLM 在什么时候触发审查。LLM 可以审查哪些类型的变更。LLM 每条评论的长度、数量和格式。LLM 输出建议的置信度门槛。LLM 是否允许自动修改代码。AI 评论在 PR 中用什么标识呈现。哪些文件、哪些操作必须由人类 reviewer 单独立判断。换句话说LLM rules 解决的是“LLM 在 code review 流程中能做什么、不能做什么、输出格式是什么”的问题。1.2 为什么要保护人类 Code Review在不少团队里引入 AI 辅助审查后出现了两种极端。第一种是过度信任。LLM 在 PR 下面列了 20 条评论人工 reviewer 觉得“AI 应该比人仔细”于是直接把所有评论复制到合并说明里。这种流程表面上是人机协作实际上人类的判断已经被 AI 的输出绑架了。第二种是过度噪音。LLM 在每个 PR 里都给出十几条泛泛建议比如“建议使用更明确的变量名”“这段代码可以抽成函数”真正关键的所有权转移、生命周期问题、unsafe 安全性问题反而被大量低价值评论淹没。Rust 团队之所以强调 protect human code review正是因为他们意识到LLM 的定位应该是 assistant而不是 authority。Rust 里太多决策依赖“为什么这样设计”“这段 unsafe 代码是否真的安全”“这个 trait 设计是否能满足未来扩展”这些不是靠统计概率能替代的。1.3 Rust 项目里 LLM 审查的典型场景根据收集到的信息Rust 团队使用 LLM 辅助审查时通常会聚焦在以下场景检查 diff 中是否存在明显的内存安全问题例如不必要的 unsafe 使用。检查错误处理是否合理是否滥用 unwrap/expect。检查并发代码是否忽略了 Send/Sync 约束。检查是否引入了不必要的 Clone 或过度性能开销。帮助新成员理解 CI 报错中较难读的编译器信息。生成 PR 描述、变更摘要降低 reviewer 的信息获取成本。这些场景的共同特点是LLM 可以给出“参考”但需要人类 reviewer 结合上下文做最终判断。1.4 一个容易被忽略的前提还有一个前提在实践中最容易被忽略LLM 的审查能力上限取决于它看到的上下文窗口。Rust 工程往往是多 crate 项目外部依赖也很复杂只看单个 PR diff 很容易漏掉跨文件影响。所以规则里必须约束审查范围宁可让 LLM 少说不允许它脱离上下文胡猜。2. Rust 生态下 LLM 审查的边界2.1 Rust 语言特性对 LLM 审查的影响Rust 语言和 JavaScript、Python 这类动态语言在代码审查上有很大差异。Rust 编译器本身已经很严格。类型系统、借用检查器、生命周期标注会在编译期拦住大量问题。所以 LLM 在 Rust 项目里做 code review不应该花太多篇幅去提“这里类型可能不对”“这里可能会越界”这种低级问题。真正值得 LLM 介入的是那些编译器无法判断、需要设计经验的问题。举个例子let mut cache HashMap::new(); loop { let key read_key(); let value cache.get(key); // 一些业务逻辑 }上面这段代码编译器不会报错但在实际业务中可能有缓存无限增长的问题。LLM 如果能看到整体上下文会建议引入容量限制或 LRU 策略。但注意这只是“建议”是否应该缓存、缓存多久、是否需要并发安全仍然需要人工 reviewer 决策。2.2 LLM 更适合处理的 Rust 审查类型从工程经验看LLM 比较适合处理以下几类 Rust 审查审查类型示例处理方式代码风格一致性是否统一使用anyhow或自定义错误类型LLM 自动检查并给出建议不必要的unwrap在可恢复错误场景中使用unwrapLLM 标记并建议?或显式处理过度嵌套多层if let导致可读性下降LLM 建议提前 return 或重构测试覆盖盲区新函数没有对应的#[test]LLM 提醒补充测试PR 描述生成从 diff 整体生成变更摘要LLM 自动生成草稿跨文件影响初筛改动核心类型后调用方是否需要同步调整LLM 标记可能受影响的位置2.3 LLM 不擅长处理的 Rust 审查类型以下内容需要规则明确限制 LLM 参与或者至少要降低 LLM 评论的优先级unsafe 代码安全性证明。LLM 可以分析 unsafe 块中是否违反 Rust 的安全不变量但最终必须由熟悉该模块的维护者确认。架构层面的 trait 设计。是否要让某个类型实现某个 trait是否要引入泛型约束这些需要结合项目长期演进方向。依赖升级决策。某个 crate 是否需要升级升级后是否会影响其他模块不能只靠 LLM 推荐。性能关键路径的取舍。LLM 看到某个循环可以改成并行但并行带来的调度开销、锁竞争问题需要 benchmark 验证。业务规则验证。代码是否符合产品需求这一点 LLM 不具备真实业务知识。在写 LLM rules 时你可以把上述内容分成“allow”和“deny”两类也可以按严重级别降权处理。3. 环境准备与工具选型3.1 前置条件在 Rust 项目里落地 LLM code review不需要太重的环境。下面是本文示例使用的基础环境操作系统Linux / macOS / WindowsWSL 均可语言环境Rust 1.75 或更高版本Cargo 正常可用项目托管GitHubGitLab 或 Gitea 也可以原理相同CI 平台GitHub Actions 或 JenkinsLLM 接入方式OpenAI API、兼容 OpenAI 协议的本地网关或自建 LLM 服务版本需要根据你的项目实际情况调整本文示例以常见环境为例重点演示配置思路。如果你所在团队已经部署了私有化 LLM 网关只需要把请求地址和密钥替换即可。3.2 场景一使用 OpenCode Review 类工具目前社区里出现了一批“Open Code Review”类的开源项目安装方式通常是cargo install open-code-review或者把工具集成到pre-commit流程中。安装到 VS Code 的方式一般是在扩展市场搜索open code review然后按扩展说明配置 API Key。这类工具的核心功能是读取当前分支相对目标分支的 diff生成评审建议然后以评论方式提交到 PR。3.3 场景二自建轻量脚本如果不想依赖第三方工具也可以自己写一个简单的审查脚本。整体思路是从 Git 中获取 diff。将 diff 与规则配置一起拼成 prompt。调用 LLM API。把返回结果写入 PR 评论或本地文件。这个方案灵活性最高方便完全控制 LLM 的行为边界。3.4 安全与合规提醒接入 LLM 时要特别注意代码隐私。不建议直接提交包含密钥的.env文件所有敏感配置都应该通过 CI 的 secret 管理机制注入。如果项目代码是商业闭源项目建议走私有化 LLM 网关不要把完整代码内容发送给外部 API。4. 完整实战案例为 Rust 项目配置 LLM Code Review Rules下面我们用一个具体的项目来演示整套配置过程。项目结构如下rust-llm-review-demo/ ├── .github/ │ └── workflows/ │ └── llm-review.yml ├── llm-rules/ │ ├── review-rules.yaml │ └── prompt-template.md ├── scripts/ │ └── llm_review.py ├── src/ │ └── main.rs └── Cargo.toml为了演示效果我会在src/main.rs里放一段常见但存在改进空间的 Rust 代码然后让 LLM 依据规则进行审查。4.1 创建项目结构先创建一个新的 Rust 项目cargo new rust-llm-review-demo cd rust-llm-review-demo创建llm-rules和scripts目录mkdir -p llm-rules scripts4.2 设计 LLM 规则文件这是整套方案的核心。规则文件我用 YAML 编写因为 YAML 在团队协作中比较容易阅读和 diff。文件路径llm-rules/review-rules.yamlreview: # 审查范围 scope: # 是否只审查 diff 中变更的行 diff_only: true # 需要排除的路径 exclude_paths: - Cargo.lock - *.md - docs/** # 允许被审查的文件类型 include_extensions: - .rs - .toml # 输出控制 output: max_comments: 10 max_comment_length: 300 language: zh-CN comment_format: suggestion # 评论的标识前缀方便后续过滤 marker_prefix: LLM Review # 置信度与严重级别 severity: levels: - critical - warning - nit # 只输出 critical 和 warning 级别的建议 min_level: warning # 行为边界 behavior: # 禁止 LLM 自动修改代码 auto_modify: false # 禁止 LLM 自动批准合并 auto_approve: false # 是否允许 LLM 在评论中提及具体函数名 allow_function_name: true # 是否允许 LLM 引用汇编或编译器内部实现细节 allow_internal_docs: false # 规则列表 rules: - id: RUST-ERR-001 description: 不推荐在可恢复错误场景中使用 unwrap/expect patterns: - unwrap() - expect( severity: warning suggestion: 建议使用 ? 运算符或显式错误处理将错误传播给调用方。 enabled: true - id: RUST-ERR-002 description: 检测不必要的 unsafe 代码 patterns: - unsafe severity: critical suggestion: 检查 unsafe 块是否必要。注意最终安全性必须由人类 reviewer 确认。 enabled: true - id: RUST-ERR-003 description: 避免过度克隆 patterns: - .clone() severity: nit suggestion: 确认 clone 是否必要避免在热路径中引入额外内存拷贝。 enabled: false这个配置的核心设计思想是把 LLM 当成一个“初级审稿人”限制评论数量避免噪音。只审查.rs和.toml文件避开Cargo.lock等自动生成文件。控制置信度门槛低价值评论直接不输出。明确禁止自动修改确保最终合并决定权在人类手里。4.3 编写 Prompt 模板规则文件只是约束真正传给 LLM 的内容还需要一个结构化的 prompt 模板。文件路径llm-rules/prompt-template.md你是一名 Rust 代码审查助手。你只提供参考建议不做最终决策。 ## 审查要求 1. 只关注 diff 中变更的代码不要对无关内容发表评论。 2. 按照以下严重级别输出评论critical、warning。 3. 每条评论必须包含文件路径、行号、问题描述、建议方案。 4. 如果存在 unsafe 代码必须提醒人类 reviewer 重点确认。 5. 不要给出没有依据的泛泛建议。 6. 不要自动修改代码。 7. 如果 diff 中没有明显问题明确回复“未发现明显问题”。 ## 代码上下文 请结合以下 Rust 项目变更进行审查。 ## 变更 diff {{DIFF_CONTENT}} ## 项目背景 {{PROJECT_CONTEXT}} ## 输出格式 请按以下 JSON 格式输出 { comments: [ { file: 文件路径, line: 行号, severity: critical/warning, message: 问题描述, suggestion: 建议方案 } ], summary: 整体评价 }使用 JSON 输出的好处是方便后续脚本解析和显示。如果你希望 LLM 直接以 Markdown 评论形式输出也可以修改模板。4.4 编写 LLM 审查脚本现在我们写一个 Python 脚本负责拼接 diff 内容和规则然后调用 LLM API。文件路径scripts/llm_review.pyimport json import os import subprocess import sys import requests def get_diff(): 获取当前分支相对 main 分支的 diff。 result subprocess.run( [git, diff, origin/main...HEAD], capture_outputTrue, textTrue, cwdos.getcwd(), ) return result.stdout def read_template(): 读取 prompt 模板。 with open(llm-rules/prompt-template.md, r, encodingutf-8) as f: return f.read() def read_rules(): 读取规则配置用于后续过滤评论。 with open(llm-rules/review-rules.yaml, r, encodingutf-8) as f: return f.read() def build_prompt(diff_content, project_context): 组装 prompt。 template read_template() prompt template.replace({{DIFF_CONTENT}}, diff_content[:30000]) prompt prompt.replace({{PROJECT_CONTEXT}}, project_context) return prompt def call_llm(prompt, api_key): 调用 LLM API这里以 OpenAI 兼容协议为例。 url os.getenv(LLM_API_URL, https://api.openai.com/v1/chat/completions) headers { Authorization: fBearer {api_key}, Content-Type: application/json, } payload { model: os.getenv(LLM_MODEL, gpt-4o-mini), messages: [ {role: system, content: 你是一个严格的 Rust 代码审查助手。}, {role: user, content: prompt}, ], temperature: 0.2, } response requests.post(url, headersheaders, jsonpayload, timeout60) response.raise_for_status() content response.json()[choices][0][message][content] return content def parse_comments(content): 解析 LLM 返回的 JSON。 try: return json.loads(content) except json.JSONDecodeError: print(LLM 返回的内容不是合法 JSON原始内容) print(content) return {comments: [], summary: } def main(): api_key os.getenv(LLM_API_KEY) if not api_key: print(缺少 LLM_API_KEY 环境变量) sys.exit(1) diff_content get_diff() if not diff_content.strip(): print(没有发现 diff跳过审查。) return project_context 这是一个 Rust 演示项目使用了 Cargo 构建工具。 prompt build_prompt(diff_content, project_context) response_content call_llm(prompt, api_key) result parse_comments(response_content) # 按规则过滤评论 comments result.get(comments, []) filtered [ c for c in comments if c.get(severity) in (critical, warning) ] print( LLM 代码审查结果 ) print(f原始评论数{len(comments)}) print(f过滤后评论数{len(filtered)}) print() for comment in filtered: print(f[{comment.get(severity).upper()}] {comment.get(file)}:{comment.get(line)}) print(f 问题{comment.get(message)}) print(f 建议{comment.get(suggestion)}) print() print(f整体评价{result.get(summary, )}) if __name__ __main__: main()这段脚本最关键的逻辑在于最后一步按规则文件中的min_level对 LLM 评论进行过滤。即使 LLM 一次性输出了很多评论我们也会把 nit 级别的建议过滤掉只保留 warning 和 critical。4.5 编写 CI 工作流为了让 LLM 审查在每次 PR 时自动触发需要把脚本挂到 CI 上。下面以 GitHub Actions 为例。文件路径.github/workflows/llm-review.ymlname: LLM Code Review on: pull_request: types: [opened, synchronize] jobs: llm-review: runs-on: ubuntu-latest permissions: contents: read pull-requests: write steps: - name: Checkout code uses: actions/checkoutv4 with: fetch-depth: 0 - name: Set up Python uses: actions/setup-pythonv5 with: python-version: 3.11 - name: Install dependencies run: | pip install requests - name: Run LLM Review env: LLM_API_KEY: ${{ secrets.LLM_API_KEY }} LLM_API_URL: ${{ secrets.LLM_API_URL }} LLM_MODEL: ${{ secrets.LLM_MODEL }} run: | python scripts/llm_review.py llm_review_output.txt - name: Upload Review Report uses: actions/upload-artifactv4 with: name: llm-review-report path: llm_review_output.txt这里需要特别注意权限配置contents: read只允许读取仓库内容。pull-requests: write允许工作流在 PR 上写评论。LLM API Key 通过 GitHub Secrets 注入不要写在代码里。4.6 示例项目中的 Rust 代码为了让审查效果更直观我准备了一段可能被 CI 检查出来的 Rust 代码。文件路径src/main.rsuse std::collections::HashMap; fn main() { let mut cache: HashMapString, String HashMap::new(); let key user_1001.to_string(); let value cache.get(key).unwrap(); // 可能触发 RUST-ERR-001 println!(value: {}, value); let data load_data(); unsafe { // 这里是一个不安全的指针操作示例仅用于演示 LLM 审查 let p data.as_ptr(); println!(pointer: {:p}, p); } } fn load_data() - Vecu8 { vec![1, 2, 3, 4, 5] }当 LLM 审查这段代码时预期会给出类似这样的建议cache.get(key).unwrap()在 key 不存在时会 panic建议使用match或?。unsafe块中的指针操作需要人类 reviewer 确认安全性。如果开启 nit 级别可能会提示HashMap可以改用带容量限制的缓存结构。4.7 运行与验证本地执行脚本验证export LLM_API_KEYyour-api-key export LLM_MODELgpt-4o-mini python scripts/llm_review.py预期输出类似 LLM 代码审查结果 原始评论数5 过滤后评论数2 [WARNING] src/main.rs:8 问题在 HashMap.get 后直接调用 unwrap如果 key 不存在会导致程序 panic。 建议使用 match 或 if let 处理 Option避免潜在运行时错误。 [CRITICAL] src/main.rs:11 问题unsafe 代码块需要重点确认。当前代码创建了指向 Vec 数据的裸指针可能存在生命周期风险。 建议请维护者确认 unsafe 块的使用是否必要并检查数据所有权和生命周期。 整体评价变更整体简单但存在 unsafe 代码和 unwrap 使用建议人工 review 重点确认。到这里一个最小可用的 LLM 辅助 code review 流程就落地了。5. 如何保证“人类评审不被替代”5.1 从流程上隔离 AI 评论在实际的团队协作中建议把 AI 评论集中在一个独立评论中而不是让 AI 像真人 reviewer 一样逐行评论。这样真人 reviewer 可以只看最终汇总也可以在本地标记已过滤。GitHub Actions 中可以这样处理把脚本输出单独评论一次不要开放逐个写入 review comments 的权限。5.2 把 AI 评论标记为“建议属性”在 GitHub 的 review 模型中AI bot 的身份应始终是member或自定义 bot 账号评论级别只允许COMMENT不允许REQUEST_CHANGES不允许APPROVE。这个限制可以通过 GitHub Actions 权限或 GitLab 的 MR 设置来实现。更稳妥的做法是AI 的审查结果只输出到 artifact 或 summary不直接写进 PR 对话流。只有当维护者主动查看时才去读取 AI 报告。5.3 永远保留人工 reviewer 的最终决定权规则文件里的auto_approve: false只是一个软约束重要的还是工作流上的设计。建议在 PR 合并前设置分支保护规则至少需要 1 个人类 reviewer 的 Approve。AI bot 的 Approve 不计入审查人数。如果 PR 变更了unsafe代码需要单独标注unsafe-reviewlabel由维护者确认后移除。这样设计之后LLM 的作用就被限制在“信息补充”和“初筛提醒”不会反过来左右合并过程。5.4 为 AI 建议建立“信任清单”另一个实用做法是给 LLM 建议分级。第一次接入的时候团队可以先跑 2 到 4 周把所有 AI 评论收集起来让资深维护者给 AI 评论打标签useful采纳了确实有帮助。noisy无关紧要浪费注意力。incorrect建议错误甚至可能误导。然后根据统计结果把经常误报的规则在review-rules.yaml中禁用把useful比例高的规则保留。这种持续优化的思路比一开始追求“AI 全自动审查”要靠谱得多。6. 常见问题与排查思路6.1 LLM 没有输出评论可能原因排查步骤解决方案API Key 配置错误检查环境变量是否注入重新配置 secret没有 diff检查git diff origin/main...HEAD是否为空确认 CI checkout 时是否拉取完整历史Prompt 要求过于严格查看规则文件中的min_level临时降级到nit看是否能输出上下文为空检查diff_content是否被截断调整脚本中的切片长度6.2 LLM 评论太多噪音过大这是最常遇到的问题。解决方案有三个维度在规则文件中调低max_comments比如从 10 改成 5。只保留critical和warning过滤掉nit。使用静态规则先做一轮过滤例如先通过正则排除README修改、Cargo.lock变化再让 LLM 分析。6.3 LLM 评论质量不稳定LLM 在不同 PR 上的表现可能有波动。比较稳定的做法是固定 model 版本不要随便切换。把 temperature 调低到 0.2 以下减少随机性。为不同审查类型准备不同的 prompt 模板比如专门针对 unsafe 代码的 prompt。增加 few-shot 示例在 prompt 中给出 1 到 2 个“好的评论”和“差的评论”例子。6.4 AI 建议被开发者无脑采纳这个问题很难从技术上完全防止只能在流程上做约束。推荐在 PR 描述中增加一个 checklist- [ ] 我已经阅读 LLM 审查报告 - [ ] 所有 AI 建议都经过人工判断不是直接复制 - [ ] unsafe 代码已由维护者确认不要小看这种轻量 checklist它能把“被动接受 AI 输出”变成“主动确认”效果会比单纯在规则文件里写“不准自动合并”要好。6.5 如何避免 CI 中泄露代码数据如果项目比较敏感可以改成私有化 LLM 网关或者只在本地运行审查脚本不把完整 diff 发送到外部服务。还可以在脚本层面做脱敏比如把硬编码的密钥、密钥文件路径替换成REDACTED再发送给 LLM。7. 最佳实践与工程建议7.1 从“小范围试点”开始不要一上来就在所有 PR 上启用 LLM 审查。比较稳妥的路线是第一阶段只在feat/**分支上启用输出到 artifact不写评论。第二阶段在人少的内部项目中启用让 AI 评论直接显示在 PR 中。第三阶段收集反馈调整规则后全量推广。7.2 把规则文件纳入版本管理llm-rules/review-rules.yaml和prompt-template.md必须纳入 Git 仓库。这样每一次规则调整都有记录也方便新人快速理解团队对 AI 审查的边界定义。7.3 关注成本与时效LLM API 调用会带来延迟和费用。从实际体验看diff 很大时token 消耗会明显上升。如果项目 PR 很频繁建议只对变更行数大于某个阈值的 PR 触发 LLM。可以考虑使用缓存策略同一个 commit SHA 的审查结果不重复计算。7.4 建立 AI 审查质量周报建议每周统计一次 LLM 建议的采纳率、误报率、漏报率。这里给出一个简单的统计维度指标说明总评论数本周 LLM 产生的评论数量采纳数开发者明确修改的评论数量误报数被维护者标记为错误的评论数量忽略数开发者查看后决定不处理的评论数量关键问题数涉及 unsafe、错误处理、并发安全的问题数量这些指标能帮助团队判断 LLM 是否真的在减轻人工 review 的负担还是只是制造了新的噪音。7.5 保持“人机协同”而非“人机对抗”在实际使用中我发现最有效的使用方式是把 LLM 当成一个“很认真的初级审稿人”。你不需要完全相信它但它能帮你在第一时间发现一些明显的低层次问题这样人类维护者可以把精力集中在架构、安全和产品语义上。对于 Rust 项目来说LLM 尤其适合做这些“体力活”整理 PR 变更摘要。检查代码格式和命名是否一致。初步排查错误处理是否规范。标记可疑的 unsafe 使用。最终是否接受、是否修改、是否合并仍然由人类维护者决定。这也是“protect human code review”的真正含义。我建议你从一个小 PR 开始试起把本文的规则文件复制到自己的项目里调整成适合团队的内容然后连续运行 2 周看看 AI 评论中哪些值得保留、哪些需要过滤。实践一遍之后你对 LLM 在 code review 流程中的边界会有更清晰的判断。

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

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

免费获取报价