Dashboard 说一切正常
有一种事故模式,我总会反复想到,因为它在一开始看上去太让人安心了。
一个 support-ticket 分类器原本运行正常。主模型超时,fallback 收到同一个 prompt,返回响应,请求图表重新变绿。可用性恢复了。用户没有看到错误。所有人都能先松一口气。
然后有人发现,一张本该进入紧急队列的工单没有被升级。
表面上没有任何明显的错误。fallback 返回了 JSON,请求成功结束,dashboard 上全是 200。但在最近一次通过审核的评估里,主路径返回的是:
而线上 fallback 返回的是:
对人来说,两者看上去可能只差一点。对产品来说,这就是把客户问题升级,和把它悄悄放进普通队列之间的差别。
这正是 LLM failover 令人不安的地方:fallback 可以救回请求,却不一定救回产品行为。
这并不意味着 failover 是坏主意。provider 的故障不该自动变成你的故障。但“备用模型工作了”的定义,应该比“另一个模型返回了一段文本”更严格。
200 不等于产品正确
一次 LLM 请求 failover 后,有三种“成功”很容易混在一起。
传输成功(transport success),指模型返回了响应。这是 HTTP 200 所能说明的事。
契约成功(contract success),指响应拥有应用需要的形状:字段存在,类型正确,关键业务校验也通过。
产品成功(product success),指应用真的做成了用户和业务期待的事。对分类器来说,就是把正确的工单以正确的优先级送进正确队列。
它们彼此相关,但不能互相替代。开头的分类器通过了基本响应格式,却改变了产品决策。另一种相关失败会更早停在契约层:应用期待 high,fallback 却自行生成了 high_priority。

无效 JSON 是比较仁慈的失败:parser 会拒绝它,错误会出现,总有人会去查。更难的是看起来合理的 JSON:一个略有不同的 enum、一个被写成字符串的 boolean、更委婉的拒绝,或者一段听上去很像正确答案、却会触发错误操作的文本。
Schema 能抓住其中一部分。对许多生产功能来说它是必要边界,但不是魔法边界。一个响应可以完全符合 schema,仍然分类错误、选错工具、过度自信,或使用不适合产品的语气。
所以结论不是放弃 fallback,而是不要把绿色响应徽章当作调查的终点。
Fallback 是另一条生产路径
“fallback”这个词听起来像备用零件:原机器坏了,临时换上同样的部件。但模型不是这种意义上的备用零件。
不同模型擅长的任务不同,重视的指令不同,解释的长度不同,输出的结构不同,选择的工具不同,拒绝的边界也不同。有些差异正是团队比较模型的原因;也正因为如此,failover 路径值得像一次产品变更那样认真对待。
还有一个在架构图里很容易漏掉的细节:prompt 不是产品唯一的输入。Provider adapter 可能转换角色、工具定义、JSON Schema 特性、reasoning 设置或 streaming 事件。主路径和 fallback 路径在意图上可能接收“同一个请求”,却实际走过不同的能力表面。测试要以真实路由的请求为准,而不是把两段复制的 prompt 分别贴进 provider playground。
对分类器来说,差异会出现在决策边界。模型可能都理解工单很严重,却对它是否应当 requires_human_review 有不同判断。面向客户的写作功能可以保留事实,却变得过短、过度道歉或过度自信。Agent 也可能给出看似正确的回答,同时选择另一个工具或传入略有不同的参数。每个输出单独看都可能说得过去;应用仍要决定哪些差异可以接受。
选择 fallback 要从一个更简单的事实出发:模型的质量特征并不相同。即使路由的目标是降低成本,模型选择仍然是产品选择,而不是透明的基础设施替换。这并不能证明某个具体 fallback 一定会改变功能;它说明评估义务很明确:候选模型必须针对产品依赖的行为进行测试。
Provider 文档从另一个角度也指向同一件事。OpenAI 的 model 页面区分 alias 和 snapshot,并说明 snapshot 会锁定一个具体版本,以维持性能和行为的一致性。这讨论的是版本漂移,不是跨 provider 的 failover。由此得到的运营建议是:固定你评估过的版本,并在候选发生变化时重新运行应用级检查;如果同一个模型的新 snapshot 都值得重新评估,那么紧急路径里的不同模型更不该默认等价。
结构化输出同样会暴露抽象层的缝隙。Google 说明 Gemini 的 structured output 支持 JSON Schema 的一个子集,其他 provider 和 adapter 也有各自的能力边界。这份文档没有说 fallback 必然会破坏 schema;它说明了“我们向所有地方发送 JSON Schema”本身还不够构成兼容性策略。
我们发布过的同一 schema 在五个 provider 上的实验,是最接近这个分类器问题的内部观察。它在一套特定 schema、prompt、模型选择、adapter 路径和 validator 下得到了不同结果。这不是 provider 排名,也不该被读成排名。它支持的结论更小:同一个应用契约在不同模型路径上可能表现不同,因此必须在计划使用的路径上测试。
这些来源不能证明每一次 failover 都会改变产品;它们共同说明,模型可互换性是一项需要验证的假设,而不是应当默认拥有的属性。
静默成功的成本
语义问题只是事故的一半。
如果主模型等待六秒后超时,备用模型再花两秒回答,用户感受到的是八秒请求,而不是两秒。若第一次尝试在失败前已处理了 token,你还可能为没有交付给用户的工作付费。具体账单取决于 provider 和错误发生的位置,但运营事实不变:一次被救回的请求可能更慢,也可能更贵。
所以 failover timeline 应展示完整链路,而不是只展示最后成功的响应。Provider failover timeline明确指出,总延迟包含此前每次尝试;在模型已处理 token 后失败时,失败尝试也可能增加真实成本。
这也是此类事故特别折磨人的原因。正常生产 bug 会给你错误码、stack trace 或清楚的红图;这里图可能是绿色的。你先查 parser,再查 prompt,再查昨天的 release,却可能花了很久才发现最重要的变化只是“回答的人”换了。
没有 attempt-level 记录时,这不是调试,而是在重建一段你并没有亲眼看见的对话。
我希望事故单上早就写好的东西
如果这个分类器重要到需要 fallback,它也重要到需要为 fallback 写一份兼容性契约。
不必是四十页采购文档,也不必要求备用模型逐字模仿主模型。它只需要清楚回答一个实际问题:这条路径改变时,哪些事情必须仍然为真?
对分类器而言,可以先准备一组冻结的代表性工单:真正紧急的案例、普通账单问题、模糊请求、敌意语言,以及正确答案是交给人工的案例。在生产前让主模型和每个 fallback 都跑这组样本。保存预期的业务结果,而不只是保存一段看上去漂亮的答案。
上图是评估路径;固定版本、shadow-test 和保留日志,则是让这条路径在发布后继续成立的方法。

然后把事故暴露出的边界写下来:
- 决定什么可以退化。 人工审核后的摘要可以有更多变化空间;控制升级的分类器则不行。
- 固定被评估的候选版本。 Provider 支持时使用带日期或版本的 model ID,把版本变更当作一个新候选。
- 做两层校验。 先校验 JSON 形状,再校验 schema 无法表达的业务规则,例如允许的优先级和必须升级的条件。
- 测试答案周围的行为。 有工具时测试调用哪个工具、传什么参数;可能拒绝时测试拒绝如何表示、被送到哪里。
- 在需要它之前 shadow-test。 让候选 fallback 先看到代表性流量或受保护的评估集,而不是等 outage 让它成为唯一选择。
- 保留证据。 记录实际服务的 provider/model、prompt 和 schema 版本、failover 原因、校验结果、延迟和成本。
关键不在于再加一层复杂 retry loop,而在于把 fallback 当成同一功能的 release candidate。
这也会改变你看 eval 的方式。不要问备用模型有没有写出和主模型相同的句子;那通常是错误标准,也会诱导脆弱的测试。应当问两个路径是否守住了功能承诺。对分类器,比较队列、紧急程度、人工审核标记和下游自动化;对抽取流程,比较经过验证的字段及这些字段触发的行为;对工具型流程,先比较允许的工具与参数边界,再比较散文表达。
评估还应包含分歧案例,而不只是明显的成功样本。放入模糊工单、应该拒绝的 prompt、格式损坏的源数据。一个在友好案例上表现完美、在边缘案例却过度自信的 fallback,正是那种平时看起来健康、关键时刻出问题的路径。一个小而持续维护的评估集,能把“我觉得备用模型大概够接近”变成 outage 前就能回答的问题。
在事故前选择失败方式
一旦把 fallback 看成另一条产品路径,问题就不再是“我们的备用模型是什么”,而是“当主行为不可用时,这个功能被允许做什么?”
对低风险草稿功能,fail open 可以合理。fallback 的总结或语气也许不同,但人在结果真正产生影响前会审核它。
对 support classifier,constrained fallback 更诚实:只有输出通过 schema 和业务规则时才能继续;否则产品应把工单交给人工,而不是制造没有依据的自信。
对不可逆操作、敏感资格判断、权限、破坏性工具调用,以及医疗、法律、金融输出,最安全的策略可能是 fail closed 或明确要求人工复核。可用性很宝贵,但它不总是你正在保护的价值。
没有一张通用表能替应用决定策略。Workflow 的负责人必须决定一个“错误但成功”的响应会付出什么代价。这项决定属于功能设计,而不该留到 provider incident 的中间。
观察真正走过的路径
事故之后,最后一个有用的问题其实很基础:到底是哪个模型回答了这个用户?

只有当请求记录同时显示失败尝试和最终返回响应的模型时,fallback chain 才是可审计的。
ModelRiver 是能在请求 timeline 中展示失败和成功 provider 尝试的一种 workflow layer。自己记录相同证据的团队也能遵循同样纪律。重点不是 dashboard,而是事后能区分 provider 故障、路由变化、契约失败和产品回归。
Fallback 不是一台备用发动机。它是你的产品的另一个版本。

