AI 编程的边界:哪些任务适合 AI,哪些必须人工把关
AI 编程的边界:哪些任务适合 AI,哪些必须人工把关

一、理解 AI 能力的"三区模型"
在使用 AI 编程工具近一年后,我逐渐形成了一个理解 AI 能力的心智模型——我称之为"三区模型"。
这个模型将 AI 能处理的任务分为三个区域:
📊 舒适区(Comfort Zone):AI 可以出色完成的、不需要人类过多干预的任务。在这个区域,AI 的效率和正确率都超过了人类。
📊 危险区(Danger Zone):AI 可以完成但看起来正确实际上有问题的任务。这个区域最危险——因为你可能被 AI 看似正确的输出误导,而忽略了潜藏的问题。
📊 禁区(No-Go Zone):绝不应该交给 AI 处理的任务。把这些任务交给 AI 可能导致安全漏洞、法律风险或严重的事故。
理解这三个区域不是一蹴而就的。我最初使用 AI 编程时,犯了不少错误——把危险区的任务当成了舒适区,或者在禁区踩了线。希望这篇文章能帮你少踩一些坑。
让我从最安全的"舒适区"开始说起。
二、舒适区详解——AI 的"拿手好戏"
舒适区的任务是 AI 在大量训练数据中反复见过、模式固定、变化少的任务。在这个区域,AI 的表现非常可靠。
2.1 样板代码(Boilerplate)生成
任何有经验的开发者都知道,项目中大量代码是"样板代码"——每次开新功能都要写的、结构几乎一样的代码。这些代码通常包括:
- RESTful API 的 CRUD 路由和控制器
- 数据库模型定义
- 表单组件的模板结构
- 项目配置文件(Webpack、Vite、ESLint等)
- Redux/Vuex 的 Store 模板
示例:生成 CRUD 控制器
Prompt:
"帮我生成一个 Express.js 的 CRUD 控制器,
模型是 Product(字段:id, name, price, category, stock, createdAt),
使用 Prisma ORM,
包含输入验证和错误处理,
使用 async/await,
所有响应统一格式为 { code, data, message }"
AI 输出:
// controllers/productController.js
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
// 统一响应格式
const response = (code, data, message = 'success') => ({ code, data, message });
// 获取所有产品(支持分页和筛选)
const getAllProducts = async (req, res) => {
try {
const { page = 1, pageSize = 20, category, minPrice, maxPrice } = req.query;
const where = {};
if (category) where.category = category;
if (minPrice || maxPrice) {
where.price = {};
if (minPrice) where.price.gte = Number(minPrice);
if (maxPrice) where.price.lte = Number(maxPrice);
}
const [products, total] = await Promise.all([
prisma.product.findMany({
where,
skip: (Number(page) - 1) * Number(pageSize),
take: Number(pageSize),
orderBy: { createdAt: 'desc' },
}),
prisma.product.count({ where }),
]);
res.json(response(200, {
list: products,
pagination: {
page: Number(page),
pageSize: Number(pageSize),
total,
totalPages: Math.ceil(total / Number(pageSize)),
},
}));
} catch (error) {
res.status(500).json(response(500, null, error.message));
}
};
// 获取单个产品
const getProductById = async (req, res) => {
try {
const { id } = req.params;
const product = await prisma.product.findUnique({ where: { id } });
if (!product) {
return res.status(404).json(response(404, null, '产品不存在'));
}
res.json(response(200, product));
} catch (error) {
res.status(500).json(response(500, null, error.message));
}
};
// 创建产品
const createProduct = async (req, res) => {
try {
const { name, price, category, stock } = req.body;
// 输入验证
const errors = [];
if (!name?.trim()) errors.push('产品名称不能为空');
if (!price || price <= 0) errors.push('价格必须大于0');
if (!category?.trim()) errors.push('产品类别不能为空');
if (errors.length > 0) {
return res.status(400).json(response(400, null, errors.join('; ')));
}
const product = await prisma.product.create({
data: {
name: name.trim(),
price: Number(price),
category: category.trim(),
stock: stock ? Number(stock) : 0,
},
});
res.status(201).json(response(201, product, '创建成功'));
} catch (error) {
res.status(500).json(response(500, null, error.message));
}
};
// 更新产品
const updateProduct = async (req, res) => {
try {
const { id } = req.params;
const { name, price, category, stock } = req.body;
const existing = await prisma.product.findUnique({ where: { id } });
if (!existing) {
return res.status(404).json(response(404, null, '产品不存在'));
}
const updateData = {};
if (name?.trim()) updateData.name = name.trim();
if (price !== undefined) {
if (price <= 0) return res.status(400).json(response(400, null, '价格必须大于0'));
updateData.price = Number(price);
}
if (category?.trim()) updateData.category = category.trim();
if (stock !== undefined) updateData.stock = Number(stock);
const product = await prisma.product.update({
where: { id },
data: updateData,
});
res.json(response(200, product, '更新成功'));
} catch (error) {
res.status(500).json(response(500, null, error.message));
}
};
// 删除产品
const deleteProduct = async (req, res) => {
try {
const { id } = req.params;
const existing = await prisma.product.findUnique({ where: { id } });
if (!existing) {
return res.status(404).json(response(404, null, '产品不存在'));
}
await prisma.product.delete({ where: { id } });
res.json(response(200, null, '删除成功'));
} catch (error) {
res.status(500).json(response(500, null, error.message));
}
};
module.exports = {
getAllProducts,
getProductById,
createProduct,
updateProduct,
deleteProduct,
};
这个控制器包含了一个产品 CRUD 操作的所有核心要素:分页查询、条件筛选、输入验证、错误处理、统一响应格式。AI 只用了不到10秒就生成了——而手写至少需要30分钟。
💡 效率提升高达90%:样板代码是 AI 舒适区中最"舒适"的部分。直接让 AI 生成,然后根据业务需求微调即可。
2.2 配置文件编写
现代前端项目的配置文件让人头大——Webpack、Vite、ESLint、Prettier、TypeScript、Tailwind CSS、PostCSS……每个工具都有自己的配置语法。
AI 非常擅长写这些配置,因为它们有固定的格式和大量的示例。你只需要描述你的需求:
Prompt:
"帮我写一个 Vite 的配置文件,用于 React + TypeScript 项目:
- 使用 @vitejs/plugin-react 插件
- 路径别名 @ 指向 ./src
- 开发服务器端口 3000
- 构建目标 es2020
- 代码分割:react、react-dom 单独打包
- CSS Modules 支持"
2.3 格式转换
需要把 JSON 转成 TypeScript 接口?把 Markdown 转成 HTML?把 CSV 转成 SQL INSERT 语句?这些是 AI 信手拈来的任务。
// 输入(JSON)
{
"user": {
"id": 1,
"name": "张三",
"roles": ["admin", "editor"]
}
}
// AI 输出(TypeScript Interface)
interface User {
id: number;
name: string;
roles: string[];
}
2.4 正则表达式
正则表达式是"写一次就忘"的语言。但 AI 对正则非常擅长:
开发者:"帮我写一个正则表达式,匹配中国大陆手机号"
AI:/^1[3-9]d{9}$/
开发者:"再写一个提取 URL 中所有查询参数的正则"
AI:/[?&](w+)=([^&s]+)/g
2.5 单元测试
单元测试是典型的"结构固定、内容变化"的任务——最适合 AI 来做:
Prompt:
"为上面的 calculateOrderTotal 函数生成 Jest 单元测试,
覆盖正常订单、含折扣券、空订单、边界值等场景"
AI 会生成一整套测试用例,包括正常路径、异常路径和边界条件。
2.6 文档与注释生成
为已有代码生成 JSDoc、README、API 文档——这些任务 AI 做得非常好:
Prompt:
"读取这个 utils.js 文件,为其中每个函数生成 JSDoc 注释"
三、危险区详解——看似正确,暗藏风险
危险区是我在使用 AI 编程中踩坑最多的地方。这些任务的特点是:AI 能完成它们,生成的代码看起来完全正确,甚至能通过编译和简单测试——但在特定条件下会暴露严重问题。
3.1 复杂业务逻辑
为什么危险
复杂业务逻辑的危险在于:AI 不了解你的业务规则全貌。它可能生成了逻辑正确的代码,但这个"正确"是基于通用情况,而不是你的特定业务场景。
具体案例:订单退款逻辑
开发者 Prompt:
"写一个电商订单退款的处理函数"
AI 可能生成的代码:
function processRefund(order) {
// AI 只考虑了简单的全额退款逻辑
return refundService.process(order.id, order.totalAmount);
}
实际业务需要考虑的复杂情况(AI 不知道的):
function processRefund(order, refundType, refundItems) {
// ① 是否在可退款时间窗口内(不同商品类别不同政策)
// ② 是否为部分退款(只退部分商品)
// ③ 已使用的优惠券如何处理(按比例退还?全额作废?)
// ④ 运费是否退还(包邮商品 vs 用户自付运费)
// ⑤ 已使用的积分如何处理
// ⑥ 退款方式限制(必须原路退回)
// ⑦ 与第三方支付平台的交互逻辑
// ⑧ 退款后是否需要触发其他流程(库存恢复、通知、对账)
}
💡 安全策略:面对复杂业务逻辑时:
- 不要让 AI 一次性生成全部代码——先和它讨论各种边界情况
- 先用自然语言和 AI 确认完整的业务规则,再让它写代码
- 关键业务逻辑必须写详细的单元测试
3.2 性能敏感的代码
为什么危险
AI 生成的代码在功能上可能是正确的,但它不了解你的性能上下文——数据量级、并发量、延迟要求。
具体案例:数据库查询优化
// AI 可能生成这样的代码(功能正确,但性能糟糕)
async function getUserReport(userId) {
const user = await db.user.findUnique({ where: { id: userId } });
// N+1 问题!
const orders = await db.order.findMany({ where: { userId } });
for (const order of orders) {
// 每个 order 都单独查一次数据库
order.items = await db.orderItem.findMany({ where: { orderId: order.id } });
for (const item of order.items) {
item.product = await db.product.findUnique({ where: { id: item.productId } });
}
}
return { user, orders };
}
// 正确的做法(用 JOIN 或 include 一次性加载)
async function getUserReport(userId) {
const user = await db.user.findUnique({
where: { id: userId },
include: {
orders: {
include: {
items: {
include: { product: true },
},
},
},
},
});
return user;
}
AI 的代码在数据量小的时候完全正常——测试数据库只有几十条数据,N+1 查询看不出任何问题。但上线后数据量增长到几十万条,1500次数据库查询直接把服务器拖垮。
⚠️ 安全策略:
- 对于涉及数据库查询的代码,始终检查是否有 N+1 问题
- 使用数据库查询分析工具(如 Prisma 的日志、MySQL 慢查询日志)
- 在代码审查时特别关注数据访问层的性能
3.3 安全相关代码
为什么危险
AI 的训练数据来自公开代码仓库,而公开代码仓库中有大量不安全的代码。AI 学到了"常见的写法",但"常见的写法"不一定安全。
具体案例:SQL 查询
// AI 可能生成的代码(SQL 注入风险)
router.get('/api/users', async (req, res) => {
const { username } = req.query;
// 直接拼接用户输入到 SQL 查询中!
const users = await db.raw(`SELECT * FROM users WHERE username = '${username}'`);
res.json(users);
});
// 安全的方式(使用参数化查询)
router.get('/api/users', async (req, res) => {
const { username } = req.query;
const users = await db.raw('SELECT * FROM users WHERE username = ?', [username]);
// 或者使用 ORM 的安全方法
const users = await db.user.findMany({ where: { username } });
res.json(users);
});
具体案例:认证逻辑
// AI 可能生成的代码(认证绕过风险)
function isAuthenticated(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ message: '未提供认证令牌' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
// ⚠️ 问题:捕获所有异常并将它们都视为"令牌过期"
// 实际上可能是"密钥不匹配"、"算法不同"等安全问题
res.status(401).json({ message: '令牌已过期,请重新登录' });
}
}
// 更安全的做法(区分不同类型的JWT错误)
function isAuthenticated(req, res, next) {
// ... 省略前面的代码
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256'], // 指定算法,防止算法混淆攻击
});
req.user = decoded;
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
return res.status(401).json({ message: '令牌已过期' });
}
if (error.name === 'JsonWebTokenError') {
return res.status(401).json({ message: '无效的认证令牌' });
}
return res.status(500).json({ message: '认证服务异常' });
}
}
⚠️ 安全策略:
- 对 AI 生成的安全相关代码(认证、授权、加密、输入验证)进行额外的安全审查
- 使用安全扫描工具(如 npm audit, Snyk, SonarQube)做二次验证
- 参考 OWASP Top 10 检查 AI 生成的 Web 代码
- 敏感操作(密码处理、Token 生成、密钥管理)最好由安全专家编写或审查
四、禁区详解——绝对不能交给 AI 的任务
有些任务,无论 AI 看起来多么"懂",都不应该交给它。
4.1 涉密数据与密钥管理
⚠️ 红线:绝对不要把生产环境的密钥、密码、证书或任何敏感数据透露给公有 AI 服务。
❌ 绝对不要做的事:
Prompt: "这是我的数据库连接字符串,帮我写个连接池配置:
mongodb+srv://admin:MyP@ssw0rd123@prod-cluster.xxxxx.mongodb.net/myapp"
Prompt: "这是我的 AWS Access Key,帮我写 S3 上传代码:
AKIAIOSFODNN7EXAMPLE / wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
Prompt: "我们公司的内部 API 文档如下:[粘贴完整的内部文档]"
为什么这是禁区?你的对话内容可能会被用于模型训练。虽然 OpenAI 和 Anthropic 都有 API 数据使用政策(API 调用的数据不会用于训练),但 ChatGPT 网页版的对话可能会。而且,即使服务商承诺不使用数据,你的 Prompt 也经过了他们的服务器——这是在增加不要的攻击面。
💡 安全做法:
- 使用环境变量而非在代码中硬编码密钥
- 在粘贴给 AI 的代码中用占位符替换敏感信息(如
REDACTED_API_KEY) - 敏感配置使用专门的密钥管理服务(如 AWS Secrets Manager、HashiCorp Vault)
- 企业环境使用私有部署的 AI 服务
4.2 合规关键代码
如果你的项目需要满足特定的合规要求(如 HIPAA(医疗)、PCI-DSS(支付)、SOC2(安全审计)),让 AI 生成合规关键代码是有法律风险的。合规不仅是"代码做什么",还包括"谁写的代码"、“谁审查了代码”、“代码变更的完整记录”。
💡 建议做法:
- 合规关键代码由有资质的开发者编写和审查
- AI 生成的代码可以作为参考,但必须有完整的人工审查记录
- 确保代码审查流程满足合规要求
4.3 关键基础设施配置
不要把生产环境的 Kubernetes 配置、数据库迁移脚本、防火墙规则交给 AI 直接部署。这类配置的一个小错误可能影响整个系统的可用性。
⚠️ 安全做法:AI 可以帮你生成草案,但这个草案必须经过:
- 人工逐行审查
- 在 Staging 环境验证
- 至少两名工程师的 Code Review
- 回滚方案的确认
五、判断框架:六大评估问题
基于我的实践经验,在决定是否把一个任务交给 AI 之前,可以问自己六个问题:
问题一:这个任务的"正确输出"是否明确可验证?
- ✅ 明确可验证:生成正则表达式、格式化代码、写单元测试
- ❌ 难以验证:复杂的架构决策、安全策略设计
问题二:任务失败(AI 出错)的后果是什么?
- ✅ 低风险:代码风格不完美、注释有误、需要小幅修改
- ❌ 高风险:数据丢失、安全漏洞、合规违规
问题三:这个任务需要多少"上下文"?
- ✅ 局部上下文:单个函数、单个文件
- ❌ 全局上下文:跨多个子系统、需要理解业务全貌
问题四:AI 在这个领域有多少训练数据?
- ✅ 训练数据丰富:流行框架(React、Express.js)的常见用法
- ❌ 训练数据稀少:小众框架、企业内部库、最新的 API
问题五:这个任务是否涉及"判断"和"权衡"?
- ✅ 纯执行性:将已知的要求翻译为代码
- ❌ 需要权衡:在多个可行方案中做选择
问题六:我是否有能力验证 AI 的输出?
- ✅ 能独立验证:我熟悉这个领域,能判断 AI 输出的质量
- ❌ 无力验证:我完全不懂这个领域,只能"信任" AI
📊 快速评估矩阵:
| 场景 | Q1 | Q2 | Q3 | Q4 | Q5 | Q6 | 评估 |
|---|---|---|---|---|---|---|---|
| 写 CRUD 接口 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 安全,放心用 |
| 写 SQL 查询 | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | 可以用,但要审查 |
| 设计数据库 Schema | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ | 需要深度参与 |
| 写认证中间件 | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ | 参考 AI 输出,自己做决策 |
| 配置 CI/CD | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | 先学习基础,再使用 AI |
六、人机协作的质量保障体系
既然有些区域是危险的,我们需要一套系统来安全地使用 AI。
6.1 “Trust but Verify” 原则
对于 AI 生成的每一段代码,默认态度应该是:信任但验证。就像你审查一个初级开发者的代码一样——你不会直接拒绝,但你会仔细检查。
6.2 分层审查策略
第一层(AI 生成后,立即):
快速扫描——检查明显的错误、不安全的 API、敏感信息泄露
第二层(提交前,自己审查):
逻辑审查——理解代码逻辑,确认符合业务需求
安全审查——检查 OWASP Top 10 相关漏洞
性能审查——检查明显的性能问题(N+1 查询等)
第三层(PR 阶段,同事审查):
团队 Code Review——另一个开发者审查代码
自动化检查——Lint、Test、Security Scan、覆盖率
第四层(部署前):
Staging 环境验证——在类生产环境测试
性能测试——压力测试和负载测试
安全扫描——自动化安全扫描工具
6.3 团队规范建议
如果你们团队在使用 AI 编程工具,建议建立以下规范:
1. 代码注释标记
// @ai-generated: 此函数由 AI 生成,已通过人工审查
// 方便后续代码审查者了解代码来源
2. AI 使用日志
记录哪些代码使用 AI 生成,以及审查结果
3. 安全红线清单
列出绝对不能交给 AI 的任务类型
4. 审查清单
AI 生成代码的特定审查项
七、总结
理解 AI 编程的边界不是要限制你的使用,而是让你用得更安全、更高效。当你清楚地知道什么可以交给 AI、什么必须自己把关时,你就真正掌握了 AI 辅助编程的精髓。
核心要点回顾:
- ✅ 舒适区放心用:样板代码、配置文件、格式转换、正则、测试、文档
- ⚠️ 危险区谨慎用:复杂业务逻辑、性能敏感代码、安全相关代码
- ❌ 禁区绝不碰:涉密数据、合规代码、生产基础设施直部署
- 💡 用好"六大评估问题"做快速判断
- 💡 建立分层审查体系,将 AI 视为需要 Code Review 的"初级开发者"
下一篇文章,我们将探讨 Token 与上下文窗口——理解 AI 的"记忆力"和"视野",这是写好 Prompt 的基础。
下一篇:Token 与上下文窗口:理解 AI 模型的「记忆」与限制