跳到內容

協議轉換說明

RouteAPI 作為統一 API 閘道,在內部自動完成不同協議格式之間的轉換。當你用 OpenAI 協議呼叫 Claude 模型,或用 Claude Messages 協議呼叫 Gemini 模型時,RouteAPI 會處理訊息結構、參數映射和響應格式的差異,讓你無需關心底層協議細節。

RouteAPI 支援三種主流協議入口:

  • OpenAI 相容協議:/v1/chat/completions、/v1/responses
  • Claude Messages 協議:/v1/messages
  • Google Gemini 協議:/v1beta/models/{model}:generateContent

當請求到達時,RouteAPI 根據路徑識別協議類型,根據模型 ID 確定目標上游,然後在兩者之間執行必要的格式轉換。

場景是否轉換說明
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

協議轉換對客戶端是透明的。你發送 OpenAI 格式的請求,就會收到 OpenAI 格式的響應,即使底層呼叫了 Claude 或 Gemini。

但轉換有局限性:

  • 目標協議不支援的參數會被忽略或使用預設值。
  • 某些協議特有的能力(如 Claude 的擴展思考、提示快取)在轉換後可能無法完整表達。
  • 轉換過程會引入輕微的延遲(通常在 10ms 以內)。

最佳實踐:優先使用目標模型原生支援的協議,可以獲得最完整的功能支援和最佳效能。

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 陣列開頭。
OpenAIClaudeGemini說明
system頂層 system 欄位systemInstruction系統提示詞位置不同
useruseruser使用者訊息,一致
assistantassistantmodel助手/模型回覆,名稱不同
tooluser 中的 tool_resultuser 中的 functionResponse工具結果歸屬不同

轉換注意事項:

  • Claude 不接受連續兩條同角色訊息,轉換時需要合併或插入佔位訊息。
  • Gemini 的 model 角色在轉換為 OpenAI 時映射為 assistant。
  • OpenAI 的 role: "tool" 在 Claude 和 Gemini 中都被合併到 user 訊息裡。

Gemini 的訊息結構叫 contents,每條訊息的角色叫 role,內容在 parts 陣列中:

{
"contents": [
{
"role": "user",
"parts": [{ "text": "解釋什麼是 API 閘道" }]
}
]
}

轉換規則:

  • OpenAI messages ↔ Gemini contents
  • OpenAI content ↔ Gemini parts
  • OpenAI assistant ↔ Gemini model
  • system prompt 轉為 systemInstruction 頂層欄位
OpenAIClaudeGemini說明
temperaturetemperaturetemperatureClaude 上限 1,OpenAI 上限 2,Gemini 上限 2
top_ptop_ptopPnucleus sampling,三者都支援
不支援top_ktopKOpenAI 不支援,轉換時丟棄
max_tokens / max_completion_tokensmax_tokens(必填)maxOutputTokensClaude 必須顯式設定
n不支援candidateCountClaude 不支援產生多個候選
stopstop_sequencesstopSequences欄位名不同,語義一致

溫度範圍轉換:

當 OpenAI 請求的 temperature 超過 1 且目標是 Claude 時,RouteAPI 會自動將其截斷到 1,避免請求被上游拒絕。

OpenAIClaudeGemini說明
response_format不支援responseMimeTypeOpenAI 支援 JSON mode 和 JSON Schema
frequency_penalty不支援frequencyPenaltyClaude 不支援懲罰參數
presence_penalty不支援presencePenaltyClaude 不支援懲罰參數
streamstreamstream三者都支援,但事件格式完全不同
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 不支援多候選產生。
OpenAIClaudeGemini說明
usermetadata.user_id不支援用於濫用偵測
seed不支援seedClaude 不支援確定性取樣
logprobs / top_logprobs不支援不支援只有 OpenAI 系模型支援
不支援thinking不支援Claude 特有的擴展思考設定
{
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查詢指定城市的當前天氣",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "城市名" }
},
"required": ["city"]
}
}
}
]
}
{
"tools": [
{
"name": "get_weather",
"description": "查詢指定城市的當前天氣",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "城市名" }
},
"required": ["city"]
}
}
]
}
{
"tools": [
{
"functionDeclarations": [
{
"name": "get_weather",
"description": "查詢指定城市的當前天氣",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "城市名" }
},
"required": ["city"]
}
}
]
}
]
}
OpenAIClaudeGemini轉換說明
tools[].type: "function"無此層級無此層級轉換時去掉 type 包裝
tools[].function平鋪在 tools[]放在 functionDeclarations[]層級結構不同
function.parametersinput_schemaparametersClaude 欄位名不同
OpenAIClaudeGemini說明
"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"] } }強制呼叫指定工具,結構差異大
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "北京,晴,23°C"
}
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "北京,晴,23°C"
}
]
}
{
"role": "user",
"parts": [
{
"functionResponse": {
"name": "get_weather",
"response": { "result": "北京,晴,23°C" }
}
}
]
}

轉換要點:

  • OpenAI 的 role: "tool" 在轉換為 Claude/Gemini 時合併到 user 訊息中。
  • 工具呼叫 ID 欄位名不同:tool_call_id vs tool_use_id vs Gemini 的函式名識別。
  • Claude 和 Gemini 要求所有工具結果必須在同一條 user 訊息內,OpenAI 允許分開多條 tool 訊息。
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {
"url": "https://example.com/image.jpg",
"detail": "high"
}
},
{ "type": "text", "text": "描述這張圖" }
]
}
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "url",
"url": "https://example.com/image.jpg"
}
},
{ "type": "text", "text": "描述這張圖" }
]
}
{
"role": "user",
"parts": [
{
"fileData": {
"mimeType": "image/jpeg",
"fileUri": "https://example.com/image.jpg"
}
},
{ "text": "描述這張圖" }
]
}
{
"type": "image_url",
"image_url": {
"url": "data:image/jpeg;base64,/9j/4AAQSkZJRg..."
}
}
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": "/9j/4AAQSkZJRg..."
}
}
{
"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 沒有對應概念。
參數原協議無法轉換到原因
top_kClaude, GeminiOpenAIOpenAI 不支援 top-k 取樣
nOpenAI, GeminiClaudeClaude 不支援多候選產生
frequency_penalty / presence_penaltyOpenAI, GeminiClaudeClaude 沒有懲罰參數
logprobsOpenAIClaude, Gemini只有 OpenAI 系模型返回對數機率
thinkingClaudeOpenAI, GeminiClaude 特有的擴展思考設定
cache_controlClaudeOpenAI, GeminiClaude 特有的提示快取控制
reasoning_effortOpenAIClaude, GeminiOpenAI o1 系列特有參數
response_format (JSON Schema)OpenAIClaude, Gemini完整的 JSON Schema 約束只有 OpenAI 支援

RouteAPI 採用以下策略:

  1. 靜默忽略:不支援的參數在轉換時直接丟棄,不影響請求成功(如 detail、logprobs)。
  2. 自動調整:超出範圍的值被截斷到合法區間(如 temperature > 1 轉發到 Claude 時截斷為 1)。
  3. 保守降級:複雜功能用簡單方式模擬(如 OpenAI 的 JSON Schema 在 Claude 上降級為工具呼叫或提示詞約束)。
  4. 拒絕請求:極少數情況下,如果核心參數無法轉換且沒有合理預設值,返回 400 錯誤(如 Claude 協議缺少 max_tokens)。

RouteAPI 在響應標頭和日誌中提供轉換資訊:

X-RouteAPI-Protocol-Conversion: openai-to-claude
X-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"
}
}
  1. 使用原生協議:儘量用模型原生協議呼叫,避免轉換損失。
  2. 避免依賴特定協議特性:不要依賴 logprobs、thinking 等單一協議特有能力,除非確定只用該協議的模型。
  3. 檢查響應標頭:關注 X-RouteAPI-Dropped-Params 標頭,了解哪些參數被忽略。
  4. 測試跨協議相容性:在測試環境驗證同一請求在不同協議/模型組合下的行為。
  5. 記錄模型 ID:日誌中記錄實際呼叫的模型 ID 和協議類型,便於排查差異。
協議input token 欄位output token 欄位total token 欄位
OpenAIprompt_tokenscompletion_tokenstotal_tokens
Claudeinput_tokensoutput_tokens無(需自行相加)
GeminipromptTokenCountcandidatesTokenCounttotalTokenCount

轉換規則:

  • 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
}
}
OpenAIClaudeGemini含義
stopend_turnSTOP自然結束
lengthmax_tokensMAX_TOKENS達到長度上限
tool_callstool_useSTOP(含 functionCall)請求呼叫工具
content_filter無對應SAFETY內容被安全過濾器攔截
stopstop_sequenceSTOP命中停止序列

轉換規則:

  • Claude end_turn → OpenAI stop
  • Claude max_tokens → OpenAI length
  • Claude tool_use → OpenAI tool_calls
  • Gemini STOP 根據是否有 functionCall 映射為 stop 或 tool_calls
  • Gemini SAFETY → OpenAI content_filter

所有協議的錯誤響應都轉換為 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.arguments JSON 字串。
  • 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 結構。

根據客戶端和模型類型選擇協議:

客戶端類型目標模型推薦協議原因
OpenAI SDKOpenAI 系模型OpenAI原生支援,零轉換
Claude CodeClaude 系模型Claude Messages原生支援,零轉換
LangChain / LiteLLM任意模型OpenAI生態相容性最好
Anthropic SDKClaude 系模型Claude Messages存取擴展思考、提示快取
自研客戶端任意模型取決於需求優先用目標模型原生協議

如果業務需要跨模型切換,避免使用以下特性:

  • 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)

在測試環境驗證以下場景:

  1. 同一協議,不同模型:確保 OpenAI 協議能正確呼叫 Claude 和 Gemini 模型。
  2. 不同協議,同一模型:驗證 Claude Messages 和 OpenAI 協議呼叫同一 Claude 模型的結果一致性。
  3. 工具呼叫往返:測試跨協議的工具定義、呼叫和結果傳遞是否正確。
  4. 邊界參數:測試 temperature: 1.5(OpenAI 合法,Claude 需截斷)、n: 2(OpenAI 支援,Claude 不支援)等邊界情況。
  5. 錯誤處理:驗證上游錯誤是否正確轉換為客戶端協議格式。

記錄以下資訊便於排查協議轉換問題:

{
"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。

如果要從一個協議遷移到另一個協議:

  1. 階段 1:雙寫測試:新協議呼叫結果僅用於對比,不影響業務。
  2. 階段 2:灰度切換:小流量切換到新協議,監控錯誤率和響應品質。
  3. 階段 3:全量切換:確認無異常後全量切換。
  4. 階段 4:清理舊程式碼:移除舊協議的適配程式碼。

每個階段都需要驗證:

  • 功能正確性(工具呼叫、多模態、串流輸出)
  • 響應品質(不同協議/模型組合的輸出差異)
  • 效能指標(延遲、token 用量、成本)
  • 錯誤處理(網路異常、限流、上游故障)

協議轉換讓你可以靈活選擇客戶端和模型,但最佳實踐仍然是優先使用目標模型的原生協議。如果必須跨協議呼叫,請在測試環境充分驗證,並在生產環境監控轉換相關的錯誤和效能指標。