同时接入多个国内大模型 API 的统一网关方案

做 AI 应用开发,最头疼的不是"调哪个模型",而是"怎么同时调 10 个模型还不乱"。每个厂商 API 格式不同、鉴权方式不同、计费规则不同、SSE 流式协议也各写各的。你写了 10 套适配代码之后,光是维护就已经筋疲力尽。这篇文章给出一个完整的统一网关方案,让你用一套协议、一个 Key、一份账单,同时接入国内所有主流大模型。


一、问题场景:你的代码是不是也长这样

如果你同时接入了 DeepSeek、通义千问、豆包、文心一言四个模型,你的代码大概率是这样的:

python 复制代码
# 现状:每接一个模型就写一套逻辑

# DeepSeek ------ OpenAI 兼容
resp = requests.post(
    "https://api.deepseek.com/chat/completions",
    headers={"Authorization": f"Bearer {deepseek_key}"},
    json={"model": "deepseek-chat", "messages": [{"role": "user", "content": prompt}]}
)

# 通义千问 ------ DashScope 协议
resp = requests.post(
    "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation",
    headers={"Authorization": f"Bearer {qwen_key}"},
    json={"model": "qwen-plus", "input": {"messages": [{"role": "user", "content": prompt}]}}
)

# 豆包 ------ OpenAI 兼容但有差异(max_tokens 字段名不同)
resp = requests.post(
    "https://ark.cn-beijing.volces.com/api/v3/chat/completions",
    headers={"Authorization": f"Bearer {doubao_key}"},
    json={"model": "ep-20240601", "messages": [{"role": "user", "content": prompt}]}
)

# 文心一言 ------ 需要先拿 access_token,再调用
token_resp = requests.post(
    f"https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id={ak}&client_secret={sk}"
)
access_token = token_resp.json()["access_token"]
resp = requests.post(
    f"https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro?access_token={access_token}",
    json={"messages": [{"role": "user", "content": prompt}]}
)

四个模型,四种鉴权方式(静态 Key / OAuth2 / AK+SK),四种请求格式,四种响应格式。这还只是文字对话场景,算上视频生成、图片生成、向量 Embedding,复杂度更高。

业务代码和模型厂商耦合致死。 换一个模型,改代码;厂商 API 升级,改代码;新增一个模型,又是一套适配逻辑。


二、统一网关方案:核心思想

解决思路一句话:在业务代码和模型厂商之间插入一个网关层,由网关来做协议翻译和路由分发。

复制代码
                         ┌──────────────────────┐
                         │     你的业务代码       │
                         │                      │
                         │  POST /v1/chat/completions
                         │  Authorization: Bearer <平台Key>
                         │  {"model": "deepseek-v4","messages":[...]}
                         └──────────┬───────────┘
                                    │
                                    ▼
                         ┌──────────────────────┐
                         │      统一网关层       │
                         │                      │
                         │ ① 鉴权:校验平台 Key  │
                         │ ② 路由:模型→通道→上游│
                         │ ③ 适配:OpenAI→厂商原生│
                         │ ④ 计费:实时扣 Token   │
                         │ ⑤ 转发:SSE 流式透传   │
                         └──────────┬───────────┘
                                    │
                   ┌────────────────┼────────────────┐
                   ▼                ▼                ▼
            ┌──────────┐    ┌──────────┐    ┌──────────┐
            │ DeepSeek │    │  通义千问 │    │   豆包    │
            │ (OpenAI) │    │(DashScope)│    │ (OpenAI) │
            └──────────┘    └──────────┘    └──────────┘

对业务代码来说,它只需要知道"调用的模型名"和"输入的消息",完全不用关心这个模型背后是哪个厂商、用的什么协议、Key 是什么。所有复杂度被网关收敛。


三、方案一:自建网关的五个核心模块

如果你有专门的工程团队、需要高度定制,可以选择自建。以下是五个核心模块的实现要点。

3.1 协议适配器

这是工作量最大的模块。核心思路是适配器模式:每个厂商一个 Adapter,实现三个方法。

java 复制代码
// Adapter 接口定义
public interface ModelAdapter {
    // ① 把 OpenAI 格式请求转为厂商原生请求
    Object toProviderRequest(ChatRequest openaiRequest, ChannelConfig channel);
    
    // ② 把厂商响应转回 OpenAI 格式
    ChatResponse toOpenAIResponse(Object providerResponse);
    
    // ③ 把厂商 SSE 事件转成 OpenAI 兼容的 SSE chunk
    String toOpenAISSEChunk(String providerSSELine);
}

以通义千问 DashScope 适配器为例:

java 复制代码
public class DashScopeAdapter implements ModelAdapter {
    @Override
    public Object toProviderRequest(ChatRequest req, ChannelConfig channel) {
        // OpenAI 格式 → DashScope 格式
        Map<String, Object> dashscopeReq = new HashMap<>();
        dashscopeReq.put("model", channel.getProviderModelName());
        dashscopeReq.put("input", Map.of("messages", req.getMessages()));
        Map<String, Object> params = new HashMap<>();
        params.put("result_format", "message");
        dashscopeReq.put("parameters", params);
        return dashscopeReq;
    }
    
    @Override
    public ChatResponse toOpenAIResponse(Object providerResponse) {
        // DashScope 响应 → OpenAI 格式
        Map<String, Object> resp = (Map<String, Object>) providerResponse;
        Map<String, Object> output = (Map<String, Object>) resp.get("output");
        List<Map<String, Object>> choices = (List<Map<String, Object>>) output.get("choices");
        Map<String, Object> choice = choices.get(0);
        Map<String, Object> message = (Map<String, Object>) choice.get("message");
        
        ChatResponse chatResp = new ChatResponse();
        chatResp.setChoices(List.of(new Choice(
            0,
            new Message(message.get("role").toString(), message.get("content").toString()),
            "stop"
        )));
        
        Map<String, Object> usage = (Map<String, Object>) resp.get("usage");
        chatResp.setUsage(new Usage(
            ((Number) usage.get("input_tokens")).intValue(),
            ((Number) usage.get("output_tokens")).intValue(),
            ((Number) usage.get("total_tokens")).intValue()
        ));
        return chatResp;
    }
    
    @Override
    public String toOpenAISSEChunk(String providerSSELine) {
        // DashScope SSE 格式 → OpenAI SSE 格式
        if (providerSSELine.startsWith("data:")) {
            // 解析 DashScope 的 output.text 转为 OpenAI 的 delta.content
            return providerSSELine; // 简化处理,实际需要 JSON 转换
        }
        return providerSSELine;
    }
}

适配器注册表:用工厂模式管理所有适配器。

java 复制代码
public class AdapterRegistry {
    private Map<String, ModelAdapter> adapters = new HashMap<>();
    
    public AdapterRegistry() {
        adapters.put("openai_compatible", new OpenAICompatAdapter());
        adapters.put("dashscope", new DashScopeAdapter());
        adapters.put("anthropic", new AnthropicAdapter());
        adapters.put("baidu_qianfan", new QianfanAdapter());
        adapters.put("zhipu", new ZhipuAdapter());
        // 新厂商只需要在这里注册一个 Adapter
    }
    
    public ModelAdapter getAdapter(String protocolType) {
        return adapters.getOrDefault(protocolType, adapters.get("openai_compatible"));
    }
}

新增一个厂商,只需要写一个 Adapter 类,不改动主流程代码。

3.2 模型路由表

路由是网关的大脑。核心数据结构:模型 → 通道 → 上游地址

数据库设计(简化版):

sql 复制代码
-- 模型定义表
CREATE TABLE lx_model_label (
    id        INT PRIMARY KEY,
    label_name VARCHAR(64),   -- 暴露给业务方的模型名,如 "deepseek-v4"
    model_type VARCHAR(32),   -- chat / embedding / image / video
    status    TINYINT DEFAULT 1
);

-- 通道表(连接模型和上游)
CREATE TABLE lx_channel (
    id             INT PRIMARY KEY,
    channel_name   VARCHAR(64),   -- 通道名称
    model_label_id INT,           -- 关联模型
    endpoint       VARCHAR(255),  -- 上游 API 地址
    protocol_type  VARCHAR(32),   -- openai_compatible / dashscope / anthropic / qianfan
    api_key        VARCHAR(255),  -- 厂商 API Key(加密存储)
    weight         INT DEFAULT 1, -- 负载权重
    status         TINYINT DEFAULT 1
);

路由逻辑:

复制代码
① 业务方请求 model="deepseek-v4"
② 查 lx_model_label → 得到 model_label_id
③ 查 lx_channel WHERE model_label_id = ? AND status = 1
④ 按 weight 加权随机选一条通道
⑤ 拿到 endpoint + protocol_type + api_key
⑥ 根据 protocol_type 获取对应 Adapter
⑦ Adapter 转换请求格式 → 发送到 endpoint

这样模型和通道完全解耦------一个模型可以配多个上游通道(负载、容灾、灰度),切换通道不影响业务方看到的模型名。

3.3 流式 SSE 代理

AI 对话的核心体验是"低首字延迟"。网关不能等上游全返回完了再发给业务方,必须边收边转

java 复制代码
// 响应式 SSE 代理核心逻辑(基于 Spring WebFlux)
public Mono<Void> proxySSE(ServerWebExchange exchange, ChannelConfig channel, String requestBody) {
    // ① Adapter 转换请求体
    ModelAdapter adapter = adapterRegistry.getAdapter(channel.getProtocolType());
    Object providerRequest = adapter.toProviderRequest(parseRequest(requestBody), channel);
    
    // ② 构建上游请求
    WebClient client = WebClient.create(channel.getEndpoint());
    return client.post()
        .uri(channel.getPath())
        .header("Authorization", "Bearer " + channel.getApiKey())
        .bodyValue(providerRequest)
        .retrieve()
        .bodyToFlux(String.class)         // 获取响应式流
        .map(chunk -> adapter.toOpenAISSEChunk(chunk))  // 逐块转换
        .flatMap(chunk -> exchange.getResponse()
            .writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(
                chunk.getBytes(StandardCharsets.UTF_8)
            ))))
        .then();
}

关键点:

  • WebClient + bodyToFlux 获取响应式流,不能用 RestTemplate(阻塞式)
  • 每拿到一个 SSE chunk,立即调用 Adapter 转换,立即 flush 给业务方
  • 不做任何 buffer 和聚合,网关增加的延迟 < 1ms

3.4 统一鉴权体系

业务方不应该持有任何模型厂商的 API Key。网关提供自己的密钥体系:

复制代码
方案:AK/SK + JWT

① 业务方在平台控制台创建 AccessKey(AK + SK)
② 调用时携带 JWT(由 SK 签发)
③ 网关 Filter 校验 JWT → 从 JWT 中读 agencyId、可用模型列表、QPS 上限
④ 校验通过后放行给路由层
⑤ 网关内部持有各模型厂商的 API Key,业务方完全不知道

这样两个价值:

  • 安全:厂商 API Key 只有平台知道,不会泄露
  • 可控:可以按 AK 维度限流、按机构维度计费、按模型维度做权限管控

3.5 Token 计费引擎

统一网关的另一个核心价值是"一份账单"------所有模型的消费都统一核算。

复制代码
计费流程:

① 请求进来时:解析模型名 → 查计费规则表 → 预估费用 → 检查余额(预扣)
② 请求返回后:从响应中提取 usage.prompt_tokens 和 usage.completion_tokens
③ 计算费用:input_tokens × 输入单价 + output_tokens × 输出单价
④ 扣减余额(Redis 原子操作)→ 写入流水表(MySQL)
⑤ 如果上游返回 cached_tokens,这部分不计费
sql 复制代码
-- 计费规则表
CREATE TABLE lx_model_price (
    id               INT PRIMARY KEY,
    model_label_id   INT,
    input_price      DECIMAL(10,6),  -- 输入价格(元/1K tokens)
    output_price     DECIMAL(10,6),  -- 输出价格(元/1K tokens)
    cache_read_price DECIMAL(10,6) DEFAULT 0,  -- 缓存命中价格
    effective_time   DATETIME        -- 价格生效时间(支持调价)
);

四、方案二:使用成熟的 AI API 聚合平台

自建网关的开发周期 2-4 个月,维护成本也高。如果你的团队没有专职基础架构人员,可以考虑使用成熟的聚合平台。

以星枢无极为例,它已经把上述五个模块全部做了标准化:

接入方式 :业务代码零改造,只需把 base_urlapi_key 换成平台提供的地址。

python 复制代码
# 之前:直连 DeepSeek
client = OpenAI(
    api_key="sk-xxxx",
    base_url="https://api.deepseek.com"
)

# 使用聚合平台后:一行改动,调所有模型
client = OpenAI(
    api_key="ak-xxxx",                    # 平台密钥
    base_url="https://api.591ll.com/v1"   # 平台统一入口
)

# 调 DeepSeek
response = client.chat.completions.create(
    model="deepseek-v4",
    messages=[{"role": "user", "content": "你好"}]
)

# 调通义千问 ------ 只改 model 名
response = client.chat.completions.create(
    model="qwen-plus",
    messages=[{"role": "user", "content": "你好"}]
)

# 调豆包 ------ 还是只改 model 名
response = client.chat.completions.create(
    model="doubao-pro",
    messages=[{"role": "user", "content": "你好"}]
)

核心能力对比

能力 自建网关 成熟聚合平台
模型接入数量 需要逐个写 Adapter 40+,平台持续接入
协议适配 需自研 开箱即用
流式 SSE 需自建响应式代理 原生支持,延迟 <50ms
Token 计费 需自研计费引擎 + Redis + MySQL 开箱即用,支持套餐和按量
密钥管理 需自建 AK/SK 体系 平台提供完整密钥管理
厂商 API 变动 自行跟进适配 平台统一维护
开发周期 2-4 个月 0 天
运维成本 需要专人 零运维
适用场景 有专职基础架构团队的大企业 中小企业、创业团队、快速验证

五、关键决策:自建还是用平台

做这个决策,问自己三个问题:

1. 模型数量有多少?

如果只接 1-2 个模型(比如 DeepSeek + Claude),自建适配器的工作量很小,用平台反而多了一层中转延迟。但如果接了 5 个以上,维护成本会指数级增长。

2. 计费逻辑复杂吗?

如果你只是自己用、看看总 Token 数就行,不需要完整的计费引擎。但如果有多个业务方需要按项目核算成本,计费系统就变成了刚需。

3. 有没有人维护?

这是最关键的问题。模型厂商 API 经常变动------改字段名、改鉴权方式、改 SSE 格式。这些变动的适配和回归测试是持续的"隐性成本"。如果没有人持续维护,三个月后网关可能一半模型都调不通。

一个简单的判断标准:

复制代码
如果你需要同时接入 ≥5 个模型 + 有多个业务方共用 → 用聚合平台
如果只接 1-2 个模型 + 单人使用 → 直连或简单自建代理即可

六、实战:用聚合平台搭建一个多模型路由 Demo

下面是一个完整的前端示例,展示如何通过统一网关同时调用 DeepSeek 和通义千问,并做模型级 fallback:

javascript 复制代码
// 多模型路由 + 降级策略
import OpenAI from 'openai';

const client = new OpenAI({
  apiKey: 'ak-xxxx',
  baseURL: 'https://api.591ll.com/v1',
  defaultHeaders: { 'Content-Type': 'application/json' }
});

// 模型优先级链:优先用主模型,失败自动降级
const MODEL_CHAIN = ['deepseek-v4', 'qwen-plus'];

async function chatWithFallback(messages, modelChain = MODEL_CHAIN) {
  for (const model of modelChain) {
    try {
      const response = await client.chat.completions.create({
        model,
        messages,
        stream: false,
      });
      console.log(`[成功] 模型: ${model}, 消耗 Token: ${response.usage.total_tokens}`);
      return response.choices[0].message.content;
    } catch (error) {
      console.warn(`[降级] 模型 ${model} 失败: ${error.message}`);
      // 自动尝试下一个模型
    }
  }
  throw new Error('所有模型均调用失败');
}

// 使用
const answer = await chatWithFallback([
  { role: 'system', content: '你是一个技术专家' },
  { role: 'user', content: '解释一下适配器模式' }
]);
console.log(answer);

这段代码的特点:

  • 只用一个 baseURL 和一个 apiKey,调所有模型
  • 业务代码只关心 model 名称,不关心背后是哪个厂商
  • Fallback 逻辑自动生效,主模型失败自动切备用

七、总结

统一网关解决的不是一个"能不能调"的问题,而是"能不能高效地管理和维护多个模型的接入"。

核心思路就三层:

  1. 统一入:业务方只认 OpenAI 兼容协议 + 平台密钥
  2. 中间转:网关做协议适配 + 路由分发 + 计费核算
  3. 多路出:网关持有各厂商 API Key,按需路由到对应上游

不管你是选择自建还是使用成熟平台,不要让业务代码和模型厂商直接耦合。把适配复杂度收敛到一个地方,是处理多模型接入的第一原则。


本文基于 2026 年 6 月的国内大模型 API 生态撰写。模型厂商和 API 格式持续变化中,具体接入细节请参考各厂商最新文档。