模型回退并不是一种单一行为。它是一组具有不同安全边界的恢复决策。
生产环境中的模型回退策略应将三种工作流区分开来:
- 当请求仍然可以安全重放时,重试或等效故障转移。
- 当另一个模型能够满足相同的能力和质量契约时,跨模型回退。
- 当输出已经到达用户,或工具副作用可能已经发生时,停止、对账或升级处理。
这种区分很重要,因为最快的恢复动作并不总是最安全的。重新执行一次失败的分类请求通常风险较低;但在流式回答进行到一半时,或者在一次不确定的支付工具调用之后,悄悄切换模型就不是这样了。
这份作战手册将回退策略转化为三种可操作的工作流,供你的团队在受控的生产发布过程中实现、测试、观测并上线。
一张表看懂模型回退决策
先从请求状态入手,而不是从供应商名称入手。
| 请求状态 | 首选工作流 | 典型动作 | 不要这样做 |
|---|---|---|---|
| 没有响应字节,瞬态传输错误 | 工作流 1 | 有上限地重试,然后切换到等效端点故障转移 | 在没有截止时间或预算的情况下重试 |
| 没有响应字节,触发限流或过载 | 工作流 1 | 遵循重试建议,加入抖动,然后切换到等效容量 | 制造同步重试风暴 |
| 主目标不可用,但存在兼容模型 | 工作流 2 | 检查回退契约,然后路由到已批准的替代模型 | 假定每个模型都支持相同的工具、模式或上下文 |
| 结构化响应验证失败 | 工作流 2 | 修复一次,或尝试满足模式契约的已批准模型 | 把 HTTP 200 当作任务成功 |
| 部分流已经发送 | 工作流 3 | 停止,标记为部分结果,并提供显式重启 | 将第二个模型无痕拼接进同一个答案 |
| 写侧工具可能已经执行 | 工作流 3 | 使用幂等记录对工具状态进行对账 | 自动重放整个模型与工具工作流 |
| 安全或策略分类不确定 | 工作流 3 | 根据产品策略进行升级处理或失败关闭 | 为了保持可用性而降低安全门槛 |
核心规则很简单:重试会保留目标,等效故障转移会保留模型契约,而跨模型回退会改变契约风险。每一步都需要更严格的资格检查。
关于断路器、错误归一化以及供应商中立控制器的更深入讨论,请参阅LLM API 回退路由作战手册。
在工作流之前:定义一个回退包络
每个请求都应以一个有上限的包络进入路由层。这个包络告诉系统在请求必须停止之前,允许进行多少恢复操作。
type FallbackEnvelope = {
requestId: string;
deadlineMs: number;
maxAttempts: number;
maxAddedLatencyMs: number;
maxCostUsd?: number;
allowEquivalentFailover: boolean;
allowCrossModelFallback: boolean;
allowAfterPartialOutput: false;
sideEffectMode: "none" | "read_only" | "write_possible";
requiredCapabilities: string[];
requiredSchemaVersion?: string;
};
这些值应当来自产品工作流,而不是来自全局默认值。后台摘要任务可以容忍比交互式编码助手更高的延迟。没有工具的聊天回答可以容忍与能够部署代码或发送邮件的代理不同的恢复行为。
该信封还可防止嵌套重试。如果 SDK、应用、网关和提供方适配器都独立重试,一个小型故障就可能放大成一次大规模尝试洪峰。选择一层来承担总尝试预算,并要求每个下游层报告它已经消耗了多少。
将回退信封转化为代码化策略
类型定义记录的是意图,但生产路由需要一个版本化策略,供运维人员在不更改应用代码的情况下审查。保持策略足够小以便审计,同时又足够具体,以防通用回退链泄漏到高风险工作流中。
这个入门配置将三类常见路由进行分离:
policy_version: 2026-08-02
routes:
interactive_chat:
deadline_ms: 12000
max_attempts: 2
max_added_latency_ms: 2500
allow_equivalent_failover: true
allow_cross_model_fallback: true
allow_after_partial_output: false
side_effect_mode: none
required_capabilities: [streaming]
structured_extraction:
deadline_ms: 30000
max_attempts: 3
max_added_latency_ms: 8000
allow_equivalent_failover: true
allow_cross_model_fallback: true
allow_after_partial_output: false
side_effect_mode: none
required_capabilities: [structured_output]
required_schema_version: invoice-v4
tool_agent_write:
deadline_ms: 45000
max_attempts: 2
max_added_latency_ms: 5000
allow_equivalent_failover: true
allow_cross_model_fallback: false
allow_after_partial_output: false
side_effect_mode: write_possible
required_capabilities: [tool_use]
上面的值只是示例,不是通用阈值。应根据面向用户的延迟目标、任务经济性、评估结果以及副作用风险来设定。关键设计决策是:具备写入能力的代理不能在不被察觉的情况下切换到行为不同的模型。
在运行时,路由器应将策略与请求状态以及观测到的失败状态结合起来。一个紧凑的决策函数可以让这条边界可测试:
type RecoveryAction =
| "retry_same_target"
| "failover_equivalent"
| "fallback_approved_model"
| "reconcile_side_effect"
| "restart_required"
| "stop";
function chooseRecovery(input: {
errorClass: string;
attemptsUsed: number;
deadlineRemainingMs: number;
partialOutput: boolean;
sideEffectState: "none" | "safe" | "uncertain";
equivalentAvailable: boolean;
approvedAlternateAvailable: boolean;
policy: FallbackEnvelope;
}): RecoveryAction {
if (input.sideEffectState === "uncertain") return "reconcile_side_effect";
if (input.partialOutput) return "restart_required";
if (input.attemptsUsed >= input.policy.maxAttempts) return "stop";
if (input.deadlineRemainingMs <= 0) return "stop";
const transient = [
"transport_transient",
"rate_limited",
"provider_overloaded",
"provider_server_error",
].includes(input.errorClass);
if (transient && input.attemptsUsed === 0) return "retry_same_target";
if (transient && input.equivalentAvailable) return "failover_equivalent";
if (
input.policy.allowCrossModelFallback &&
input.approvedAlternateAvailable
) {
return "fallback_approved_model";
}
return "stop";
}
将候选选择与恢复决策分开。chooseRecovery 决定允许哪一种工作流;随后由候选选择器按能力、上下文、区域、成本和质量策略过滤目标。这种分离让事故复盘更容易,因为团队可以区分“我们选错了恢复工作流”与“我们选错了备用模型”。
对策略进行版本化,并将该版本附加到每一次尝试追踪中。当出现回退回归时,运维人员应能够回答:是哪一版策略做出的决策、哪些候选是可用的,以及当时还剩多少预算。
工作流 1:重试,然后等效故障切换
当操作可重放,并且系统未暴露部分输出或进入不确定的副作用状态时,使用此工作流。
等效目标是另一条仍能保持关键契约的路径:相同的模型行为类别、所需能力、模式预期、安全配置,以及兼容的上下文限制。它可能来自不同区域、部署、提供商端点或容量池。
步骤 1:规范化故障
将提供商特定的响应映射到一个较小的内部分类体系:
transport_transientrate_limitedprovider_overloadedprovider_server_errorauthentication_or_permissioninvalid_requestdeadline_exhaustedcontract_failurepartial_outputside_effect_uncertain
通常只有前四类才符合自动重放条件。身份验证、权限和无效请求错误应直接停止,因为切换到不同端点不太可能修复请求。契约失败属于工作流 2。部分输出和不确定副作用属于工作流 3。
步骤 2:计算剩余预算
在每次尝试之前,请检查:
remaining time > estimated next-attempt latency + response safety margin
remaining attempts > 0
remaining added latency > 0
remaining cost budget > estimated attempt cost, when a cost ceiling exists
如果任何必需预算已耗尽,就退出,而不是再尝试一个提供商。
步骤 3:使用退避和抖动进行重试
在可用时,使用提供商的重试指导。否则,应用带抖动的指数退避,并将延迟控制在请求截止时间内。
function retryDelayMs(attempt: number, retryAfterMs?: number): number {
if (retryAfterMs !== undefined) return retryAfterMs;
const base = Math.min(250 * 2 ** attempt, 4_000);
const jitter = Math.random() * base * 0.3;
return Math.round(base + jitter);
}
抖动很重要,因为许多同时发起请求的客户端否则会按相同的时间表重试,并延长过载事件。你的 LLM 限流指南 应定义 RPM、TPM、队列、并发和重试预算如何相互作用。
步骤 4:切换到等效容量
如果同一目标仍然不健康,则仅在检查以下条件后路由到等效端点:
- 电路处于关闭状态,或处于半开状态以进行探测。
- 目标支持所需的输入和输出模式。
- 目标可以在其上下文限制内接受该请求。
- 目标使用预期的安全和数据处理配置。
- 此次尝试仍符合截止时间和成本范围。
等效故障转移通常比更换模型风险更低,因为它的目标是保持响应契约不变。
步骤 5:记录恢复原因
返回如下路由结果:
{
"workflow": "retry_equivalent_failover",
"primary_attempts": 2,
"equivalent_failover_attempts": 1,
"recovered": true,
"recovery_reason": "provider_overloaded",
"added_latency_ms": 684
}
除非你的产品明确承诺透明度,否则不要向终端用户暴露内部提供商细节。但请将其保留在追踪和运营日志中。
工作流 2:受控的跨模型回退
只有在备用模型已针对该任务预先批准时,跨模型回退才适用。模型能返回文本还不够;它必须满足工作流契约。
步骤 1:创建能力契约
为每一类路由定义不可妥协的要求。
{
"route_class": "support_ticket_triage_v3",
"required": {
"input": ["text"],
"output": ["json_schema"],
"tools": [],
"minimum_context_tokens": 24000,
"schema": "triage-result-v3",
"languages": ["en", "es", "de"],
"safety_profile": "customer-support-standard"
},
"fallback_models": [
"approved-model-b",
"approved-model-c"
]
}
对于使用工具的路由,请包括工具选择行为、并行工具支持、参数 schema 处理,以及模型是否能可靠遵循“不要调用”条件。对于结构化输出,在每次尝试后都要根据 schema 验证实际响应。
步骤 2:将传输成功与任务成功分开
HTTP 成功响应仍然可能在产品工作流中失败。至少从三个层面进行评估:
- 传输成功:提供方返回了完整响应。
- 契约成功:响应已解析、与 schema 匹配,并正确使用了受支持的工具。
- 任务成功:输出确实以可接受的质量水平完成了用户的工作。
在比较回退候选项时,这一区分至关重要。一个响应率很高但经常出现 schema 或工具失败的模型,并不是可靠的回退方案。
步骤 3:按策略对已批准候选项排序
生产路由器可以使用运营信号对符合条件的目标进行评分,而不必假设某个模型在所有场景下都是最优。
type Candidate = {
id: string;
capabilitiesPass: boolean;
circuitOpen: boolean;
estimatedLatencyMs: number;
estimatedCostUsd: number;
recentContractSuccess: number;
recentTaskSuccess: number;
};
function eligible(candidate: Candidate, envelope: FallbackEnvelope): boolean {
return (
candidate.capabilitiesPass &&
!candidate.circuitOpen &&
candidate.estimatedLatencyMs <= envelope.maxAddedLatencyMs &&
(envelope.maxCostUsd === undefined ||
candidate.estimatedCostUsd <= envelope.maxCostUsd)
);
}
避免为每项任务都使用静态的“主模型、备份、再备份”列表。代码生成的最佳回退集合,可能与抽取、翻译、视觉或工具执行的最佳集合不同。
步骤 4:验证回退输出
先应用确定性检查:
- JSON 或 schema 验证
- 必填字段检查
- 工具参数验证
- 引文或 URL 格式检查
- 长度和语言约束
- 禁止输出模式
然后再加入工作流特定的质量检查。这些可以是轻量规则、任务评估器、抽样人工复核,或经过验证的裁判模型。如果质量门禁失败,不要把该回退标记为已恢复。
步骤 5:以金丝雀方式推进策略变更
在扩大新的回退模型使用范围之前:
- 回放离线评估集。
- 在策略允许的情况下运行影子流量。
- 仅对一小部分符合条件的失败启用该候选项。
- 比较契约成功、任务成功、延迟和成本。
- 只有当恢复价值大于回归风险时才扩大范围。
使用一个 LLM API 可观测性 schema 跟踪这些指标,为每次尝试记录一个路由和一个 span。
工作流 3:停止、协调或升级
有些失败不应该触发另一次模型调用。正确的回退是受控停止。
案例 1:部分流式输出
一旦响应 token 已经到达用户,静默切换模型可能会造成矛盾、重复内容、损坏的代码块,或突然的风格变化。这也会让最终响应难以归因和调试。
请改用以下明确结果之一:
- 以可恢复错误结束流,并提供一个“重试”操作。
- 提供从头重新开始回答的选项。
- 仅当应用程序有既定的恢复协议,且新模型接收到完全相同的已接受前缀时,才继续。
默认应为 allowAfterPartialOutput: false。
Case 2: 不确定的工具副作用
假设某个模型选择了支付、电子邮件、部署、工单或数据库写入工具。即使在你的编排器记录结果之前连接失败,该工具也可能已经成功。重放整个工作流可能会重复该副作用。
使用以下方式保护写入侧工具:
- 基于用户操作而不是提供方尝试生成的幂等键。
- 带有
planned、started、succeeded、failed和unknown状态的持久化执行记录。 - 在工具边界进行去重。
- 在任何重放之前进行一次对账查询。
- 对仍然不确定的高影响操作进行人工审核。
type ToolExecution = {
operationId: string;
toolName: string;
state: "planned" | "started" | "succeeded" | "failed" | "unknown";
externalReference?: string;
};
function nextAction(execution: ToolExecution): "continue" | "reconcile" | "stop" {
if (execution.state === "succeeded") return "continue";
if (execution.state === "failed") return "stop";
return "reconcile";
}
将提供方凭据与工具凭据分开管理。安全 API 密钥管理指南涵盖了相关的密钥和访问控制模型。
Case 3: 安全、权限或策略不确定性
可用性不应削弱安全或授权决定。如果回退候选项不支持所需的策略控制,则该路由不具备资格。如果系统无法判断某项操作是否被允许,应根据产品的风险模型采取默认拒绝或升级处理。
Case 4: 没有候选项满足契约
返回应用程序可以处理的类型化失败:
{
"status": "unavailable",
"reason": "no_eligible_fallback",
"retryable": true,
"retry_after_ms": 30000,
"request_id": "req_123"
}
清晰的降级响应,胜过一个看似成功、却违反 schema、使用了错误工具或执行了错误副作用的答案。
将这三个工作流整合为一个状态机
编排层应明确这一转换。
START
-> PRIMARY_ATTEMPT
-> SUCCESS: 验证并返回
-> TRANSIENT + replayable: WORKFLOW_1
-> CONTRACT_FAILURE + approved alternate: WORKFLOW_2
-> PARTIAL_OUTPUT or SIDE_EFFECT_UNCERTAIN: WORKFLOW_3
WORKFLOW_1
-> 在预算内重试
-> 在预算内进行等效故障转移
-> 如果允许兼容的替代项:WORKFLOW_2
-> 否则:STOP
WORKFLOW_2
-> 能力检查
-> 替代尝试
-> 合约和任务验证
-> 仅在验证成功后返回
-> 否则:STOP
WORKFLOW_3
-> 标记部分或不确定状态
-> 在可能时协调外部副作用
-> 提供显式重启或人工升级处理
-> 绝不静默重放不安全的工作
这也是多模型网关的正确边界。将模型访问集中到一个 OpenAI 兼容端点后,可以减少集成重复,但应用仍然需要提供工作流意图:截止时间、副作用模式、所需工具、schema 版本,以及是否允许跨模型回退。Flatkey 为希望在不同模型提供商之间使用一个密钥和一个统一集成入口的团队提供统一的 API 访问层;但最安全的路由策略仍然始于明确的应用合约。
在启用自动回退前先执行五次故障演练
从未处理过受控故障的回退路径,只是一张示意图。针对会触发不同安全边界的故障,测试每一类路由。
| 演练 | 注入条件 | 预期行为 | 需保留的证据 |
|---|---|---|---|
| 1. 主路径超时 | 将主模型延迟到超过其单次尝试超时 | 仅在总截止时间和尝试预算仍然充足时重试 | 尝试时间戳、前后预算、最终路由原因 |
| 2. 限流突发 | 返回一系列有上限的限流响应 | 应用抖动,遵循重试建议,并避免同步重试 | 退避分布、队列深度、恢复次数和截止时间耗尽次数 |
| 3. 无效结构化输出 | 返回 HTTP 成功但正文不符合 schema | 标记合约失败,仅尝试一个经批准且支持 schema 的替代项,再次验证 | 验证错误、候选资格记录、已接受任务结果 |
| 4. 流中断开连接 | 在用户可见 token 之后结束连接 | 停止流并要求显式重启 | 部分输出标志、面向用户的状态、未发生静默拼接的确认 |
| 5. 含糊的工具结果 | 在可能已执行写入类工具后丢弃响应 | 在任何重放之前按操作 ID 进行协调 | 幂等记录、外部状态查询、重复副作用计数 |
先在本地或预发布环境中运行这些演练,然后再在范围严格受限的生产 game day 中运行。目的不是证明每个请求都能存活下来,而是证明系统会以预期状态失败,能够暴露足够的证据来诊断事件,并且不会消耗比策略允许更多的延迟、成本或副作用风险。
对于每一次演练,分别验证以下四个层面:
- 决策正确性:路由器选择了预期的工作流。
- 预算正确性:所有尝试都保持在共享的截止时间、尝试次数和成本范围内。
- 输出正确性:最终结果通过了契约和任务验证,或返回了明确的降级状态。
- 审计正确性:跟踪记录捕获了策略版本、失败类别、候选资格、路由原因以及用户可见结果。
每当你更改提供商适配器、重试所有者、模型候选项、模式版本、工具契约或流式实现时,都要重复这项演练。即使公开 API 的形态看起来没有变化,这些变更也可能影响重放安全性。
在生产前使用回退就绪度评分卡
仅仅通过几个理想路径测试,并不足以启用自动回退。一个路由只有通过五个相互独立的发布门槛,才应获得自动化资格。
| 门槛 | 通过条件 | 证据 | 以下情况阻止自动回退 |
|---|---|---|---|
| 重放安全性 | 团队能够证明在每个尝试边界上,请求是否可以安全重复 | 副作用分类、幂等性设计、部分输出规则 | 在没有对账键的情况下可能已经发生了写入 |
| 契约兼容性 | 每个候选项都支持所需的上下文、工具、模式、多模态和策略控制 | 带版本的能力矩阵和契约测试 | 仅凭模型家族或营销标签就假定兼容 |
| 任务质量 | 替代方案能为该路由的真实工作负载产生可接受的结果 | 路由专用评估集和经审查的失败案例 | 只有传输成功或通用基准分数可用 |
| 预算控制 | 重试和回退共享同一个截止时间、尝试上限和成本上限 | 显示预算消耗的失败演练跟踪记录 | 多层可以独立重试,或超过调用方的截止时间 |
| 运行控制 | 值班工程师可以识别、禁用并解释一次回退决策 | 策略版本、路由原因、kill switch、仪表板、运行手册 | 若不完整部署整个应用,就无法隔离恢复路径 |
将评分卡视为发布工件。记录路由类别、策略版本、已批准候选项、评估器版本、演练结果、负责人和审查日期。单一全局的“已启用回退”标志会隐藏过多风险;批准应按工作流类别分别进行。
可复制的就绪记录
fallback_readiness:
route_class: support_ticket_extraction
policy_version: fallback-v4
owner: ai-platform
primary_target: primary-model
approved_candidates:
- equivalent-deployment
- alternate-model
gates:
replay_safety: pass
contract_compatibility: pass
task_quality: pass
budget_control: pass
operational_control: pass
evidence:
capability_matrix: contracts/support-ticket-v3.yaml
evaluation_set: evals/support-ticket-2026-08.jsonl
failure_drill_run: drills/2026-08-03.json
dashboard: ai-routing/support-ticket
runbook: runbooks/support-ticket-fallback.md
release:
mode: canary
rollback_owner: oncall-ai-platform
next_review_at: 2026-09-03
该文件不需要以这种确切格式存在。关键在于,发布决策是可审查的,并且与生产跟踪记录中记录的同一策略版本相关联。
分四个阶段推出模型回退策略
自动回退不应从离线测试直接跳到每个生产请求。请使用四个阶段,在决策错误变成用户可见之前将其暴露出来。
阶段 1:影子跟踪决策
以仅观察模式运行回退控制器。主路径仍然决定用户响应,而控制器记录它本来会怎么做。
审查:
- 策略将故障标记为可重试的频率。
- 候选模型具备资格的频率。
- 哪个预算会阻止恢复。
- 策略是否在部分输出或不确定的副作用之后建议回退。
- 提供方标准化后的错误是否保留了足够细节以用于事故诊断。
影子模式特别适合发现过于宽泛的规则,例如“每次 429 都回退”或“任何 schema 错误后都尝试另一个模型”。这些规则在代码审查中看起来合理,但在真实请求状态下可能表现很差。
阶段 2:对低风险工作流进行金丝雀发布
仅为一小部分回放安全流量启用回退,例如只读分类、提取或后台摘要。排除写入类工具、安全敏感决策以及具有用户可见流式输出的路由。
使用路由级结果将金丝雀与仅主路径进行比较:
- 被接受的任务率,而不仅仅是 HTTP 成功率。
- 恢复带来的额外延迟。
- 每个已接受任务的成本变化。
- 按候选模型划分的契约验证失败。
- 截止时间耗尽和无可用回退的比率。
- 用户取消或明确重试率。
不要因为提供方错误率下降就扩大金丝雀范围。只有当最终用户结果仍然可接受,并且恢复路径保持在其边界内时,才扩大范围。
阶段 3:按风险类别约束自动恢复
仅扩展通过就绪度评分卡的工作流类别。保持策略差异显式:
| 风险类别 | 默认自动化 | 所需保障措施 |
|---|---|---|
| 只读,无流式输出 | 重试、等效故障转移、经批准的跨模型回退 | 契约和任务验证 |
| 只读,带流式输出 | 仅在首个对用户可见字节之前恢复 | 部分输出状态和显式重启 |
| 使用工具,且为只读工具 | 在工具执行前重试;验证备用工具契约 | 工具 schema 和工具选择测试 |
| 使用会写入的工具 | 在执行结果含糊不清后停止并协调 | 持久化操作 ID 和外部状态查询 |
| 安全、权限或合规决策 | 按产品已批准的策略失败 | 不得出于可用性而降低策略要求 |
这一阶段是网关与应用契约交汇之处。网关可以标准化错误、强制执行预算并选择可用容量。应用仍必须说明输出是否已泄露、是否可能发生副作用,以及哪些质量或策略检查是强制性的。
阶段 4:逐步扩大并重新认证变更
以受控步长增加流量。在每一步中,都要保留禁用某个策略版本、某个路由类别、某个提供方适配器或某个候选项的能力,而无需关闭整个路由层。
当以下任一项发生变化时,重新运行相关的评分卡门控:
- 模型或模型版本。
- 提供方适配器或端点。
- 提示模板或系统指令。
- 工具定义或权限范围。
- 结构化输出 schema。
- 重试归属或超时配置。
- 流式传输或客户端行为。
- 安全策略或质量评估器。
当其假设发生变化时,回退就会失效。对于先前提示、schema 或工具集已获批准的候选项,不应因惯性而继续自动具备资格。
在启用金丝雀发布前定义回滚触发条件
只有当团队事先就何种情况会停止它达成一致时,金丝雀发布才是安全的。应使用与路由相关的触发条件,而不是等待大范围事故。
当你观察到以下情况时,应回滚或禁用受影响的策略:
- 重复或不确定的写入侧副作用。
- 跨模型契约成功但任务成功不可接受。
- 部分流失败或不可见响应拼接增加。
- 由恢复尝试导致的重复期限耗尽。
- 预算上限被超出或被忽略。
- 候选项选择违反了必需的能力或安全策略。
- 部署后回退原因分布出现无法解释的变化。
- 事故期间缺少策略版本或尝试级别的追踪数据。
回滚动作应与故障一样局部。根据事件不同,这可能意味着禁用某个候选项、将某条路由强制为仅等效故障转移、将 allowCrossModelFallback 设为 false、为某个提供方打开熔断,或将工作流恢复为仅主路径模式。
避免使用需要重建应用程序的回滚机制。在事故期间,恢复策略经常变化,而最安全的响应通常是带有可审计版本的配置变更,而不是紧急代码补丁。
为每次回退事件使用同一份事故工作表
当每个提供商暴露不同的错误形态、而每个应用又记录不同的请求状态时,回退事故就会变得难以诊断。请记录一份与提供商无关的工作表。
fallback_incident:
incident_id: inc-2026-08-03-001
route_class: support_ticket_extraction
request_id: req_123
policy_version: fallback-v4
request_state:
output_started: false
side_effect_mode: none
tool_execution_state: not_started
deadline_remaining_ms: 1820
attempts_remaining: 1
primary_failure:
normalized_class: overloaded
provider_status: 529
retry_guidance_present: true
recovery_decision:
workflow: cross_model_fallback
candidate: alternate-model
reason: equivalent_capacity_unavailable
validation:
transport_success: true
contract_success: true
task_success: false
failure_reason: required_field_omitted
user_outcome:
state: explicit_failure
partial_output: false
duplicate_side_effect: false
containment:
action: disable_candidate_for_route
owner: oncall-ai-platform
最重要的区别是恢复成功与用户成功之间的区别。回退请求可以返回有效的 HTTP 响应,但仍然可能在模式上失败、选错工具、遗漏必需事实,或违反该路由的质量阈值。事故复盘应当一路追踪结果,直到面向用户的任务。
运行一次 60 分钟的模型回退演练日
单元测试证明的是各个分支能够执行。回退演练日证明的是整个恢复系统在期限、重试、流、验证、工具、遥测和操作员控制相互作用时是否能正确运作。
每次只针对一种工作流类别开展演练。不要从全局提供商故障模拟开始。像只读抽取或内部摘要这样狭窄的路由,会产生更清晰的证据,并在策略有误时限制影响范围。
定义演练日章程
在任何人注入故障之前,先写一份一页章程。该章程可以防止演练变成即兴故障。
game_day:
id: fallback-gd-2026-08-04-extraction
route_class: structured_extraction
policy_version: fallback-v4
environment: staging
exercise_owner: ai-platform
incident_commander: reliability
primary_target: primary-model
approved_fallbacks:
- equivalent-deployment
- alternate-schema-capable-model
traffic_scope:
synthetic_requests: 100
production_percentage: 0
safety_limits:
stop_after_minutes: 60
max_error_rate_percent: 5
max_duplicate_side_effects: 0
max_unexplained_route_decisions: 0
success_definition:
- every request ends accepted, explicitly degraded, or safely stopped
- no request exceeds the shared attempt budget
- no partial stream is silently continued by another model
- every fallback decision includes a policy version and route reason
先使用合成流量或可回放且安全的流量。如果该路由可能触发写入,请将工具替换为受控测试替身,或使用支持幂等性查询的沙箱。演练日应测试恢复控制,而不是拿客户状态来冒险。
分配四个角色
保持团队足够小以便快速决策,但要将观察与执行分开。
| 角色 | 演练期间的职责 | 不得执行 |
|---|---|---|
| 演练负责人 | 启动场景,控制时间线,并宣布停止条件 | 在未记录的情况下于场景中途更改回退策略 |
| 操作员 | 监视路由健康状况,禁用候选项,并使用终止开关 | 注入故障或编辑证据 |
| 观察员 | 记录时间戳、截图、跟踪信息和用户可见结果 | 通过手动更正请求来帮助路由器“通过”测试 |
| 应用负责人 | 评判任务质量和工作流特定的退化情况 | 仅根据 HTTP 成功就批准结果 |
对于非常小的团队,一个人可以兼任两个角色,但注入故障的人不应是唯一评估系统是否正确响应的人。
构建场景阶梯
从最不含糊的故障开始,只有在路由通过前一阶后才增加风险。
| 阶梯 | 注入方式 | 路由器应证明什么 | 晋级要求 |
|---|---|---|---|
| 1. 干净的等效故障转移 | 在返回响应字节之前让主端点不可用 | 它可以切换到等效容量,而不改变应用契约 | 结果被接受,只有一个路由原因,共享预算得到遵守 |
| 2. 重试压力 | 返回一小段有上限的可重试错误突发 | 退避和抖动可在不放大尝试次数的情况下生效 | 没有嵌套重试放大;截止时间仍然具有权威性 |
| 3. 语义契约失败 | 返回一个传输成功但结构无效的结果 | 由校验而不是状态码来控制是否接受 | 备用项符合条件,其结果通过同一验证器 |
| 4. 部分流 | 在可见输出之后断开连接 | 系统停止并将答案标记为部分完成 | 没有静默的模型拼接;重启是显式的 |
| 5. 不确定的工具完成 | 在写入可能已执行后丢失模型响应 | 工作流在回放前协调外部状态 | 操作 ID 查询完成;重复写入始终为零 |
| 6. 回退退化 | 让已批准的备用项变慢或质量更低 | 止损和回滚规则优先于可用性压力 | 候选项在预定义阈值处被移除,或自动化被禁用 |
不要直接跳到复杂的跨模型场景。如果等效故障转移无法保住预算和跟踪契约,那么再加入一个行为不同的模型只会让诊断更困难,而不会更真实。
在明确边界注入故障
标注故障进入请求生命周期的精确边界。“提供方失败”对有用的测试记录来说过于含糊。
type InjectionPoint =
| "before_connect"
| "after_connect_before_headers"
| "after_headers_before_body"
| "after_partial_stream"
| "after_tool_dispatch_before_ack"
| "after_tool_ack_before_model_response"
| "after_transport_success_before_validation";
边界决定哪些恢复操作是安全的。在连接前发生的超时通常可以重试。用户已经看到输出后的断开连接需要显式重启。写侧工具调用之后丢失确认需要进行对账。把这三者都当作同一种超时类别处理,才会让重复操作和前后不一致的响应进入生产环境。
如果你的故障注入层无法定位这些边界,请在演练前将边界标记添加到提供方适配器或编排层。粗粒度的故障开关对可用性测试有用,但对重放安全测试来说不够。
每个请求记录一行证据
Game day 应该产出请求级账本,而不仅仅是仪表盘截图。紧凑的一行记录能让无法解释的决策显现出来。
| 字段 | 示例 | 重要性 |
|---|---|---|
request_id |
req_01J... |
关联网关、模型、校验器和工具证据 |
scenario_id |
partial-stream-01 |
将结果与注入条件对应起来 |
policy_version |
fallback-v4 |
证明是哪条路由规则做出了决策 |
failure_class |
stream_interrupted |
区分传输、契约、策略和工具方面的不确定性 |
injection_point |
after_partial_stream |
确立重放安全性 |
attempts_used |
1/2 |
检测重试放大 |
elapsed_ms |
4830/12000 |
显示剩余的截止时间预算 |
cost_budget_state |
within |
防止恢复过程忽视单位经济性 |
selected_action |
restart_required |
记录路由器的决策 |
candidate_id |
none |
显示是否考虑过其他模型 |
validator_result |
not_run |
区分传输恢复与任务接受 |
side_effect_state |
none |
明确对账要求 |
user_outcome |
partial_marked |
记录客户实际体验到的结果 |
operator_action |
none |
区分自动恢复与人工遏制 |
将该账本与策略快照、校验器版本、故障配置和仪表盘导出一并存储。没有这些版本信息,下一次适配器或模型变更之后,就无法复现一次通过的演练。
用晋升规则为演练评分
使用三种可能的决策:晋级、修复并重新运行,或停止自动化。避免使用模糊的“基本通过”结果。
仅当以下所有条件都成立时,才将路线晋级:
- 每个请求都有一个已解释的终态。
- 没有任何尝试链超过共享截止时间、尝试次数或配置的成本上限。
- 每个被接受的输出都通过该路线的验证器或评估规则。
- 部分输出和不确定的副作用进入明确的停止或协调状态。
- 运营人员无需部署应用代码即可禁用某个候选项或整个策略。
- 告警能够识别恢复失败和有害恢复,例如回退成功但任务质量不可接受。
当安全模型正确,但证据或实现不完整时,选择修复并重新运行。例如,缺少路线原因、告警触发太晚,或某个候选项通过了契约但未达到延迟目标。
当演练发现重放歧义、重复副作用、静默流拼接、无法解释的路由、策略绕过,或当前状态机无法表示的故障模式时,选择停止自动化。这些是设计缺口,不是调优问题。
使用可复制的战日评分卡
game_day_result:
game_day_id: fallback-gd-2026-08-04-extraction
route_class: structured_extraction
policy_version: fallback-v4
evaluator_version: extraction-eval-v7
started_at: 2026-08-04T09:00:00Z
completed_at: 2026-08-04T10:00:00Z
scenarios:
equivalent_failover: pass
retry_pressure: pass
semantic_contract_failure: pass
partial_stream: pass
uncertain_tool_completion: not_applicable
fallback_degradation: fix
totals:
requests: 100
accepted: 94
explicitly_degraded: 6
unsafe_or_unexplained: 0
duplicate_side_effects: 0
deadline_violations: 0
decision: fix_and_rerun
blockers:
- alternate p95 latency exceeded the route objective during degradation
owner: ai-platform
rerun_due: 2026-08-11
示例值仅用于说明。请使用你自己的路线目标和评估阈值。关键在于最终决策要指向保留证据和指定负责人。
将发现转化为发布控制
通过将每个发现转化为以下四类持久控制之一,结束战日:
- 策略变更:候选资格、尝试预算、截止时间或路线类别规则。
- 契约测试:能力、模式、工具、流式传输或安全兼容性检查。
- 运营控制:告警、仪表板、熔断开关、候选隔离或事件流程。
- 产品行为:显式重启、降级状态消息、人工确认或协调界面。
不要用一份观察列表来结束演练。没有负责人、控制类型和重新运行条件的发现,会在真实事件中再次出现。
对于这些演练背后的遥测层,请使用LLM API 可观测性指南。关于重试归属和速率限制行为,请将 game day 与LLM 速率限制与重试策略配套使用。如果你的团队仍在定义网关边界,请从LLM 网关入门指南开始。
七天实施顺序
团队可以按照这个顺序,从临时性的模型列表推进到受控的工作流作战手册:
- 第 1 天 — 清点路由:对输出模式、副作用风险、工具、模式、截止期限以及当前重试归属进行分类。
- 第 2 天 — 定义边界:按路由类别设置尝试次数、延迟、成本、能力和重放限制。
- 第 3 天 — 建立契约:记录已批准的候选项,并测试工具、模式、上下文、多模态和策略兼容性。
- 第 4 天 — 为决策加仪表:记录归一化故障、请求状态、策略版本、候选资格、预算、验证结果和用户结果。
- 第 5 天 — 运行故障演练:注入超时、速率限制突发、无效输出、中途断开连接以及含糊的工具执行。
- 第 6 天 — 影子运行与金丝雀发布:先观察决策,然后对一个预先定义回滚触发条件的低风险窄路由启用。
- 第 7 天 — 复盘并扩展:在扩大范围之前,检查已接受任务率、额外延迟、成本差异、不安全重放信号以及无可用回退事件。
这个顺序刻意以工作流为先。选择一个排序模型列表只是很小的一步。真正的生产工作是证明系统何时可以继续、何时必须验证,以及何时必须停止。
模型回退策略上线检查清单
策略
- 每个路由类别都有一个回退边界。
- 回退策略以版本化且可审阅的配置形式存在。
- 可重试错误在各个提供商之间被统一归一化。
- 总重试预算只有一个负责人。
- 等效端点与备用模型区分开来。
- 跨模型候选项具有版本化的能力契约。
- 默认情况下,部分输出会禁用透明回退。
- 写入侧工具使用持久化的幂等记录。
验证
- 传输、契约和任务成功率分别进行度量。
- 结构化输出在回退后进行验证。
- 针对每个模型测试工具参数和工具选择行为。
- 回退评估集代表真实的路由类别。
- 新候选项需通过离线评估和生产金丝雀发布。
- 对每个适用的路由类别,五项故障演练都必须通过。
运维
- 每次尝试都会记录路由原因、目标、延迟和结果。
- 仪表板会分别显示主路径、重试、等效故障切换和跨模型恢复。
- 告警包括期限耗尽和无可用回退率。
- 熔断器使用受控的半开探测。
- 事故复盘包括用户可见质量和重复副作用风险。
- 每次尝试轨迹都会记录当前生效的回退策略版本。
- 每条路由都有已完成的就绪检查表和指定负责人。
- 金丝雀回滚触发条件和窄范围 kill switch 都要经过测试。
- 事故工作表会记录请求状态、验证和用户结果。
证明回退正在发挥作用的指标
不要只针对提供商错误率进行优化。要跟踪用户结果。
| 指标 | 回答的问题 |
|---|---|
| 重试恢复率 | 同一目标上的重试是否值得其延迟? |
| 等效故障切换恢复率 | 冗余容量能否安全地恢复服务? |
| 跨模型契约成功率 | 备用响应是否满足所需接口? |
| 跨模型任务成功率 | 用户是否仍然完成了预期工作? |
| 新增回退延迟 | 恢复会增加多少延迟? |
| 回退成本增量 | 恢复路径的成本是多少? |
| 部分流失败率 | 系统多大程度上会进入无法恢复的展示状态? |
| 副作用协调率 | 系统需要多频繁在继续之前验证外部状态? |
| 重复副作用事故 | 重放保护是否失效? |
| 无可用回退率 | 路由契约是否过于严格,还是容量不足? |
按路由类别对这些指标进行分段。汇总恢复率可能会掩盖这样一个事实:回退对抽取效果很好,但对代码生成或工具使用效果很差。
在发布过程中,按策略版本和发布阶段比较这些指标。这样就可以把提供商事故与控制器变更、候选变更或扩大金丝雀范围区分开来。
常见问题
什么是模型回退策略?
模型回退策略是一种用于决定 AI 请求何时应重试同一目标、故障切换到等效容量、切换到已批准的替代模型,或者因为重放不安全而停止的策略。
重试和回退有什么区别?
重试是对同一目标或部署重复请求。等效故障切换会把请求转移到旨在保持相同模型契约的容量上。跨模型回退会更换模型,因此需要对能力和质量进行验证。
每个 429 错误都应该触发另一个模型吗?
不应该。首先要对限制类型进行分类,遵守重试指导,检查剩余期限,并使用有边界的重试或队列。在已批准的替代容量存在时,切换模型可能有帮助,但它也可能改变输出质量、工具行为或成本。
流式响应可以在回答中途回退吗?
通常来说,在 tokens 已经到达用户之后就透明切换并不更安全。除非应用有经过测试的恢复协议,否则应停止流并提供显式重启。
一个路由应该有多少个回退模型?
使用经过批准且数量最少、但能提供有意义恢复的模型集合。每增加一个候选项,都会增加评估、监控和事件响应工作。一个很长但未经测试的列表并不等于韧性。
回退逻辑应该放在哪里?
将提供方规范化、路由、尝试预算和可观测性集中在网关或编排层中。将特定于工作流的意图——副作用风险、schema 要求、安全策略和质量阈值——保留在应用附近。
团队应该如何推出自动模型回退?
先以影子模式启动,仅对可回放且安全的工作流做金丝雀发布,在扩大流量之前定义回滚触发条件,并且在模型、提示词、工具、schema、重试归属或安全要求发生变化时重新认证回退策略。
围绕工作流风险构建回退
最好的模型回退策略不是“试下一个模型”。它是一个有边界的决策系统:
- 工作流 1 使用重试和等效容量恢复可重放请求。
- 工作流 2 仅在能力和质量检查通过后切换模型。
- 工作流 3 当输出或副作用使恢复不安全时,停止自动重放。
这种设计在提升可用性的同时,不会掩盖契约失败或重复用户操作。如果你的团队正在标准化跨模型提供方的访问,请使用 Flatkey 的统一 OpenAI 兼容 API 层作为集成面,然后将这些特定于工作流的封装附加到每条生产路由上。



