做 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_url 和 api_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 逻辑自动生效,主模型失败自动切备用
七、总结
统一网关解决的不是一个"能不能调"的问题,而是"能不能高效地管理和维护多个模型的接入"。
核心思路就三层:
- 统一入:业务方只认 OpenAI 兼容协议 + 平台密钥
- 中间转:网关做协议适配 + 路由分发 + 计费核算
- 多路出:网关持有各厂商 API Key,按需路由到对应上游
不管你是选择自建还是使用成熟平台,不要让业务代码和模型厂商直接耦合。把适配复杂度收敛到一个地方,是处理多模型接入的第一原则。
本文基于 2026 年 6 月的国内大模型 API 生态撰写。模型厂商和 API 格式持续变化中,具体接入细节请参考各厂商最新文档。