AI 网关与模型路由:多模型统一入口、fallback、限流与成本控制

AI 网关与模型路由深度实战:多模型统一入口的抽象层、按任务/成本/延迟的路由策略、fallback 与故障转移、限流与配额管理、成本核算与观测,以及在边缘函数和 Vercel 上搭建 AI 网关的落地路径。

一、引言

当你的应用同时用 OpenAI、Anthropic、Google 的模型,或者在「贵模型质量高、便宜模型够用」之间做选择时,直接在各处代码里写死供应商 SDK 会很快失控:每次切换供应商要改一堆代码、某个供应商挂了没有兜底、月底账单不知道花在哪了。

AI 网关(AI Gateway)解决的是「多模型调用」的工程问题:一个统一入口,对外给业务一个稳定接口,对内做路由、fallback、限流、缓存、成本核算。本文拆解 AI 网关的架构、路由策略、fallback 机制、限流与成本控制,并给出在 Vercel / Cloudflare 上的落地代码。

二、为什么需要 AI 网关

2.1 直接调多个 SDK 的问题

问题一:代码耦合——换供应商要改业务代码
问题二:无兜底——某供应商故障,功能直接不可用
问题三:无观测——每笔调用成本、延迟、成功率看不见
问题四:无统一限流——各模型各自为政,无法全局配额

2.2 网关的定位

业务代码 → AI 网关(统一入口)
             ├── 路由:按任务/成本/延迟选模型
             ├── fallback:主模型失败自动切换
             ├── 限流:按租户/密钥/模型配额
             ├── 缓存:同 prompt 结果缓存降成本
             └── 观测:token/成本/延迟/成功率
            → OpenAI / Anthropic / Google / 开源模型

心法:AI 网关不是「又一个代理」,而是把 LLM 供应商当成「可插拔的算力池」——业务侧只认一个接口,供应商怎么换、怎么降级,全部收敛在网关里。

三、统一入口:多 Provider 抽象

3.1 统一接口契约

给业务侧一个稳定的「chat 完成」接口,网关内部适配各家 SDK:

// 统一请求/响应类型
type ChatRequest = {
  model: string            // 业务侧逻辑模型名,如 'smart' | 'fast'
  messages: Array<{ role: string; content: string }>
  temperature?: number
  maxTokens?: number
}

type ChatResponse = {
  content: string
  usage: { inputTokens: number; outputTokens: number }
  provider: string
  model: string
}

3.2 Provider 适配层

// provider.ts:各家供应商统一适配
const providers = {
  openai: {
    async chat(req: ChatRequest) {
      const res = await fetch('https://api.openai.com/v1/chat/completions', {
        method: 'POST',
        headers: { Authorization: `Bearer ${process.env.OPENAI_KEY}` },
        body: JSON.stringify({ model: 'gpt-4o', ...req }),
      })
      const data = await res.json()
      return {
        content: data.choices[0].message.content,
        usage: { inputTokens: data.usage.prompt_tokens, outputTokens: data.usage.completion_tokens },
        provider: 'openai',
        model: 'gpt-4o',
      }
    },
  },
  anthropic: {
    async chat(req: ChatRequest) {
      const res = await fetch('https://api.anthropic.com/v1/messages', {
        method: 'POST',
        headers: {
          'x-api-key': process.env.ANTHROPIC_KEY,
          'anthropic-version': '2023-06-01',
        },
        body: JSON.stringify({ model: 'claude-sonnet', max_tokens: req.maxTokens ?? 1024, messages: req.messages }),
      })
      const data = await res.json()
      return {
        content: data.content[0].text,
        usage: { inputTokens: data.usage.input_tokens, outputTokens: data.usage.output_tokens },
        provider: 'anthropic',
        model: 'claude-sonnet',
      }
    },
  },
}

3.3 模型映射表

// model-mapping.ts:逻辑模型 → 各供应商实际模型
const modelMap = {
  fast: { openai: 'gpt-4o-mini', anthropic: 'claude-haiku', google: 'gemini-1.5-flash' },
  smart: { openai: 'gpt-4o', anthropic: 'claude-sonnet', google: 'gemini-1.5-pro' },
}

export function resolveModel(logical: string, provider: string): string {
  return modelMap[logical]?.[provider]
}

细节:给业务侧暴露「逻辑模型名」(fast / smart)而不是供应商真名——底层换模型、换供应商,业务代码零改动。

四、路由策略:按任务、成本与延迟选模型

4.1 路由维度

按任务复杂度:摘要/分类 → fast;推理/创作 → smart
按成本预算:单次调用预算低 → fast
按延迟要求:实时聊天 → 低延迟模型;后台批量 → 成本优先
按租户等级:免费用户 → fast;付费用户 → smart

4.2 实现一个路由函数

// router.ts:基于任务的策略路由
type TaskKind = 'classification' | 'summarize' | 'creative' | 'reasoning'

function routeFor(task: TaskKind): string {
  switch (task) {
    case 'classification': return 'fast'      // 便宜、快
    case 'summarize':      return 'fast'
    case 'creative':       return 'smart'
    case 'reasoning':      return 'smart'     // 推理要强
  }
}

// 网关主入口
export default async function gateway(req: ChatRequest & { task?: TaskKind }) {
  const logical = routeFor(req.task ?? 'reasoning')
  const provider = pickPrimaryProvider()       // 默认主供应商
  return callProvider({ ...req, model: logical }, provider)
}

4.3 动态路由:按响应质量/失败率自适应

// 失败率驱动的自适应:连续失败就切更稳的供应商
const health = { openai: 0, anthropic: 0 }    // 滑动窗口失败率

function pickPrimaryProvider(): string {
  if (health.openai > 0.1 && health.anthropic < 0.05) return 'anthropic'
  return 'openai'
}

五、Fallback:主模型挂了怎么办

5.1 两级 fallback 模型

第一级:同供应商换模型(gpt-4o → gpt-4o-mini)
第二级:换供应商(openai → anthropic → google)
// fallback.ts:供应商链 + 重试
const FALLBACK_ORDER = ['openai', 'anthropic', 'google']

async function callWithFallback(req: ChatRequest): Promise<ChatResponse> {
  const errors: string[] = []
  for (const provider of FALLBACK_ORDER) {
    try {
      return await providers[provider].chat(req)
    } catch (err) {
      errors.push(`${provider}: ${err.message}`)
      // 达到最大重试次数则继续下一个供应商
      continue
    }
  }
  throw new Error(`all providers failed: ${errors.join(' | ')}`)
}

5.2 超时与重试语义

// 每次调用带超时,超时视为失败 → 触发 fallback
async function callWithTimeout(req: ChatRequest, provider: string, ms = 15000): Promise<ChatResponse> {
  const controller = new AbortController()
  const timer = setTimeout(() => controller.abort(), ms)
  try {
    return await providers[provider].chat(req, { signal: controller.signal })
  } finally {
    clearTimeout(timer)
  }
}

铁律:fallback 只对「可重试」的请求生效。幂等、可重复的请求(摘要、分类、改写)放心 fallback;涉及计费/扣费的请求要先明确「重试是否会重复扣费」,必要时落记录再重试。

5.3 Fallback 的观测与告警

每次 fallback 打日志:from / to / reason / 耗时
统计 fallback 率:> 5% 说明主供应商长期不稳
fallback 消耗的成本单独核算,避免「隐藏成本」

六、限流与配额管理

6.1 限流维度

按租户:每个 API key / 用户一个配额
按模型:贵模型单独限流(防止被刷爆成本)
按速率:RPM(每分钟请求)与 TPM(每分钟 token)双维度

6.2 用 KV/Redis 实现令牌桶

// rate-limit.ts:基于 KV 的滑动窗口限流
async function checkLimit(env: { KV: KVNamespace }, tenantId: string, limit = 60): Promise<boolean> {
  const now = Date.now()
  const windowMs = 60_000
  const key = `ratelimit:${tenantId}:${Math.floor(now / windowMs)}`

  const count = Number((await env.KV.get(key)) ?? '0')
  if (count >= limit) return false

  await env.KV.put(key, String(count + 1), { expirationTtl: 120 })
  return true
}

export default async function gateway(req: Request, env: { KV: KVNamespace }) {
  const tenant = req.headers.get('x-api-key') ?? 'anonymous'
  const allowed = await checkLimit(env, tenant, 60)
  if (!allowed) return Response.json({ error: 'rate_limited' }, { status: 429 })
  // ... 继续路由
}

6.3 配额与告警

- 配额用尽:返回 429 + Retry-After
- 接近配额(80%):预警告警
- 超预算:自动降级到便宜模型(fast 兜底),而不是直接拒绝

七、成本控制与观测

7.1 成本核算

// 单价表($/1M token,示例值)
const PRICING = {
  'gpt-4o':        { input: 2.5, output: 10 },
  'gpt-4o-mini':   { input: 0.15, output: 0.6 },
  'claude-sonnet': { input: 3, output: 15 },
  'claude-haiku':  { input: 0.25, output: 1.25 },
}

function estimateCost(provider: string, model: string, usage: { inputTokens: number; outputTokens: number }): number {
  const p = PRICING[model]
  return ((usage.inputTokens / 1_000_000) * p.input + (usage.outputTokens / 1_000_000) * p.output)
}

// 每笔调用后记录成本
const cost = estimateCost(res.provider, res.model, res.usage)
await logUsage({ tenant, task, model, tokens, cost, latencyMs })

7.2 缓存:同 prompt 结果复用

// 缓存 key:模型 + 消息(仅对可缓存任务生效)
async function cachedChat(env: { KV: KVNamespace }, req: ChatRequest): Promise<ChatResponse> {
  const cacheKey = `ai:${req.model}:${JSON.stringify(req.messages).slice(0, 200)}`
  const hit = await env.KV.get(cacheKey)
  if (hit) return JSON.parse(hit)

  const res = await callWithFallback(req)
  await env.KV.put(cacheKey, JSON.stringify(res), { expirationTtl: 3600 })
  return res
}

心法:缓存只对「确定性任务」(分类、摘要、翻译、关键词提取)安全,创作类任务别缓存。缓存命中率直接等于成本节省率。

7.3 观测面板

核心指标:
  每笔调用成本(分模型 / 分租户 / 分任务)
  成功率 / fallback 率 / 平均延迟 / token 消耗
  预算实时进度条(当月已花 / 预算)

输出:
  结构化日志 + 指标(配合 OTel 体系)

完整的观测闭环可参考 可观测性与错误追踪 的方法论,AI 网关尤其要把「成本」做成一等指标。

八、总结

AI 网关与模型路由的核心要点:

  1. 统一入口是基础:业务只认一个稳定接口,供应商差异收敛在网关适配层。
  2. 逻辑模型名隔离:暴露 fast / smart 而非供应商真名,底层换模型零改动。
  3. 路由要按任务/成本/租户:分类摘要走便宜模型,推理创作走强模型,免费用户降级。
  4. fallback 必须有链:同供应商换模型 → 换供应商,超时视同失败,幂等请求才可重试。
  5. 限流双维度:按租户 RPM/TPM,贵模型单独配额,超预算自动降级而非拒绝。
  6. 成本是一等指标:每笔调用记账,缓存确定性任务,fallback 消耗单列。
  7. 观测与预算闭环:成功率、延迟、成本、预算进度全部可视化,超阈值告警。

模型能力会快速迭代,但「网关 + 路由 + fallback + 限流 + 成本」这套工程骨架是稳定资产。把它搭好,你的 AI 应用就能随时接最强的模型、随时切换供应商、账单永远可控。边缘侧可以再叠加 AI 应用部署 的推理路径与 边缘缓存策略 的缓存分层,让 AI 调用既省又稳。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「tools」更多文章

  1. 密钥与环境配置:Vercel、Cloudflare 环境变量与密钥轮换实战
  2. Web 安全加固:CSP、HSTS、安全响应头与 XSS 防护实战
  3. 图片与媒体优化:Image CDN、AVIF-WebP 与响应式图片实战