随着大语言模型(LLM)在软件开发中的普及,AI生成的代码已从"玩具"走向生产环境。然而,AI并非银弹,其生成的代码常隐藏着逻辑漏洞、安全缺陷、可维护性差等问题。
AI生成代码的质量高度依赖提示词的质量和后续人工审查的严谨性。开发者应:
- 写提示词时:明确需求、约束、安全要求,提供示例和边界条件。
- 审查时:遵循"需求对齐→静态分析→逻辑走查→安全审查→性能评估→规范检查"的流程。
- 修复时:善用AI作为审查助手,通过针对性提示词找出漏洞并获取修复建议。
- 建立反馈循环:将审查发现的问题总结为"提示词注意事项",不断迭代改进生成质量
本文系统梳理AI生成代码的常见问题,提出一套可落地的人工审查流程,并深入探讨如何通过改进提示词(Prompt Engineering)来预防和修复代码缺陷,最后给出完整案例。
一、AI生成代码的常见问题
1. 逻辑与语义错误
- 表面正确实则错误:AI可能生成语法无误但业务逻辑不符的代码。例如,把"用户注销后删除数据"误写成"用户登录时删除数据"。
- 边界条件遗漏 :循环、递归、数组索引等易出现
off-by-one错误,或者未处理空输入、极大值等。 - 需求理解偏差:提示词含糊时,AI会自行"脑补"需求,导致功能偏离。
2. 安全漏洞
- 注入风险:拼接SQL、命令、路径时未做参数化或转义。
- 敏感信息泄露:硬编码API密钥、密码;日志中输出用户隐私。
- 权限与认证缺失:生成接口代码时忽略鉴权或CSRF防护。
3. 性能与资源问题
- 低效算法:使用O(n²)甚至指数级算法,未利用缓存、索引。
- 内存泄漏:未释放文件句柄、数据库连接、网络套接字。
- 过度设计:为简单任务生成复杂架构,增加维护成本。
4. 可维护性与规范问题
- 命名混乱 :变量名
a、temp、data等无意义。 - 结构混乱:函数过长、重复代码、缺乏模块化。
- 文档缺失:无注释、无类型提示、无错误处理说明。
5. 兼容性与环境问题
- 版本不匹配:使用旧版/新版API,未考虑目标环境。
- 平台差异:代码在Windows可用但Linux报错,或反之。
二、人工审查的详细流程与步骤
审查AI代码不能仅靠"肉眼看",需遵循系统化流程。以下五阶段模型适用于个人或团队。
阶段1:需求对齐与静态分析
目标:确认代码意图与需求一致,并捕获基础语法/类型错误。
步骤:
- 重读原始需求:将需求拆解为功能点列表,逐条对比代码实现。
- 运行静态分析工具 :对Python用
ruff/pylint,JavaScript用ESLint,Java用SpotBugs。检查未定义变量、未使用导入、可疑表达式。 - 类型检查 :若语言支持,运行
mypy、tsc --noEmit等。
示例 :
需求:"从用户输入中提取邮箱并验证格式"。AI返回代码仅用正则r'\S+@\S+',但未排除@@等非法情况。静态工具可能不报错,但需求对齐时发现漏洞。
阶段2:逻辑走查与单元测试补充
目标:发现业务逻辑错误和边界遗漏。
步骤:
- 手动追踪关键路径:用铅笔和纸模拟输入流,检查分支覆盖。
- 编写针对性测试 :
- 正常用例:一个典型输入验证输出。
- 边界用例:空值、最大值、最小值、长度为1、奇数/偶数等。
- 异常用例:非法类型、超长输入、并发请求。
- 使用断言验证不变量:检查循环前后条件、数据完整性。
示例:AI生成计算平均数的函数:
python
def average(nums):
return sum(nums) / len(nums)
审查时添加空列表测试,发现ZeroDivisionError。修复需处理空输入。
阶段3:安全审查
目标:识别并消除漏洞。
步骤:
- 数据流追踪:从用户输入到敏感操作(数据库、文件系统、网络)的路径。
- 检查常见漏洞模式 :
- SQL注入:寻找字符串拼接SQL。
- XSS:前端渲染时是否使用
innerHTML而非textContent。 - 路径遍历:文件路径是否由用户控制且未过滤
../。
- 依赖安全性 :使用
pip-audit、npm audit检查第三方库漏洞。 - 敏感信息扫描 :用
gitleaks、trufflehog查找硬编码密钥。
示例:AI生成登录接口:
python
query = f"SELECT * FROM users WHERE name='{username}' AND password='{password}'"
审查发现SQL注入,修复为参数化查询。
阶段4:性能与资源管理评估
目标:避免明显性能瓶颈和资源泄漏。
步骤:
- 复杂度分析:识别嵌套循环、递归深度、大数据量处理。
- 资源生命周期 :检查文件/连接是否在
finally或with中关闭。 - 内存使用:对大列表是否使用生成器;缓存策略是否合理。
- 基准测试(可选) :若代码将用于高并发,用
timeit或ab压测。
示例 :AI生成批量处理文件函数,每次迭代打开文件但未关闭,导致文件句柄耗尽。审查时要求使用with open(...)。
阶段5:可维护性与规范审查
目标:确保代码长期可读、可扩展。
步骤:
- 命名审查:变量、函数、类名是否符合领域语言。
- 结构重构:函数是否超过50行?重复代码是否可提取?
- 文档检查:关键函数是否有docstring?复杂算法是否有注释?
- 风格一致性 :运行
black、prettier等格式化工具。
示例 :AI生成一个200行的process_data函数,内部有5层嵌套。审查后拆分为parse_input、transform、validate三个函数。
三、通过改进提示词预防代码问题
"垃圾进,垃圾出"。优化提示词能从源头减少缺陷。
1. 明确需求与上下文
原则:避免模糊词汇,提供业务背景、输入输出示例、约束条件。
差提示词:
"写一个函数处理用户数据。"
改进后:
"用Python写一个
validate_user_data(user_dict)函数。输入是字典,包含name(字符串,长度1-50)、age(整数,0-120)、
- 若
name为空或长度超50,抛出ValueError并说明原因。- 年龄不在0-120范围抛
ValueError。- email必须匹配标准邮箱正则(例如
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$)。
返回True如果全部通过,否则False。给出类型注解和docstring。使用re模块。"
2. 指定技术约束与安全要求
原则:明确禁止危险操作,要求使用安全API。
差提示词:
"写一个数据库查询。"
改进后:
"用Python的
sqlite3模块写一个函数get_user_by_name(name)。
- 必须使用参数化查询(
?占位符),禁止字符串拼接SQL。- 处理数据库连接异常,用
try/finally确保连接关闭。- 如果用户不存在,返回
None。- 添加注释说明为何使用参数化查询。"
3. 要求包含错误处理与边界条件
原则:主动要求AI考虑异常情况。
差提示词:
"写一个计算文件行数的函数。"
改进后:
"用Python写
count_lines(file_path)函数。
- 如果文件不存在,抛
FileNotFoundError并输出友好信息。- 如果文件为空,返回0。
- 使用
with open确保文件自动关闭。- 处理编码错误(如
UnicodeDecodeError),捕获后返回-1。- 添加单元测试示例,至少覆盖正常、空文件、不存在文件三种情况。"
4. 提供正反例与格式要求
原则:通过少样本提示(few-shot)引导AI生成符合规范的代码。
示例:
"参考以下风格编写函数:
pythondef safe_divide(a: float, b: float) -> float: '''Return a/b if b != 0, else raise ValueError.''' if b == 0: raise ValueError("Division by zero") return a / b现在写一个
safe_sqrt(x),要求:如果x<0抛ValueError,否则返回平方根。添加docstring和类型注解。"
5. 分步生成与迭代
原则:复杂任务拆解为小步骤,让AI先生成伪代码或接口,再逐步实现。
示例:
"第一步:列出实现用户注册功能所需的子任务(如输入验证、密码哈希、数据库存储、发送邮件)。第二步:针对每个子任务,生成Python函数签名和docstring。第三步:依次实现每个函数。第四步:生成集成测试代码。"
四、用提示词检查并修复已有代码问题
除了生成新代码,提示词也可用于代码审查和修复。将AI作为"审查助手",提供代码和审查指令。
1. 安全漏洞检查提示词
模板:
"请审查以下Python代码,找出所有安全漏洞(SQL注入、XSS、命令注入、路径遍历、硬编码密钥等)。对每个漏洞,说明位置、风险等级(高/中/低)、攻击示例,并给出修复后的代码片段。
python# 待审查代码
实际案例 :
用户提供了一段拼接Shell命令的代码:
python
import os
def ping(host):
os.system(f"ping -c 1 {host}")
提示词审查后,AI输出:
- 漏洞 :命令注入。攻击者输入
host = "127.0.0.1; rm -rf /"会执行任意命令。 - 修复 :使用
subprocess.run参数列表,避免shell解析;或严格校验host为IP格式。
2. 逻辑错误与边界条件检查
模板:
"请分析以下函数的逻辑,列出可能的边界条件遗漏或逻辑错误。针对每种问题给出测试用例(输入和期望输出),并重写函数使其健壮。
python# 代码
案例:
python
def binary_search(arr, target):
left, right = 0, len(arr)
while left < right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid
return -1
AI审查发现:当arr为空时len(arr)=0,循环不执行返回-1正确;但若target小于所有元素,最终left=0, right=0返回-1正确;看似无问题。但更深入的审查可能指出:若数组未排序,结果不可靠。提示词可要求增加前置条件检查。
3. 性能优化检查
模板:
"以下代码可能存在性能问题,请分析时间/空间复杂度,指出瓶颈,并给出优化方案(例如使用缓存、生成器、更高效的数据结构)。
python# 代码
案例:
python
def find_duplicates(lst):
duplicates = []
for i in range(len(lst)):
for j in range(i+1, len(lst)):
if lst[i] == lst[j] and lst[i] not in duplicates:
duplicates.append(lst[i])
return duplicates
AI提示词修复:使用set记录已见元素,O(n)复杂度。
4. 可读性与重构
模板:
"请重构以下代码,使其符合PEP8规范,函数长度不超过20行,变量命名清晰,并添加必要的注释和类型注解。保持功能不变。
python# 代码
五、完整案例:从提示词生成到审查修复
需求
开发一个Python函数,从CSV文件读取销售数据,计算每个产品的总销售额,并返回按销售额降序排列的字典。
初始提示词(较差)
"写一个函数处理CSV销售数据。"
AI生成代码:
python
import csv
def process_sales(file):
with open(file) as f:
reader = csv.reader(f)
next(reader)
result = {}
for row in reader:
product = row[0]
amount = float(row[1])
if product in result:
result[product] += amount
else:
result[product] = amount
return dict(sorted(result.items(), key=lambda x: x[1], reverse=True))
人工审查发现的问题
- 无类型注解 ,参数
file含义不明。 - 未处理文件不存在 :
open会抛FileNotFoundError,未给出友好信息。 - CSV格式假设固定:假设第一列产品名、第二列金额,未校验列数或类型。
amount转换可能抛ValueError(如空字符串、非法字符),导致程序崩溃。- 排序正确,但若产品名为空也会被计入。
- 未使用
with? 实际使用了with open,但未指定编码,可能中文乱码。 - 性能尚可,但若文件极大,可用生成器处理。
改进提示词重新生成
"用Python写一个函数
calculate_sales_totals(csv_file_path: str, product_col: int = 0, amount_col: int = 1) -> dict。要求:
- 读取CSV文件,跳过标题行,从
product_col取产品名,从amount_col取销售额(可能为浮点数)。- 如果文件不存在,抛出
FileNotFoundError并给出明确消息。- 如果某行数据不完整或金额无法转换为float,跳过该行并记录警告(使用
logging.warning),不中断整个处理。- 返回字典,键为产品名(字符串),值为总销售额(float),按销售额降序排列。
- 添加类型注解、docstring。
- 使用
utf-8编码打开文件。- 给出至少一个单元测试示例,覆盖正常、文件缺失、金额非法三种情况。"
AI生成改进代码(片段):
python
import csv
import logging
from typing import Dict
def calculate_sales_totals(csv_file_path: str, product_col: int = 0, amount_col: int = 1) -> Dict[str, float]:
'''Read sales CSV and return {product: total_amount} sorted desc.'''
sales = {}
try:
with open(csv_file_path, 'r', encoding='utf-8') as f:
reader = csv.reader(f)
next(reader, None) # skip header
for row in reader:
if len(row) <= max(product_col, amount_col):
logging.warning(f"Skipping malformed row: {row}")
continue
product = row[product_col].strip()
if not product:
logging.warning(f"Skipping row with empty product: {row}")
continue
try:
amount = float(row[amount_col])
except (ValueError, IndexError):
logging.warning(f"Invalid amount in row: {row}")
continue
sales[product] = sales.get(product, 0.0) + amount
except FileNotFoundError:
raise FileNotFoundError(f"CSV file not found: {csv_file_path}")
return dict(sorted(sales.items(), key=lambda item: item[1], reverse=True))
再次用提示词进行安全检查
向AI发送:"请检查以上代码是否存在安全问题或资源泄漏,并给出改进建议。"
AI可能指出:使用logging.warning前未配置basicConfig,可能不输出;建议在模块级配置。此外,若CSV文件极大,一次性读入内存可优化为逐行处理(当前代码已逐行,无内存问题)。最终代码通过审查。