協議轉換說明
RouteAPI 作為統一 API 閘道,在內部自動完成不同協議格式之間的轉換。當你用 OpenAI 協議呼叫 Claude 模型,或用 Claude Messages 協議呼叫 Gemini 模型時,RouteAPI 會處理訊息結構、參數映射和響應格式的差異,讓你無需關心底層協議細節。
轉換機制概述
Section titled “轉換機制概述”RouteAPI 作為協議適配層
Section titled “RouteAPI 作為協議適配層”RouteAPI 支援三種主流協議入口:
- OpenAI 相容協議:
/v1/chat/completions、/v1/responses - Claude Messages 協議:
/v1/messages - Google Gemini 協議:
/v1beta/models/{model}:generateContent
當請求到達時,RouteAPI 根據路徑識別協議類型,根據模型 ID 確定目標上游,然後在兩者之間執行必要的格式轉換。
何時需要協議轉換
Section titled “何時需要協議轉換”| 場景 | 是否轉換 | 說明 |
|---|---|---|
| OpenAI 協議 → OpenAI 系模型 | 否 | 原樣透傳 |
| Claude Messages → Claude 系模型 | 否 | 原樣透傳 |
| OpenAI 協議 → Claude 系模型 | 是 | OpenAI → Claude Messages |
| OpenAI 協議 → Gemini 系模型 | 是 | OpenAI → Gemini contents |
| Claude Messages → OpenAI 系模型 | 是 | Claude Messages → OpenAI |
| Claude Messages → Gemini 系模型 | 是 | Claude Messages → Gemini |
轉換的透明性
Section titled “轉換的透明性”協議轉換對客戶端是透明的。你發送 OpenAI 格式的請求,就會收到 OpenAI 格式的響應,即使底層呼叫了 Claude 或 Gemini。
但轉換有局限性:
- 目標協議不支援的參數會被忽略或使用預設值。
- 某些協議特有的能力(如 Claude 的擴展思考、提示快取)在轉換後可能無法完整表達。
- 轉換過程會引入輕微的延遲(通常在 10ms 以內)。
最佳實踐:優先使用目標模型原生支援的協議,可以獲得最完整的功能支援和最佳效能。
訊息格式轉換
Section titled “訊息格式轉換”OpenAI messages ↔ Claude messages
Section titled “OpenAI messages ↔ Claude messages”system prompt 處理差異
Section titled “system prompt 處理差異”OpenAI 把 system prompt 作為 messages 陣列的第一條訊息:
{ "messages": [ { "role": "system", "content": "你是一個嚴謹的技術助手。" }, { "role": "user", "content": "解釋什麼是 API 閘道" } ]}Claude 把 system prompt 放在頂層獨立欄位:
{ "system": "你是一個嚴謹的技術助手。", "messages": [ { "role": "user", "content": "解釋什麼是 API 閘道" } ]}轉換規則:
- OpenAI → Claude:提取第一條
role: "system"訊息,移到system欄位。 - Claude → OpenAI:將
system欄位內容轉為role: "system"訊息,插入messages陣列開頭。
role 映射
Section titled “role 映射”| OpenAI | Claude | Gemini | 說明 |
|---|---|---|---|
system | 頂層 system 欄位 | systemInstruction | 系統提示詞位置不同 |
user | user | user | 使用者訊息,一致 |
assistant | assistant | model | 助手/模型回覆,名稱不同 |
tool | user 中的 tool_result | user 中的 functionResponse | 工具結果歸屬不同 |
轉換注意事項:
- Claude 不接受連續兩條同角色訊息,轉換時需要合併或插入佔位訊息。
- Gemini 的
model角色在轉換為 OpenAI 時映射為assistant。 - OpenAI 的
role: "tool"在 Claude 和 Gemini 中都被合併到user訊息裡。
OpenAI messages ↔ Gemini contents
Section titled “OpenAI messages ↔ Gemini contents”Gemini 的訊息結構叫 contents,每條訊息的角色叫 role,內容在 parts 陣列中:
{ "contents": [ { "role": "user", "parts": [{ "text": "解釋什麼是 API 閘道" }] } ]}轉換規則:
- OpenAI
messages↔ Geminicontents - OpenAI
content↔ Geminiparts - OpenAI
assistant↔ Geminimodel - system prompt 轉為
systemInstruction頂層欄位
取樣參數對照
Section titled “取樣參數對照”| OpenAI | Claude | Gemini | 說明 |
|---|---|---|---|
temperature | temperature | temperature | Claude 上限 1,OpenAI 上限 2,Gemini 上限 2 |
top_p | top_p | topP | nucleus sampling,三者都支援 |
| 不支援 | top_k | topK | OpenAI 不支援,轉換時丟棄 |
max_tokens / max_completion_tokens | max_tokens(必填) | maxOutputTokens | Claude 必須顯式設定 |
n | 不支援 | candidateCount | Claude 不支援產生多個候選 |
stop | stop_sequences | stopSequences | 欄位名不同,語義一致 |
溫度範圍轉換:
當 OpenAI 請求的 temperature 超過 1 且目標是 Claude 時,RouteAPI 會自動將其截斷到 1,避免請求被上游拒絕。
輸出控制參數
Section titled “輸出控制參數”| OpenAI | Claude | Gemini | 說明 |
|---|---|---|---|
response_format | 不支援 | responseMimeType | OpenAI 支援 JSON mode 和 JSON Schema |
frequency_penalty | 不支援 | frequencyPenalty | Claude 不支援懲罰參數 |
presence_penalty | 不支援 | presencePenalty | Claude 不支援懲罰參數 |
stream | stream | stream | 三者都支援,但事件格式完全不同 |
stream_options.include_usage | 始終返回 | generateContentRequest.stream=true 時自動返回 | 用量統計返回方式不同 |
轉換行為:
frequency_penalty和presence_penalty轉發到 Claude 時會被忽略。response_format: { type: "json_object" }轉發到 Claude 時會透過工具呼叫模擬,或在 system prompt 中新增 JSON 輸出提示。n > 1轉發到 Claude 時會被重設為 1,因為 Claude 不支援多候選產生。
元資訊與控制
Section titled “元資訊與控制”| OpenAI | Claude | Gemini | 說明 |
|---|---|---|---|
user | metadata.user_id | 不支援 | 用於濫用偵測 |
seed | 不支援 | seed | Claude 不支援確定性取樣 |
logprobs / top_logprobs | 不支援 | 不支援 | 只有 OpenAI 系模型支援 |
| 不支援 | thinking | 不支援 | Claude 特有的擴展思考設定 |
工具呼叫轉換
Section titled “工具呼叫轉換”工具定義格式差異
Section titled “工具定義格式差異”OpenAI tools 格式
Section titled “OpenAI tools 格式”{ "tools": [ { "type": "function", "function": { "name": "get_weather", "description": "查詢指定城市的當前天氣", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名" } }, "required": ["city"] } } } ]}Claude tools 格式
Section titled “Claude tools 格式”{ "tools": [ { "name": "get_weather", "description": "查詢指定城市的當前天氣", "input_schema": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名" } }, "required": ["city"] } } ]}Gemini tools 格式
Section titled “Gemini tools 格式”{ "tools": [ { "functionDeclarations": [ { "name": "get_weather", "description": "查詢指定城市的當前天氣", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名" } }, "required": ["city"] } } ] } ]}工具定義轉換規則
Section titled “工具定義轉換規則”| OpenAI | Claude | Gemini | 轉換說明 |
|---|---|---|---|
tools[].type: "function" | 無此層級 | 無此層級 | 轉換時去掉 type 包裝 |
tools[].function | 平鋪在 tools[] | 放在 functionDeclarations[] | 層級結構不同 |
function.parameters | input_schema | parameters | Claude 欄位名不同 |
tool_choice 映射
Section titled “tool_choice 映射”| OpenAI | Claude | Gemini | 說明 |
|---|---|---|---|
"auto" | { "type": "auto" } | "AUTO" | 模型自動決定 |
"none" | { "type": "none" } | "NONE" | 禁止呼叫工具 |
"required" | { "type": "any" } | "ANY" | 必須呼叫工具 |
{ "type": "function", "function": { "name": "get_weather" } } | { "type": "tool", "name": "get_weather" } | { "functionCallingConfig": { "allowedFunctionNames": ["get_weather"] } } | 強制呼叫指定工具,結構差異大 |
工具結果格式轉換
Section titled “工具結果格式轉換”OpenAI 工具結果
Section titled “OpenAI 工具結果”{ "role": "tool", "tool_call_id": "call_abc123", "content": "北京,晴,23°C"}Claude 工具結果
Section titled “Claude 工具結果”{ "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_01A09q90qw90lq917835lq9", "content": "北京,晴,23°C" } ]}Gemini 工具結果
Section titled “Gemini 工具結果”{ "role": "user", "parts": [ { "functionResponse": { "name": "get_weather", "response": { "result": "北京,晴,23°C" } } } ]}轉換要點:
- OpenAI 的
role: "tool"在轉換為 Claude/Gemini 時合併到user訊息中。 - 工具呼叫 ID 欄位名不同:
tool_call_idvstool_use_idvs Gemini 的函式名識別。 - Claude 和 Gemini 要求所有工具結果必須在同一條
user訊息內,OpenAI 允許分開多條tool訊息。
多模態內容轉換
Section titled “多模態內容轉換”圖片 URL 格式轉換
Section titled “圖片 URL 格式轉換”OpenAI 格式
Section titled “OpenAI 格式”{ "role": "user", "content": [ { "type": "image_url", "image_url": { "url": "https://example.com/image.jpg", "detail": "high" } }, { "type": "text", "text": "描述這張圖" } ]}Claude 格式
Section titled “Claude 格式”{ "role": "user", "content": [ { "type": "image", "source": { "type": "url", "url": "https://example.com/image.jpg" } }, { "type": "text", "text": "描述這張圖" } ]}Gemini 格式
Section titled “Gemini 格式”{ "role": "user", "parts": [ { "fileData": { "mimeType": "image/jpeg", "fileUri": "https://example.com/image.jpg" } }, { "text": "描述這張圖" } ]}Base64 編碼處理
Section titled “Base64 編碼處理”OpenAI base64 格式
Section titled “OpenAI base64 格式”{ "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,/9j/4AAQSkZJRg..." }}Claude base64 格式
Section titled “Claude base64 格式”{ "type": "image", "source": { "type": "base64", "media_type": "image/jpeg", "data": "/9j/4AAQSkZJRg..." }}Gemini base64 格式
Section titled “Gemini base64 格式”{ "inlineData": { "mimeType": "image/jpeg", "data": "/9j/4AAQSkZJRg..." }}轉換規則:
- OpenAI 的
data:URI 會被解析,media_type從 URI 前綴提取,純 base64 部分傳給目標協議。 - Claude 要求單獨的
media_type欄位,不接受data:URI。 - Gemini 使用
inlineData而非fileData來表示 base64 內容。 - OpenAI 的
detail參數(low/high)在轉換時丟失,Claude 和 Gemini 沒有對應概念。
不支援的參數處理
Section titled “不支援的參數處理”哪些參數無法轉換
Section titled “哪些參數無法轉換”| 參數 | 原協議 | 無法轉換到 | 原因 |
|---|---|---|---|
top_k | Claude, Gemini | OpenAI | OpenAI 不支援 top-k 取樣 |
n | OpenAI, Gemini | Claude | Claude 不支援多候選產生 |
frequency_penalty / presence_penalty | OpenAI, Gemini | Claude | Claude 沒有懲罰參數 |
logprobs | OpenAI | Claude, Gemini | 只有 OpenAI 系模型返回對數機率 |
thinking | Claude | OpenAI, Gemini | Claude 特有的擴展思考設定 |
cache_control | Claude | OpenAI, Gemini | Claude 特有的提示快取控制 |
reasoning_effort | OpenAI | Claude, Gemini | OpenAI o1 系列特有參數 |
response_format (JSON Schema) | OpenAI | Claude, Gemini | 完整的 JSON Schema 約束只有 OpenAI 支援 |
如何處理不相容參數
Section titled “如何處理不相容參數”RouteAPI 採用以下策略:
- 靜默忽略:不支援的參數在轉換時直接丟棄,不影響請求成功(如
detail、logprobs)。 - 自動調整:超出範圍的值被截斷到合法區間(如
temperature > 1轉發到 Claude 時截斷為 1)。 - 保守降級:複雜功能用簡單方式模擬(如 OpenAI 的 JSON Schema 在 Claude 上降級為工具呼叫或提示詞約束)。
- 拒絕請求:極少數情況下,如果核心參數無法轉換且沒有合理預設值,返回 400 錯誤(如 Claude 協議缺少
max_tokens)。
警告和錯誤提示
Section titled “警告和錯誤提示”RouteAPI 在響應標頭和日誌中提供轉換資訊:
X-RouteAPI-Protocol-Conversion: openai-to-claudeX-RouteAPI-Dropped-Params: frequency_penalty,presence_penalty如果轉換失敗或參數衝突,返回標準錯誤響應:
{ "error": { "message": "Parameter 'max_tokens' is required for Claude models", "type": "invalid_request_error", "param": "max_tokens", "code": "missing_required_parameter" }}- 使用原生協議:儘量用模型原生協議呼叫,避免轉換損失。
- 避免依賴特定協議特性:不要依賴
logprobs、thinking等單一協議特有能力,除非確定只用該協議的模型。 - 檢查響應標頭:關注
X-RouteAPI-Dropped-Params標頭,了解哪些參數被忽略。 - 測試跨協議相容性:在測試環境驗證同一請求在不同協議/模型組合下的行為。
- 記錄模型 ID:日誌中記錄實際呼叫的模型 ID 和協議類型,便於排查差異。
響應格式統一
Section titled “響應格式統一”usage 欄位標準化
Section titled “usage 欄位標準化”| 協議 | input token 欄位 | output token 欄位 | total token 欄位 |
|---|---|---|---|
| OpenAI | prompt_tokens | completion_tokens | total_tokens |
| Claude | input_tokens | output_tokens | 無(需自行相加) |
| Gemini | promptTokenCount | candidatesTokenCount | totalTokenCount |
轉換規則:
- Claude → OpenAI:
input_tokens→prompt_tokens,output_tokens→completion_tokens,計算total_tokens = input_tokens + output_tokens。 - Gemini → OpenAI:
promptTokenCount→prompt_tokens,candidatesTokenCount→completion_tokens,totalTokenCount→total_tokens。 - OpenAI → Claude:
prompt_tokens→input_tokens,completion_tokens→output_tokens,丟棄total_tokens。
Claude 的提示快取欄位也會保留:
{ "usage": { "prompt_tokens": 120, "completion_tokens": 45, "total_tokens": 165, "cache_creation_input_tokens": 80, "cache_read_input_tokens": 40 }}finish_reason 標準化
Section titled “finish_reason 標準化”| OpenAI | Claude | Gemini | 含義 |
|---|---|---|---|
stop | end_turn | STOP | 自然結束 |
length | max_tokens | MAX_TOKENS | 達到長度上限 |
tool_calls | tool_use | STOP(含 functionCall) | 請求呼叫工具 |
content_filter | 無對應 | SAFETY | 內容被安全過濾器攔截 |
stop | stop_sequence | STOP | 命中停止序列 |
轉換規則:
- Claude
end_turn→ OpenAIstop - Claude
max_tokens→ OpenAIlength - Claude
tool_use→ OpenAItool_calls - Gemini
STOP根據是否有functionCall映射為stop或tool_calls - Gemini
SAFETY→ OpenAIcontent_filter
錯誤響應統一
Section titled “錯誤響應統一”所有協議的錯誤響應都轉換為 OpenAI 格式(當客戶端用 OpenAI 協議請求時):
{ "error": { "message": "Invalid API key", "type": "invalid_request_error", "param": null, "code": "invalid_api_key" }}Claude 原始錯誤:
{ "type": "error", "error": { "type": "authentication_error", "message": "invalid x-api-key" }}轉換為 OpenAI 格式後,type 映射為 invalid_request_error,code 設定為 invalid_api_key。
範例 1:OpenAI 請求 → Claude 格式
Section titled “範例 1:OpenAI 請求 → Claude 格式”原始 OpenAI 請求:
{ "model": "claude-sonnet-4-5", "messages": [ { "role": "system", "content": "你是一個嚴謹的技術助手,回答保持簡潔。" }, { "role": "user", "content": "解釋什麼是 API 閘道" } ], "temperature": 0.7, "max_tokens": 150, "stream": false}轉換後的 Claude 請求:
{ "model": "claude-sonnet-4-5", "system": "你是一個嚴謹的技術助手,回答保持簡潔。", "messages": [ { "role": "user", "content": "解釋什麼是 API 閘道" } ], "temperature": 0.7, "max_tokens": 150, "stream": false}關鍵變化:
- system 訊息從
messages陣列提取到頂層system欄位。 messages現在只包含user和assistant訊息。
範例 2:Claude 工具呼叫 → OpenAI 格式
Section titled “範例 2:Claude 工具呼叫 → OpenAI 格式”Claude 工具呼叫響應:
{ "id": "msg_01XFDUDYJgAACzvnptvVoYEL", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [ { "type": "text", "text": "我來查一下北京的天氣。" }, { "type": "tool_use", "id": "toolu_01A09q90qw90lq917835lq9", "name": "get_weather", "input": { "city": "北京" } } ], "stop_reason": "tool_use", "usage": { "input_tokens": 120, "output_tokens": 45 }}轉換為 OpenAI 格式:
{ "id": "msg_01XFDUDYJgAACzvnptvVoYEL", "object": "chat.completion", "created": 1726567890, "model": "claude-sonnet-4-5", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "我來查一下北京的天氣。", "tool_calls": [ { "id": "toolu_01A09q90qw90lq917835lq9", "type": "function", "function": { "name": "get_weather", "arguments": "{\"city\":\"北京\"}" } } ] }, "finish_reason": "tool_calls" } ], "usage": { "prompt_tokens": 120, "completion_tokens": 45, "total_tokens": 165 }}關鍵變化:
content陣列拆分:text區塊提取為content欄位,tool_use區塊轉為tool_calls陣列。tool_use.input物件序列化為function.argumentsJSON 字串。stop_reason: "tool_use"→finish_reason: "tool_calls"。input_tokens→prompt_tokens,output_tokens→completion_tokens,增加total_tokens。- 增加 OpenAI 格式的頂層欄位:
object、created、choices陣列。
範例 3:Gemini 多模態 → OpenAI 格式
Section titled “範例 3:Gemini 多模態 → OpenAI 格式”Gemini 響應:
{ "candidates": [ { "content": { "parts": [ { "text": "這張圖片顯示了一個現代化的使用者介面,包含導覽列、內容區域和側邊欄。" } ], "role": "model" }, "finishReason": "STOP", "index": 0 } ], "usageMetadata": { "promptTokenCount": 258, "candidatesTokenCount": 32, "totalTokenCount": 290 }}轉換為 OpenAI 格式:
{ "id": "chatcmpl-gemini-abc123", "object": "chat.completion", "created": 1726567890, "model": "gemini-2.0-flash-exp", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "這張圖片顯示了一個現代化的使用者介面,包含導覽列、內容區域和側邊欄。" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 258, "completion_tokens": 32, "total_tokens": 290 }}關鍵變化:
candidates[0].content.parts[0].text→choices[0].message.content。role: "model"→role: "assistant"。finishReason: "STOP"→finish_reason: "stop"(轉為小寫)。usageMetadata欄位名映射到 OpenAI 的usage結構。
最佳實踐總結
Section titled “最佳實踐總結”1. 選擇合適的協議
Section titled “1. 選擇合適的協議”根據客戶端和模型類型選擇協議:
| 客戶端類型 | 目標模型 | 推薦協議 | 原因 |
|---|---|---|---|
| OpenAI SDK | OpenAI 系模型 | OpenAI | 原生支援,零轉換 |
| Claude Code | Claude 系模型 | Claude Messages | 原生支援,零轉換 |
| LangChain / LiteLLM | 任意模型 | OpenAI | 生態相容性最好 |
| Anthropic SDK | Claude 系模型 | Claude Messages | 存取擴展思考、提示快取 |
| 自研客戶端 | 任意模型 | 取決於需求 | 優先用目標模型原生協議 |
2. 避免依賴特定協議的特性
Section titled “2. 避免依賴特定協議的特性”如果業務需要跨模型切換,避免使用以下特性:
- OpenAI 特有:
logprobs、seed、完整 JSON Schema 約束、reasoning_effort(o1 系列) - Claude 特有:
thinking、cache_control、mcp_servers - Gemini 特有:
grounding、codeExecution
通用特性集合(三者都支援):
- 基礎聊天對話(
messages/contents) - 串流輸出(
stream) - 溫度控制(
temperature,注意範圍差異) - 工具呼叫(
tools,注意格式差異) - 多模態輸入(圖片,注意格式差異)
- 停止序列(
stop/stop_sequences/stopSequences)
3. 測試跨協議相容性
Section titled “3. 測試跨協議相容性”在測試環境驗證以下場景:
- 同一協議,不同模型:確保 OpenAI 協議能正確呼叫 Claude 和 Gemini 模型。
- 不同協議,同一模型:驗證 Claude Messages 和 OpenAI 協議呼叫同一 Claude 模型的結果一致性。
- 工具呼叫往返:測試跨協議的工具定義、呼叫和結果傳遞是否正確。
- 邊界參數:測試
temperature: 1.5(OpenAI 合法,Claude 需截斷)、n: 2(OpenAI 支援,Claude 不支援)等邊界情況。 - 錯誤處理:驗證上游錯誤是否正確轉換為客戶端協議格式。
4. 監控和日誌
Section titled “4. 監控和日誌”記錄以下資訊便於排查協議轉換問題:
{ "request_id": "req_abc123", "client_protocol": "openai", "model_id": "claude-sonnet-4-5", "upstream_protocol": "claude", "conversion_required": true, "dropped_params": ["frequency_penalty", "logprobs"], "adjusted_params": {"temperature": {"original": 1.8, "adjusted": 1.0}}, "latency_ms": 856, "conversion_overhead_ms": 8}關鍵指標:
- 轉換成功率:協議轉換失敗導致的 400 錯誤占比。
- 轉換延遲:協議轉換引入的額外延遲(通常 5-15ms)。
- 參數丟棄率:哪些參數最常被丟棄,是否影響業務。
- 跨協議錯誤率:OpenAI → Claude 呼叫的錯誤率是否高於 OpenAI → OpenAI。
5. 漸進式遷移策略
Section titled “5. 漸進式遷移策略”如果要從一個協議遷移到另一個協議:
- 階段 1:雙寫測試:新協議呼叫結果僅用於對比,不影響業務。
- 階段 2:灰度切換:小流量切換到新協議,監控錯誤率和響應品質。
- 階段 3:全量切換:確認無異常後全量切換。
- 階段 4:清理舊程式碼:移除舊協議的適配程式碼。
每個階段都需要驗證:
- 功能正確性(工具呼叫、多模態、串流輸出)
- 響應品質(不同協議/模型組合的輸出差異)
- 效能指標(延遲、token 用量、成本)
- 錯誤處理(網路異常、限流、上游故障)
協議轉換讓你可以靈活選擇客戶端和模型,但最佳實踐仍然是優先使用目標模型的原生協議。如果必須跨協議呼叫,請在測試環境充分驗證,並在生產環境監控轉換相關的錯誤和效能指標。