AI 代码审查的五个判断维度

0阅读13分钟

代码审查正在变成一个流程问题

半年前,我的代码审查方式还算稳定:打开 PR,看 diff,按经验判断。审查同事写的代码,问题通常出在逻辑遗漏或架构取舍上——这些可以靠经验补位。

但审查 AI 生成的 PR 之后,老办法不太好使了。AI 写出来的代码往往语法正确、结构清晰、注释工整,单看每一行都觉得「没毛病」。但合在一起跑,问题就冒出来了——漏改的文件、没处理的空值、绕过了项目封装层直接调 API。

我后来意识到,这不是审查能力的问题,是审查方式的问题。靠脑子里的经验清单去逐行看 AI 代码,效率低、容易漏,而且每次审查的关注点不一样。更靠谱的做法是:把审查规则写成可重复执行的规范,让每次审查走同一条路径。

换句话说,审查 AI 代码,需要的是一套可重复执行的 SOP,而非一组零散的注意点。

为什么凭经验看 PR 不够了

先说数据。GitClear 在 2025 年底发布了一项覆盖两亿多行代码变更的研究,发现 AI 辅助代码的代码克隆(duplicate code)增长了 4 倍,重构代码占比从 2021 年的 25% 降到不足 10%。[1] 代码复制量首次超过了代码迁移量。

Stack Overflow 2025 年开发者调查的数据更值得注意:84% 的开发者在使用 AI 工具,但只有 29% 信任它们——信任度比上年下降了 11 个百分点。[2] 用的人越来越多,信的人越来越少。

arXiv 在 2025 年底的一篇论文调查了 72 篇相关研究,给出了具体的缺陷分布:功能缺陷被讨论最多(56 篇提及),其次是语法错误(32 篇)和可靠性问题(21 篇),其中一项针对 GitHub Copilot 的实证研究发现约 40% 的 AI 生成代码包含安全漏洞。[3]

这些数据指向同一个问题:AI 生成的代码在表面上做得不错(语法、命名、格式),但在行为正确性、边界处理、安全意识和项目一致性上,需要更系统的审查方法。

靠经验逐行看 PR 的第一个问题是不可重复。同一个审查者,今天关注安全,明天关注性能,审查结果随状态波动。第二个问题是不可传递。一个人的经验清单无法直接交给团队里的其他人。第三个问题是不可扩展。当 AI 工具让 PR 数量翻倍时,人脑逐行审查会成为瓶颈。

InfoQ 在 2026 年 6 月的一篇报道中提到了 DoorDash 的经验:他们的 AI 审查系统必须「只给出可执行的建议,才能赢得信任,而不是制造噪音」。[4] 人对人也是一样——审查规范如果不聚焦、不可执行,审查者很快就会跳过它。

三层审查 SOP

经过多个项目的实践,我把 AI 代码审查拆成了三层。每层解决不同的问题,层与层之间有明确的输入输出关系。

流程图画布 · 115%
Mermaid 流程图加载中...

规范层:把项目约束写成 AI 和人都能读的规则

这是最容易被忽视、也最有杠杆效应的一层。

Google 的工程实践文档把代码审查分成七个维度:Design、Functionality、Complexity、Tests、Naming、Comments、Style。[5] 这是一套通用标准,但它缺少项目特有的约束。比如,你的项目要求 API 业务逻辑放在 modules/ 而不是 routes/,要求单文件不超过 500 行,要求 Server Components 优先——这些规则如果只存在于团队负责人的脑子里,AI 不知道,新成员也不知道。

AGENTS.md 和 CLAUDE.md 是近两年兴起的开放标准,用来把项目约束写成 AI 编码代理可以读取的格式。截至 2026 年,超过 6 万个开源项目使用了 AGENTS.md。[6] 它的作用不只是指导 AI 写代码——更重要的是,它把审查标准从「人脑里的经验」变成了「可追溯、可执行的文档」。

在一个实际项目中,规范层通常包含这些内容:

规范类别典型规则示例审查时的作用
代码边界API 业务逻辑放 modules/ 不放 routes/审查时按目录结构判断归属是否正确
文件约束单文件不超过 500 行审查时自动标记超标文件
框架约束React Server Components 优先审查时检查 Client/Server 边界
测试策略测试放 apps/tests/,营销页不写单测审查时判断测试位置和覆盖范围
安全规则用户输入必须净化,禁止 dangerouslySetInnerHTML 直渲审查时重点检查数据流安全

Addy Osmani 在讨论如何为 AI 代理写好规格说明时提出了一个三层约束模型:[7]

  1. 常规安全操作Always):例如「提交前必须跑测试」。
  2. 需要人工确认的操作Ask):例如「修改数据库 schema 前必须询问」。
  3. 绝对禁止的操作Never):例如「永远不要提交密钥」。

这个分层思路同样适用于审查规范——不是所有规则都需要同等的审查力度。把约束分成「自动检查」「人工必查」「按需抽查」三级,审查效率会高很多。

流程层:让每个 PR 走同一条审查路径

有了规范,下一步是定义审查流程。

我在项目里用到的审查流程来自 review-code-review 这个 Skill(可以理解为一份可执行的审查 SOP)。它定义了审查的输入、顺序、严重级别和合并决策,让每次审查走同一条路径。

审查顺序按风险从高到低排列:

  1. 需求符合度:是否实现了该实现的内容,有无遗漏、越界和破坏性变更。
  2. 正确性:边界条件、错误路径、并发、异步、状态一致性。
  3. 安全性:认证授权、输入校验、注入、XSS、CSRF、敏感信息。
  4. 架构边界:目录归属、依赖方向、抽象必要性、API 契约。
  5. 测试与验证:测试是否覆盖高风险逻辑,验证命令是否匹配改动。
  6. 性能与可靠性:N+1、重复请求、内存泄漏、缓存、降级路径。
  7. 可维护性:复杂度、命名、可读性。
  8. 风格:只看工具无法自动处理且影响理解的问题。

这个顺序的关键设计是:从需求开始,从风格结束。大多数审查者习惯从命名和格式看起(因为容易看出问题),但 AI 代码在风格和命名上通常做得不错,花时间在上面的回报率很低。把注意力先放在高风险维度上,效率更高。

严重级别也需要明确定义,否则审查者和作者会在「这个问题到底要不要改」上反复拉锯:

级别含义对合并的影响
Blocker安全漏洞、数据丢失、核心功能错误、构建失败不能合并
Major重要边界遗漏、错误处理不足、架构边界破坏应在本次修复
Minor可读性、局部可维护性、非阻塞性能优化建议修复
Nit命名、表达、风格偏好可选微调
Question需要作者解释意图暂不当作结论

合并决策只有四种明确状态:Approved、Approved with follow-ups(可合并但需记录后续)、Changes requested、Needs context。不允许出现「没看完就写 LGTM」的情况。

danicat.dev 的一篇实践文章提出了一个值得采纳的原则:在 AI 时代,「代码是一次性的」,审查者应该只看提交本身的质量,而不是看它是谁写的。[8] 这意味着审查标准必须写在 PR 之外的文档里(AGENTS.md、审查 Skill),而不是依赖审查者当时的状态。

执行层:五维度审查 + AI 预筛

流程层定义了「按什么顺序看」,执行层解决「具体看什么」。

在规范层和流程层的基础上,我把执行层的注意力集中在五个维度。这五个维度来自对 AI 生成代码的风险特征的观察——它们是 AI 最容易出错、也最容易被「看起来可以」掩盖的领域。

维度审查重点AI 常见表现严重等级
行为正确性用户可见行为是否符合需求只改了列表页,没检查详情页和分类页🔴 阻塞合并
边界条件空数据、失败、非法输入是否处理主路径顺畅,但空列表显示空白🔴 阻塞合并
数据流数据来源、转换、组件传递是否清晰Client 组件意外导入 server-only 模块🔴 安全漏洞
验证证据是否有可复现的命令、截图或测试结果PR 只写「已测试」,没有具体证据🟡 要求补充
维护成本是否贴近项目现有模式,是否过度抽象为单一场景新增通用 helper🟢 建议优化

下面展开每个维度的具体审查方法。

行为正确性

AI 擅长实现单点功能,但容易遗漏需求涉及的其他入口和相邻功能。

比如让 AI「给图片列表加上无限滚动」。Agent 大概率会写出一个不错的 IntersectionObserver 实现,加上骨架屏加载和错误重试。但审查时需要多看几步:

  • 用户切换筛选条件后,滚动位置是否重置?
  • 用户从详情页返回列表页,滚动位置是否需要恢复?
  • 无限滚动的加载状态是否和已有的骨架屏组件风格一致?

这些是上下文问题,AI 不知道图片列表组件同时被收藏页和搜索结果页引用,也不知道旧版列表接口有一个特殊的缓存策略需要保持一致。

我在实践中形成的一个习惯是:审查 AI 代码时,先不看它改了什么,而是先看它没改什么。需求涉及的其他模块是否需要同步改动?已有的数据模型是否支撑新功能?这些「沉默的遗漏」往往比代码里的 bug 更难发现。

边界条件

主路径通常没问题,但空值、错误、异常输入经常没有处理。

假设让 AI 实现一个「根据标签名搜索文章」的 API。AI 大概率会写出这样的代码:

// ❌ 主路径能跑通,边界全部缺失
async function searchByTag(tag: string) {
  const articles = await db.article.findMany({
    where: { tags: { some: { name: tag } } }
  })
  return articles
}

这段代码能正常工作——如果传入的 tag 合法、标签存在、数据库正常、结果不为空。但实际运行中会遇到:

// ✅ 把实际会碰到的情况逐一处理
async function searchByTag(tag: string) {
  // 空标签、超长输入
  if (!tag || tag.length > 50) {
    return { articles: [], total: 0 }
  }
 
  try {
    const articles = await db.article.findMany({
      where: { tags: { some: { name: tag } } }
    })
 
    // 空结果——是否需要推荐相似标签?
    if (articles.length === 0) {
      const similar = await suggestSimilarTags(tag)
      return { articles: [], total: 0, similarTags: similar }
    }
 
    return { articles, total: articles.length }
  } catch (error) {
    logger.error('Tag search failed', { tag, error })
    // 数据库异常时降级到缓存
    return getCachedArticlesByTag(tag)
  }
}

左边的代码能跑通 demo,右边的代码才能撑住日常使用。差异在于是否预想了主路径之外的情况。AI 的训练数据里,大多数示例代码展示的是正常路径,异常处理往往被简化或省略。这种现象在当前各类语言模型中普遍存在。

数据流

AI 对框架约束的理解来自训练数据中的通用模式,而非项目的具体规则。它知道 React 组件可以用 useState,但不知道你项目的 Server Components 里不能用。它知道 fetch 可以发请求,但不知道项目的 API 调用必须走统一的 client 封装。

更值得留意的是安全相关的数据流问题。AI 生成的代码在数据净化方面经常偷懒。原因多半是训练数据里大量示例代码没有做净化,而安全规则又往往是项目特有的。比如,某个项目可能允许管理员富文本中包含 <iframe>,但用户投稿内容必须过滤掉。这类判断需要人来完成。

数据流问题AI 常见表现审查者需要追问
Server / Client 边界Client 组件里用了 Node.js API这个代码运行在浏览器还是服务端?
XSS 风险dangerouslySetInnerHTML 直接渲染内容来源是否可信?是否经过净化?
路径遍历用原始文件名存储文件文件名是否经过安全处理?
SQL 注入拼接查询字符串是否使用了参数化查询?
跨层数据传递数据转换发生在错误的层转换逻辑应该在 server、API 层还是组件?

审查数据流时,可以沿着一条数据从来源走到终点:它在哪里产生、经过了哪些函数、最终渲染在哪里。这条路径上每个节点都值得看一眼。

验证证据

如果 PR 作者声称「已测试」,但没有给出测试证据,这个声称只是意见,不是事实。

Stack Overflow 的调查里有一个值得注意的发现:审查者面对 AI 代码时会产生「判断负担」——他们发现手动验证 AI 输出花费的时间和自己写代码差不多。[2] 审查者要面对的是「看起来能跑但实际不工作的代码」「自信但错误的解释」和「引用不存在的 API」。

## ❌ 不合格的验证描述
- 已测试
- 功能正常
- LGTM
 
## ✅ 有具体证据的验证描述
### 验证步骤
1. `pnpm typecheck` — 通过,无类型错误
2. `pnpm test -- --filter=search` — 18 tests passed
3. 浏览器验收:
   - 搜索有结果:标签筛选正常,URL 参数同步 ✅
   - 搜索无结果:显示「暂无相关文章」和推荐标签 ✅
   - 空输入:提交按钮禁用,不发送请求 ✅
4. 截图见附件

验证证据的门槛可以概括为:如果另一个人拿到同样的代码和同样的步骤,能否复现出同样的结果?能复现的是证据,不能复现的是声明。

维护成本

GitClear 的数据在这一点上最有说服力:代码克隆增长 4 倍,重构比例从 25% 降到不足 10%。[1] AI 工具正在改变开发者的代码编写方式——更倾向于复制和微调,倾向于为单一场景创建过度抽象。

// ❌ AI 常见的过度抽象:为单一场景创建通用 helper
// utils/dateFormatterFactory.ts
export function createDateFormatter(
  locale: string,
  timezone: string,
  options: Intl.DateTimeFormatOptions
) {
  return new Intl.DateTimeFormat(locale, { ...options, timeZone: timezone })
}
// 只在 ArticleDate 组件中用了一次
 
// ✅ 更贴近项目实际的做法
// ArticleDate.tsx 内联实现
const formatDate = (date: string) =>
  new Intl.DateTimeFormat('zh-CN', {
    year: 'numeric', month: 'long', day: 'numeric'
  }).format(new Date(date))

判断维护成本的经验法则:如果一段抽象只有一处消费,它更接近内联代码而不是基础设施。

一个完整案例:从 PR 到合并决策

把三层 SOP 串起来看一个实际场景。

需求:给内容平台加上文章收藏功能。

Agent 的 PR:改了文章详情页,加了一个收藏按钮,用 localStorage 存储收藏状态。

规范层检查:对照 AGENTS.md 里的约束——

  • 「测试放 apps/tests/」→ PR 没有新增测试 🟡
  • 「Server Components 优先」→ 收藏按钮用了 'use client',需要确认是否必要 🟡
  • 「API 调用走统一 client 封装」→ 收藏接口直接用了 fetch,应该走项目 API client 🔴

流程层检查:按风险顺序审查——

  1. 需求符合度:收藏列表页未实现 🔴
  2. 正确性:未登录用户可以点击,点击后才提示登录;快速连续点击无防抖 🔴
  3. 安全性:无明显安全问题 🟢
  4. 架构边界:localStorage 在 SSR 中访问需要做环境判断 🟡
  5. 测试与验证:PR 只写了「本地测试通过」🔴

执行层输出

严重级别发现
Blocker收藏接口未走项目 API client,绕过了统一的鉴权和重试逻辑
Major收藏列表页未实现,需求覆盖不完整
Major未登录用户可以点击收藏按钮,交互逻辑不完整
Major验证证据不足,缺少具体命令输出和截图
MinorlocalStorage 在 SSR 中需要做环境判断

合并决策:Changes requested。需要修复 Blocker 和 Major 后重新提交。

这个案例展示了三层 SOP 的协作方式:规范层提供了项目特有的判断标准(API client 封装、Server Components 策略),流程层确保了审查不跳步(从需求开始,不是从命名开始),执行层聚焦在 AI 最容易遗漏的五个维度上。

如何构建你自己的审查 Skill

上面的三层结构听起来完整,但实际落地需要一个起点。

我的建议是从 Google 的七维度标准出发,叠加项目特有的约束。具体步骤:

  1. 盘点项目约束。把散落在 README、wiki、聊天记录里的项目规则整理出来。优先整理最容易出错的边界(目录归属、文件约束、安全规则)。
  2. 写成 AGENTS.md 或 CLAUDE.md。用 Addy Osmani 的三层模型(Always / Ask / Never)组织规则。[7]
  3. 定义审查顺序和严重级别。按风险从高到低排列审查维度。定义 3-5 个严重级别,每个级别对应明确的合并影响。
  4. 写成 Skill 或模板。把审查流程写成可重复执行的文档。每次审查时按这个文档走,不需要靠记忆。
  5. 迭代。每季度回顾一次审查中发现的遗漏,把新模式补进规范层。

Re-entry 在一篇关于 AI 代码审查策略的文章中提出了六个组织要素,[9] 其中有几个和建设审查 Skill 直接相关:

要素对审查 Skill 的启示
工具注册和范围Skill 里应该列出项目允许的工具和 API 调用方式
作者问责审查模板里应该要求作者签名验证证据
风险分级验证不同风险等级的改动需要不同强度的审查
自动化门禁风格、类型检查等低维问题交给 CI,人工聚焦高维判断
数据与 Prompt 卫生Skill 里应该标注哪些数据不能出现在 AI 输出中
审计痕迹PR 模板里应该有 AI 辅助标记字段

最后一个建议:不要把审查 Skill 写成一本大书。InfoQ 报道中 Cloudflare 的经验是「专用代理比单一通用审查者效果更好」。[4] 审查 Skill 也一样——按维度拆分成可独立执行的小检查,比写一份面面俱到的长文档更容易坚持。

检查清单

把三层 SOP 压缩成一份每次审查可以用的快速清单:

## AI 代码审查 SOP 快速清单
 
### 规范层(审查前 30 秒)
- [ ] 对照 AGENTS.md 检查目录归属和文件约束
- [ ] 确认框架约束(Server/Client、API 封装层)
- [ ] 检查是否有 Never 级规则被违反
 
### 流程层(按顺序审查)
- [ ] 需求符合度:是否完整,有无遗漏和越界
- [ ] 正确性:边界条件、错误路径、并发状态
- [ ] 安全性:认证授权、输入校验、注入、XSS
- [ ] 架构边界:目录归属、依赖方向、API 契约
- [ ] 测试与验证:高风险逻辑有测试,验证命令匹配改动
- [ ] 性能:N+1、重复请求、内存泄漏
- [ ] 可维护性:复杂度、命名、文件是否过大
 
### 执行层(五维度聚焦)
- [ ] 行为正确性:AI 没改的文件是否需要同步改动
- [ ] 边界条件:空值、错误、非法输入是否处理
- [ ] 数据流:来源→转换→终点路径是否清晰安全
- [ ] 验证证据:有无可复现的命令和截图
- [ ] 维护成本:是否贴近项目模式,有无过度抽象
 
### 合并决策
- [ ] 给出明确结论:Approved / Approved with follow-ups / Changes requested / Needs context
- [ ] Blocker 和 Major 数量统计
- [ ] 未覆盖的风险面说明

收束

审查 AI 代码的核心变化是:从「凭经验逐行看 PR」转向「按规范走流程」。

规范层把项目约束从人脑搬到文档,流程层让每次审查走同一条路径,执行层把注意力聚焦在 AI 最容易遗漏的维度上。三层叠在一起,审查就不再依赖审查者的状态,而是依赖可重复执行的规范。

84% 的开发者在用 AI 工具,但只有 29% 信任它们。这个信任差距,靠的是一次更仔细的审查,更靠一套能持续运转的 SOP。


Sources:

评论 0

0 / 1000