AI 代码评审深入:把审查变成可落地的工作流

一、回顾:AI 代码评审的价值

站内基础篇「ai-code-review」已经说明,AI 代码评审的核心价值在于把资深工程师的注意力,从重复、机械的挑错中解放出来。这里不再复述概念,只补三点容易被人忽略的事实:

  • AI 评审最擅长的是「覆盖广度」。一个人一次最多盯住十几个文件,AI 可以在几秒到几分钟内把整个改动差异(diff)扫一遍,且不会因为疲劳漏看。
  • AI 评审最不擅长的是「意图判断」。它不知道这次改动的业务目标,也不清楚历史债务为何存在,所以它给出的结论需要人来加权。
  • 因此,本篇的立场是:AI 评审不是替代人工评审,而是把人工评审从「找低级错误」升级到「做架构与意图判断」。下面所有设计都围绕这个分工展开。

二、评审维度清单

要把 AI 评审变成可落地的流程,第一步是给它一张「查什么」的清单。建议固定为六个维度,并让提示词或配置每次都按这个顺序逐项检查。

1. 正确性(Correctness)

  • 边界条件:空集合、空字符串、负数、零、极大值。
  • 并发与竞态:共享状态是否有锁或多线程保护。
  • 控制流:是否可能走到未初始化分支、死循环、资源未释放。
  • 数值与类型:整数溢出、浮点比较、类型强制转换。

2. 安全(Security)

  • 注入类:SQL 注入、命令注入、LDAP/NoSQL 注入、模板注入。
  • 越权类:缺少鉴权、缺少鉴权后的资源归属校验(IDOR)。
  • 密钥与凭证:硬编码 token、密钥、私钥、口令出现在改动中。
  • 依赖与供应链:引入的库版本是否存在已知漏洞(需结合 SCA 工具)。

3. 性能(Performance)

  • 循环内查询数据库或发起网络请求。
  • N+1 查询、全表扫描、缺少索引。
  • 不必要的大对象拷贝、同步阻塞调用。
  • 缓存缺失或缓存穿透、缓存键设计不合理。

4. 可维护性(Maintainability)

  • 重复代码、过长的函数与过深的嵌套。
  • 命名是否传达意图、是否有误导性注释。
  • 魔法数字、全局可变状态、隐式依赖。
  • 改动是否破坏了既有模块边界。

5. 测试覆盖(Test Coverage)

  • 新增分支是否配套单元测试。
  • 关键路径是否有集成测试或端到端测试。
  • 测试是否覆盖失败路径与异常路径。
  • 测试是否可能因为断言过松而「假绿」。

6. 规范(Conventions)

  • 与团队代码风格、提交规范是否一致。
  • 接口契约、错误处理风格是否统一。
  • 日志、监控、链路追踪是否按约定埋点。

三、实战工作流

一个可落地的 AI 评审工作流,应当回答三个问题:什么时候触发、评论如何分级、哪些情况必须人工介入。

3.1 触发:在 Pull Request 上自动跑

最自然的触发点是 Pull Request 的开启与更新。下面是一段 GitHub Actions 配置示例,它在 PR 更新时拉取 diff,调用你自己的评审服务(或 LLM 网关),再把结果以评论形式回写。

name: ai-code-review

on:
  pull_request:
    types: [opened, synchronize, reopened]

permissions:
  pull-requests: write
  contents: read

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - name: 检出代码
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: 获取差异
        id: diff
        run: |
          git fetch origin ${{ github.event.pull_request.base.ref }}
          DIFF=$(git diff origin/${{ github.event.pull_request.base.ref }}...HEAD)
          echo "diff<<EOF" >> "$GITHUB_OUTPUT"
          echo "$DIFF" >> "$GITHUB_OUTPUT"
          echo "EOF" >> "$GITHUB_OUTPUT"

      - name: 调用 AI 评审
        env:
          REVIEW_TOKEN: ${{ secrets.REVIEW_TOKEN }}
          PR_DIFF: ${{ steps.diff.outputs.diff }}
        run: |
          # 将 PR_DIFF 与提示词一起提交给评审网关,
          # 评审网关调用 LLM 并把结构化结果回写到本工作流。
          curl -s -X POST https://review-gateway.internal/v1/review \
            -H "Authorization: Bearer $REVIEW_TOKEN" \
            -H "Content-Type: application/json" \
            -d "{\"diff\": $(jq -Rn --arg d "$PR_DIFF" '$d | tojson')}"

      - name: 回写评论
        uses: actions/github-script@v7
        with:
          script: |
            // 读取评审网关产出的评论文件,逐条写入 PR 评论
            const fs = require('fs');
            const comments = JSON.parse(fs.readFileSync('review-result.json', 'utf8'));
            for (const c of comments) {
              await github.rest.pulls.createComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                pull_number: context.issue.number,
                body: c.body,
                commit_id: context.payload.pull_request.head.sha,
                path: c.path,
                position: c.position
              });
            }

3.2 分级:让评论带严重度

AI 不应该把「拼写笔误」和「SQL 注入」放在同一优先级。建议统一四级严重度,并在每条评论里标注:

  • 严重(Blocker):安全漏洞、数据丢失风险,必须修。
  • 高(High):正确性或并发缺陷,强烈建议修。
  • 中(Medium):可维护性、性能隐患,建议修。
  • 低(Low):风格、规范,可酌情处理。

下面是一段提示词示例,要求模型按维度与严重度产出结构化结果,而不是自由发挥。

你是一名资深代码评审工程师。请对以下 Pull Request 差异进行评审。

要求:
1. 逐项检查以下维度:正确性、安全、性能、可维护性、测试覆盖、规范。
2. 每条问题必须包含:文件路径、近似行号(基于 diff 行号)、严重度(严重/高/中/低)、问题说明、修改建议。
3. 严重度定义:
   - 严重:安全漏洞、数据丢失风险。
   - 高:正确性或并发缺陷。
   - 中:可维护性、性能隐患。
   - 低:风格、规范。
4. 不要编造行号;若无法确定行号,请基于 diff 片段定位。
5. 不要给出与改动无关的建议。
6. 输出严格为 JSON 数组,不要输出额外解释文字。

待评审差异:
{{DIFF}}

3.3 人工复核门槛:哪些情况不允许 AI 直接放行

AI 评审的结论不计入合并门槛是行业常见做法。参考 GitHub Copilot 代码审查的官方行为,它的评审始终以「Comment」形式留下,而不是「Approve」或「Request changes」,因此不会算作合并所需的审批、也不会阻止合并(已核验)。基于此,建议设置以下人工复核门槛:

  • 任何「严重」或「高」评论未解决前,必须由一名人类评审者手动批准。
  • 涉及鉴权、支付、数据迁移、权限模型的改动,强制二级人工复核。
  • 自动评审失败(服务超时、输出无法解析)时,降级为纯人工评审,不得静默放行。
  • AI 评审仅作为参考意见,最终合并决策权在人工。

四、工具箱

不同团队规模与平台,适合的工具不同。下面给出四类,部分细节标注为待核实。

4.1 各平台 Copilot 类审查

  • GitHub Copilot 代码审查:在 PR 的 Reviewers 里请求 Copilot 即可,通常不到 30 秒返回;支持 Lite(默认值,偏向常见 bug、安全漏洞、风格)与 Balanced(更深入的复杂逻辑与跨服务分析,使用更高推理能力的模型)两档审查力度(已核验)。可通过 .github/copilot-instructions.mdAGENTS.md.github/instructions/**/*.instructions.md 做仓库级与路径级的自定义审查指引(已核验)。
  • GitLab Duo 等同类能力的具体覆盖范围与定价,本文列出但待核实。

4.2 CodeRabbit

官方定位为 AI 代码审查产品,强调对每个 Pull Request / Merge Request 自动审查,并提供优先级排序、风险分级与「理解你的队列」等能力,包括一个把 PR 当看板(Kanban)来管理的视图(已核验)。它宣称在 AI 代码审查领域有业界领先的上下文能力,并支持持续学习(已核验)。关于其具体支持的严重度标签、与 Sonar/Q linter 的集成深度、价格档位,本文待核实。

4.3 SonarQube + AI

SonarQube 长期以静态分析、代码异味、安全热点著称。近年 Sonar 引入了 AI 辅助能力,例如自动生成修复建议与更自然的评审解释(具体功能名称与可用性待核实)。建议把 Sonar 的规则引擎作为「硬规则」层,把 LLM 作为「解释与建议」层,二者互补:Sonar 负责稳定、可复现的告警,AI 负责把告警翻译成人话并给上下文。

4.4 自定义 LLM 评审 bot

当团队有私有代码、强合规要求,或想嵌入自有规范时,自建 bot 最灵活。核心组件:

  • 取 diff 的程序(如上文 GitHub Actions)。
  • 提示词模板(如上文结构)。
  • 一个把结构化结果写回 PR 的服务。
  • 一个「误报反馈」通道,让工程师标记假阳性,用于迭代提示词。

下面是一个最小可运行的 Python 评审回写片段示例:

import json
import os

import requests


def post_review_comments(repo: str, pr_number: str, comments: list[dict]) -> None:
    """把结构化评审结果逐条写回 GitHub Pull Request。"""
    token = os.environ["GITHUB_TOKEN"]
    base = f"https://api.github.com/repos/{repo}/pulls/{pr_number}/comments"
    headers = {
        "Authorization": f"Bearer {token}",
        "Accept": "application/vnd.github+json",
    }
    for c in comments:
        payload = {
            "body": f"[{c['severity']}] {c['message']}\n建议:{c['suggestion']}",
            "commit_id": os.environ["PR_HEAD_SHA"],
            "path": c["path"],
            "position": c["position"],
        }
        resp = requests.post(base, json=payload, headers=headers, timeout=30)
        resp.raise_for_status()


if __name__ == "__main__":
    with open("review-result.json", encoding="utf-8") as fh:
        data = json.load(fh)
    post_review_comments(
        repo=os.environ["GITHUB_REPO"],
        pr_number=os.environ["PR_NUMBER"],
        comments=data,
    )

五、陷阱

把 AI 评审接进流程后,这些坑最常见,也最容易被忽视。

5.1 假阳性:AI 说错了

AI 可能把正确的代码判成有 bug,尤其在它不了解项目约定时。典型场景:

  • 把「故意的兼容写法」当成冗余。
  • 把「框架要求的样板代码」当成可删除重复。
  • 在没有完整上下文时,误判变量未使用或未初始化。

应对:每条 AI 评论都可一键标记误报,并沉淀进提示词与 AGENTS.md,减少重复误报。

5.2 假阴性:AI 没看见

比假阳性更危险的,是它沉默地放行了真问题。原因通常是:

  • 上下文不足,只看单文件而漏掉跨文件影响。
  • 改动依赖了它没读到的配置或生成代码。
  • 提示词没覆盖到的维度被直接跳过。

应对:把安全、权限、数据迁移类改动设为强制人工复核;对关键路径保留 Sonar 之类的硬规则兜底。

5.3 盲目采纳:把 AI 当权威

  • 不要因为 AI 没评论就认为代码没问题。它没看到的,不等于不存在。
  • 不要让 AI 评论直接自动合并。合并决策必须人工拍板。
  • 警惕「AI 已经审过了」带来的责任稀释:最终对线上事故负责的,仍然是人和团队。

5.4 上下文不足漏看跨文件问题

单 PR diff 往往不够。一个接口签名改动,影响的是所有调用方;一个共享类型改名,可能让其他服务编译失败。建议:

  • 让评审 bot 在必要时拉取被改动符号的定义与调用方(通过代码检索或索引)。
  • 对「破坏性改动」加标签,触发更大范围的扫描。
  • 跨仓库、跨服务的改动,提升人工复核等级而不是依赖 AI。

小结

AI 代码评审的价值不在「替代人」,而在「把人从低级错误中解放出来」。落地时请记住四件事:第一,用固定的六维清单约束 AI 查什么;第二,让评论带严重度,使严重与高级别问题不被淹没;第三,设置人工复核门槛,AI 结论只作参考、不计入合并审批;第四,正视假阳性、假阴性与盲目采纳三类陷阱,用反馈闭环和硬规则兜底。把这几条串起来,AI 评审才会从玩具变成真正可依赖的工作流。

参考与延伸阅读

  • GitHub Docs:Using GitHub Copilot code review(审查行为、力度档位、自定义指令)已核验。
  • CodeRabbit 官网:AI 代码审查的产品定位与自动审查、风险分级、PR 看板能力已核验。
  • GitHub Copilot 代码审查不计入所需审批、不阻止合并的官方说明已核验。
  • SonarQube 的 AI 辅助修复与解释功能的具体名称与可用性待核实。
  • GitLab Duo 代码审查等同类平台能力、CodeRabbit 的严重度标签与价格档位待核实。
本文累计阅读