我写了一个轻量 AI 网关库,多模型路由 + 自动降级 + 预算控制,一个包全搞定

我写了一个轻量 AI 网关库,多模型路由 + 自动降级 + 预算控制,一个包全搞定

路由、降级、限流、成本控制------调多个 LLM 必须解决的四个问题,一个零依赖 TypeScript 库搞定。

背景:调 AI 接口为什么越来越像运维

你的项目一开始只用 OpenAI,一个 fetch 搞定。后来产品说"Claude 写文案更好",加一个 Provider。再后来"GPT-4 太贵了,简单任务用 mini",加路由逻辑。然后有天 OpenAI 429 了------整个服务挂了。

于是你的代码变成了这样:

typescript 复制代码
async function callAI(messages, taskType) {
  if (taskType === 'copywriting') {
    try {
      return await callAnthropic(messages);
    } catch (e) {
      return await callOpenAI(messages); // 降级
    }
  } else if (taskType === 'chat' && user.tier === 'free') {
    return await callOpenAI(messages, 'gpt-4o-mini'); // 省钱
  } else {
    return await callOpenAI(messages, 'gpt-4o');
  }
  // 还要加预算检查、重试、日志、超时......
}

这段代码有几个问题:

  1. 路由逻辑硬编码------加个 Provider 要改代码、重新部署
  2. 降级是 try-catch 嵌套------三层降级链写出来想吐
  3. 没有预算控制------凌晨跑了个 bug 循环,早上起来账单 $200
  4. 没有统一日志------出了问题翻三个 Provider 的日志找原因

这些不是"高级需求",是每个调多模型的项目必须解决的基础设施。

所以我做了 ai-gateway-lite------一个轻量级 AI 网关库,用 JSON 配置替代硬编码,一个包解决路由、降级、预算、日志四个问题。

bash 复制代码
npm install ai-gateway-lite

它能做什么

多模型路由

用 JSON 配置路由规则,按 taskType、feature、userTier 灵活分发请求:

json 复制代码
{
  "rules": [
    {
      "name": "premium-complex",
      "priority": 100,
      "match": { "taskType": "complex", "userTier": "premium" },
      "target": { "provider": "anthropic-main", "model": "claude-sonnet-4-20250514" }
    },
    {
      "name": "default-chat",
      "priority": 10,
      "match": {},
      "target": { "provider": "openai-main", "model": "gpt-4o-mini" }
    }
  ]
}

优先级匹配------VIP 用户走 Claude,普通用户走 Mini,一行代码不用改。

自动降级

Provider 挂了?自动切换到下一个:

json 复制代码
{
  "name": "complex-fallback",
  "steps": [
    { "provider": "anthropic-main" },
    { "provider": "openai-main", "model": "gpt-4o", "when": ["timeout", "provider_error"] },
    { "provider": "openrouter-fallback", "when": ["timeout", "rate_limit", "provider_error"] }
  ]
}

降级触发条件可以精确控制------只有 timeout 和 provider_error 才降级,budget_exceeded 不降级(钱不够就别换个更贵的)。

预算控制

怕跑飞?设个每日上限:

json 复制代码
{
  "name": "global-daily",
  "scope": { "type": "global" },
  "window": "day",
  "limits": { "maxTotalTokens": 1000000, "maxCostUsd": 10.0, "warnAt": 0.8 },
  "enforcement": "hard"
}
  • hard 模式:超了直接拒绝
  • soft 模式:超了告警,但还是放行
  • warnAt: 0.8:用到 80% 时就开始告警

流式输出

SSE 流式响应,所有 Provider 统一接口:

typescript 复制代码
const stream = await gateway.chatStream({
  messages: [{ role: "user", content: "写一首关于 TypeScript 的诗" }],
});

for await (const chunk of stream.stream) {
  process.stdout.write(chunk.delta);
}

const summary = await stream.getUsageSummary();
console.log(`Tokens: ${summary.totalTokens}, Cost: $${summary.estimatedCostUsd.toFixed(4)}`);

成本估算

每次请求自动计算费用,内置 OpenAI / Anthropic 主流模型定价表:

typescript 复制代码
const res = await gateway.chat({
  messages: [{ role: "user", content: "Hello!" }],
});

console.log(res.estimatedCostUsd); // 0.0001

自定义模型?注册一下就行:

typescript 复制代码
import { registerModelPricing } from "ai-gateway-lite";

registerModelPricing("my-model", {
  inputPer1kTokens: 0.001,
  outputPer1kTokens: 0.002,
});

30 秒上手

bash 复制代码
# 1. 安装
npm install ai-gateway-lite

# 2. 配 Key
export OPENAI_API_KEY=sk-xxx
typescript 复制代码
import { Gateway, loadConfigFromObjects } from "ai-gateway-lite";

const config = loadConfigFromObjects({
  providers: [
    {
      name: "openai",
      provider: "openai",
      models: ["gpt-4o-mini"],
      auth: { type: "apiKey", envVar: "OPENAI_API_KEY" },
    },
  ],
  routes: {
    rules: [{ name: "default", priority: 0, match: {}, target: { provider: "openai" } }],
  },
  budgets: [],
});

const gateway = new Gateway({ config });

const res = await gateway.chat({
  messages: [{ role: "user", content: "Hello!" }],
});

console.log(res.content);
console.log(`Cost: $${res.estimatedCostUsd.toFixed(4)}`);

不想写代码先试试?clone 下来跑 demo:

bash 复制代码
git clone https://github.com/hyxnj666-creator/ai-gateway-lite.git
cd ai-gateway-lite && npm install
cp .env.example .env   # 填上你的 API Key
npm run demo

访问 http://localhost:3170,三个接口直接能用:

方法 路径 说明
POST /v1/chat 非流式对话
POST /v1/chat/stream SSE 流式对话
GET /health 健康检查 + Provider 列表

核心设计决策

为什么用 JSON 配置而不是代码

路由规则、降级链、预算策略------这些应该是运维配置,不是代码逻辑。

用 JSON 配置意味着:

  • 不改代码就能调整路由------"Claude 涨价了,简单任务切到 Mini",改 JSON 重启就行
  • 配置可以版本管理------谁改了什么一目了然
  • 可以动态加载------未来支持从远程配置中心读取

当然,如果你嫌建配置文件麻烦,loadConfigFromObjects() 直接传对象也行。

为什么零运行时依赖

Node.js 18+ 自带 fetch、ReadableStream、TextDecoder------调 HTTP API 需要的东西全有了。

不依赖 openai、@anthropic-ai/sdk 等官方 SDK 意味着:

  • 体积小------整个包 47KB(minified)
  • 不会被 SDK 更新 break------直接对接 REST API,接口稳定
  • 支持任何 OpenAI 兼容的 Provider------DeepSeek、Groq、Together 都能通过 OpenRouter 或自定义 Provider 接入

为什么只重试特定状态码

重试逻辑只对 429(限流)、502、503、504(后端故障)和网络超时进行重试,其他错误直接报错。

原因是:

  • 400(参数错误)------重试也没用
  • 401/403(鉴权失败)------重试也没用
  • 500(内部错误)------可能重试有用,但 500 通常意味着逻辑 bug,盲目重试可能加重问题

指数退避 + 可配置重试次数:

json 复制代码
{
  "retry": { "maxAttempts": 3, "backoffMs": 1000 }
}

首次等 1s,第二次 2s,第三次 4s。避免雪崩。


架构一览

bash 复制代码
请求进来
  │
  ├── 路由匹配(taskType / feature / userTier → 最高优先级规则)
  │
  ├── 预算检查(超限?hard → 拒绝 / soft → 告警放行)
  │
  ├── 执行请求
  │    ├── 成功 → 返回
  │    └── 失败 → 分类错误(timeout / rate_limit / provider_error)
  │              └── 触发降级链 → 下一个 Provider
  │
  ├── 记录用量(token / 费用 / 延迟)
  │
  └── 返回 GatewayResponse(含 estimatedCostUsd)

内置 Provider

Provider 流式 认证方式
OpenAI ✅ Bearer token(OPENAI_API_KEY)
Anthropic ✅ x-api-key(ANTHROPIC_API_KEY)
OpenRouter ✅ Bearer token(OPENROUTER_API_KEY)

需要接其他 Provider?实现 Provider 接口即可:

typescript 复制代码
class MyProvider implements Provider {
  readonly name = "my-provider";
  readonly family = "custom";

  async chat(options: ProviderRequestOptions): Promise<ProviderResult> {
    // 你的实现
  }
}

const gateway = new Gateway({
  config,
  customProviders: {
    custom: (config) => new MyProvider(),
  },
});

错误处理

所有错误统一为 GatewayError,带结构化字段:

typescript 复制代码
import { GatewayError } from "ai-gateway-lite";

try {
  await gateway.chat(request);
} catch (err) {
  if (err instanceof GatewayError) {
    console.log(err.kind);       // "provider_timeout" | "provider_rate_limit" | "budget_exceeded" | ...
    console.log(err.httpStatus);  // 504, 429, etc.
    console.log(err.retryable);   // true / false
  }
}

HTTP 状态码自动分类:

状态码 分类 可重试
401/403 provider_auth ❌
429 provider_rate_limit ✅
408/504 provider_timeout ✅
500 provider_error ❌
400 provider_response ❌

和同类方案的对比

维度 ai-gateway-lite LiteLLM Portkey
形态 npm 包,嵌入应用 Python 服务 SaaS + SDK
语言 TypeScript Python 多语言
运行时依赖 0 大量 SDK 依赖
路由配置 JSON 声明式 代码/YAML Dashboard
降级链 ✅ 多步骤 + 条件触发 ✅ ✅
预算控制 ✅ 内置 有限 ✅
流式 ✅ SSE ✅ ✅
成本追踪 ✅ 内置定价表 ✅ ✅
部署 无需部署,库引用 需部署服务 第三方 SaaS
开源 MIT MIT 部分开源
包大小 47KB - -

核心差异 :ai-gateway-lite 是嵌入式的------不需要额外部署一个网关服务,npm install 进项目直接用。适合中小团队或个人项目,不想多维护一个服务的场景。


适用场景

  • SaaS 产品------按用户等级分配不同模型,VIP 走 GPT-4,免费用户走 Mini
  • AI 应用后端 ------统一网关层,不管接几个 Provider 都是一个 gateway.chat()
  • 成本敏感项目------设好每日预算,再也不怕凌晨跑飞
  • 高可用场景------主 Provider 挂了自动切备用,用户无感知

开源地址

bash 复制代码
npm install ai-gateway-lite

84 个测试,TypeScript strict 模式,Node.js 18/20/22 全版本 CI 通过。


如果对你有帮助,给个 ⭐ 或者掘金点个赞,是我继续迭代的动力。

有问题或建议欢迎提 issue 或评论区交流。

相关推荐
噜哥带你玩转 AI 大模型34 分钟前
从 “if-else“ 到 Agent:Dify 进阶实战,让 AI 自己判断、自己调工具
agent·工作流·dify·多轮对话·httpapi
威哥爱编程36 分钟前
Python 智能体开发实战 05:一问教材内容它就编?先把答案从你自己的资料里检索出来
python·agent
java_prinln41 分钟前
RAG 面试题介绍
python·agent
威哥爱编程44 分钟前
Python 智能体开发实战 06:一个函数里塞了八百行?用状态机把批改流程拆成能回退的步骤
python·agent
熊猫钓鱼>_>1 小时前
四种书签,一条时间线:一个读书 App 的记忆颗粒与它的边界案例【鸿蒙心迹】
开发语言·华为·ai编程·harmonyos·鸿蒙·openharmony·鸿蒙pc
yz_aiks1 小时前
WorkBuddy 使用技巧:Claude Code 国内平替,签到免费用、自动签到和手机远程控制电脑
人工智能·ai编程·效率工具·claude code·workbuddy
星际码仔2 小时前
Codex Co²:AI 时代的结对编程,应该是怎样的?
openai·ai编程·vibecoding
key_3_feng3 小时前
评估与对齐:Agent 自我进化的护栏
前端·数据库·agent
念念不忘 必有回响3 小时前
从0到1:搭建你的第一个 MCP 服务器实战
agent·mcp
时早玖沐3 小时前
KOOK「找不到语音频道」:同一个错误假设,我犯了三次
python·开源·ai编程