AI图鉴

AI图鉴首页 › AI 资讯 › 如何审核 AI 生成的代码:一份可落地的 6 层检查清单

如何审核 AI 生成的代码:一份可落地的 6 层检查清单

来源:掘金 · 发布时间:10-29 · 由 AI图鉴每小时自动聚合

AI 写的代码,到底该怎么审?本文给出一份可落地的 6 层检查清单,从语法正确性到安全审计逐层把关,帮你把 AI 生成的代码变成可信的 production 代码。

全文摘要:AI 把「写代码」变便宜了,但把「证明代码对」变贵了——一份 patch 让 AI 写可能只要 5 分钟,人验证却要 45 分钟。本文给出一份可落地的 6 层检查清单,从「让工具先当裁判」到「审意图不审语法」「审测试不审实现」「审边界与安全」,并用一个能跑的评审脚本把「审什么」变成可执行的规则。

目录

- 先说结论:代码便宜了,验证变贵了

- 为什么 AI 的代码要换一套审法

- 第 1 层:让工具先当裁判

- 第 2 层:审意图,不审语法

- 第 3 层:审测试与边界

- 第 4 层:审安全与幻觉依赖

- 第 5、6 层:拆分与回滚、该不该自己重写

- 一个能跑的 AI 代码评审器

- 症状 → 原因 → 解法对照表

1. 先说结论(The Verdict)

结论先给:AI 生成的代码,审查的重点不是「它写得对不对」,而是「它为什么看起来对」。

一份 AI patch 能在 5 分钟内写完,但验证它往往要 45 分钟。2026 年的残酷算式是:写代码的边际成本已经被压到接近零,证明正确的成本成了新的税。所以审核 AI 代码,本质是把你的注意力从「读代码」挪到「验证代码」上。

审核 AI 代码的六层清单,一句话概括:让机器先过一遍机器能过的,人只盯机器会犯的那些「看起来对」的错。

六层分别是:

打个比方 🎭:AI 像一个语速极快、偶尔胡说的助手。它 5 分钟给你 10 页纪要,你看起来效率爆炸,但真正花掉的是你逐句核对的那 45 分钟。审核 AI 代码,就是学会先不读那 10 页,而是先问「这些他是怎么来的」。

参考资料:

2. 为什么 AI 的代码要换一套审法(Why a Different Review)

先破一个误区:很多人以为 AI 代码的错是「语法错」或「编译不过」。恰恰相反,那些错 CI 一道就能拦住,根本不用人看。

真正危险的是幻觉正确性(hallucinated correctness):代码能编译、能过所有测试、看起来完全正常,但它是错的。

GitHub 的官方指引把这类错列为最需警惕的 red flag:明显的幻觉(调用不存在的 API、引用越界变量)会被 CI 抓住;而危险的那个更隐蔽——分页的 off-by-one、某条从没被测试打到的分支上缺失的权限检查、在某个边界条件下短路掉的校验,这些全都藏在「通过测试」的伪装下。

参考资料:

- Agent pull requests are everywhere. Here's how to review them. -- GitHub Blog ⭐值得阅读

- Review AI-generated code -- GitHub Docs

为什么 AI 的错长得跟人不一样? 三个结构性原因:

① 模式匹配,不是推理。 AI 生成代码是基于训练数据里的模式做预测,它不知道自己「预测错」了。它给出的代码带着「我很确定」的自信,哪怕那个 API 是它脑补出来的。

② 同源假设。 让同一个 AI 既写代码又写测试,两个产物共享同一套「什么是对」的假设。测试不是从需求倒推的,而是从代码倒推的——这种测试叫同义反复测试(tautological tests)。它通过,是因为它反向验证了自己的实现,而不是验证了需求。

③ 没有外部基准。 工业界应对 AI 代码质量问题的默认手段是「再上一个 AI reviewer」。但这在结构上是循环论证:生成器和评审器从同一个 artifact 出发,共享同一套训练分布,因此它们的失败是相关的。评审是在拿代码验证代码,而不是拿意图验证代码。

类比 🎭:这就像让同一个人既出考题又出答案。答案是「对」的,只是因为它是从考题里推出来的,但考题本身可能就出错了。

后果是量化的。 CodeRabbit 分析了 470 个真实 GitHub PR,发现 AI 生成代码产生的问题量约为人类代码的 1.7 倍——其中逻辑与正确性错误比人类代码多 75%。这些不是 toy benchmark,是生产仓库里的实测。

参考资料:

- The Specification as Quality Gate: Three Hypotheses on AI-Assisted Code Review -- arXiv

- AI broke QA and nobody noticed -- Jia Wei Ng

- Test-First Strategy for AI-Generated Code (2026) -- Alex Cloudstar

所以审法要换:机器先过机器能过的关,人只盯「看起来对」的错。

3. 第 1 层:让工具先当裁判(Let the Tools Judge First)

人不是好的编译器。别把十分钟花在肉眼看语法上,那是一个确定性工具几秒就能干完的事。

第 1 层就是让机器先跑一遍它该跑的:编译、lint、类型检查、已有测试套件。这一关的目的不是「证明代码对」,而是先砍掉所有「不对」的明显形态,把人的注意力留给后面几层。

对 Python 项目,第 1 层可以是:

python -m compileall . # 语法与编译

ruff check . # 静态 lint

mypy . # 类型检查

pytest -q # 现有测试套件

对前端 / Node 项目,第 1 层就是 CI 里那几行:

- run: npm ci

- run: npx tsc --noEmit

- run: npx eslint .

- run: npm test -- --coverage

关键规则:生成的代码过不了第 1 层,就不该合入。 这不是「AI 不行」,而是「不管是人写的还是 AI 写的,都要先过同一套 CI 关卡」。第 1 层跟审核人没有关系,它是一道自动闸门,帮你把「机器能判的错」和「只有人能判的错」分开。

⚠️ 但这道闸门有个盲区:它报错你当然能看见;可它没报错不代表代码对。能被编译器和 linter 抓住的是「明显的错」,抓不住的是「看起来对、但是错」。后者正是后面几层的战场。

参考资料:

- 7 Checks to Run Before Trusting AI-Generated Code -- KDnuggets ⭐值得阅读

- AI-Generated Code Quality: What Breaks, Why, and How to Ship It Safely -- Sarah D. Hashnode

4. 第 2 层:审意图,不审语法(Review the Intent)

过了第 1 层的闸门,代码已经能编译、能过测试了。但能跑 ≠ 解决了正确的问题。

这一层你要问的不是「这段代码怎么写得好不好」,而是**「这段代码在解决正确的问题吗?」** 它验证两个东西:是否贴合项目目的,是否符合你的架构约定。

GitHub 官方给的三个自审问题,直接照搬:

- 这段代码解决了正确的问题吗? 有没有跑偏需求,还是只是「看起来在干活」。

- 它遵循了我们的约定吗? 队伍里的 naming、error handling、分层、目录归属。

- 它有没有被硬塞进错误的层? AI 擅长把逻辑放进「最顺手」的位置,而不是「应该」的位置——比如把业务规则写进 controller、把数据访问塞进 view。

关键前提:你必须先给 AI 足够的 context。 在生成之前,把 README、文档、最近的 PR 作为起点喂给 AI,告诉它「哪些来源可信、哪些不可用、给出好的示例」。这决定了第 2 层审起来是「对着一团陌生代码懵」还是「一判就中」。

审核方同样需要 context。若你只拿 PR 本身去审,那和 AI 生成时看到的一样单薄。你至少要拿到:仓库结构与依赖关系、架构约束与 service 边界、API contract 与向后兼容预期、编码规范、历史事故与 postmortem、上一轮 review comment 里接受的 trade-off。这些 context 决定你能否判断「这段代码到底该不该长这样」。

类比 🎭:审语法像改作文的错别字,审意图像看这篇作文有没有跑题。错别字交给词典(编译器)就行,跑不跑题只有知道题目的人能判断。

这里有个反直觉但重要的点:第 2 层往往要求你先怀疑「测试是不是也错了」。 如果 AI 把「需求 A」错误理解成了「需求 B」,它写出的测试也是验证需求 B 的——测试全绿,但做的不是你要的事。所以前两层的核心是:先确定「什么是对的」,再谈「代码对不对」。

参考资料:

- Review AI-generated code -- GitHub Docs ⭐值得阅读

- Agentic AI and Code Reviews: Toward a Pattern Language for Code Reviews in the Age of Agentic AI -- Readwise

- How to Build an AI Coding Workflow as a Software Engineer -- Dev Fieldnotes

5. 第 3 层:审测试与边界(Review the Tests, Then the Boundary)

先读测试,再读实现。 这是审核 AI 代码最关键的一条。

两个原因:

① 测试能帮你判断「AI 理解对了需求没有」。 如果 AI 把需求理解偏了,它的测试也会验证那个偏掉的需求。所以第一件事是看:这些测试真的在验证验收标准吗,还是只是从实现反推的回声? 后者叫同义反复测试——它通过,只因为它验证的是实现本身在干什么,而不是该干什么。

② 边界是 AI 最常栽的地方。 AI 默认走 happy path。所以你要盯着测试里有没有真实覆盖这些边界:

- 空输入、单元素、最大长度、零、负数

- null / None / undefined、缺失字段

- 错误路径:坏输入时代码确实抛/返回特定错误,且测试断言的是那个具体错误,而不只是「会抛异常」

类比 🎭:这像审一份保险条款,重点不是看「正常理赔怎么走」(那当然写得通),而是看「地震、洪水、自杀这些极端情况赔不赔」。AI 把正常路径写得再漂亮都没意义,边界才是它真正会翻车的地方。

canary 信号: 如果 AI 写的测试都是用来证明实现、没有边界用例、且断言过于宽松(比如只断言「不抛异常」而不断言「返回特定值」),那这套测试基本没有验证价值。它不过是给「看起来对」的代码盖上了一个「看起来被测过」的章。

数据能证明这一点。 对开源项目 agent 生成测试的研究发现:AI 在边界用例的多样性上甚至超过人类(variety 0.62 vs 0.32),但在稳定性上落后——人类测试的 flaky 率为 0.30,AI 生成的为 0.41。也就是说,AI 会想到更多边界,但它的测试更不稳定、更脆,更容易误报。这恰恰说明:AI 能想到的边界和「可靠验证这些边界」是两回事。

参考资料:

- Reviewing AI-Generated Tests: A Code-Review Checklist 2026 -- QASkills.sh ⭐值得阅读

- AI-Generated Tests Are Testing the AI's Blind Spots, Not Your Code -- Dzaki Amri Zaidaan

- Beyond Test Presence: Assessing the Quality and Robustness of Agent-Generated Tests -- arXiv

6. 第 4 层:审安全与幻觉依赖(Review Security & Hallucinated Dependencies)

这一层是不能省的。因为 AI 生成代码时不做对抗性思考——它不会像攻击者那样想「我该怎么绕过这道校验」。但攻击者会。

审核时要扫的那几类,基本就是 OWASP 的 instinct 清单:

① 注入类(injection)。 AI 特别爱用字符串拼接拼 SQL / 查询,爱把用户输入直接塞进命令:

- SQL 注入:query 里的字符串拼接

- NoSQL 注入:来自用户 JSON 的 MongoDB 操作符

- XSS:dangerouslySetInnerHTML、未转义模板、LLM 渲染的原始 HTML

- 命令注入:exec、subprocess 里未校验的输入

② 授权缺失。 记住 authentication ≠ authorization。AI 很容易只写了「登录了没」,却漏了「这个用户有没有权限干这件事」。尤其是那些测试里从来没打到的分支。

③ 硬编码 secrets。 AI 常忘了把 key、token、password 塞进环境变量,而是直接写在源码里:

OPENAI_API_KEY = "sk-abc123def456" # 危险:硬编码密钥

DATABASE_URL = "postgres://user:password@localhost/db" # 危险:明文密码

④ 过度宽泛的输入绑定(mass assignment)。 把整个请求体直接绑定到对象,「用户传什么就设什么」,导致越权字段被覆盖。

⑤ 幻觉依赖(hallucinated dependencies)——这个最隐蔽。 AI 会引用一个根本不存在、或已过时的库。更危险的是:一个不存在的包名可以被攻击者注册——你 npm install 了 AI 编出来的名字,等于装了一个别人塞的包。所以新加的任何 dependency 都要单独审,确认它存在于 lockfile、license 合规、来源可信。

⑥ 针对代码生成 agent 本身的注入。 别忘了,现在很多代码是你让 agent 写的。agent 的 context 来自仓库、网络、连接的 tool。攻击者可以把隐藏指令塞进 issue body、PR 描述、AGENTS.md、MEMORY.md——这就叫 间接提示注入(indirect prompt injection),是对开发工作流本身的攻击。

类比 🎭:AI 像那个把「钥匙塞在门口花盆下」的人,觉得方便就行;攻击者则是挨个翻花盆的小偷。AI 不思考「谁会来翻」,所以这类坑它自己发现不了,只能由审的人从攻击者视角补上。

参考资料:

- Secure Coding with AI -- OWASP Cheat Sheet Series ⭐值得阅读

- The 15 Security Failures Your AI Coding Assistant Ships by Default -- Prodogon

- OWASP Top 10 for AI-Generated Code: A01-A10 Fixes (2026) -- Vibe Eval

- LLM Prompt Injection Prevention -- OWASP Cheat Sheet Series

7. 第 5、6 层:拆分与回滚、该不该自己重写(Scope, Rollback & Rewrite)

前 4 层审的是「这段代码本身对不对」,第 5、6 层审的是**「这段代码该不该以这个形态存在」**。

第 5 层:审拆分与回滚(Review Scope & Rollback)

AI 最大的毛病之一是scope 失控:一个 5 分钟的 patch,它顺手改了 10 个文件、删了 3 个无关函数、加了 2 个新依赖。所以这一层要看:

- diff 是否原子化:一个 PR 只做一件事,文件改动范围可控。

- 有没有意外删除:git diff | grep '^-',对每个被删的符号全仓库搜一遍,确认它真的死了,而不是被 AI 误删。

- 回滚点是否存在:改之前留一个 recovery branch。

读 diff 的顺序也有讲究:按风险从高到低读,别从上到下读。 先扫删除(^-),再读新增,最后看被 AI 一顺带改掉的「无关区域」。

类比 🎭:这像收快递。你打开包裹第一眼看的是「有没有少东西」,而不是「赠品漂不漂亮」。AI 的 PR 最常「少东西」——少了边界处理、少了一行权限校验、少了一个原本在用的函数。

第 6 层:该不该自己重写(When to Rewrite Instead of Fix)

这是最容易踩坑的一层——沉没成本。AI 给你一大坨跑不通的代码,你本能想「修一修就好」。但很多时候,删掉重来比修补更便宜。

Kunal Ganglani 的 checklist 给了明确的「删除重写」判据,命中任一条就删:

- diff 超过 300 行,且改动不是机械性的

- 一个小 feature 新增了 2 个以上新依赖

- 读了 10 分钟还讲不清意图

- 引入了新抽象,却没有删掉旧抽象

- 测试全绿,但这段代码你用不了(比如沟通成本、可维护性超过它的价值)

为什么该删? 因为「AI 生成的、藏着模糊意图的大 diff」,你修它花的认知成本,往往超过「给它一个更紧的 prompt 重新生成一遍」。你不是在修代码,你是在为一个模糊的约定做生产级兜底——这是最不划算的事。

⚠️ 一个必须记住的原则:别让同一个工具既写代码、又写「批准这段代码」的测试。 如果 AI 生成的代码和验证它的测试出自同一套假设,那这套验证是循环的。测试要由独立视角来写。

参考资料:

- Review AI-Generated Code Checklist [2026]: Beat the Bottleneck -- Kunal Ganglani ⭐值得阅读

- Review an AI Agent PR and Remove Unneeded Changes -- BetterToken

- How to Inspect AI-Generated Diffs Before Accepting -- AI Tools Guidebook

8. 一个能跑的 AI 代码评审器(A Runnable AI-Code Reviewer)

前面聊的都是「怎么审」,这一节把它落成一段直接能跑的脚本。它用正则 + 规则把「第 4 层安全与幻觉依赖」里最常被 AI 坑的几类 - 硬编码密钥、可疑 API、过度宽松绑定、危险 eval/exec、字符串拼 SQL - 自动扫出来。

这样你拿到一段 AI 代码,先跑这个脚本,把「机器能判的」先挑出来,再用人来做 2、3、5、6 层。只依赖标准库 🐍。

"""AI 代码初筛器:对一段 Python 源码做静态规则扫描,标记常见 AI 生成陷阱。

不替代人工评审,只做「第 1 层 + 第 4 层」的机器预筛:

1. 硬编码密钥 / token / password

2. 可疑或易误用的 API(eval、exec、pickle、dangerous 类)

3. 过度宽泛的输入绑定(mass assignment)

4. 字符串拼接 SQL(注入风险)

5. 危险子进程调用

"""

import re

import sys

from dataclasses import dataclass, field

# 每条规则:正则 + 严重级别 + 说明

@dataclass

class Rule:

"""一条静态检查规则。

属性:

pattern: 用于匹配源码的正则表达式

severity: 严重级别,'critical' 或 'warning'

message: 命中后的人读提示

"""

pattern: re.Pattern

severity: str

message: str

@dataclass

class Finding:

"""一次命中的检查结果。

属性:

line_no: 源码行号

severity: 严重级别

message: 提示信息

matched: 命中的原文片段

"""

line_no: int

severity: str

message: str

matched: str

# AI 生成代码最常见的安全陷阱规则表;pattern 匹配的是「危险写法」的特征

RULES = [

# 1) 硬编码密钥 / token / 密码,如 OPENAI_API_KEY = "sk-..."

Rule(re.compile(r'(api[_-]?key|secret|password|passwd|token|pwd)\s*=\s*["\'][^"\']{6,}["\']', re.I),

'critical', '疑似硬编码密钥/口令,应改为环境变量'),

# 2) 危险动态执行,AI 常把用户输入直接交给 eval/exec

Rule(re.compile(r'\b(eval|exec)\s*\('),

'critical', '危险动态执行:用户输入若进入 eval/exec 可导致任意代码执行'),

# 3) 不安全的反序列化,pickle 可被构造为代码执行

Rule(re.compile(r'\bpickle\.loads?\b'),

'critical', 'pickle 反序列化不安全,可能被构造为 RCE'),

# 4) 字符串拼接 SQL,最经典的注入入口

Rule(re.compile(r'\b(FROM|WHERE|INSERT|UPDATE|DELETE)\s+.*%|\+\s*[a-z_]+\s*\+\s*["\']'),

'critical', '疑似 SQL 字符串拼接,应改用参数化查询'),

# 5) 未校验输入直接进子进程命令,命令注入风险

Rule(re.compile(r'\b(os\.system|os\.popen|subprocess\.(run|call|Popen))\s*\('),

'warning', '子进程调用:确认命令未拼接未经校验的输入'),

# 6) 过度宽泛的输入绑定,把整个 request body 塞进对象

Rule(re.compile(r'\b(update|setattr)\s*\(.*\*\*,?\s*\w+'), # 如 Model(**request.json())

'warning', '疑似 mass assignment:将整块输入直接绑定到对象'),

]

def scan(source: str, filename: str = "<code>") -> list[Finding]:

"""对一段源码逐行执行规则扫描。

参数:

source: 待扫描的源码字符串

filename: 仅用于输出展示的文件名

返回:

扫描命中的 Finding 列表;无命中返回空列表

数据流示例:源码行 "key = 'sk-123'" → 规则1命中 → Finding(line, 'critical', ...)

"""

findings: list[Finding] = [] # 收集所有命中

for line_no, line in enumerate(source.splitlines(), start=1): # 逐行检查

for rule in RULES: # 遍历每条规则

m = rule.pattern.search(line) # 在当前行做正则匹配

if m: # 命中则记录一条结果

findings.append(Finding(line_no, rule.severity,

rule.message, m.group(0).strip()))

return findings

def report(findings: list[Finding]) -> None:

"""打印扫描结果,按严重级别排序。

参数:

findings: scan() 返回的命中列表

"""

if not findings: # 无命中

print("✅ 未发现明显 AI 生成陷阱(仍需人工评审意图与边界)")

return

order = {"critical": 0, "warning": 1} # 严重级别优先级

for f in sorted(findings, key=lambda x: order[x.severity]): # 先 critical 后 warning

tag = "🔴" if f.severity == "critical" else "🟠"

print(f"{tag} L{f.line_no} [{f.severity.upper()}] {f.message}")

print(f" 命中片段: {f.matched}")

# 一段测例:故意包含多种 AI 常见陷阱的伪代码

SAMPLE = '''

OPENAI_API_KEY = "sk-abc123def456" # 危险:硬编码密钥

sql = "SELECT * FROM users WHERE id = " + uid # 危险:SQL 字符串拼接

data = pickle.loads(payload) # 危险:pickle 反序列化

result = eval(user_expr) # 危险:eval 用户表达式

os.system("ping " + host) # 警告:子进程拼接输入

User(**request.json()) # 警告:mass assignment

'''

if __name__ == "__main__": # 直接运行脚本

report(scan(SAMPLE, "sample.py")) # 扫描样例并打印报告

实际输出:

🔴 L2 [CRITICAL] 疑似硬编码密钥/口令,应改为环境变量

命中片段: OPENAI_API_KEY = "sk-abc123def456"

🔴 L3 [CRITICAL] 疑似 SQL 字符串拼接,应改用参数化查询

命中片段: WHERE id = " + uid

🔴 L4 [CRITICAL] pickle 反序列化不安全,可能被构造为 RCE

命中片段: pickle.loads(payload)

🔴 L5 [CRITICAL] 危险动态执行:用户输入若进入 eval/exec 可导致任意代码执行

命中片段: eval(user_expr)

🟠 L6 [WARNING] 子进程调用:确认命令未拼接未经校验的输入

命中片段: os.system("ping " + host)

🟠 L7 [WARNING] 疑似 mass assignment:将整块输入直接绑定到对象

命中片段: User(**request.json())

这个脚本的价值不在于规则多全,而在于它把「第 4 层安全」里原本靠人眼扫的东西,变成了一个可复用的确定性检查。你完全可以把它接进 CI,让它在 review 之前先跑一遍——这就是第 1 层「让工具先当裁判」的落地。

类比 🎭:这个脚本像个安检门,先帮你把「带刀带枪」的拦下来。安检门挡不住「伪装成旅客的罪犯」(对应意图与边界层),但能让你别再花时间在「查刀」上。拿它当第一道过滤,人只处理剩下的高价值判断。

参考资料:

- Bandit: Audit AI-Generated Python for Security Flaws -- CodeCut ⭐值得阅读

- Comparing Human and LLM Generated Code: The Jury is Still Out! -- arXiv

9. 症状 → 原因 → 解法对照表(Symptom Table)

把前面六层压缩成一张能直接对号入座的表。建议按「我最近最像哪一行」来读,而不是从头读到底 📋。每一行都是一个真实的、会在生产里冒出来的坑。

⚠️ 一个必须记住的口径:别把「能编译、能过测试」当成「代码对」的证据。 第 1 层机器闸门只砍掉「明显错」,真正值钱的是后面五层——它们决定这段代码该不该以这个形态存在。

参考资料:

- 2026年AI编码工具实战:六款主流Agent与IDE深度评测 -- 尧图网站定制

- Review AI-Generated Code Checklist [2026]: Beat the Bottleneck -- Kunal Ganglani ⭐值得阅读

什么时候该用它? 当你拿到一段「看起来都对」的 AI 代码,先用这张表对号入座,把最像你的那几行指出来逐层去查。什么时候不该用? 当这段代码是你在自己维护多年的仓库里改既有逻辑时,别机械套六层,而要先补足 context——context 不够,任何检查清单都审不出真问题。

本专栏《AI开发范式》聚焦如何让 AI 真正成为开发效率的放大器,而不是返工源头。 从「怎么让 AI 写得更稳」到「怎么写才不让 AI 带偏架构」,每篇都配一段可直接运行的代码,每月更新两

本文由 AI图鉴自动抓取并翻译,版权归原作者所有;如需查看原文请点下方链接。

相关 AI 工具

相关免费羊毛

同源资讯

阅读原文(juejin.cn)

标题、摘要与正文均来自该信源的公开页面,版权归原作者所有;本站自动聚合与翻译,仅作出处跳转。

← 返回 AI 资讯列表AI工具图鉴首页

返回 AI图鉴首页 · 提交工具 · 商务合作 · 关于本站 · 598 款 AI 工具分类导航 · 每日 AI 热门项目与动态