news 2026/10/22 11:12:42

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
VibeCoding生成代码酿事故:云账单一夜暴涨,循环重试成烧钱元凶

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_size=100) 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 生成的代码,我现在重点只查三类地方:

  1. 主循环结构:有没有能永远继续下去的 while 循环,每个 continue 分支会不会导致核心业务永远不退出;
  2. 重试函数:外层是否有限制次数,重试是否嵌套了另一个重试,同一份数据会不会被多个层级的重试反复消费;
  3. 日志输出:每次失败都打日志没问题,但日志内容里是否包含了可能产生额外费用的副作用操作。

如果这三类地方都没问题,再把服务部署到预发布环境,用极小流量跑一遍,人为模拟一段时间的故障,观察循环是否会在合理时间内退出。模型的“代码能跑”和系统的“正确可靠”之间,隔着一次完整的故障演练。

7. 最后一段话:别把 AI 当成替你思考的替身,要当成需要验收的下游

回头再看那晚的一万美金账单,我心里很清楚:这个坑不是 VibeCoding 这个工具本身挖的,而是我拿它当替身,放弃了验收责任。AI 写代码,我负责部署,这个流程里缺少了最关键的工程质量关口。

现在我也还在用 VibeCoding,而且频率不低。只不过我的使用方式变了——它出代码,我出边界条件、错误语义和成本预算。每一次让 AI 生成代码之前,我会先花三分钟想一下:它写的这个循环如果故障持续一小时会怎样?如果数据规模扩大一百倍会怎样?如果第三方接口突然报错会怎样?

这些问题想清楚了,AI 生成代码的效率优势才能安全地拿得到手。一个小技巧是,把你想到的这些故障场景直接写进提示词里,让模型在生成时就考虑它们。这比我事后盯着代码找问题要轻松得多,也可靠得多。

如果你现在也在用 AI 辅助编程,我的建议是:先从最不起眼的循环结构和重试逻辑查起。往往让你付出最大代价的,不是那些看起来很复杂的并发问题,而是最简单的“没有退出条件”加“按次计费接口”的组合。花十分钟给 AI 生成的代码加上护栏,比凌晨爬起来看账单要划算得多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 11:09:34

IPv4协议功能实现原理深度解析:从头部字段到数据包转发全流程

1. 从一个让人抓狂的排障现场说起前阵子帮一个做嵌入式开发的朋友排查一个局域网通信故障,现象很典型:两台设备在同一台交换机下,A 能 ping 通 B,B 却 ping 不通 A,抓包一看,A 发出的请求到了 B&#xff0c…

作者头像 李华
网站建设 2026/10/22 11:09:34

Windows下Claude Code安装与API Key配置实战指南

如果你平时在终端里干活的时间比在IDE里还长,那你大概率会喜欢上Claude Code。简单说,它是一个跑在命令行里的编码代理,你敲一句自然语言,它就能读懂项目结构、定位文件、修改代码,甚至顺手执行命令帮你跑测试。我在Wi…

作者头像 李华
网站建设 2026/10/22 11:08:48

高维张量计算的几何本质:从shape、stride到广播与归约

ops-nn 这个名字,最早是我们内部给一个高维张量算子库起的代号。它要解决的事情,简单说就是:在神经网络训练和推理过程中,把各种形状乱七八糟的高维张量,高效地完成搬运、变形、合并和计算。叫它"几何引擎"&…

作者头像 李华
网站建设 2026/10/22 11:08:37

OpenClaw十分钟升级指南:源码、预编译与包管理器全流程避坑

先说明一句,升级OpenClaw这件事,标题说“十分钟教你”,实际操作起来确实可以做到十分钟以内,前提是你已经走对流程。如果你上来就 git pull 然后重新编译,大概率会因为子模块缺失、编译参数不对、资源文件路径变了这…

作者头像 李华
网站建设 2026/10/22 11:07:10

零基础也能玩转Vibe Coding:用自然语言让AI帮你写软件

最近身边好多人都在讨论 Vibe Coding,但绝大多数人第一反应都是:"这不是程序员的活儿吗?我一个做运营、做设计、甚至做销售的,怎么可能碰?" 说实话,我一开始也是这么想的。直到跟着一门叫"闪…

作者头像 李华
网站建设 2026/10/22 11:04:55

Playwright MCP 性能基准测试:三种浏览器执行方式对比与踩坑总结

我最近在做一个内部小工具,把浏览器自动化能力接进 MCP,起因很简单:团队想让我测一下,同一条指令在不同执行方式下,到底要多长时间才能拿到结果。跑下来之后发现,数据比预想的有意思得多。比如同样是“打开…

作者头像 李华