
1. 事故现场下单100元导出的Excel里躺着10000元先简单交代一下背景。我在一家做电商SaaS服务的公司做后端开发主要维护订单中心的导出模块——运营每天都要用它拉订单明细、做对账、算佣金。功能本身是老代码一直跑得很稳直到前阵子公司推AI辅助编程我接需求时顺手让大模型生成了一段导出订单金额的新逻辑想着第二天就能提测。结果导出完我人差点原地失业。事故很典型。运营那边拉了一份当天的订单列表打电话过来说“订单金额不对全部多了两个零。”我第一反应是“数据库坏了还是下边代码把金额和数量搞混了”看了一眼线上数据随手拿一单核对用户下单金额确实是100.00元数据库里order_amount字段的值也是100.00。没毛病。但导出的Excel里这一行显示的是10000.00。没错整整多了两个零。不仅这一单当日所有订单金额全部放大了一百倍。唯一让当天业务损失没进一步扩大的原因是运营做的是日终对账巡检数据没有直接推给财务。你知道这性质有多严重——如果对接的是实际打款、佣金结算、供应商结款这会直接造成账实不符甚至引发生产事故。我第一时间把Excel下载下来确认数据形态又用SQL查了库里的原值确定源数据是正常的之后脑子里的排查路径开始升级难道是导出模块里的单位换算逻辑被改坏了还是AI生成代码时带了不该带的除法真正的原因让人无语。查了半天发现是单位没对齐——订单表里存的是“分”导出的模板按“元”渲染而AI生成代码时私自帮我加了一层“除以100”的金额处理。问题是这层处理在Excel渲染成“元”之后又被执行了一次相当于整个导出链路里发生了双重换算。用户真实成交金额100元系统库存10000分AI生成了一个按分读取数据的逻辑又在导出前除了一次100最终输出到Excel就成了10000元。当时项目紧这个导出逻辑是“顺手让AI写的”我review的时候只看了主流程没看单位换算这段——因为Excel模板那边之前一直用的都是“金额不分单位前端自己拼”的老约定。我漏检了AI也理解偏了。这次翻车让我把“让AI生成代码”这件事的边界重新想了一遍。后面我把整个排查链路和落地防范梳理了出来今天完整记录一下希望能帮到在做订单、对账、财务、数据导出相关功能的同行——尤其是那些正准备把AI生成的代码直接塞进生产环境的同学。2. 排查复盘为什么会把三个环节都漏掉我复盘整个事故时最大的感触不是“AI写错了”而是为什么这么明显的错误却一路溜过了代码审查、单元测试和联调验收。这比AI本身写错代码更值得警惕。2.1 从加载导出模块到定位单位的完整排查链先说排查过程这部分是可复现的。我的排查链路分五步每一步都有明确目的第一步复现。我直接从后台调取当天的导出任务找到运营下载的那份Excel文件对比原始订单数据。筛选出三列订单编号、订单金额、下单时间。肉眼扫一下就知道是全部订单金额异常不是个别脏数据。这一步的核心是确认问题范围——全量异常基本排除数据问题而是逻辑问题。第二步隔离。我找到导出链路里最关键的三个环节取数SQL、Java层的金额处理逻辑、Excel渲染模板。在测试环境里分别做了三个最小化验证直连数据库查询某订单的order_amount值正常在Java层直接打印读取出来的金额值发现已经被“除以100”了去掉Java层处理直接从SQL取数到Excel发现金额正常。第三步定位。发现Java层多了“除以100”的逻辑后我去查了这段代码是谁写的——对就是AI生成的那段。我当时的prompt只写了“导出订单金额”没有明确指定单位换算AI基于“数据库金额是分导出报表要显示元”这个常见需求假设主动补了一层换算。但实际项目里订单表里的金额字段本来就是“元”无需换算。多除一次100就全乱了。第四步追踪影响范围。我检查了AI生成代码里是否还有其他类似的“主动补全”重点看有没有对日期、时间戳、状态枚举、百分比字段做额外处理。结果是金额之外它在“订单状态”的映射上也做了假设——把数据库里的1/2/3映射成了文案“待支付/已支付/已关闭”但我们的老代码约定1/2/3另有含义。如果上线那会导致状态显示错乱又是一颗雷。第五步归一测试。修复金额单位问题后我用三组历史订单数据跑了一遍全量导出对比测试确认旧导出文件和修复后的文件金额完全一致。同时为金额字段加了格式化断言后续只要金额不对就会立即触发测试失败。2.2 单测全绿却漏掉重灾区的经典困境这次事故还有一个很讽刺的点AI生成代码时顺带给我写了一组单元测试测试全绿。我后来看了下测试用例测试的是“金额字段传入100返回1”——也就是它自己生成的处理逻辑测试自己的预期输出。因为AI在生成测试时也是按它自己的假设写的这个测试用例根本没有保护真实业务语义。这就涉及一个很关键的问题测试的有效性取决于测试者对业务规则的理解AI生成的测试很可能只是“自证”逻辑而不是验证业务正确性。我后来跟团队沟通这件事大家一致认同AI生成的测试可以作为冒烟测试的基础但核心业务断言必须由熟悉业务的人来补尤其是金额、税率、单位、状态映射这类一错就是大事故的字段。2.3 为什么说“人工核对看不出问题”反而是最大的问题评审时我跟同事确认了一个事实运营之前下载旧版本的导出文件看到的金额都是元。切换到新版本之后他们第一眼也没觉得数据有问题——因为10000元看起来像“今天订单多”而不是“明显异常”。如果当天是整单金额全部翻一百倍人眼在没有参照物的情况下很难察觉。我做了个实验把同一份订单数据分别用旧版和新版导出拿给两个运营同学看问他们“哪个是正常的”。结果两个人都说“新版”更顺眼因为数字更大更“好看”。这给我一个很深的教训凡是财务字段绝不能依赖人工目检。必须在代码层、模板层双向加验证机制才防得住这种“看起来合理”的灾难。3. 根因拆解两个“零”背后是单位契约的缺失网上很多人聊AI生成代码翻车第一反应是骂模型笨。但从业多年我很清楚这次事故不是AI“蠢”而是整个系统里压根没有把单位作为一等契约来管理。两个零只是表象真正的雷埋在字段语义约定里。3.1 为什么金额单位会成为隐藏的定时炸弹先列一下这个领域常见的金额存储方式存储方式示例优点常见问题以“元”存储100.00直观、便于直接读库浮点数精度问题计算时易丢精度以“分”存储10000整数运算无精度问题业务展示前必须换算漏换就翻百倍以“厘”存储100000更高精度换算层级多出错概率更大很多老系统的order_amount字段用的是DECIMAL(10,2)默认“元”但有些支付系统对接时要求金额以“分”为单位传输。在同一个系统里数据库、Java层、Excel模板、前端展示、对外接口之间经常存在不一致的单位约定。更麻烦的是很多项目的数据库注释里根本不写金额单位只有看建表SQL才知道。这就给了AI“自由发挥”的空间。我翻了我们的代码库发现一个惊人的事实金额单位在项目里至少有五种隐含约定数据库管理层存的是“元”订单服务对外接口传输时转“分”导出模块老逻辑直接输出“元”的字符串运营报表的新需求希望Excel里显示“万元”且保留两位小数前端订单详情页按“分”格式化后除以100展示成“元”。这五个约定在AI生成的代码里只被捕捉到了“元”和“分”那一对它调皮地在取数后加了个“除以100”的转换却完全没想到这份新报表本身期望的输入单位已经是“元”。每一个环节单独看都是合理的——这就是单位换算问题最阴险的地方。3.2 大模型为什么会自顾自地“理解”你的单位这个现象有必要展开聊聊。大模型在生成代码时不是从“数据库字段定义”出发而是从海量训练数据里的“常见范式”出发。在它的记忆里“后端金额字段存分展示层转元”这个模式太常见了它就会默认套用。如果你在prompt里没有显式声明“金额字段存的就是元”它就按概率生成处理逻辑。还有一个更隐蔽的问题大模型的生成是滑动窗口式的它的“记忆”不长。你前面已经说了“金额字段为Decimal(10,2)单位为元”但在生成了三十行取数逻辑后它可能早已把那段上下文冲淡重新回到“分转元”的模式。这就是为什么代码里只有那一段逻辑是错的其他部分都正常——不是它不懂是上下文丢失。3.3 人类程序员其实也会犯同样的错写到这里我必须说一句公道话单位没对齐这件事人类程序员同样经常犯。我早期刚接手老系统时也犯过一次从库里取金额直接塞给前端前端拿来就除以100结果用户看到的金额全部缩小了一百倍。那次是因为我读了建表语句没细看注释默认以为“单价一般存的是分”结果那表存的就是元。所以这次AI翻车本质上不是“AI特有的问题”而是“项目缺少单位契约规范”这个老问题的再次暴露。AI只是把这个老问题以更典型的姿态端到了我们面前——它没有项目上下文判断能力只会按大概率猜。而人类程序员要靠经验、代码注释、Review才能不踩雷。4. 这场事故教会我的防御清单这次之后我把整个团队的代码规范补充了一轮。核心思路不是“不用AI生成代码”而是把单位契约显性化让AI和人都无法自由发挥。4.1 代码层的黄金四则第一数据入口统一转标准单位。我们强制所有从数据库读取的金额字段进入业务层后立即调用MoneyUtil.toFen()或MoneyUtil.toYuan()里的一种标准转换然后在整个业务流转过程中不再做单位变更。只有到对外输出边界Excel、接口、打印才允许显示层转换。这样AI生成的代码里如果出现“除以100”一眼能看出来是多余的。第二禁止魔法数字。原来的代码里很常见amount / 100这种写法根本不知道100跟什么相关。现在我们全量替换成MoneyUtil.DIVISOR_FEN_TO_YUAN常量名字就是注释。AI生成代码时如果看到这种常量声明大概率会沿用而不是另起炉灶。第三金额字段声明单位注释。每个数据库字段映射实体属性时强制支持Column(comment 订单金额(元))这种带单位的注释实体类上也有ApiModelProperty(value 订单金额, example 100.00, notes 单位元)。这些注释既是给人看的也是喂给AI的上下文。第四导出模板绑定数据类型。Excel模板里的金额列不做任何“格式化”动作直接从模型取值输出。单位的换算必须在Java层完成模板只负责展示。这个原则很重要——否则AI可能在模板里再给你加一层“除以100”那就是三重复合错误。4.2 测试层的断言要防“自证陷阱”现在团队里新增了一个环节AI生成代码后我不会直接跑AI自带的测试而是先问三个问题测试断言的数据是我们生产的真实样例还是AI编造的断言值是业务正确值还是AI处理逻辑的结果测试命名是否押注了业务规则比如“金额100元导出仍为100”而不是“金额10000分导出为100元”。这三个问题一过AI生成测试的“自证陷阱”基本就堵死了。然后我再追加一组“契约测试”把数据库里真实某单的金额、导出文件里的金额、Excel模板里的展示值三者做一致性断言任何一个环节不一致测试就炸。这个测试我现在觉得是所有导出功能标配。4.3 评审清单里把“单位”单列成一项代码评审里的Checklist以前都是“是否处理空值”“是否考虑并发”现在我把“单位”也作为强制项加进去了。评审时先口头问开发者三件事你这个金额字段从哪来库里是什么单位你处理之后输出到哪目标单位是什么中间有没有算过用什么公式三句话问完AI生成的“隐藏除以100”基本无处遁形。因为即使写得再自然这三个回答一旦对不上就是隐患。这个检查步骤简单到让人觉得可笑但恰恰是70%以上单位事故的防线。5. 把这次教训写进AI提示词的四个要素既然问题出在AI那么对策也应该有一半落在AI的使用方式上。我现在写prompt不再是“帮我导出订单”而是变成了一个有明确约束的任务说明。分享一下当前的模板你可以根据自己的场景调整5.1 显式声明字段与单位不给AI“猜”的空间我现在的prompt开头一定是这样的请生成订单导出Excel的代码。约束如下 1. order_amount字段类型为DECIMAL(10,2)数据库中单位为“元”不要做任何单位换算 2. 导出文件中的金额列需要保留两位小数但仅仅是格式化为字符串不改变数值 3. 不要修改状态字段的枚举映射1待支付2已支付3已关闭4已退款 4. 不要自动增加业务校验、日志埋点、缓存逻辑除非我明确要求 5. 输出代码后附上你识别出的字段清单和单位约定。这里一定要写“数据库单位为元”而不是“金额单位为元”。因为数据库才是源头AI最容易在源头出错。另外补一句“不要做任何单位换算”直接把它的默认假设按死。5.2 要求AI输出“字段契约表”让它在生成代码后附一张字段表和单位说明。如果它输出的字段名跟数据库不匹配或者它自己都说不清这个字段的单位那基本可以断定这段代码有问题。我测试过几次模型在生成代码时如果被要求“输出字段契约表”它会更倾向于谨慎甚至主动在注释里标注单位这比我事后看代码强得多。5.3 要求AI先复述业务规则再生成这个技巧特别好用。可以在prompt里加一句先复述你对本段业务规则的理解重点是金额字段从数据库到导出文件的单位变化链路确认无误后再生成代码。我让AI“先想后写”生成的代码质量明显提升。虽然它复述的可能仍然有错但至少错误前置到我审查的第一屏而不是埋在几十行代码中间。5.4 用“边界样例”引导AI自查最后我会在prompt里放一组测试数据请用以下数据自测 订单A数据库order_amount100.00导出金额应为100.00 订单B数据库order_amount0.01导出金额应为0.01 订单C数据库order_amount9999999999.99导出金额应为9999999999.99。这三组样例覆盖了正常值、极小值、极大值AI如果用“乘以100”或“除以100”的逻辑自测时就会暴露。我在实测中发现加入样例后AI生成的金额处理逻辑极少再跑偏因为它在“自证”时会拿样例试算逻辑错误马上自己否掉。6. 复盘之后我对AI辅助编程的操作边界有了新定义这次事故之后我没有因噎废食停用AI编程而是把AI的定位调成了“生成底层样板代码的助手”不是“业务逻辑的决策者”。现在我的操作习惯是这样的AI生成的代码我只接收几类内容DTO类、Mapper接口、工具方法、单元测试骨架、CRUD模板。凡是涉及金额、状态枚举、时间窗口、税率、外键关联这类业务规则的逻辑我默认要逐行review或干脆手写。不是不信任AI而是这类逻辑一旦出错代价太高不是我愿意用“让AI猜一次”来赌的。另外我养成了一个习惯任何导出类功能上线前必须做一次全量对比验证。拿旧版本的文件和新版本的文件逐行做diff哪怕只是多了个空格都要搞清楚原因。我的做法是写一个简单的对比脚本读取两个文件的每一行转成JSON后比对。这个操作一次就能覆盖金额、状态、日期、备注等所有列的异常变化远比人眼扫靠谱。这个脚本本身也可以让AI生成但我会让它先输出对比逻辑再跑数据。它会发现一切差异而我要做的是逐条确认差异是否合理解释。金额差两个零在这个diff下是无处遁形的。7. 如果重来一次我会这样处理同类需求写到最后再分享一个如果重新回到事故那天我会走的流程。这套流程现在已经成为我接导出需求的标准动作。第一接到需求先不急着写代码把和“导出”相关的边界条件列出来。包括导出哪些字段、每个字段的类型和单位、导出模板格式、运营如何校验导出的数据。这些信息我会先整理成一页纸发给业务确认。这一步花十分钟但能规避80%的返工。第二让AI做的事限制在“把数据库字段映射成导出对象”这一步明确告诉它“只能改名不能算数”。对就是禁止它在映射过程中写任何运算逻辑。金额也好、占比也好、折扣也好一律在映射外部另行处理。这个约束简单粗暴却直接卡死了单位换算翻车点。第三加一道“金额链路染色”验证。我在代码里对每个金额字段打日志从入库到导出的每一步都记录原始值和处理后的值。上线后跑一次全量导出看日志里的染色值是否全程一致。这个验证看起来冗余但确实是财务功能上线前最该做的事。第四写一份“导出字段清单”文档挂在wiki上。文档里明确每个字段的源表、源字段、类型、单位、转换规则、输出格式。我这次事故的根源就在于这个文档缺失AI只能靠猜我也只能靠review堵漏。有了文档AI生成时的上下文就明确多了——至少在prompt里能直接引用。第五对AI生成的金额代码做强制性人工重写。这条听起来很反AI但我的经验是金额、时间、枚举三个类型AI生成后我必重写哪怕只改一行也算。因为这三个类型最容易出现“隐含业务规则导致润物细无声式的错误”重写一遍能强制我建立完整的心智模型上线前心里有底。以上就是我这次AI生成代码翻车事件的完整复盘。它让我意识到一件比“AI写错代码”更值得警惕的事在一个单位约定混乱、规则注释缺失的旧系统里AI生成代码时会把每一个隐藏的歧义以最高概率的模式补全——而人类也一样会。所以修复的关键不是怪AI而是重构系统里那些“靠默契才能不出错”的隐性约定。等这些约定都变成显性的契约之后再回过头看AI生成的代码确实能省不少事。只是在那之前每一段涉及钱、时间和状态的代码我都坚持多花十分钟亲手验一验。