AI API 回退策略:建立提供商故障矩阵
根据故障类型、响应阶段、幂等性和 Attempt Budget,决定何时重试、同契约回退、停止、核对副作用或调查路由。
AI API 的重试、路由回退与模型替换是三种不同动作。重试在相同契约下重复一次尝试;路由回退把所请求的模型和协议发送到另一条符合条件的渠道或分组;模型替换则有意改变请求的模型,可能同时改变质量、价格、延迟、工具行为、Context 上限与输出格式。
可靠的策略应在事故发生前决定允许哪种动作。决策必须考虑故障类型、是否已经向调用方发送响应、重放是否具备幂等性,以及用户 Deadline 内还能容纳多少次尝试。
区分重试、回退与模型替换
在配置、日志和 Runbook 中使用不同名称。
| 动作 | 发生的变化 | 合适用途 | 主要风险 |
|---|---|---|---|
| 同路由重试 | 时间与 Attempt Number | 从同一路由的短暂瞬时故障中恢复 | 持续向不健康的依赖增加负载 |
| 同契约回退 | 上游渠道、账户或显式排序的分组 | 某条路径失败时保持所请求的模型与协议 | 名义等价的路由之间存在隐藏兼容差异 |
| 模型替换 | 模型 ID 或命名模型策略 | 产品明确批准的质量、成本或可用性权衡 | 静默改变行为与账单 |
不要把这三种动作都叫作“重试”。运营人员需要知道请求是被重新执行、转到另一条路径,还是由另一个模型回答。对用户而言,模型替换必须是明确的产品契约,而不是不可见的恢复捷径。
一些 Gateway 支持有序的模型或提供商步骤。例如,Cloudflare 回退文档会暴露最终成功的是哪一步。这里的重要设计原则不是复制某个厂商的具体策略,而是在路由变化时保留 Attempt 级证据。
重放请求前通过四道 Gate
仅凭状态码无法构成重试策略。应评估四项条件:
- **故障类型:**故障是瞬时、永久、由调用方造成,还是结果不明确?
- **响应阶段:**故障发生在 Header 前、有效输出前,还是输出已经送达之后?
- **幂等性:**完整应用操作能否重复执行而不产生重复副作用?
- **尝试预算:**剩余时间是否足够,并且还有未使用的 Attempt?
Google Cloud 重试策略也为一般 API 强调两个基础判断:响应决定重试是否可能有用,幂等性决定重放是否安全。该文档把 408、429、5xx、Socket Timeout 与断连列为常见瞬时错误,同时提醒非幂等操作需要更严格的条件。
在 AI 工作流中,幂等性不止涉及模型 HTTP 请求。重放同一 Prompt 可能再次提出发送邮件、退款、部署或数据库写入。因此,即便推理调用本身只读,Tool Execution 也要使用独立且稳定的幂等键,并持久化执行结果。
从故障矩阵开始制定策略
下表是一套保守的应用侧策略。Gateway 可能在应用看到最终结果之前,已经完成内部同契约渠道回退,因此两层策略必须协同,而不能彼此重复。
| 故障或阶段 | 同路由重试 | 同契约回退 | 停止或调查 | 原因 |
|---|---|---|---|---|
| 客户端校验错误、不支持字段或请求格式错误 | 否 | 否 | 修正请求 | 重放相同非法契约不会成功 |
| Gateway 鉴权、授权、额度或策略拒绝 | 否 | 否 | 停止并修正账户或策略 | 不能通过另一提供商路由绕过 Gateway 决策 |
| 输出前出现上游凭据或账户故障 | 不再使用故障路由 | 可以,前提是存在已验证的合格渠道 | 隔离并调查故障渠道 | 可以在移除不健康凭据的同时保持用户契约 |
响应输出前出现网络故障或 408 |
幂等时最多一次有界尝试 | 可以 | 到达 Deadline 后停止 | 故障可能是瞬时的,但断连后是否完成可能不明确 |
输出前出现 429 |
延迟重试时遵循 Retry-After |
另一条同契约路由有容量时可以 | 预算耗尽后停止 | 立即连续重试会放大限流 |
输出前出现上游 500、502、503 或 504 |
使用 Backoff 的有界重试 | 可以 | 路由反复失败时调查 | 通常是瞬时故障,但不能证明所有路由都安全 |
| 向下游输出前收到 Schema 非法或格式错误的提供商响应 | 通常不重试 | 只回退到已验证相同 Schema 的路由 | 隔离或调查兼容性 | 重复调用同一不兼容实现通常无效 |
| 模型拒绝或符合安全策略的完成结果 | 否 | 否 | 返回模型结果 | 有效拒绝不是需要绕过的基础设施故障 |
调用方取消或下游 499 |
否 | 否 | 立即停止 | 调用方已经不需要结果,重试只会浪费容量和成本 |
| 可见内容或 Tool Argument 已送达后的部分 Stream | 不做透明重放 | 不做透明回退 | 标记为 Partial,由应用决定 | 第二条 Stream 可能重复或矛盾于已交付输出 |
| Tool 副作用的完成状态不明确 | 完成核对前不重试 | 完成核对前不回退 | 查询幂等记录或下游系统 | 再次推理可能提出相同副作用 |
这张矩阵刻意区分响应阶段。任何下游输出之前收到 503,与已经渲染 400 个文本 Token 后连接断开,不是同一种故障。
把已经开始的 Stream 视为 Commit Boundary
在下游输出开始前,Gateway 通常可以丢弃失败 Attempt,并尝试另一条合格路由,而不暴露两份答案。首个有效输出 Byte 送达调用方后,透明重放就不再安全。
重新启动的 Stream 可能:
- 重复答案开头;
- 生成不同的后续内容;
- 用新的 Call ID 发出重复函数调用;
- 在缺少清晰边界的情况下改变用量和成本;
- 使客户端无法判断 Event 属于哪一次尝试。
如果 Stream 在输出开始后断开,应使用原始 Request Identity 返回明确的 Partial 或传输错误。应用可以提供显式“重试”操作、从安全的应用检查点继续,或丢弃部分输出。不能像什么都没发生一样,把新的模型 Stream 拼接到旧 Stream 后面。
对于 Function Calling,安全边界更严格:在任何重试能够重新产生调用之前,先持久化已经接受的 Tool Call Identity 与副作用结果。Function Calling 对比说明了 Call ID 与应用幂等性如何配合。
同时限制 Backoff、Attempt 与总时长
Exponential Backoff 会把重复尝试分散到更长时间。Jitter 可以避免共享故障恢复时,大量客户端同步重试。
delay_cap = min(max_delay, base_delay * 2^retry_index)
sleep_for = random_between(0, delay_cap)
当有效的 Retry-After 没有超过用户 Deadline 时,应遵循它。Backoff 本身不是重试许可,故障与幂等性 Gate 必须先通过。
应定义总预算,而不只是重试次数:
- 一次用户操作的最大 Attempt 数;
- 包含排队与 Backoff 的最大总时长;
- 选择回退分组前后的最大 Attempt 数;
- 生成有用答案所需的最小剩余时间;
- 从调用方向每个活跃 Attempt 传播取消信号。
对 Deadline 为 15 秒的交互请求,执行三次每次 10 秒的 Attempt 并不是真实可行的策略。后续 Attempt 无法在用户契约内完成。批处理工作负载可以使用更长预算,但仍需设置最终 Deadline 与持久化 Job Identity。
防止多层重试放大
假设 SDK 发送一次初始请求并重试两次,即三个客户端 Attempt。Gateway 对每个客户端 Attempt 执行一次主渠道和两次渠道回退,即三个 Gateway Attempt。上游 Proxy 又为每次 Attempt 重试一次,即两次提供商调用。
3 client attempts × 3 gateway attempts × 2 upstream attempts = 18 provider calls
一次用户操作现在变成 18 次提供商调用。事故期间,这会同时增加队列、限流、成本与恢复时间。
应明确分配重试 Owner:
- Gateway 负责即时的同契约渠道回退;
- 应用决定完整用户操作是否可以再次执行;
- Gateway 已重试时,SDK 自动重试应关闭或严格限制;
- 异步任务使用一个持久 Job ID 和 Attempt Ledger;
- 调用方取消后,任何一层都不能开始新 Attempt。
同时记录“当前层的 Attempt Number”和稳定的端到端 Request Identity。否则每一层看起来都只尝试了两三次,组合后的放大却被隐藏。
验证回退是否真的保持契约
相同的公开模型名称,不能证明两条路由行为完全相同。把渠道加入同契约回退集合前,应测试:
| 契约范围 | 必须保留的证据 |
|---|---|
| 模型身份 | 请求模型仍然可用,且没有静默 Mapping |
| 端点 | Responses 或 Chat Completions 请求能按配置接受 |
| Streaming | Event Type、终止、用量与取消都被正确处理 |
| Structured Output | 所需 JSON Schema 子集与 Strict 行为有效 |
| Function Calling | Tool 定义、Call ID、参数 Streaming 与结果能完整往返 |
| 限制 | Context、输出、Rate 与并发限制适合工作负载 |
| 错误 | Status 与 Error Body 可以分类,且不暴露 Secret |
| 用量与成本 | 已理解 Token 核算、Cache 字段、Service Tier 与价格策略 |
| 安全与区域 | 满足要求的策略、数据路径与驻留约束 |
如果候选路由在任何必需范围失败,它就不是该工作负载的透明回退。它仍可能在另一项明确的产品策略下使用。
切换到另一个模型始终属于这样的策略决策。必须定义允许的模型、质量下限、价格上限、Tool Contract 和面向用户的说明。不能仅仅因为路由报错,就使用更便宜或能力更弱的模型。
理解 Modelflare 当前的回退语义
Modelflare 会为 API 客户端请求的模型搜索符合条件的路径。普通 API Key 有一个主分组,并可配置有序回退分组;Smart API Key 根据配置的路由策略,在账户可用分组中进行评估。两种机制都不应静默地把请求模型替换为另一个模型。
分组 RPM 准入发生在计费和任何上游请求之前。如果所选分组已满,可以继续评估有序回退分组或 Smart Routing 候选;没有合格分组时,请求返回 429。
在所选分组内,渠道优先级定义账户回退顺序。上游错误发生后,故障渠道会被排除,并继续选择剩余合格渠道。当前渠道回退独立于 RetryTimes 和 AutomaticRetryStatusCodes。它在成功、合格路由耗尽、调用方取消,或下游响应已经开始而不能透明重放时停止。
这些是内部同模型路径决策,不代表客户端可以再增加一层无限重试。使用 Reliable AI API Routing设计分组与渠道,再通过 AI API 错误排查区分 Gateway 策略错误与上游故障。
为每次 Attempt 保留证据
最终 200 不能证明第一条路由成功;最终 Channel ID 也无法描述失败 Attempt。应保留足够元数据来还原完整路径:
- 稳定 Request ID 与调用方可见的 Correlation ID;
- Attempt 顺序以及所选分组/渠道引用;
- 每次 Attempt 使用的模型与端点契约;
- 故障 Status、Error Class 与 Stream 阶段;
- 下游输出是否已经开始;
- 时间里程碑与取消状态;
- 可用时的输入、输出与缓存 Token 用量;
- 按已完成或可计费 Attempt 归属成本;
- 最终原因:成功、耗尽、取消、部分完成或策略停止。
不要仅为诊断回退而保存 API Key、原始 Prompt、原始 Response 或提供商凭据。经过脱敏的 Error Class 与时间元数据通常已经足够;只有在明确配置的失败调查中,才使用受限且短期保留的请求归档。
在生产流量依赖策略前完成演练
使用真实协议边界与安全、确定性的输入执行 Staging 演练:
- 在 Header 前使主渠道不可用,并验证下一条合格的同模型路径;
- 返回限流,验证 Attempt 上限与
Retry-After处理; - 取消调用方,并证明之后没有新 Attempt 启动;
- 在输出后中断 Stream,并证明没有透明重放;
- 发送非法请求,并证明回退不会掩盖错误;
- 重复一次工具工作流,并证明只记录一次副作用;
- 耗尽全部路由,并验证只返回一个清晰的最终错误;
- 检查 Attempt Ledger,并核对用量与成本。
先把策略发布到小范围工作负载,分别监控 Attempt Count、回退后成功率与原始成功率,并保留快速移除不健康路由的能力。目标不是最大化回退频率,而是在有界 Deadline 内、保留每次 Attempt 证据的前提下,仅在能够安全保持原始模型契约时恢复请求。