AI 代码评审与单元测试生成实战:让大模型成为研发副驾

代码评审(Code Review)和单元测试,一直是研发流程里最耗人力、又最容易被临时省略的两个环节。把大模型请进评审与测试环节,既能守住质量底线,又能把工程师从重复劳动里解放出来——这正是 AI 辅助研发最务实的落地姿势。

一、为什么把大模型请进 Code Review

人肉评审的瓶颈很明显:资深工程师的时间最贵,而大部分评审意见其实是高度模式化的——空指针风险、魔法数字、异常处理缺失、命名歧义、并发隐患。这些恰恰是语言模型擅长捕捉的信号。把第一遍”找茬”交给模型,让人聚焦在架构权衡、业务正确性和安全边界上,评审效率和质量都能上一个台阶。

单元测试同理。写测试最枯燥的部分是搭脚手架:构造入参、Mock 依赖、补边界用例。模型能根据函数签名和上下文,几秒内吐出可运行的测试骨架,工程师只需要校验和补充业务断言。它不是替代测试,而是把”从零写起”变成”审改即可”。

二、AI 代码评审的三种落地姿势

2.1 IDE 内联评审:写代码时即时反馈

在编辑器里选中一段代码,让模型给出改进建议。VS Code 配合支持聊天侧栏的插件,就能做到”选中即评审”。好处是零延迟、不打断心流,适合捕捉局部坏味道。

2.2 CI 门禁式评审:合并前自动把关

把评审作为流水线的非阻塞检查:通过就在 PR 里贴出建议,不通过也不强制卡死(避免误杀)。这是性价比最高的落地方式,详见我们用 GitHub Actions 搭建自动化工作流的思路,把模型调用串进同一个流水线即可。

2.3 PR 自动评论:团队协作的共识沉淀

在 Pull Request 里由机器人自动评论,所有成员都能看到模型的意见,还能把高频问题沉淀成团队规范。配合VS Code 的高效编辑技巧,开发者本地改完、推送后立刻收到评审,闭环最短。

三、写出高质量评审 Prompt

模型输出质量高度依赖 Prompt。一份能稳定产出可用评审的 System Prompt,应当明确角色、范围、输出结构和禁忌。下面是一段可直接套用的模板:

你是一名资深代码评审工程师,请对下面的 diff 做 Code Review。
要求:
1. 只关注真实风险,不要挑格式毛病的刺;
2. 每条意见给出:严重程度(阻塞/建议/可选)、问题位置、修复示例;
3. 特别关注:空指针/越界、资源未释放、并发竞争、异常吞掉、注入风险;
4. 不编造不存在的 API;不确定就写明"需确认"。
输出用 Markdown 列表,最多 10 条,按严重程度排序。

关键纪律是”不挑格式刺”和”不编造 API”——这两句能把模型从废话和幻觉里拉回来,显著提升信噪比。

四、单元测试生成:从函数签名到测试骨架

假设有一个计算购物车折扣的函数,让模型基于签名生成 pytest 骨架:

# 被测函数
def calc_discount(price: float, vip: bool, coupon: float | None) -> float:
    if price <= 0:
        raise ValueError("price must be positive")
    rate = 0.9 if vip else 1.0
    final = price * rate
    if coupon:
        final = max(0.0, final - coupon)
    return round(final, 2)

# 模型生成的测试骨架
import pytest
@pytest.mark.parametrize("price,vip,coupon,expect", [
    (100, False, None, 100.0),
    (100, True, None, 90.0),
    (100, True, 20, 70.0),
    (100, True, 200, 0.0),   # 优惠券不能让价格变负
])
def test_calc_discount(price, vip, coupon, expect):
    assert calc_discount(price, vip, coupon) == expect

def test_negative_price_raises():
    with pytest.raises(ValueError):
        calc_discount(-1, False, None)

注意模型自动补了”优惠券不能让价格为负”和”负数抛异常”两个边界用例——这正是人容易漏、模型擅长的地方。你只需确认这些断言符合业务预期,再补上集成层面的用例即可。这和我们用Java 模式匹配化简分支逻辑、用结构化并发收敛异常路径的思路一脉相承:先把边界说清,测试才好写。

五、实战:用 GitHub Actions 接一个评审机器人

下面是一个最小可用的工作流:在 PR 打开或更新时,取出 diff,调用一个 OpenAI 兼容的聊天接口(本地 Ollama、云端网关均可),把评审结果作为评论回写。把密钥放在仓库 Secrets 里,切勿硬编码。

# .github/workflows/ai-review.yml
name: ai-code-review
on:
  pull_request:
    types: [opened, synchronize]
jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - name: Run AI review
        env:
          LLM_BASE: ${{ secrets.LLM_BASE }}
          LLM_KEY:  ${{ secrets.LLM_KEY }}
        run: python .ci/ai_review.py

脚本侧负责取 diff 并调用模型(伪代码骨架):

import os, subprocess, requests, json

diff = subprocess.run(
    ["git", "diff", "origin/main...HEAD"], capture_output=True, text=True
).stdout

resp = requests.post(
    os.environ["LLM_BASE"] + "/chat/completions",
    headers={"Authorization": "Bearer " + os.environ["LLM_KEY"]},
    json={
        "model": "local-model",
        "messages": [
            {"role": "system", "content": "你是代码评审工程师,只输出 Markdown 列表。"},
            {"role": "user", "content": "评审以下 diff:\n" + diff[:12000]},
        ],
    }, timeout=60,
)
review = resp.json()["choices"][0]["message"]["content"]
print(review)  # 通过 gh pr comment 回写到 PR

生产环境记得给 diff 做长度截断、加超时与失败兜底,否则大模型偶发慢响应会拖垮整个流水线。

六、避坑清单:AI 评审的五个误用

模型不是银弹,用错方式反而会引入噪声。下面这张表是落地时最常见的五个坑:

误用表现正确做法
把评审当门禁硬卡死模型误报导致合并不了,团队开始无视告警非阻塞提示,严重问题才人工升级
让模型挑格式毛病满屏”建议加空格”,信噪比崩塌Prompt 明确”只关注真实风险”
盲信生成的测试断言本身写错,绿了也是假阳性人必须核对断言是否符合业务
喂未脱敏代码密钥、PII 随 diff 外发CI 前做脱敏与 Secrets 扫描
无长度截断超大 diff 触发限流或超时报错截断 + 分文件分批评审

七、效果怎么度量

别只凭感觉。建议跟踪三个指标:评审平均耗时、被人工采纳的建议占比、上线后由评审漏掉的缺陷数。先在小团队灰度两周,确认信噪比达标再推广。记住一句话:AI 评审的目标是”让人少做重复判断”,而不是”替人做最终决定”。把这点想透,工具才真正变成研发的副驾。

八、团队落地路线图:三周灰度法

别一上来就全量铺开。最稳妥的节奏是三周分阶段灰度,每一阶段都有明确的退出标准,避免”上了又没人用”的烂尾。下面这张表可以直接当成推广计划抄走:

阶段动作退出标准
第一周IDE 内联评审,个人自愿试用试用者反馈”有用”占比超六成
第二周CI 非阻塞评论,小团队 3–5 人建议被人工采纳率稳定超三成
第三周复盘指标,决定是否全量合并前缺陷数同比下降可观测

每一阶段都要保留”一键关闭”的能力——模型偶发抽风时,能立刻退回纯人工评审,团队信任才不会在一次误报里崩掉。把自动化当成默认开启的辅助,而不是强制的门禁,这条灰度纪律比任何工具都重要。

上一篇 单体架构还是微服务:技术选型决策框架
下一篇 Starship 实战:用 Rust 打造跨 Shell 高颜值终端提示符