跳到正文

目录

new-api:把多个 LLM API 收进一个网关,顺便把计费也做了

new-api:把多个 LLM API 收进一个网关,顺便把计费也做了

如果你同时接了 OpenAI、Claude 和 Gemini 的 API,每切一次模型就要改客户端、各自管 Key、分别对账。这件事的麻烦不在开发量,而在运维和成本两个维度持续累积。new-api 在这层麻烦上盖一个统一入口:所有模型对外暴露 OpenAI 兼容格式,请求进来后由网关决定发给谁、怎么计费、额度还剩多少。

new-api(QuantumNous/new-api)是开源网关,AGPL-3.0,Go 后端 + React 管理台,2026 年中约 4 万 + star,支持把各家模型互转成 OpenAI / Claude / Gemini 兼容格式,并提供渠道分组、加权分发、自动重试、令牌与额度管理。它不是"又一个 OpenAI 代理",而是把模型访问变成可治理的资源池。

这篇文章不重抄 README 的功能清单。下面把三条主线拆开,跟一个真实请求走完整流程,最后给"什么时候该用、什么时候别用"的判断。

三条主线:网关到底做了什么

new-api 对外是兼容 OpenAI /v1/chat/completions 的 HTTP 服务,对内可以拆成三条独立主线:

客户端 (OpenAI SDK / curl / LangChain)
   ┌──────────────────────────────────────────┐
   │              new-api 网关                 │
   │                                          │
   │  ① 格式转换:OpenAI 请求 ↔ 各厂格式      │
   │  ② 渠道路由:按模型/渠道/权重选上游       │
   │  ③ 额度计费:预检额度 → 转发 → 按用量结算 │
   │                                          │
   └──────┬──────────┬──────────┬─────────────┘
          ▼          ▼          ▼
      OpenAI     Claude     Gemini ...

三条线职责分明:

主线负责什么不负责什么
格式转换把某家原生格式翻译成 OpenAI / Claude / Gemini 兼容格式不做语义理解、不改写 prompt、不缓存流式内容
渠道路由按模型名匹配渠道集,按权重加权分发,处理失败重试与熔断降级不做跨渠道 session 亲和、不做全局负载感知
额度计费请求前做额度预检,请求后按实际 token 用量结算、记流水不管真实货币支付、不做跨模型预算优化

三条线几乎正交——可以只开格式转换而关掉计费,或只做路由不开格式转换。下面跟一个真实请求看它们怎么协作。

一次请求的完整路径

假设团队后端用 openai Python 库发请求,但这次想打 Claude:

import openai

client = openai.OpenAI(
    base_url="http://new-api:3000/v1",
    api_key="sk-user-abc123"
)

response = client.chat.completions.create(
    model="claude-3-5-sonnet",
    messages=[{"role": "user", "content": "解释一下 B+ 树的写入路径"}]
)

请求进入 new-api 后依次经过:

  1. 鉴权sk-user-abc123 解析到对应令牌,查出它的模型权限和剩余额度。
  2. 额度预检:检查令牌对该模型的剩余额度是否大于零。不足则直接返回 4xx,不把请求转发到上游——这一步把成本风险挡在转发之前,而不是先消耗再对账。
  3. 渠道匹配claude-3-5-sonnet 落到 Claude 渠道集。如果配了多个同款渠道(比如不同账号的 Key),池内按权重选一个。
  4. 格式转换:OpenAI 格式的请求体被译成 Anthropic 原生格式(system 提到顶层、role 映射、content 结构调整),temperaturemax_tokens 等参数做字段名和取值范围的换算。
  5. 转发并等待:请求发到上游。若超时或返回 5xx,按重试配置换渠道重试或直接报错。
  6. 响应回译:Anthropic 返回的 content 数组(可能含 texttool_use 两种 block)重组回 OpenAI 的 choices[0].message.content
  7. 额度结算:按响应里的 usage(输入/输出 token 数)和该模型倍率扣减额度,写进流水。

整个过程对调用方透明——代码里只改了 base_urlmodel。正是这 7 步,把"聚合多模型"从客户端胶水收进了网关层。

格式转换:不只是字段重命名

格式转换常被当成"把 messages 换个字段名",实际要处理的比想象中多。

请求体结构差异

OpenAI 的 chat completion 结构最"扁平"——参数全在顶层、messages 是统一数组。Claude 把 system 提到顶层字段,Gemini 把整个请求包进 contents。转换器面对的不只 1:1 映射,而是结构的拆解重组:

请求元素OpenAIClaudeGemini
system promptmessages[0].role="system"顶层 system 字段systemInstruction 字段
user/assistantmessages[*].rolemessages[*].rolecontents[*].role
tool callingtools 数组tools 数组(字段名不同)tools + functionDeclarations
图片输入content: [{type:"image_url",...}]content: [{type:"image",source:{...}}]parts: [{inlineData:{...}}]
流式stream: truestream: truestream: true(SSE 事件格式不同)

流式(streaming)最容易出问题。三家都用 SSE,但事件名、data 字段结构、finish_reason 的枚举值不一样。转换器必须在首字节到达后就逐事件翻译,不能等整个响应收完再处理,否则流式的实时性就丢了。

功能子集的取舍

不是所有 OpenAI 参数都有下游对应物。response_format: {type: "json_object"} 在 Claude 里要靠 prompt 约束实现,logprobs 在 Gemini 里没有等价物,seed 各厂支持程度不一。转换器的策略是"能转就转,转不了就静默丢弃并记日志",而不是硬造一个看起来能用的假参数——后者会在生产里制造难排查的行为差异。

渠道路由:配置在管理台,行为在四层

路由配置不在某个 config.yaml 的路由块里,而是在管理后台维护"渠道 + 分组"。渠道存上游的 Key、模型列表、权重、限流值和健康状态;分组决定不同令牌可用的渠道集合。一次请求的路由决策实际涉及四层信息:

  1. 模型 → 渠道集:请求里的 model 匹配到支持的渠道。同一模型配了多个渠道(比如不同账号的同一厂商 Key),全部入选候选池。
  2. 渠道健康:每个渠道维护可用/不可用标记。连续失败会被自动熔断,熔断期不参与权重分配,冷却后再试探恢复。
  3. 权重分发:在健康候选池里按权重做加权随机。两个权重 50 的渠道,各约一半概率被选中——这是"把同一份流量摊到不同账号"的典型做法。
  4. 重试与降级:选中的渠道返回 429 或 5xx,按配置换渠道重试;降级方向一般是"同模型其他渠道 → 同厂商功能接近的模型 → 报错"。

值得留意:这里是无状态路由——连续两次请求即使模型相同也可能落到不同渠道。需要同一会话始终走同一渠道的(session 亲和),得在网关上层(负载均衡或客户端)做 sticky session,new-api 自身不提供。

限流保护

new-api 在渠道层面支持 QPS(每秒请求)和 TPM(每分钟 token)限制。这两个值配在渠道上,不是从上游 rate-limit 响应头里动态解析的。所以配置时要把值设得比上游实际限制略低,给突发流量留缓冲。

部署:一个 docker-compose up,但要关注三件事

基础部署

git clone https://github.com/QuantumNous/new-api.git
cd new-api

# 按需调整 docker-compose.yml:端口、数据库、时区
docker-compose up -d

网关默认监听 3000 端口,访问 <主机>:3000 进入管理台。仓库自带 docker-compose 编排,默认拉起 MySQL 作为主存储(轻量场景也可换用 SQLite 单文件)。官方文档统一见 https://docs.newapi.pro

生产环境要额外处理的三件事

数据库持久化:网关的用户、令牌、渠道、额度、流水都落在数据库里。容器默认的数据卷要挂到宿主机的持久化目录(-v ./data:/data),或直接用外部数据库——重装容器就丢数据,不是可选优化,是上线前提。

反向代理:网关前的 Nginx/Caddy 至少做两件事。一是 HTTPS 终结,令牌不能明文过公网;二是把请求体大小上限调大,因为图片输入(vision 场景)的请求体可能超过 Nginx 默认的 1MB。

日志与监控:new-api 日志默认打 stdout,可用 Docker 日志驱动采集。但成本分布更适合直接看数据——每次请求的模型、token 用量、扣费金额、状态码都会记流水,管理员按用户和模型聚合就能看到成本构成。

计费系统:额度模型比价格表更重要

new-api 不接支付宝或 Stripe,也不是支付系统——它做的是额度管理 + 在线充值入口。

额度模型

额度用内部"额度单位(quota)“计量,价格通过模型倍率换算:用量 = (token 数 / 1000) × 模型倍率 × 常量。倍率 1.0 大致对应某型号每千 token 的基准价,管理员按上游报价给每个模型设倍率。每个令牌可配独立总额度和可用模型集,管理员在后台充值。

这个模型简单,但有几个生产里容易踩的点:

  • 用整数额度单位:网关内部按整数 quota 存储和扣减,把价格换算成固定整数倍率,能避免大量小额调用后浮点误差累积——这属于部署时按自身计价需求落实的运维细节。
  • 预检与实扣分离:请求发出前没法知道实际会用多少 token,所以先做额度预检(大于零才转发),跑完再按实际 usage 结算。一个只剩一点额度的用户仍可能打出一个消耗较多 token 的长回复——网关不在请求中途掐断。
  • 共享额度:默认一个令牌的额度是对其可用模型共享的。想让不同模型有独立上限,需要另配模型级限额。

用量与流水

管理员后台可按用户、模型、时间范围查调用流水,也能后台直接查数据库做聚合。要出成本报表,优先基于流水表聚合,而不是依赖接口的分页列表——数据量上去后,分页接口不是为报表设计的。

什么时候用,什么时候不用

明确该用的场景

  • 团队里 3 个以上开发者调不同厂商 API。统一入口的收益不是少写几行代码,而是 Key 不再散落各人 .env、调用量不用手动汇总、换模型不用改客户端。
  • 要给不同用户 / 项目分配不同额度。内部平台对外提供 AI 能力、各租户有独立预算,额度 + 令牌模型直接对得上。
  • 要屏蔽上游 API 变动。厂商改了 SDK 接口,客户端不动,网关层兼容即可。

不该用或要慎重的场景

  • 只有一家厂商、一个模型。网关层多一跳网络和序列化开销,直接用官方 SDK 更快更少错。
  • 对流式延迟极度敏感(如实时语音对话)。格式转换和额度记录都是额外开销,虽然通常只在几十毫秒内,但每一跳都要算。
  • 要靠网关保持会话状态。new-api 不维护 conversation state,系统提示词和对话历史由客户端管。产品逻辑强依赖服务端多轮状态,网关够不到这一层。
  • 要面向 C 端做真实收费。new-api 只管额度不管钱,真收费还要独立的支付和订单系统。

如果决定上车,建议的顺序

  1. 开发环境 docker-compose up,配一个 OpenAI 渠道,确认调用链路通。
  2. 加上第二个厂商(如 Claude),验证格式转换在流式和非流式下都正常。
  3. 接现有用户体系——令牌识别决定额度管理能否落地。
  4. 配额度与模型倍率,先用小额额度验证结算,确认流水记录无误再放开。
  5. 上线前补反向代理与数据库持久化。

相关资源

资源链接
GitHub 仓库github.com/QuantumNous/new-api
官方文档docs.newapi.pro

参与讨论

使用 GitHub 登录。欢迎补充事实、异议与实践。