Sub2API:把 Claude、OpenAI、Gemini 统一接入,一个开源 AI API 网关的设计与边界

Unified LLM API Gateway

图片来源:ANG YAO AI · Unified LLM API Gateway

当你只调用一家模型时,接入是一件很简单的事:拿到 API Key,写一个 HTTP 请求,结束。

但真实的工程现场往往不是这样。

同一个应用里,可能要同时用到 Claude 的长文本推理、OpenAI 的工具调用、Gemini 的多模态能力;同一个团队里,十个人共用几个上游账号;同一个项目里,财务想知道这个月到底烧了多少钱、花在谁身上、花在哪个模型上。

到了这一步,问题就从“怎么调一个接口”变成了“怎么管住一堆账号和一堆 Key”。

GitHub 上的开源项目 Sub2API 正是围绕这个问题构建的:一个位于 AI 应用与上游模型服务之间的网关,统一管理上游账号、下游 API Key、请求调度和用量结算。

它不是一个模型,也不是一个聊天界面。它是一层基础设施。

一、Sub2API 是什么?

用一句话概括:Sub2API 是一个多上游 AI 账号的统一接入与分发网关。

它向下游提供一套统一的 API 入口,向上游管理多个平台的账号凭证,中间负责鉴权、配额、调度、转发和记账。

传统直连方式下,每个应用都要自己处理所有上游:

AI 应用
   │
   ├──────── Claude API
   │
   ├──────── OpenAI API
   │
   └──────── Gemini API

引入网关后,应用只需要认识一个地址:

                 AI 应用
                    │
                    ▼
              Sub2API Gateway
                    │
          ┌─────────┼─────────┐
          │         │         │
          ▼         ▼         ▼
       Claude     OpenAI    Gemini
          │         │         │
          ▼         ▼         ▼
       上游账号   上游账号   上游账号

这个位置决定了它的价值:把“账号”这件脏活从应用层剥离出去。

应用不再持有任何上游凭证,也不会因为某个账号被封、某个 Token 过期而需要改代码发版;这些事都收敛到网关内部处理。

需要提前说清楚的是:Sub2API 是网关和管理系统,不是模型提供方。模型能力、服务稳定性、计费价格,归根结底仍由上游服务商决定。加一层网关不会让上游变得更快或更便宜,它只是让“管理”变得可能。

二、它到底解决了哪些问题?

1. 多类型上游账号的统一管理

上游账号并不是同一种东西。至少存在两类截然不同的接入形态:

类型 凭证形态 主要难点
OAuth 授权账号 访问令牌 + 刷新令牌 令牌过期、刷新失败、需要重新授权
API Key 账号 静态密钥 配额、限流、密钥轮换

这两类账号的生命周期管理逻辑完全不同:前者要处理授权流程和令牌刷新,后者要处理密钥有效性和配额消耗。

Sub2API 把两者都纳入同一套账号模型,并支持账号状态的启用、禁用、异常标记,以及账号与分组之间的多对多关联。

账号分组的价值在于权限的间接层:用户不直接绑定账号,而是绑定分组;分组里放哪些账号,由管理员决定。换账号、加账号、下线账号,都不需要改动用户侧配置。

2. 面向用户的 API Key 分发

这是整个系统对外的门面。

管理员为每个用户(或每个应用)生成独立的 API Key,并把 Key 绑定到某个分组:

用户 API Key 分组 配额
用户 A sk-user-a... Claude 组 独立配置
用户 B sk-user-b... OpenAI 组 独立配置
用户 C sk-user-c... Gemini 组 独立配置

带来的直接效果有三个:

  • 凭证隔离:下游看不到、也拿不到上游凭证。
  • 权限收敛:一个 Key 能访问哪些模型,由分组决定。
  • 责任可追溯:每一次调用都能落到具体用户身上。

对于团队内部使用场景,第三点往往比前两点更重要——没有归属的用量统计,等于没有统计。

3. 账号调度与并发控制

当一个分组里存在多个可用账号时,网关需要决定:这次请求该发给谁。

                用户请求
                   │
                   ▼
              API Gateway
                   │
                   ▼
              Account Scheduler
                   │
          ┌────────┼────────┐
          │        │        │
          ▼        ▼        ▼
       Account A Account B Account C
          │        │        │
          ▼        ▼        ▼
        Claude   Claude   Claude

调度策略要同时满足几类相互拉扯的约束:

  • 负载均衡:避免所有请求压在同一账号上。
  • 可用性过滤:跳过已标记异常、配额耗尽或限流的账号。
  • 并发上限:账号级并发、用户级并发都要卡住。
  • 速率限制:请求速率与 Token 速率是两种不同的维度。
  • 会话一致性:同一会话尽量落在同一账号上。

最后一条就是所谓的粘性会话(Sticky Session)。

对多轮对话和 AI 编程工具来说,这一点很关键。如果同一段对话的相邻两轮被随机打到不同上游账号,可能带来上下文缓存失配、会话状态断裂,甚至行为不一致。粘性会话用“牺牲一点负载均衡”换取“会话稳定”。

这是一个典型的工程权衡:完美的均摊并不总是正确目标。

4. Token 级用量统计与成本核算

按请求次数计费是错的。

同一个请求,输入 100 Token 和输入 100K Token 的成本差三个数量级;不同模型的单价不同;缓存命中与未命中的价格也不同。所以计量必须落到 Token 粒度:

用户 模型 输入 Token 输出 Token
用户 A Claude 120,000 30,000
用户 B GPT 80,000 20,000
用户 C Gemini 200,000 50,000

有了这层数据,管理员才能回答那些真正影响决策的问题:

  • 哪个用户消耗最多?
  • 哪个模型调用频率最高?
  • 哪个上游账号承担了主要流量?
  • 谁的配额即将耗尽?
  • 各模型的成本占比如何?

这也是网关类项目中最容易被低估的部分:记账系统的复杂度,往往不低于转发系统。

5. 内置支付与订阅

项目还集成了支付能力,README 中列出的渠道包括:

  • 易支付(EasyPay)
  • 支付宝官方支付
  • 微信官方支付
  • Stripe

也就是说,订阅、充值、配额发放可以在同一套系统里闭环,不需要额外接一个计费服务。

不过这里必须打一个折扣:支付渠道是否可用,取决于商户资质、服务商配置和所在地区的合规要求。代码里有对接能力,不等于你就能合法开通。

6. 多平台、多协议接入

项目覆盖的上游平台大致是这些:

平台 主要接入方向
Anthropic Claude Claude 模型及相关工具调用
OpenAI GPT、Codex 等相关调用
Google Gemini Gemini 模型及相关工具
xAI Grok Grok 模型及相关工具
Antigravity 相关的 Claude、Gemini 接入能力

这里有一个必须区分的概念:

统一入口 ≠ 统一协议。

OpenAI 的 Chat Completions 与 Responses API、Anthropic 的 Messages API,在请求结构、流式事件格式、工具调用表达、错误码语义上都不一样。真正的网关要做的是协议适配:把下游的一种请求翻译成上游能懂的形式,再把上游的流式响应翻译回来。

所以 Sub2API 不是一层 reverse_proxy。它需要理解协议。

三、技术架构:Go + Vue 3 + PostgreSQL + Redis

从源码结构看,这是一个标准的前后端分离项目。README 公布的主要技术栈:

层级 技术
后端 Go(Gin)
ORM Ent
前端 Vue 3
构建 Vite
样式 Tailwind CSS
数据库 PostgreSQL 15+
缓存 Redis 7+
部署 Docker Compose / Linux 二进制

具体版本号以项目 README 与 go.mod 为准,本文不将其作为稳定事实引用。

目录结构大致如下:

sub2api/
│
├── backend/
│   ├── cmd/
│   │   └── server/
│   ├── internal/
│   │   ├── config/
│   │   ├── model/
│   │   ├── service/
│   │   ├── handler/
│   │   └── gateway/
│   └── resources/
│
├── frontend/
│   └── src/
│       ├── api/
│       ├── stores/
│       ├── views/
│       └── components/
│
├── deploy/
│   ├── docker-compose.yml
│   ├── .env.example
│   └── install.sh
│
├── docs/
├── openspec/
└── tools/

这个分层值得注意的地方是 gateway 与 handler 的分离:

  • handler 面向管理面(admin API):账号 CRUD、用户管理、Key 管理、统计查询。
  • gateway 面向数据面(proxy API):真正承载模型流量的转发逻辑。

管理面和数据面的关注点完全不同。 管理面追求功能完整、交互友好,可以慢;数据面追求延迟低、并发高、错误可控,必须快。把它们混在一个模块里,是很多自建网关最后难以维护的根本原因。

各层职责

Go + Gin:负责 HTTP 路由、中间件、鉴权、限流和请求转发。Go 的 goroutine 模型适合处理大量长连接的流式响应——网关最典型的负载就是“很多条 SSE 连接同时挂着”。

Ent:以代码方式定义 schema 并生成类型安全的查询。相比运行时反射式的 ORM,它在编译期就能发现字段错误,对 schema 频繁演进的项目更友好。

Vue 3 + Vite:管理后台,承担用户、账号、Key、分组、用量、系统设置、运营管理等界面。

PostgreSQL:持久化用户、账号、Key、分组、订阅、用量明细等业务数据。用量记录是典型的时序型写入负载,表设计和索引策略会直接决定后台统计页的响应速度。

Redis:缓存与运行时协调。它的关键作用不是“加速查询”,而是跨实例共享状态:限流计数器、并发槽位、会话粘性映射。当网关部署多个实例时,如果这些状态只存在进程内存里,那么“全局并发上限”就是个假命题。

四、一次请求的完整生命周期

理解一个网关,最好的方式是把一条请求从头跟到尾。

以 Claude Code 调用 Claude 模型为例:

Claude Code
     │
     ▼
Sub2API API Endpoint
     │
     ▼
API Key Authentication
     │
     ▼
User / Subscription Check
     │
     ▼
Group & Model Resolution
     │
     ▼
Rate Limit / Concurrency
     │
     ▼
Account Scheduler
     │
     ▼
Upstream Account
     │
     ▼
Claude Service
     │
     ▼
Response Transformation
     │
     ▼
Usage Recording
     │
     ▼
Claude Code

拆成六个阶段:

第一阶段:鉴权。 校验 API Key 是否存在、是否被禁用、是否过期。这一步必须是常数时间或接近常数时间——它位于每个请求的热路径上。通常的做法是哈希后查缓存,而不是每次查库。

第二阶段:配额与权限检查。 用户是否有权访问该分组?订阅是否有效?是否触发请求数、Token 数或并发上限?这里的判断顺序会影响用户体验:先检查“无权限”还是先检查“超额”,返回的错误信息不同。

第三阶段:模型与账号解析。 从请求体中识别目标模型,映射到分组内可用的账号集合,再由调度器选出具体账号。这一步要处理模型别名、能力映射(比如某账号不支持某模型),以及粘性会话的命中判断。

第四阶段:请求转发。 按上游协议重新构造请求:改 header、替换认证信息、转换 body 结构、保持流式语义。这是协议适配的核心,也是最容易出 bug 的地方。

第五阶段:响应处理。 上游返回 SSE 流时,需要边转发边解析,从中提取 usage 信息;遇到错误响应,要判断是可重试(换账号)还是应直接透传。

第六阶段:用量结算。 把输入/输出/缓存 Token 数、模型、账号、用户、耗时落库,更新配额余量,计算费用。

这条链路里有一个不太显眼但很关键的设计约束:流式响应意味着“响应还没结束,账单就已经在生成”。 如果连接中途断开,usage 可能缺失或截断,结算逻辑必须有兜底策略。

五、几个值得单独拿出来讲的工程问题

1. 并发控制为什么必须放在 Redis

假设账号 A 的最大并发设为 5:

Account A
最大并发:5

当前请求:
Request 1
Request 2
Request 3
Request 4
Request 5

新请求:
Request 6 → 等待或拒绝

如果网关只有一个实例,用信号量就够了。但生产环境通常是多实例 + 负载均衡,此时每个实例各持一个信号量,实际并发就变成了 5 × 实例数。

所以并发槽位必须共享。而流式请求的持有时间可能长达几分钟,这就要求槽位有租约和超时释放机制——否则一次进程崩溃就会永久泄漏并发额度。

2. 粘性会话的实现代价

粘性会话的本质是维护一张“会话标识 → 账号”的映射,并给它设置过期时间。

听起来简单,但边界情况不少:

  • 目标账号突然不可用了,会话要不要迁移?
  • 迁移后上下文缓存失效,成本如何补偿?
  • 映射表过期时间设多长?太短没效果,太长则负载严重倾斜。

这类问题的答案没有标准解,取决于业务更怕“会话断裂”还是更怕“账号过载”。

3. 账号异常的分类处理

上游账号会以多种方式失败:

异常 合理处置
访问令牌过期 触发刷新,成功则重试
OAuth 刷新失败 标记账号需重新授权
配额耗尽 临时移出调度池,按周期恢复
HTTP 429 限流 退避 + 短期降权
上游 5xx 有限次重试,可切换账号
网络超时 区分“请求未送达”与“响应丢失”

关键在于区分可重试与不可重试。对非幂等的生成请求盲目重试,可能造成重复计费;而对明确可重试的错误不做切换,则会白白浪费用户配额。

4. 计费准确性比功能数量更难

计费的准确性依赖三件事同时成立:

  1. 上游确实返回了 usage(有些流式场景需要主动请求或从末尾事件中提取)。
  2. 价格目录与实际计费规则一致(含缓存 Token 的差异化定价)。
  3. 结算在故障场景下不丢不重。

任何一环出问题,账单就会失准。而账单失准是这类系统最致命的缺陷——功能少一点用户能忍,账算错了没人能忍。

5. 协议转换的隐藏工作量

把 OpenAI 格式转成 Anthropic 格式,表面上是字段映射,实际上要处理:

  • 系统提示词的位置差异(system 角色 vs 顶层 system 字段)。
  • 工具调用(tool call)的结构与流式增量表达。
  • 停止原因(finish_reason / stop_reason)的语义对齐。
  • 错误码体系不一致导致的客户端重试行为差异。

每一条都不难,但组合起来就是一个需要持续维护的兼容层。上游协议一改,网关就得跟。

六、怎么部署

项目提供两条部署路径,官方推荐 Docker Compose。

方式一:Docker Compose

mkdir -p sub2api-deploy
cd sub2api-deploy

curl -sSL \
  https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh \
  | bash

docker compose up -d
docker compose logs -f sub2api

部署脚本会准备 Compose 配置、环境变量、数据目录以及必要的安全密钥。

方式二:Linux 二进制 + systemd

curl -sSL \
  https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install.sh \
  | sudo bash

安装后用 systemd 托管:

sudo systemctl start sub2api
sudo systemctl enable sub2api
sudo systemctl status sub2api

首次启动需要通过 Setup Wizard 配置数据库、Redis 和管理员账号。

部署时的几点现实提醒

  • 别把管理后台直接暴露在公网。 这类系统一旦后台被拿下,等于所有上游账号一起失守。
  • 数据库和 Redis 不要用默认密码。 部署脚本生成的随机密钥要保存好。
  • 反向代理要放宽超时。 流式响应可能持续数分钟,Nginx 默认 60s 会直接截断。
  • 先做容量评估再上线。 网关本身也要吃 CPU 和连接数,尤其在做流式解析时。

七、它适合谁?

场景 适用价值
个人多模型统一接入 一个入口管理不同模型的调用
团队 AI 资源管理 分配 Key、控制配额、统计成本
多模型应用开发 减少应用侧对接多家上游的工作量
内部 AI API 平台 统一鉴权、审计与计量
API 网关学习 研究 Go 网关、协议适配、账号调度
订阅与计费系统研究 观察用户、套餐、额度、账单的建模方式

对于想研究网关设计的开发者,我认为最值得读的是三块:

  1. 账号调度器:如何在一个不断变化的可用集合上做选择。
  2. 协议适配层:如何把一种语义映射到另一种语义。
  3. 计量与结算:如何在流式、失败、重试的场景下仍算得准。

这三块也是任何 AI 网关的核心工程能力,换成别的语言或框架,问题结构是一样的。

八、局限与风险:必须说清楚的部分

1. 它不提供模型

Sub2API 是网关和账号管理系统。模型由上游服务商提供,能力上限、价格、稳定性都不由它决定。

2. 可用性不会因为加了网关而变好

恰恰相反,链路变长了,故障点变多了。网关能做的只是把上游的不稳定包装得更可控(重试、切换、降级),而不是消除它。

3. 最关键的:服务条款风险

项目官方明确提示:使用该项目可能违反 Anthropic 等上游服务商的服务条款,并声明项目仅供技术学习与研究使用,未授权任何个人或组织基于项目开展商业运营。

这不是免责声明式的客套话,而是这类项目的根本性约束。

把订阅账号的额度转卖给第三方、把个人账号包装成 API 对外销售,在多数上游服务商的条款下都是明确禁止的。一旦被判定违规,后果是账号封禁、额度清零,甚至影响关联账户。

所以部署之前需要逐项核对:

  • 上游服务商的授权范围与账号使用政策。
  • 数据处理与隐私要求(请求内容会经过你的服务器)。
  • 当地法律法规对转售、代充、支付结算的要求。
  • 是否具备相应的经营资质。

技术上做得到,和合规上做得了,是两件完全不同的事。

4. 许可证

项目使用 LGPL-3.0-or-later。二次开发、修改后分发、以及以库的形式静态链接,都有对应的义务要求。商用前应让法务确认,而不是只看“开源”两个字。

九、总结

Sub2API 的价值不在于“能转发请求”——那是 Nginx 就能做的事。

它的价值在于把分散的东西收拢成一套体系:

  • 上游账号从“一堆散落的凭证”变成“可调度、可监控、可下线”的资源池。
  • 下游访问从“共享一个 Key”变成“一人一 Key、按组授权、按量计费”。
  • 成本从“月底看账单吓一跳”变成“实时可查、按用户按模型拆解”。

从技术视角看,它覆盖了 Go 后端服务、AI API 网关架构、多协议兼容、OAuth 凭证管理、负载均衡与粘性会话、Redis 分布式并发控制、Token 级计费、PostgreSQL 数据建模和 Vue 3 管理后台——一个相当完整的工程样本。

如果你只是想统一调用几个模型,它确实偏重;但如果你想理解“围绕模型调用建立一套账号、权限、调度、计量和运营体系”到底要处理哪些问题,它是一个很好的阅读对象。

最后仍要回到那句话:这类项目的技术价值与合规风险是同时存在的。 研究它的架构是一回事,把它当成生意是另一回事。前者值得投入时间,后者需要先过法务这一关。

项目与参考资料

本文依据项目 README、仓库目录结构与公开资料整理,功能列表与技术栈以仓库当前状态为准;文中架构示意图用于解释通用网关原理,不代表项目的完整实现细节。部署与商用前请自行核对上游服务条款与许可证要求。




Enjoy Reading This Article?

Here are some more articles you might like to read next: