Vibe Coding 一时爽,线上事故火葬场:我的三次 AI 编程翻车实录
本文参与掘金活动:谁还没被AI坑过
前言
"Vibe Coding" 这个词,相信各位开发者都不陌生。自从 Andrej Karpathy 提出这个概念以来,无数开发者(包括我)开始沉迷于"让 AI 写代码,我只负责描述需求"的快感中。
说实话,第一次用 Cursor 让它帮我写完一个完整的 CRUD 接口时,我整个人都飘了——"以后写代码就是动动嘴皮子的事儿"。
那段时间我几乎魔怔了,逢人就安利:"你试试 Cursor,真的,写代码效率提升 10 倍不是梦!"甚至在团队周会上,我还专门做了一次分享,标题就叫《Vibe Coding:后端开发的未来》。
然而,现实很快给了我三记响亮的耳光。
今天不聊理论,直接分享我在真实项目中被 AI 坑得最惨的三次经历,希望能给各位提个醒。
第一坑:AI 帮我写的"优雅"代码,把数据库锁了 40 分钟
事故现场
那是一个再普通不过的需求:批量更新用户状态。我用 Cursor 描述了一下需求:
"帮我写一个批量更新用户状态的接口,要优雅一点"
AI 给出的代码看起来非常"优雅":
async def batch_update_user_status(user_ids: list, new_status: str):
"""批量更新用户状态"""
async with get_db_session() as session:
# AI 觉得这样写很 "Pythonic"
users = await session.execute(
select(User).where(User.id.in_(user_ids)).with_for_update()
)
for user in users.scalars():
user.status = new_status
user.updated_at = datetime.now()
await asyncio.sleep(0.01) # AI 说这是 "模拟真实业务延迟"
await session.commit()
看着挺对的吧?with_for_update() 加行锁,逐个更新,最后提交。代码风格优雅,注释清晰,甚至还有类型标注。我当时还觉得 AI 真贴心,连异步处理都考虑到了。
问题在哪?
当 user_ids 有 5000 个的时候,这段代码会:
- 对 5000 行加排他锁
- 循环中
await asyncio.sleep(0.01)让整个事务持续 50 秒 - 锁持有期间,所有涉及这些用户的查询全部阻塞
上线当天下午,告警群炸了——数据库连接池耗尽,大量请求超时。DBA 同事冲过来问我:"你那个批量更新接口怎么回事?数据库里全是锁等待!"
我当时还一脸懵:"不可能啊,代码是 AI 帮我写的,很优雅的..."
DBA 同事看了一眼代码,沉默了三秒钟,然后说了一句让我至今难忘的话:"AI 写的代码,你不 review 的吗?"
修复过程
# ❌ AI 给的代码:逐行更新,锁持有时间长
for user in users:
user.status = new_status
await asyncio.sleep(0.01) # 无意义的延迟
# ✅ 正确做法:批量操作,减少锁持有时间
async def batch_update_user_status(user_ids: list, new_status: str):
async with get_db_session() as session:
await session.execute(
update(User)
.where(User.id.in_(user_ids))
.values(status=new_status, updated_at=datetime.now())
)
await session.commit()
教训:AI 不会帮你考虑并发和性能,它只会写出"看起来对"的代码。批量操作的边界条件、锁策略、性能影响,这些都得你自己把关。
第二坑:RAG 检索出来的"正确"答案,让客服回复了错误信息
事故现场
我们做了一个基于 RAG 的客服助手,用 LangChain + Milvus 搭建。测试阶段效果惊艳,领导拍板上线。
技术方案看起来很完美:
- 文档切片 → 向量化 → 存入 Milvus
- 用户提问 → 向量检索 → 取 Top 5 相关文档 → 送入 LLM 生成回答
上线第一周,一切正常。直到有个用户问:"你们的退货政策是什么?"
AI 回复:
"根据我们的政策,商品签收后 90 天内 可以无理由退货,运费由买家承担。"
实际政策是 7 天。
用户拿着这个回复截图发了条微博,阅读量 10 万+,标题是《XX 公司客服 AI 竟然胡说八道,90 天退货政策是真是假?》。
公关部门连夜加班处理,我则被叫到会议室"喝茶"。
问题在哪?
排查后发现,知识库里有两份文档:
- 一份是 2024 年的退货政策(7 天)
- 一份是某个竞品的分析报告,提到了"某平台 90 天退货政策"
向量检索时,"退货政策" 和竞品分析报告的相似度竟然更高(0.87 vs 0.82),因为那份报告里"退货政策"这个词出现的频率更高。
更坑的是,那份竞品分析报告的标题是《行业标杆:XX 平台 90 天退货政策深度解析》,里面的措辞非常肯定,AI 自然就"信以为真"了。
AI 忠实地执行了 RAG 的指令:用检索到的内容回答问题。它不知道这份文档是竞品的,也不知道 90 天和 7 天在业务上有多大的区别。
修复方案
# 不是代码的问题,是架构设计的问题
# 需要增加:
# 1. 文档来源标注
# 2. 置信度阈值
# 3. 多路召回 + 交叉验证
# 4. 关键信息的人工审核机制
retriever = VectorRetriever(
score_threshold=0.85, # 置信度阈值
top_k=5,
reranker=CohereReranker() # 二次排序
)
# 关键信息必须标注来源
response = llm.invoke(
f"基于以下参考资料回答问题,如果不确定请说'我不确定'。\n"
f"注意:请优先参考公司官方文档,而非第三方分析报告。\n"
f"参考资料:{context}\n"
f"用户问题:{query}"
)
教训:RAG 不是万能药。向量相似度不等于语义正确性。关键业务场景必须有兜底机制,不能让 AI 自由发挥。
第三坑:AI 生成的正则表达式,匹配了所有人的手机号
事故现场
这是一个看起来最简单的需求:验证用户输入的手机号格式。我让 Copilot 帮我写正则:
import re
# AI 给出的正则
PHONE_PATTERN = r'^1[3-9]\d{9}$'
def validate_phone(phone: str) -> bool:
return bool(re.match(PHONE_PATTERN, phone))
看起来没问题对吧?标准的中国大陆手机号正则。我当时还特意测试了几个号码,都能正确匹配,就直接提交了。
问题在哪?
上线后,运营反馈:"为什么所有用户的手机号都能通过验证?"
我一看日志,发现用户输入的是各种格式:
138-1234-5678(带横杠)+86 13812345678(带国际区号)138 1234 5678(带空格)(010)12345678(带括号的座机)13812345678@qq.com(把邮箱当手机号填了)12345678901(随便填的 11 位数字)
AI 的正则 ^1[3-9]\d{9}$ 只能匹配纯数字格式。但我没告诉它要支持哪些格式,它就按最标准的来了。
更坑的是,前端那边用的是另一个正则,两边不一致导致数据对不上。前端以为验证通过了就直接提交,后端以为格式不对就静默失败,用户则以为自己填对了。
最后排查了两天,才发现是正则不一致的问题。
修复方案
# ✅ 正确做法:先清洗再验证
import re
def normalize_phone(phone: str) -> str:
"""去除所有非数字字符"""
digits = re.sub(r'\D', '', phone)
# 处理国际区号
if digits.startswith('86'):
digits = digits[2:]
return digits
def validate_phone(phone: str) -> tuple[bool, str]:
"""验证手机号,返回 (是否有效, 标准化后的号码)"""
normalized = normalize_phone(phone)
if not re.match(r'^1[3-9]\d{9}$', normalized):
return False, ""
return True, normalized
教训:AI 写的正则只能处理"理想输入"。真实世界的用户输入千奇百怪,你必须考虑数据清洗和边界情况。而且,前后端的校验规则必须统一定义,不能各写各的。
反思:Vibe Coding 的正确姿势
被坑了三次之后,我总结出了一套"防坑指南":
1. AI 是副驾驶,不是主驾驶
AI 擅长: 你不该指望 AI:
✅ 快速生成代码骨架 ❌ 考虑并发和性能
✅ 处理标准化的 CRUD ❌ 理解业务上下文
✅ 写单元测试 ❌ 保证数据一致性
✅ 解释报错信息 ❌ 预测用户行为
✅ 生成文档和注释 ❌ 处理安全边界
2. 建立"三审"机制
# 我现在的代码审查流程
review_checklist = {
"功能正确性": "AI 生成的代码逻辑对不对?",
"边界条件": "空值、超大输入、并发场景?",
"安全风险": "SQL 注入、XSS、敏感信息泄露?",
"性能影响": "时间复杂度、内存占用、数据库查询次数?",
"可维护性": "代码可读性、错误处理、日志记录?"
}
3. 让 AI 帮你测试,而不只是写代码
# 我现在会让 AI 生成测试用例,然后用测试来验证它写的代码
prompt = """
请为以下函数生成单元测试用例,覆盖:
1. 正常输入
2. 边界条件(空值、极大值、极小值)
3. 异常输入(类型错误、格式错误)
4. 并发场景(如果适用)
函数代码:{code}
"""
4. 关键业务逻辑,AI 写完必须人工 review
我现在的工作流是这样的:
- 用 AI 生成代码骨架
- 人工 review 核心逻辑
- AI 生成测试用例
- 跑测试,发现问题再迭代
- 上线前做 code review
虽然比"纯 Vibe Coding"慢了一点,但至少不会翻车。
写在最后
Vibe Coding 确实很爽,但它就像自动驾驶——你可以让它帮你处理大部分场景,但你必须随时准备接管方向盘。
被 AI 坑不可怕,可怕的是被坑了还不知道为什么。每一次翻车都都是一次学习机会,关键是你要搞清楚:是你的 prompt 写得不够好,还是 AI 本身就处理不了这种场景?
如果你也有被 AI 坑过的经历,欢迎在评论区分享,让我们一起避坑!
💡 本文所有代码示例均为简化版本,实际项目中需要根据具体场景调整。
📌 参考资料: