一场“小更新”引发的全线雪崩

我们花了两年时间,构建了一个让全公司上瘾的工具。
它只做一件事:把“帮我拉一下去年Q3华北区的销售数据,按城市分”这样的大白话,变成精确的API指令。
一个自然语言请求,会翻译成这样的JSON:

{
"description": "User requested sales volume for the given date range...",
"api_call": "/api/sales_volume",
"post_body": {
"start_date": "2026-01-01",
"end_date": "2026-03-31",
"region": "northeast"
}
}

剩下的,是传统的后端工程——调API、跑数据、生成报告、发邮件。到了2025年中,这个系统每月处理数百份报告,成了团队获取临时数据的默认通道。而这一切的核心契约,就是模型必须返回一个结构化的、字段分明的JSON对象。

我们基于Claude Sonnet 3.5构建了它。从3.5升级到3.5,再升级到4.0,都像升级一个稳定的npm包一样平静。
直到我们滚动部署Sonnet 4.5。

模型开始“自作主张”:当“乐于助人”成了系统Bug

噩梦从一连串“数据不对”的投诉开始。
问题很快定位到:对于一部分请求,新版模型开始把post_body里的查询参数,一股脑塞进了description字段。
原来的字段是:“用户请求了XX时间段的销售数据…”
现在变成了:“用户请求了XX时间段的销售数据,API调用参数如下:{…一整段JSON…}”。

连锁反应立即爆发。
第一个故障模式下游系统只认post_body作为请求体,这个字段现在是空的。于是API被空参数调用——要么返回全时间段、全区域的数据,要么直接报500错误。
第二个故障模式更意外新模型变得“谨慎”了。面对模糊请求,它不再默默尝试,而是会反问一句:“您指的是所有区域,还是特定区域?” 但我们系统的管线里,根本没有设计“人机多轮对话”的路径。

一夜之间,一个我们以为“简单问题”的升级,让系统在“做错事”和“不做事”之间随机摇摆。 我们紧急回滚到4.0,但过程异常痛苦:在我们部署4.5期间,团队已经基于它的行为添加了新功能。回滚意味着,所有这些新功能都需要在旧模型上重新测试验证。

传统工程方法,为何在这里彻底失效?

这暴露了一个残酷的现实:在LLM时代,软件工程的“破坏半径”概念正在失效。
传统升级一个库或驱动,你可以读更新日志、跑单元测试、做回归验证。系统行为是确定的,你可以密集采样,有信心预测。破坏半径在架构上是可界定的。

但LLM组件完全不同。你不能diff一个模型版本——从4.0到4.5,不是打补丁,而是彻底更换了你依赖的核心功能。输入是无限的(自然语言),可能的失败模式也是无限的(模型可能做的任何不同行为)。这是一种“无限破坏半径”:你无法提前枚举一次更改的所有下游影响。

事后复盘发现,我们的提示词一直有“未明说的漏洞”。我们告诉模型返回一个三字段的JSON,并描述了每个字段的用途,但从未明确强调“description字段必须是纯文本字符串,不能包含其他字段的序列化内容”。旧版本的模型从上下文“推断”出了这个约束。而Sonnet 4.5,显然更努力地想“提供帮助”,于是它觉得,把参数也放在描述里能让响应更“有用”。从模型视角看,这是对一个模糊指令的合理解读。

Bug不在模型。Bug在于我们的假设:模型会像以前一样,帮我们填补规格的空白。 三次成功的升级训练我们相信,那些空白是安全的。

终极防御:把“评估集”当作系统的真正规格

我们学到的血泪教训是:在LLM系统中,评估套件(Evals)——而不是提示词——才应该是系统的正式规格。
提示词是规格的“实现”,模型是“解释器”。而评估集,就是规格本身。任何模型或提示词的更改,必须通过评估集才能部署。

一个能抓出这次4.5回归的测试案例可能长这样:

def test_description_contains_no_serialized_payload(response):
desc = response["description"].lower()
forbidden = ["curl", "post_body", "{", "http://", "https://"]
assert not any(token in desc for token in forbidden), \
f"description leaked structured content: {response['description']}"

几百条这样的属性检查,部分是为关键不变量手工编写的,部分从真实流量生成,部分由LLM充当评委来打更模糊的“语气分”,构成一道门槛。模型升级和提示词更改,应该像合并代码一样,必须让整个评估套件变绿才能进行。

评估当然昂贵,会随着产品迭代漂移,LLM评分本身也有不确定性。它也只能捕获你“想到了”的失败模式——我们团队没人写过“描述字段不应包含curl命令”的断言,因为没人想过模型会这么做

评估不是银弹。但它是当前唯一能限制破坏半径的方法:密集采样你真正关心的输入输出行为,并在行为发生偏移时拒绝部署。

我们正驶入无人区:AI工程的“规格”空白

这个故事指向一个更大的问题:当AI代理开始自主执行更复杂的任务——写代码、调动资金、调度基础设施——那么“模型通过了我们的冒烟测试”和“我们知道这个系统在生产中会做什么”之间的鸿沟,将成为未来数年AI工程的核心挑战。

工程界尚未发展出编写有效评估的知识体系。在自然语言输入空间里,什么算“覆盖”?CI/CD系统并非为概率性测试结果而设计。

能够弥合这个鸿沟的团队,将是那些停止把评估当作质量保证的“事后想法”,而开始将其视为系统真正规格的团队。 否则,每一次例行升级,都可能是一场未知的冒险。


【锐评】:把AI系统当成传统软件来升级,本质上是在用算盘控制核反应堆——你的控制精度,永远跟不上它的潜在变异速度。

参考链接:
https://venturebeat.com/orchestration/when-claude-changed-everything-changed-managing-ai-blast-radius-in-production