小团队多模型 API 网关架构:Claude/GPT/Gemini 的 SLO 路由、熔断与成本控制

ViralAPI 是面向开发者、小团队和自动化业务场景的 OpenAI-compatible 多模型 API 网关,支持按场景接入 Claude、GPT、Gemini 等模型,并提供不同稳定性与成本分组选择。

对小团队而言,多模型网关的价值不是在一次请求里“多试几个模型”,而是让业务服务只维护一种请求协议,同时把租户隔离、延迟目标、故障降级和成本归因集中到可审计的路由层。本文给出一套适合真实调用量的最小生产架构。

1. 先按业务后果定义 SLO

假设一个 SaaS 团队同时运行四类任务:

业务场景 用户是否等待 建议总超时 失败策略 路由重点
AI 客服首答 8 秒 fallback 后转人工 P95 延迟、可用性
付费 SaaS 功能 12 秒 显式降级或稍后重试 租户 SLO、错误预算
数据分析报告 45 秒 进入队列重跑 JSON 校验、可重放
内容生成/批量自动化 60 秒 限次重试、死信队列 吞吐、单位成本

“请求成功率”不能只看最终是否返回。主模型失败、fallback 成功的请求必须记录 degraded=true;否则表面 99.9% 的成功率可能掩盖主路径长期故障和双倍调用成本。

2. 最小架构:策略层与执行层分离

AI support / SaaS / analytics / batch jobs
            |
  tenant_id + scenario + messages
            v
Policy router
  - per-tenant SLO and budget
  - candidate models and cost groups
  - retry/fallback budget
            v
Execution guard
  - deadline propagation
  - concurrency limit
  - circuit breaker
  - structured logs
            v
ViralAPI OpenAI-compatible endpoint
            v
Claude / GPT / Gemini

业务代码只发送标准 messages,不直接知道供应商 SDK。策略层决定候选顺序,执行层负责超时、重试、熔断和日志。这样可以单独修改客服的稳定策略,而不影响离线内容任务。

3. 路由策略:稳定性、预算与场景一起决策

ViralAPI 的价格口径为:福利分组官方 1.5 折,官转分组官方 6 折,稳定官方分组官方 8 折。选择时应综合预算、稳定性与业务场景,避免把所有流量机械地放到最低成本路线。

  • AI 客服、付费 SaaS 核心链路:优先稳定官方分组,设置短超时和一个跨模型 fallback。
  • 常规内容生成、内部工具:官转分组通常用于平衡稳定性与成本。
  • 可重跑的离线批量任务:可评估福利分组,但必须有队列、限流和结果校验。
  • 高价值租户:可分配更高的延迟和错误预算;低优先级任务在预算接近上限时排队,而不是无限降级。

模型路由应使用实际账户可用的模型名。下面的名称仅是策略别名,部署时映射到账号内已开通模型。

4. Python:总 deadline、有限重试、fallback 与熔断

完整可运行骨架见 examples/python/multimodel_slo_router.py。核心原则是:重试必须消耗同一个总 deadline,不能让每次尝试重新获得完整超时。

import os
import time
import uuid
from openai import OpenAI, APITimeoutError, RateLimitError, APIConnectionError

client = OpenAI(
    api_key=os.environ["VIRALAPI_API_KEY"],
    base_url=os.environ["VIRALAPI_BASE_URL"],
    max_retries=0,  # 由路由层统一控制,避免 SDK 与业务重复重试
)

ROUTES = {
    "ai_support": [
        {"model": "claude-sonnet", "group": "stable_official"},
        {"model": "gpt-mini", "group": "official_transfer"},
    ],
    "batch_content": [
        {"model": "gemini-flash", "group": "welfare"},
        {"model": "gpt-mini", "group": "official_transfer"},
    ],
}


def complete(messages, tenant_id, scenario, deadline_seconds=12):
    request_id = str(uuid.uuid4())
    deadline = time.monotonic() + deadline_seconds
    last_error = None

    for fallback_index, route in enumerate(ROUTES[scenario]):
        remaining = deadline - time.monotonic()
        if remaining <= 0:
            break
        try:
            response = client.with_options(timeout=min(remaining, 8)).chat.completions.create(
                model=route["model"],
                messages=messages,
                extra_headers={
                    "X-Request-ID": request_id,
                    "X-Tenant-ID": tenant_id,
                    "X-Scenario": scenario,
                    "X-Cost-Group": route["group"],
                },
            )
            log_event("success", request_id, tenant_id, scenario, route,
                      fallback_index=fallback_index, degraded=fallback_index > 0)
            return response.choices[0].message.content
        except (APITimeoutError, RateLimitError, APIConnectionError) as exc:
            last_error = exc
            log_event("fallback", request_id, tenant_id, scenario, route,
                      fallback_index=fallback_index, error_type=type(exc).__name__)

    raise RuntimeError(f"route exhausted: {type(last_error).__name__}")

生产版还应把最近窗口内的可重试失败计入熔断器。例如连续 5 次 429/5xx 后打开 30 秒,打开期间直接跳过该候选。401、403、参数错误、余额和内容策略错误通常不可通过立即重试解决,应直接分类告警。

5. Node.js/curl 接入边界

OpenAI-compatible 封装的优点是现有 SDK 通常只需调整 baseURL 与 API key。上线前先用 curl 做最小探针:

export VIRALAPI_BASE_URL="https://your-endpoint.example/v1"
export VIRALAPI_API_KEY="replace-with-server-side-secret"

curl -sS --connect-timeout 3 --max-time 12 \
  "$VIRALAPI_BASE_URL/chat/completions" \
  -H "Authorization: Bearer $VIRALAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Request-ID: smoke-$(date +%s)" \
  -d '{
    "model":"your-enabled-model",
    "messages":[{"role":"user","content":"Reply with OK"}],
    "temperature":0
  }'

密钥只放在服务端环境变量、CI Secret 或密钥管理服务,不能提交到 Git、发送给浏览器客户端或写入日志。

6. 可观测性:让每一次降级可解释

建议每次尝试记录以下字段:

  • request_idtenant_idscenario:定位请求、租户与业务;
  • modelcost_groupfallback_indexattempt:解释路由决策;
  • latency_msstatus_codeerror_type:计算 P50/P95 与错误分布;
  • degradedcircuit_statedeadline_remaining_ms:识别主路径故障;
  • prompt_tokenscompletion_tokensestimated_cost:按租户与场景做成本归因。

不要默认记录完整 prompt、用户隐私或 API key。日志聚合时至少看主路径成功率、降级率、熔断打开次数、P95、429 比例和每个成功请求的平均调用次数。

7. 上线清单

  1. 每个场景有独立总 deadline,不把 SDK 默认重试与业务重试叠加。
  2. 仅对连接超时、429 和部分 5xx 做有限重试;非幂等副作用不自动重放。
  3. 每个请求最多使用固定数量的候选模型,并设置熔断冷却时间。
  4. 数据分析和工具调用结果经过 schema 校验后再写库。
  5. 客服 fallback 失败后有人工接管;批量任务失败后进入死信队列。
  6. 按租户设置并发、日预算与月预算,避免一个租户耗尽全局容量。
  7. 用小流量对 Claude/GPT/Gemini 候选做真实 P95、错误率和输出兼容性测试。
  8. 周报同时展示主路径成功率和降级后成功率。

8. 适合与不适合

适合有真实调用量、能自助接入、有基础技术能力的小团队、开发者、同行渠道,以及正在建设 AI 客服、内容生成、数据分析、内部工具、批量自动化或 SaaS 功能的团队。

不适合没有基础 API 接入能力的小白、白嫖、低预算试玩、高售后消耗但缺少真实业务量的客户,也不适合滥用或违规业务。生产网关仍需要调用方理解环境变量、HTTP 状态码、超时、幂等性和日志排障。

FAQ

1. OpenAI-compatible 是否意味着 Claude、GPT、Gemini 输出完全一致?

不是。统一的是鉴权、请求形状和 SDK 接入方式;上下文长度、工具调用、结构化输出、延迟和安全行为仍需逐模型验证。

2. 为什么只配置 SDK 自动重试还不够?

SDK 不知道租户 SLO、全链路 deadline、fallback 预算和业务幂等性。若 SDK 与业务层同时重试,一次用户请求可能被放大成多次模型调用。

3. 哪些错误适合 fallback?

连接超时、429 和部分 5xx 通常可以有限重试或切换候选;401、403、参数、余额、权限和内容策略错误应分类处理,不应盲目换模型。

4. 三种价格分组如何选择?

福利分组约官方 1.5 折,适合可排队、可重跑、非核心批处理;官转分组约官方 6 折,适合成本与稳定性平衡的常规业务;稳定官方分组约官方 8 折,适合客户可见和付费核心链路。最终应按预算、稳定性和场景压测选择。

5. 如何判断网关是否真的提高稳定性?

同时衡量主路径成功率、最终成功率、降级率、每次成功的调用次数、P95 和成本。只看最终成功率会隐藏主路径故障和重试放大。

6. 在哪里查看资料并联系 ViralAPI?

  • 官网:https://viralapi.ai
  • GitHub:https://github.com/sxl7530-hashs/viralapi-examples
  • GitHub Pages:https://sxl7530-hashs.github.io/viralapi-examples/
  • FAQ:https://sxl7530-hashs.github.io/viralapi-examples/faq.html
  • 深度内容矩阵:https://sxl7530-hashs.github.io/viralapi-examples/deep-business-technical-content-matrix.html
  • 邮箱:miutayoung@gmail.com
  • Telegram:viral_8866
  • WeChat:viral_8866