# 《WaLiAPI - 本地 LLM API 网关》第1-4节:多供应商协议适配

作者:小傅哥
博客:https://bugstack.cn (opens new window)

沉淀、分享、成长,让自己和他人都能有所收获!😄

大家好,我是技术UP主小傅哥。

上一节我们实现了 OpenAI 和 DeepSeek 适配器——它们都遵循 OpenAI 协议,转发是"透传"式的。但 Claude 和 Gemini 的 API 协议与 OpenAI 完全不同:认证方式、请求体结构、响应格式、Token 计数字段都不一样。这一节我们来做真正的"协议翻译"。

# 一、本章诉求

  1. 分析 OpenAI / Claude / Gemini 三家协议的差异
  2. 实现 Claude 适配器:OpenAI 请求 → Claude 请求,Claude 响应 → OpenAI 响应
  3. 实现 Gemini 适配器:同样的双向转换
  4. 实现 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

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

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 响应体结构对比

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

Claude 响应

{
  "id": "msg_xxx",
  "content": [{"type": "text", "text": "你好!"}],   // 内容是 block 数组
  "usage": {"input_tokens": 10, "output_tokens": 5}  // 字段名不同
}
1
2
3
4
5

Gemini 响应

{
  "candidates": [{
    "content": {"parts": [{"text": "你好!"}], "role": "model"}
  }],
  "usageMetadata": {"promptTokenCount": 10, "candidatesTokenCount": 5}
}
1
2
3
4
5
6

适配器的职责就是做这两次转换:请求时 OpenAI → 上游格式,响应时上游格式 → OpenAI。这样下游应用完全无感知。