资讯动态

VibeCoding生成代码酿事故:云账单一夜暴涨,循环重试成烧钱元凶

发布时间:2026/10/22 11:12:49 来源:尧图企业网站定制
1. 深夜一条账单异常提醒我以为是平台误报凌晨一点多我正在卫生间刷牙准备睡觉手机突然连着震了好几下。某云厂商的消费提醒一条接一条弹出来虽然是半夜数字却让我瞬间清醒单日账单折合人民币七万多也就是差不多一万美金。我的第一反应是账号被盗了。因为我的项目平时一天消耗不超过几十块钱这突然冒出来的五位数账单怎么想都像是有人拿我的密钥在跑矿机。但我打开控制台的消费明细之后发现不是被盗。账单里占比最大的几项都指向我自己部署的一个数据同步服务而那个服务是我前几天用 VibeCoding 快速攒出来的。所谓 VibeCoding简单来说就是让 AI 大模型在很高的抽象层级上辅助你写代码你用自然语言描述需求和思路模型负责补全逻辑你大部分时候只做方向性指点并不逐行细看生成结果。我原本觉得这个项目又小又简单应该十拿九稳结果它就在我眼皮子底下空烧了一整夜的经费。这篇文章不打算卖惨也不打算劝退 AI 辅助编程。我想把整个事故从头到尾拆一遍代码是怎么被 VibeCoding 写出来的Bug 是怎么一步步把账单撑爆的我又是怎么在凌晨通过日志和账单数据把它定位出来的。最后会附上我现在对 AI 生成代码的审查清单和资源护栏配置。如果你也在用 AI 辅助写代码尤其是涉及按量计费、云资源的项目这篇内容应该对你有用。先交代一下背景。我手头有个数据迁移需求把某个旧系统中的数据持续同步到一个新的数据仓库里。因为迁移期间业务还在不断产生新数据所以不能做一次性拷贝得做一个常驻的同步进程定时从旧系统读取增量数据写入新系统。功能本身不复杂我就走了 VibeCoding 的快捷路线让模型帮忙生成了整个同步脚本。脚本里包含一个主循环、一个重试机制、还有一套日志记录部署到服务器之后就自动跑起来了。前面几天一切正常直到那次源端接口发生故障。2. 现场还原这个 Bug 是怎么一步步把账单点着的2.1 我让 AI 写的到底是个什么东西当时我给出的需求描述大概是这样的写一个常驻运行的 Python 同步脚本从旧系统的分页接口读取数据每页 100 条写入新系统的批量导入接口如果某个批次失败就自动重试不能因为单条数据问题中断整个同步流程。同时需要记录每次同步的日志方便排查问题。听起来是不是很正常模型也确实给我生成了一段逻辑完整的代码。核心结构简化以后大概长这样def sync_loop(): cursor 0 while True: rows fetch_from_source(cursor, page_size100) if not rows: # 没有更多数据时等 60 秒再继续轮询 time.sleep(60) continue for row in rows: ok write_to_target(row) if not ok: retry_write(row) cursor len(rows)从局部看这个循环没什么毛病。有数据就处理没数据就等一会再查处理失败了还有重试函数兜底。当时我大概扫了一眼看到它有 sleep、有重试、有日志就觉得差不多可以上线了。问题出在模型对这个“没有数据”的分支处理得太理想了。同步逻辑真正跑起来之后等到旧系统的分页接口因为后端数据库连接数打满而返回异常时fetch_from_source 这个函数并没有往外抛异常而是被模型包了一层容错异常被吞掉后返回空列表。于是主循环看到了空 rows就认为当前没有待同步的数据老老实实 sleep 60 秒然后继续循环。对我这个业务来说这是一个致命误判。旧系统接口故障的这几分钟里同步进程一直在空转每过 60 秒去请求一次连续失败连续判定为“没有新数据”然后继续睡继续请求。单看一次请求什么都查不出来就让它顺着这个思路重复执行。这就是“无限重试空转”的典型形态。2.2 真正烧钱的其实是账单侧的蝴蝶效应如果只是空转请求其实烧不了太多钱。旧系统的分页接口是内网服务不会直接按调用次数计费。但那台云服务器是按 CPU 和网络流量计费的循环空转本身消耗的资源和正常同步差不多一夜顶多也就几十上百块。真正的成本炸弹在入口和它触发的外部服务上。我当时的同步架构里给每条数据的写入做了回调验证而这个验证逻辑调用了第三方服务的按次计费接口每成功一次都会产生费用。模型在生成重试逻辑的时候把“重试”设计成了指数退避和线性重试的混合逻辑是某条数据写入失败后推入重试队列每轮循环重试一次直到成功为止。由于我要求“不能中断整个同步流程”模型将单个任务的失败和整个循环的继续运行完全解耦了这意味着某个数据写入持续失败时外层的 while 循环不会停重试队列也不会停。全过程是这样的源端接口故障导致读取到一批脏数据或空数据写入目标端时持续失败失败的数据被推进重试队列外层循环继续从源端拉取新数据新数据也可能带着同样的错误特征每个重试任务都会调用一次按量计费的第三方校验接口校验接口每次失败还会返回错误码触发下一次重试。到源端故障恢复之前这个循环已经用完全错误的中间状态反复执行了一个多小时按次计费接口的调用量从平时每天的几百次暴涨到每小时几十万次。除了接口费用日志也在偷偷往上加码。模型生成的日志逻辑会为每一次重试输出完整的请求参数和响应体。正常情况下这没什么但重试量大之后日志服务按写入量计费的那部分代价也线性膨胀。我后来去翻那天的日志文件大小发现同步服务自己的日志比过去一周加起来都大里面全是同一条数据的重试记录。所以这场事故并不是一个单点 Bug而是三个环节叠加的结果烧钱环节平时状态Bug 期间的变化云服务器资源常驻运行低占有率空转请求导致网络和 CPU 曲线异常拉高按次计费的第三方接口每天几百次调用每轮重试都调用达到每小时几十万次日志写入与存储小流量重试日志无限增长按写入量和存储量双重计费单看任何一个环节都不会造成万美金级别的账单但三者叠加就会在短短几个小时里把账户余额烧穿。3. 根因拆解不是一个简单的“死循环”而是一组错误的组合这个事故最迷惑人的地方在于单看代码它不像一个典型的死循环——代码里有 sleep有重试保护有日志看起来考虑得挺周全。但程序真正的运行结果却和一个死循环完全一致。来逐步还原一下问题链条。3.1 第一层AI 把“断连”和“空数据”混为一谈在任何同步系统里“没有新数据”和“数据源出了故障”都应该是两种完全不同的状态。没有新数据是需要等待的温情况数据源故障是需要报警和熔断的异常情况。但在我当时的自然语言描述里我把它们统称为“如果没有数据就等一下再试”。模型忠实地实现了我的描述把这两种情况合并成了一个分支任何拿不到数据的情况都 sleep 然后重试。这件事给我的教训是我不怪模型误解而是怪我根本没有在需求里区分这两个状态的语义。VibeCoding 的工作方式本来就容易造成这种语义压缩。你在对话里用一句话描述需求模型就会把它理解为一种统一逻辑而不会自行判断哪些情况应该走熔断。真正的解决办法是在需求描述里显式地拆分业务状态# 期望的逻辑形态 if not rows: if source_healthy(): sleep(60) continue else: raise SourceUnavailableError(source down, need alert)3.2 第二层重试逻辑没有设置总预算和时间上限“重试”是程序里最常见、也最容易被滥用的一种保护机制。它的本意是处理临时抖动但如果没有重试次数上限和总时间上限就会变成把错误不断重复无限次。我后来检查模型生成的重试函数发现它确实有一个最大重试次数的参数但那个参数初始值是 10而且模型把每次重试的间隔设置得很短。问题是每一次重试都涉及按次计费的外部调用那么 10 次重试就等于 10 次费用。这个服务同时处理几千个批次时每一批都重复 10 次总费用就完全失控了。合理的重试设计应该是这样的单条数据最多重试 3 次超过后进入死信队列整个同步进程每分钟最多处理 N 个重试任务防止突发流量冲垮外部接口设置半开熔断连续失败超过某个阈值直接暂停重试 5 分钟而不是继续高频尝试。我当时没有在需求中提这些要求模型自然也就没有实现。3.3 第三层日志记录把错误放大了十倍另外还有一个隐藏的帮凶就是日志系统。模型为每条失败记录生成了多行结构化日志看起来很便于排查。但当失败数量从每小时几十条暴涨到几十万条时日志管道的写入本身会拖慢主循环导致更多超时超时又触发更多重试。这里还体现了一个 VibeCoding 场景下的典型问题AI 生成代码时会倾向于让每个环节都“无脑健壮”每个函数都加异常捕获每个异常都打日志每个失败都尝试重试。这种冗余策略在正常流量下问题不大一旦遇到故障多个冗余机制会互相增强形成正反馈循环最终把资源耗尽。我甚至怀疑如果那天晚上没有日志不断写入磁盘进程可能早因为内存压力被系统杀掉了账单反而不会那么夸张。日志保护了进程的存活同时也让燃烧持续了更久。这算是整个事故里最讽刺的一点。4. 凌晨两点的排查链路从账单到日志再到代码找到 Bug 的过程比我想象中高效因为账单数据本身就是最高效的监控指标。这里分享一下我当时完整的排查顺序。4.1 第一步别急着去读代码先看账单构成遇到线上故障尤其是费用突增这种问题人的第一反应常常是“我要找到那段写错的代码”。但代码范围很大而且很多时候代码从局部看没问题你盯着它看半小时也看不出哪里会烧钱。更好的做法是先看账单把资源消耗大头按顺序列出来再倒推代码路径。我当时打开云厂商控制台的费用中心按产品和服务维度看了消费 TOP 10。排在第一位的是“API 调用次数”第二是“日志服务”第三才是“云服务器”。这意味着问题一定出在 API 调用频率上而不是 CPU 或者带宽。然后把消费曲线切到小时维度能看到一段非常清晰的锯齿状波形每隔一分钟左右就会出现一个小高峰持续不断。这种固定间隔的规律几乎是同步重试循环的典型特征。4.2 第二步用日志验证时间线确定了“频率异常”这个方向之后我再切到应用日志搜索错误等级最高的关键字比如 ERROR、FAILED、RETRY。果然在同一时间段内出现了海量同类错误。把一条典型的错误时间戳和账单曲线做对比发现完全对齐。这个时候我基本已经可以确认是同步进程的循环逻辑出了问题剩下的事情只是去代码里把具体的循环路径找出来。4.3 第三步聚焦在循环和重试的异常分支有了明确方向再去看 AI 生成的代码就很快了。当时我在编辑器里只查两个模式所有带 while True 的循环所有在 except 块里调用了重试函数或者在 if 分支里调用了 sleep 的代码路径。结果不到十分钟就找到了那个主循环。前面已经说过这个循环里的关键在于它把空 rows 当成正常情况处理。我一开始还以为是模型漏加了退出条件后来再仔细看它其实有退出条件只是那个退出条件写在了另一个函数里主循环本身没有被覆盖到。很多 AI 生成的 Bug 都有这个特点每个模块单独看都是有边界的但模块之间的组合逻辑缺少同一套契约。比如重试函数有次数上限但主循环不停地把失败的数据重新塞进队列于是次数上限变成了摆设最终由主循环的结构决定实际行为。5. 这不是偶然VibeCoding 的“快速编码”为什么更缺护栏经历了这次事故我专门回看了那段时间用 AI 辅助写出来的所有代码发现一个规律出问题的代码都有一个共同点——局部健壮整体脆弱。换句话说模型的注意力集中在每一个函数的内部逻辑上但对全局资源边界、总预算、退出条件这类跨层约束往往不会主动设防。为什么会出现这种局面我认为有三个层次的原因。5.1 模型擅长“补全”不擅长“全面验证”VibeCoding 的本质是自动补全它会根据你给的需求片段补完一整段看起来合情合理的代码。但它没有条件像有经验的工程师那样在真正部署前从系统层面做验证——比如评估当某种异常持续一个小时后系统会怎样、成本是多少。模型生成代码的目标是满足你的语言描述不是保证一个分布式生产环境的行为可预测。你可以把模型想成一个高效但急于交差的实习生你让它处理某个异常它就会写一个处理异常的代码块但它不会主动问“如果这个异常持续一整天怎么办”“重试十次和重试一百次的成本差异有多大”。这些判断来自工程经验而模型接受到的上下文里根本没有这些信息。5.2 “没有预算意识”的代码生成和人类工程师相比模型生成的代码在功能上往往很完整但缺少“预算护栏”。这里的预算不单指金钱也包括资源预算重试次数预算、运行时间预算、内存消耗预算、日志大小预算。人类工程师在写代码时会凭经验在线程池、超时、重试、限流这些地方设置保守的默认值。比如“一个按钮不能让人连点一百次”“一个任务失败三次就不该再自动重跑”。但要指望模型按照合理的默认值来生成代码你需要把这些默认值写进需求提示词里否则模型大概率只会给出功能上“正确”的实现。5.3 我在依赖 AI 编码时提前放弃了工程角色这是最刺痛我的一点。那段代码出问题和 AI 有关系但主要责任在我。我使用 VibeCoding 时把大量工程判断也一起托付了出去只关注“能不能跑”不再关注“跑错了会不会产生严重后果”。其实这不是 AI 编码特有的问题。所有程序员借助快速框架和低代码工具时都容易陷入过度自信的陷阱。工具降低的是写代码的门槛而不是承担系统后果的门槛。门槛越低批判性审查就越应该被前置而不是被省略。6. 止血与加固我现在给 AI 生成代码上了哪些护栏这次事故之后我给自己定了一套规矩凡是 AI 辅助写的代码都必须过一遍护栏配置和人工审查流程。下面是现在落地执行的几个措施直接按步骤列出来。6.1 资源层的“强制熔断”配置在写任何代码之前先到云控制台把预算定额和告警配好设置每日消费额度超过某个值就禁用当前云账号的运行权限为每个计算实例设置自动关机策略比如 CPU 使用率持续 20 分钟低于 5% 就自动停止第三方按次计费接口必须设置应用层的日调用量上限或者至少配置一个半开的熔断开关日志服务设置写入量预警超过阈值后只保留错误级别日志。配置这些并不耗费多少时间但它们等于给系统加了一个保险丝。即使 AI 生成的代码再有想象力资源层的熔断也能把损失限制在可承受范围内。6.2 提示词里强制加入“边界护栏”要求现在我在让 AI 生成代码时会在需求描述的末尾固定追加一段话“所有循环必须包含明确的退出条件所有重试逻辑必须设置最大次数、退避策略和熔断阈值所有捕获异常的分支必须区分可恢复错误和致命错误不要在异常分支无限重试。”这个方法看起来简单但对模型输出的影响非常大。模型会顺着提示词的约束往代码里注入边界判断生成的代码结构会明显更谨慎。你自己仍然要扫一遍但需要修改的地方会少很多。6.3 强制 Review 清单最低限度的代码审查不能省。针对 AI 生成的代码我现在重点只查三类地方主循环结构有没有能永远继续下去的 while 循环每个 continue 分支会不会导致核心业务永远不退出重试函数外层是否有限制次数重试是否嵌套了另一个重试同一份数据会不会被多个层级的重试反复消费日志输出每次失败都打日志没问题但日志内容里是否包含了可能产生额外费用的副作用操作。如果这三类地方都没问题再把服务部署到预发布环境用极小流量跑一遍人为模拟一段时间的故障观察循环是否会在合理时间内退出。模型的“代码能跑”和系统的“正确可靠”之间隔着一次完整的故障演练。7. 最后一段话别把 AI 当成替你思考的替身要当成需要验收的下游回头再看那晚的一万美金账单我心里很清楚这个坑不是 VibeCoding 这个工具本身挖的而是我拿它当替身放弃了验收责任。AI 写代码我负责部署这个流程里缺少了最关键的工程质量关口。现在我也还在用 VibeCoding而且频率不低。只不过我的使用方式变了——它出代码我出边界条件、错误语义和成本预算。每一次让 AI 生成代码之前我会先花三分钟想一下它写的这个循环如果故障持续一小时会怎样如果数据规模扩大一百倍会怎样如果第三方接口突然报错会怎样这些问题想清楚了AI 生成代码的效率优势才能安全地拿得到手。一个小技巧是把你想到的这些故障场景直接写进提示词里让模型在生成时就考虑它们。这比我事后盯着代码找问题要轻松得多也可靠得多。如果你现在也在用 AI 辅助编程我的建议是先从最不起眼的循环结构和重试逻辑查起。往往让你付出最大代价的不是那些看起来很复杂的并发问题而是最简单的“没有退出条件”加“按次计费接口”的组合。花十分钟给 AI 生成的代码加上护栏比凌晨爬起来看账单要划算得多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑