设计一个 AI 代码审查助手
一句话回答
用 Git 平台的 Webhook 在创建或更新合并请求时触发,接收服务验签、去重、入队后立即返回;Worker 拉取 diff 和必要的上下文,过滤掉锁文件和生成代码,把大 diff 按文件或代码块拆分并发审查;Prompt 明确只关注缺陷、安全、性能这类问题并按严重程度分级,要求结构化输出;结果校验行号后写回对应的代码行。难点在于控制误报(置信度阈值、去重、数量上限、收集开发者反馈)、成本,以及代码隐私。
详细解析
第一步:澄清需求
- 平台:GitHub、GitLab 还是自建平台?决定 Webhook 的格式和写评论的接口
- 规模:每天多少个合并请求(下文简称 PR)、平均改动多少行,决定并发和成本
- 定位:只给建议,还是作为合并前的必要检查?一般先做建议,误报率稳定后再考虑卡点
- 范围:代码风格交给 Lint 和格式化工具,AI 只看工具查不出来的问题
- 隐私:代码能不能发给云端模型?需不需要私有化部署
第二步:整体架构
Git 平台 ──Webhook(PR 创建、有新提交)──► 接收服务
│ 验签 → 按(仓库, PR, head SHA)去重 → 入队 → 立即返回 2xx
▼
任务队列
▼
审查 Worker
1 拉取 diff、变更文件的全文、PR 标题和描述、仓库里的审查规则文件
2 过滤:锁文件、生成的代码、二进制文件、超大文件
3 拆分:按文件,大文件按代码块(带上前后文)
4 并发调用模型(限制并发数),得到结构化的问题列表
5 后处理:校验行号、按置信度过滤、去重、限制条数
6 写回:行内评论 + 一条总结评论
│ │
▼ ▼
大模型(云端 / 私有化) 数据库:审查记录、评论指纹、反馈、成本
第三步:核心模块和数据模型
接收 Webhook:用原始请求体校验签名(见代码示例)。平台对响应时间有要求,而审查要跑几十秒甚至更久,所以只入队、不处理。同一个 PR 会因为多次 push、重新打开等收到多个事件,按 head commit SHA 去重;有新提交时,取消旧 SHA 还没跑完的任务。
获取上下文:只看 diff 容易误判,比如看不到被调用函数的定义。在 token 预算内补充:变更文件的完整内容或周边若干行、被修改函数的定义和调用方、PR 描述和关联的需求(理解改动意图)、仓库根目录的审查规则文件(团队约定)。
Prompt 和输出:
- 明确要关注的:逻辑错误、边界条件、空值和异常处理、并发问题、安全漏洞、明显的性能问题
- 明确不关注的:代码风格、命名偏好(除非团队规则要求)
- 没有问题就返回空列表,不要为了凑数提意见
- 每个问题包含行号、严重程度(
blocker/major/minor)、类别、置信度、说明和修改建议,用 JSON Schema 约束输出格式(见让模型稳定输出 JSON)
写回评论:行内评论只能放在 diff 里出现的行上,而模型自己数的行号经常对不上。做法是把 diff 转成带新文件行号的文本交给模型,返回后检查行号是否在可评论的行里,不在就并入总结评论。
数据表:
reviews:仓库、PR 编号、head SHA、状态、token 用量、费用review_comments:文件、行号、严重程度、问题指纹、平台上的评论 IDcomment_feedback:评论 ID、反馈类型(有用 / 误报)、是否引发了代码修改
第四步:关键难点
控制误报。误报多了,开发者会直接忽略所有评论,这比漏报更伤:
- 只发布置信度高于阈值的问题,
minor级别只放进总结评论 - 每个 PR 的评论数量设上限,按严重程度排序
- 对每个问题再做一次校验(让模型对照代码自查,或者用另一次调用复核),过滤站不住的
- 用"文件 + 问题类型 + 相关代码"生成指纹去重,有新提交后不重复发同样的评论
- 收集反馈:评论下的"有用 / 误报"、评论后代码是否被修改(采纳率),误报样本加入评测集
大 diff 和跨文件问题。按文件拆开审查会丢失全局信息,比如改了函数签名而调用方没改。先用一次调用生成整个 PR 的变更摘要,作为每个文件审查的公共上下文;超出预算时按风险排优先级(核心目录、改动大的文件优先),并在总结里说明哪些文件没有审查。
成本。只审查新增的提交(上次审查的 SHA 到新 SHA 之间的 diff);按文件内容的哈希缓存结果;跳过纯文档、纯配置的改动;简单文件用小模型、核心文件用大模型;把规则和 PR 摘要放在 Prompt 前部,利用提示缓存;按仓库设置每月预算。
代码隐私和安全:
- 代码是核心资产:使用不留存、不用于训练的企业协议,或者私有化部署开源模型
- 发送前检查 diff 里有没有误提交的密钥,有就直接告警,并且不要把它发给模型
- 机器人的平台令牌只给读代码和写评论的权限,不给批准和合并的权限
- PR 内容是不可信输入:有人可能在代码注释里写给 AI 的指令,比如要求它"批准这个 PR",或者对某段代码保持沉默。机器人只能发评论,注入造不成越权操作,但可能让评论失真、漏掉真正的问题,所以 AI 的结论不能代替人工审查
第五步:扩展与优化
- 支持在评论里 @ 机器人追问,或者让它给出修改补丁,复用同一次审查的上下文
- 和静态分析结合:把 Lint、类型检查、安全扫描的结果作为输入,让模型解释和补充,而不是重复报告
- 把被频繁采纳的问题沉淀成团队规则或 Lint 规则,用确定性的工具代替模型
代码示例
import crypto from 'node:crypto'
// 校验 GitHub 的 X-Hub-Signature-256:必须用原始请求体,不能用解析后再序列化的 JSON
export function verifySignature(rawBody: Buffer, header: string | undefined, secret: string) {
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
const a = Buffer.from(header ?? '')
const b = Buffer.from(expected)
return a.length === b.length && crypto.timingSafeEqual(a, b) // 定长时间比较,防时序攻击
}
// 把平台返回的单个文件的 patch(从 @@ 开始)转成带新文件行号的文本,并记录可评论的行
export function annotatePatch(patch: string) {
const lines: string[] = []
const commentable = new Set<number>()
let newLine = 0
for (const raw of patch.replace(/\n$/, '').split('\n')) { // 有的平台的 diff 以换行结尾,去掉末尾的空行
const hunk = raw.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/)
if (hunk) {
newLine = Number(hunk[1])
lines.push(raw)
} else if (raw.startsWith('-')) {
lines.push(` ${raw}`) // 删除的行在新文件里没有行号
} else if (!raw.startsWith('\\')) { // 跳过 "\ No newline at end of file"
commentable.add(newLine)
lines.push(`${String(newLine).padStart(4)} ${raw}`)
newLine++
}
}
return { text: lines.join('\n'), commentable }
}
面试官可能追问
怎么评估这个审查助手的效果?
离线:从历史 PR 里找后来被修复的缺陷(比如有对应修复提交的),看助手能不能在原始 PR 上发现,算召回率;人工标注一批评论是否成立,算精确率。线上:看采纳率(评论后代码被修改的比例)、误报反馈率,以及评论被直接标记为已解决、代码却没改的比例。每次改 Prompt 或换模型,先在离线评测集上对比。
只给 diff,模型经常因为看不到上下文而误报,怎么改进?
按需补充上下文:通过代码搜索或语言服务找到被修改函数的定义、调用方和相关类型;也可以给模型提供"读取文件""搜索代码"的工具,让它在步数和 token 预算内自己查。同时要求模型在缺少信息时说明假设、降低置信度,而不是直接下结论。
能不能让 AI 审查通过后自动批准合并?
不建议。漏报无法避免,PR 内容还可能带有针对机器人的提示注入,自动批准等于把合并权限交给了攻击者能影响的模型。AI 适合作为辅助,批准由人来做。少数确定性高的检查(比如发现提交了密钥)可以作为阻止合并的条件,但它们通常用规则实现,不依赖模型判断。
代码不能发到云端,私有化模型效果又差一些,怎么办?
先用自己的评测集量化差距,再决定取舍:可以按仓库的敏感程度分级,核心仓库用私有化模型,其他仓库在签订数据协议后用云端模型;私有化模型聚焦在误报少的问题类型上,降低对模型能力的要求;把团队规则、静态分析结果作为输入,弥补模型能力的不足。
易错点
- 在 Webhook 的处理函数里同步执行审查:平台等不到及时的响应,会把这次投递记为失败
- 用解析后的 JSON 重新序列化去算签名,结果和平台的签名对不上
- 让模型自己数行号,评论贴错了位置甚至写不进去
- 只追求"发现更多问题",不控制误报,最后没人看评论
AI 模拟面试官
用自己的话回答,AI 对照参考答案打分、指出遗漏,再追问,最多 3 轮
这道题你掌握了吗?
选一个最接近的状态,没掌握的题会出现在"我的进度 · 待复习"里。
学习记录暂存在本机浏览器。登录后自动同步到账号,换设备也能看到。