我点了“推荐”,AI 智能体就把代码推上了生产环境
一次谨慎迁移之前,智能体在五天内引发了两次生产事故。其中一次,是我点了它推荐的推送选项。这篇文章回看我的原始指令、批准时漏问的问题,以及后来调整的七条协作规则。
后来那场持续一周、按计划执行的迁移,起点是五天里的两次生产事故。第一次,我要求先上预发布环境;智能体说需要先推送 main,保证不会影响正式网站,并把这个选项标为“推荐”。我点了。这个保证是错的,新构建在生产环境运行了约 22 小时。
第二次,我批准了一个“只读”的生产数据快照。读取耗尽了数据库当月的请求额度,页面开始返回 503,订阅源却悄悄退回旧副本,继续返回 200。
问题也出在我的批准方式上:我批准了操作,也一并接受了智能体对安全性的判断。下面保留当时的英文指令和拼写错误,逐条看哪些要求有效,哪些边界没有说清。

网站和这次改动
我的网站 williamliu.ai 部署在 Vercel,播客音频放在 Cloudflare R2,Upstash 保存一份小型索引。智能体已经在本地完成了 40 个提交,将媒体文件改成包含内容哈希的文件名。本地测试全部通过,但这批改动还没有接触过真实环境。
第一次事故:预发布之前,先推了生产分支
我说:
"push to staging first"
先验证再上线,这个方向没错。但仓库里的预发布脚本要求提交已经在 main 上,理由是要验证“Vercel 实际构建的内容”。另有一条按精确提交创建预发布部署的路径,却拒绝了我当时的工作副本。智能体于是让我选择,并把下面这一项标为 Recommended:
Push main, then deploy:staged. "Creates a Vercel deployment record but does NOT touch www.williamliu.ai — aliases only move on
vercel alias set/deploy:promote."
我点了,智能体执行了推送。Vercel 的 Git 集成随即构建该提交;项目又开启了自动分配自定义域名,因此新部署直接接管了 www 和根域名。智能体的安全判断来自它自己的部署文档:对 CLI 部署路径成立,对 Git 推送路径不成立。

第二天晚上,智能体才发现域名已经切换,承认:“I was wrong, and production changed.” 接下来的影响报告先说生产环境坏了,再说 RSS 订阅源坏了,最后说有 137 个播客下载返回 404。我不断追问:
"stop, which production is broken"
"the webs ite is not boroken, the RSS feed is broken. is that right"
"I can still play audio on the website"
每问一次,影响范围就缩小一次。那 137 个失败,是拿本地构建将要生成的 URL 去请求生产环境得到的。它们证明生产环境无法提供新文件名对应的文件,却不能证明订阅者真的拿到了这些 URL:线上订阅源里仍然是旧地址。智能体连续三次夸大了影响。我们随后回滚到了上一个部署。
第二次事故:“只读”也耗请求额度
四天后,智能体同时提出两个操作,我点了 "approve both A and B":
- A:锁住生产部署。 将冻结的
release分支设为生产分支,关闭自动分配域名。 - B:对生产数据存储做一次只读快照。
A 正是需要的修复。B 扫描了约 10,000 个键,每个键发一条命令。当时数据库使用免费套餐,每月上限为 500,000 次请求,正常流量已让用量接近上限。这轮扫描在三分钟内耗尽了剩余额度,运行时页面开始返回 503。
更难察觉的是,播客订阅源仍然返回 200。它们自动退回了旧构建附带的副本,只有 17 期,而不是 28 期。HTTP 200 只能说明请求得到了成功响应,不能说明内容正常。 第二天早上,我升级了数据库套餐。
回看我的指令

"push to staging first":方向对,条件不完整。 我说了目标,却没说边界。发现预发布需要先经过生产环境后,智能体就把生产环境当成了必经之路。更完整的要求是:"stage it. If staging requires touching production in any way, stop and ask me."
点选 "Push main, then deploy:staged (Recommended)":判断错了。 我批准操作时,也接受了智能体未经验证的“这个操作安全”的说法。只要追问一句 "What here is irreversible, and how do you know it isn't?",证据不足就会显现。当时诚实的回答只能是:“我在文档里读到的。”
"stop, which production is broken" / "the webs ite is not boroken, the RSS feed is broken. is that right":问得对。 智能体遇到警报时会夸大影响。要求它只陈述已经测到的影响,才把“生产环境宕了”收窄为一个具体、范围小得多的结论。
"this is a deeper issue, why we break production directly. find the learning and make architectual changes to ensure it does not happen in the future":问到了结构问题。 预发布在流程上排在生产部署之后;所有防护都在 CLI 路径,Git 推送路径却没有。这需要改流程,不能只修当次症状。
"ask codex using astra with reasoning high to review the incident and compare its finding with claude's":这是那一周最有价值的指令。 另一家厂商的模型先独立审查事故复盘,再对照结论,纠正了三处夸大:订阅者受到的影响、“映射从未创建”的判断,以及“执行一次 vercel alias ls 就能发现问题”的教训。
最后一条在逻辑上不成立。列出别名,只能看到域名现在指向哪里,不能说明下一次推送会做什么。要证明未来某次状态变化是安全的,就得在安全环境里实际触发它。
点选 "approve both A and B":只对了一半。 A 解决了部署路径的问题。批准 B 之前还缺一句:"how many requests will this make, and how much quota is left?"
"be critical of what you need to build focus on the crirical changes" / "prioritze fixjng bugs first before making more feature changes":优先级对。 事故刚发生时,继续往仓库里加功能,是最容易扩大风险的事。
"I'm suspecting that you didn't consider a migration plan from the previous file name format to the new file name format with the sha":大体判断对了。 调查发现,纸面上的计划确实存在:先纳入旧记录,再创建新记录,最后切换。但计划把 git push 排在数据迁移之前,步骤只有文字说明,也从未演练。我的怀疑抓住了实际问题,随后才有了开启后面三篇文章的指令:"carefuly execute the migration plan, verify and deploy."
后来我怎样组织智能体工作
1. 把防护放在操作入口。 没有任何一条智能体指令,能保护一条根本不读取指令的执行路径。我们将生产分支改为 release,关闭自动分配域名。这样,推送 main 就不能直接影响用户,必须显式切换别名才行。
2. 核对“推荐”背后的证据。 批准涉及生产环境的操作之前,让智能体区分哪些已验证、哪些只是推断。我们现在会给根因分析中的每条陈述加上标记:VERIFIED 表示有实际观测支持,INFERRED 表示文档推论或假设。
3. 在指令里写明何时停下。 “做 X”会让智能体寻找任何能完成 X 的路径。“做 X;如果必须经过 Y,就停下来问我”,才限定了我接受的路径。
4. 先准备数据,再切换代码,并让程序检查前置条件。 只写在计划文档里的前置条件,可能被一次不看计划的推送跳过。这次迁移把每个前置条件都变成了执行器必须通过的检查。
5. 事故复盘也要审查。 那一周,另一家厂商模型独立审查复盘时,找到的夸大陈述比任何一次代码审查都多。
6. 批量读取生产数据之前,先算预算。 只读仍会消耗请求额度。免费套餐的上限,足以让一次数据清点变成事故。先核对预计请求数和剩余额度。
7. 按阶段分配角色。 规划阶段,我让不同厂商的两个强模型分别写计划、审计划,都使用较高推理强度;实现阶段,一个写,另一个审。当时的原话是:"in the planning phase you shall use gpt-6-astra with high effort and Opus 5.5 high effort."
两次事故都始于智能体依据一个没有经过验证的判断,自信地采取行动。我批准操作时,也批准了这个判断。后来的调整,是让判断有证据可查,并把防护设在无法绕过的执行入口。
下一篇:更多智能体,同一个盲点。迁移计划有了,为什么审查仍然没发现真实平台的限制?