# 《WaLiAPI - 本地 LLM API 网关》第1-4节:多供应商协议适配
作者:小傅哥
博客:https://bugstack.cn (opens new window)
沉淀、分享、成长,让自己和他人都能有所收获!😄
大家好,我是技术UP主小傅哥。
上一节我们实现了 OpenAI 和 DeepSeek 适配器——它们都遵循 OpenAI 协议,转发是"透传"式的。但 Claude 和 Gemini 的 API 协议与 OpenAI 完全不同:认证方式、请求体结构、响应格式、Token 计数字段都不一样。这一节我们来做真正的"协议翻译"。
# 一、本章诉求
- 分析 OpenAI / Claude / Gemini 三家协议的差异
- 实现 Claude 适配器:OpenAI 请求 → Claude 请求,Claude 响应 → OpenAI 响应
- 实现 Gemini 适配器:同样的双向转换
- 实现 Custom 适配器:OpenAI 兼容端点的通用兜底
# 二、三家协议对比
# 2.1 认证方式对比
| 供应商 | 认证头 | 示例 |
|---|---|---|
| OpenAI | Authorization: Bearer <key> | Bearer sk-xxx |
| Claude | x-api-key: <key> + anthropic-version | x-api-key: sk-ant-xxx |
| Gemini | URL 查询参数 ?key=<key> | ?key=AIza... |
# 2.2 请求体结构对比
OpenAI 格式(我们的网关对外统一格式):
{
"model": "gpt-4o",
"messages": [
{"role": "system", "content": "你是助手"},
{"role": "user", "content": "你好"}
],
"max_tokens": 4096,
"temperature": 0.7,
"stream": false
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Claude 格式:
{
"model": "claude-sonnet-4-20250514",
"system": "你是助手", // system 是顶层字段,不在 messages 里
"messages": [ // 只允许 user/assistant
{"role": "user", "content": "你好"}
],
"max_tokens": 4096, // 必填!
"temperature": 0.7,
"stream": false
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Gemini 格式:
{
"systemInstruction": { // system 独立字段
"parts": [{"text": "你是助手"}]
},
"contents": [ // 不叫 messages,叫 contents
{"role": "user", "parts": [{"text": "你好"}]}, // content 变成 parts 数组
{"role": "model", "parts": [{"text": "你好!"}]} // assistant 叫 model
],
"generationConfig": { // 参数收口到 generationConfig
"temperature": 0.7,
"maxOutputTokens": 4096 // 字段名也不同
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 2.3 响应体结构对比
OpenAI 响应:
{
"id": "chatcmpl-xxx",
"object": "chat.completion",
"choices": [{
"index": 0,
"message": {"role": "assistant", "content": "你好!"},
"finish_reason": "stop"
}],
"usage": {"prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Claude 响应:
{
"id": "msg_xxx",
"content": [{"type": "text", "text": "你好!"}], // 内容是 block 数组
"usage": {"input_tokens": 10, "output_tokens": 5} // 字段名不同
}
1
2
3
4
5
2
3
4
5
Gemini 响应:
{
"candidates": [{
"content": {"parts": [{"text": "你好!"}], "role": "model"}
}],
"usageMetadata": {"promptTokenCount": 10, "candidatesTokenCount": 5}
}
1
2
3
4
5
6
2
3
4
5
6
适配器的职责就是做这两次转换:请求时 OpenAI → 上游格式,响应时上游格式 → OpenAI。这样下游应用完全无感知。

