当你的生产系统同时接入 Claude 和 OpenAI,真正难的不是“接上 API”,而是在故障发生时还能稳态服务。一个供应商偶发 429/5xx、区域波动或模型超时,都会把下游体验打穿。

这篇给你一套可直接落地的双供应商网关方案:健康探测、熔断切换、SLA 分级回退、以及可观测性闭环。目标不是追求“永不失败”,而是失败可控、成本可控、体验可控

先给结论:三层防线

  • 第一层:主动健康探测(分钟级)
    • 按模型维度探测 p95 latencyerror rate429 rate
  • 第二层:熔断 + 半开试探(秒级)
    • 连续错误触发熔断,短窗口后半开试探恢复
  • 第三层:SLA 回退策略(请求级)
    • 按请求等级自动降级:质量优先 / 成本优先 / 延迟优先

没有这三层,你的“多供应商”通常只是多了两份账单。

架构图(文字版)

  1. Client 带上 x-sla-tier(gold/silver/bronze)和 x-intent(chat/code/summarize)
  2. Gateway 根据路由策略选主模型(例如 claude-sonnetgpt-4.1
  3. 发送前先查熔断状态 + 近 1 分钟健康分
  4. 失败时按策略回退:同供应商降级模型 → 异供应商切换
  5. 所有决策写入 metrics/log,支持回放与审计

1) 健康探测:别只看“活着没”

只做 /healthz=200 基本没意义。你需要“可服务健康度”评分。

推荐指标

  • provider_request_success_ratio_1m
  • provider_p95_latency_ms_1m
  • provider_429_ratio_1m
  • provider_5xx_ratio_1m
  • provider_timeout_ratio_1m

评分公式(示例)

health_score = 100
  - (p95_latency_ms / 1000) * 8
  - timeout_ratio * 60
  - ratio_5xx * 80
  - ratio_429 * 40

if health_score < 65 => unhealthy

2) 熔断与切换:宁可短时降级,不要雪崩

熔断参数(可直接起步)

  • 连续失败阈值:5
  • 打开时长:30s
  • 半开试探请求数:3
  • 半开成功阈值:2/3

Go 伪代码

type CircuitState string
const (
  Closed CircuitState = "closed"
  Open   CircuitState = "open"
  HalfOpen CircuitState = "half_open"
)

func Route(req Request) Target {
  candidates := rankedTargets(req) // 按 intent + 成本 + 质量排序
  for _, t := range candidates {
    if breaker[t].Allow() && health[t] >= 65 {
      return t
    }
  }
  return emergencyFallback(req) // 最后兜底
}

3) SLA 回退:把“失败策略”写成规则,而不是临时拍脑袋

三级 SLA 建议

  • gold(关键请求)
    • 允许跨供应商切换 + 重试 1 次 + 高质量模型优先
  • silver(默认请求)
    • 允许同供应商降级模型,再跨供应商一次
  • bronze(成本敏感)
    • 禁止高价回退,超预算直接返回降级结果

YAML 策略样例

sla:
  gold:
    max_retries: 1
    allow_cross_provider_failover: true
    quality_floor: high
  silver:
    max_retries: 1
    allow_cross_provider_failover: true
    quality_floor: medium
  bronze:
    max_retries: 0
    allow_cross_provider_failover: false
    quality_floor: basic
    token_budget_per_req: 4000

4) 最小可行网关(MVP)命令清单

启动本地策略热更新(示例)

export ROUTER_CONFIG=/etc/llm-router/policy.yaml
export ROUTER_REFRESH_SEC=15
./llm-gateway --listen :8080

压测主路由与回退

hey -z 30s -c 20 -m POST \
  -H 'x-sla-tier: silver' \
  -H 'content-type: application/json' \
  -d '{"intent":"chat","prompt":"explain circuit breaker"}' \
  http://127.0.0.1:8080/v1/respond

人工触发熔断演练

curl -X POST http://127.0.0.1:8080/admin/breakers/open \
  -d '{"provider":"claude","model":"sonnet"}'

常见错误与排障

错误 1:频繁来回切换(抖动)

现象: 路由在两家供应商之间疯狂跳,延迟反而更高。
根因: 没有最小停留时间(min dwell time)。
修复: 给每条路由加 hold_for: 20s,在窗口内只允许更高优先级故障打断。

错误 2:熔断恢复后立刻再炸

现象: Half-open 一放量就再次超时。
根因: 半开探测流量过大。
修复: 半开阶段限制并发 + 单独超时预算(例如 2s),先小流量恢复。

错误 3:成本失控

现象: 故障时大量请求被切到更贵模型,账单暴涨。
根因: 缺少“故障态预算闸门”。
修复: 增加 degraded_mode_cost_cap,超上限后强制走 silver/bronze 策略。

观测与告警(必须做)

至少拉齐这三类面板:

  • 可用性:按 provider/model 的成功率与错误率
  • 体验:按 SLA 分组的 p50/p95 延迟
  • 成本:token 消耗与每 1k 请求成本

告警不要设太多,先把这两条做稳:

  • gold tier success_ratio_5m < 99%
  • failover_rate_5m > 15%

总结

双供应商不是“两个 API key”,而是一套容灾操作系统。先把健康探测、熔断、SLA 回退三件事落地,再谈高级优化(A/B 路由、语义缓存、智能预算)。

如果你现在要上生产,先做 MVP:

  1. 每分钟健康评分
  2. 固定阈值熔断
  3. gold/silver/bronze 三档回退

这三步做完,90% 的线上惊魂夜会少很多。