請求標頭
閘道支援三個選用請求標頭:兩個把你自己的鏈路上下文帶進我們的日誌,一個讓同一段工作階段固定落在同一個上游,使 prompt caching 持續命中。三個都不帶也完全不影響呼叫。
| 請求標頭 | 閘道的處理 | 用途 |
|---|---|---|
X-Trace-Id | 回顯在回應上,並記錄在這次呼叫上,用來把它和同一個 trace 的其他呼叫歸在一起。 | 把一次呼叫對回你自己的鏈路追蹤系統。 |
X-Span-Id | 回顯在回應上,並記錄在這次呼叫上;在 trace 的瀑布圖裡作為這次呼叫的名稱。 | 標識該鏈路中的某一個 span。 |
X-Session-Id | 回顯在回應上,並記錄在這次呼叫上。帶同一個值的連續請求會被固定到同一條通道、同一把上游 key。 | 讓一段工作階段裡的 prompt caching 持續命中。 |
X-Request-ID | 不採信你傳入的值。你傳的值會以 X-Client-Request-ID 回顯;回應裡的 X-Request-ID 永遠是閘道自己產生的 UUIDv7。 | 閘道側對這次呼叫的唯一識別 —— 提工單時報這個。 |
怎麼發
curl https://synthorai.io/v1/chat/completions \
-H "Authorization: Bearer $SYNTHORAI_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Session-Id: conv-8f3a2b" \
-H "X-Trace-Id: 7c1d9e40f2b84a17" \
-H "X-Span-Id: 2f9b41c8" \
-d '{
"model": "claude-sonnet-5",
"messages": [{"role": "user", "content": "Hello"}]
}' 其他工具已在傳送的請求標頭
如果一個呼叫沒有帶我們的這三個請求標頭,閘道會讀取程式設計 agent、SDK 或追蹤標準本來就會傳送的對應標頭,這些執行不需你做任何改動就能在鏈路追蹤裡歸組。
| 請求標頭 | 視為 | 傳送方 |
|---|---|---|
X-Claude-Code-Session-Id | 工作階段 | Claude Code |
X-Litellm-Session-Id | 工作階段 | LiteLLM SDK, OpenHands |
Session-Id | 工作階段 | OpenAI Codex CLI |
Helicone-Session-Id | 工作階段 | Helicone |
X-OpenWebUI-Chat-Id | 工作階段 | Open WebUI |
X-Opencode-Session-Id | 工作階段 | OpenCode |
baggage: session.id=… | 工作階段 | W3C Baggage (OpenTelemetry) |
baggage: gen_ai.conversation.id=… | 工作階段 | W3C Baggage (OpenTelemetry) |
baggage: langfuse_session_id=… | 工作階段 | Langfuse SDK |
X-Session-Affinity | 工作階段 | OpenClaw, OpenCode, Kilo Code |
X-Litellm-Trace-Id | Trace | LiteLLM SDK |
X-Portkey-Trace-Id | Trace | Portkey |
traceparent | Trace;其中的 parent id 視為 span | W3C Trace Context (OpenTelemetry) |
X-Claude-Code-Prompt-Id | Trace | Claude Code |
X-Interaction-Id | Trace | GitHub Copilot |
X-Codex-Turn-Metadata | Trace | OpenAI Codex CLI |
baggage: langfuse_trace_id=… | Trace | Langfuse SDK |
X-Portkey-Span-Id | Span | Portkey |
X-OpenWebUI-User-Id | 終端使用者(_user 標籤) | Open WebUI |
baggage: user.id=… | 終端使用者(_user 標籤) | W3C Baggage (OpenTelemetry) |
baggage: langfuse_user_id=… | 終端使用者(_user 標籤) | Langfuse SDK |
兩者同時存在時,一律以我們自己的標頭為準。Session-Id 和 X-Codex-Turn-Metadata 只在 Codex CLI 的請求裡讀取,X-Interaction-Id 只在 GitHub Copilot 的請求裡讀取,依 User-Agent 辨識。完全沒帶 trace ID 的呼叫,回應裡的 X-Trace-Id 是閘道自己的請求 ID。
在 /v1/chat/completions 和 /v1/responses 上,工作階段 ID 也可以放在請求本文裡,欄位名稱是 session_id(用 OpenAI SDK 時放進 extra_body)。請求標頭優先於它;請求送給供應商之前,這個欄位會被移除。
OpenClaw 每次模型呼叫都帶 traceparent,它的執行不用設定就會歸成 trace;要依工作階段分組,請在模型上開啟 compat.sendSessionAffinityHeaders。Open WebUI 開啟 ENABLE_FORWARD_USER_INFO_HEADERS 後會帶上對話 id 和使用者 id;同時送來的姓名和電子郵件標頭不會讀取。不送這些資訊的 agent(Cursor、Aider、Cline、Continue 和大多數 agent 框架)需要在自訂標頭裡加上 X-Session-Id。
W3C Trace Context 與 Baggage
traceparent 提供 trace,其中的 parent id 作為 span;啟用 OpenTelemetry 的 HTTP 自動埋點後會自動帶上。其餘資訊放在 baggage 裡,鍵名沿用 OpenTelemetry:工作階段用 session.id 或 gen_ai.conversation.id,agent 用 gen_ai.agent.id 和 gen_ai.agent.name,終端使用者用 user.id。這些需要你的程式自己寫進 baggage:OpenTelemetry SDK 不會把 span 屬性複製進去。agent 名稱會作為 agent 的類型保存,只接受字母、數字、.、_ 和 -。tracestate 不會讀取。
工作階段裡的 agent
會啟動子 agent 的程式設計 agent,會在每次呼叫上說明它來自哪個 agent。閘道把這些資訊隨呼叫一起保存,於是工作階段可以按 agent 看花費,鏈路裡能看出哪些呼叫是壓縮或輔助呼叫,以及兩次呼叫之間執行了哪些工具。這些請求標頭都不影響路由:子 agent 仍然走它所在工作階段的上游。
| 請求標頭 | 視為 | 傳送方 |
|---|---|---|
X-Agent-Id | Agent | Synthorai |
X-Parent-Agent-Id | 啟動它的 agent 或工作階段 | Synthorai |
X-Claude-Code-Agent-Id | Agent | Claude Code |
X-Claude-Code-Parent-Agent-Id | 啟動它的 agent 或工作階段 | Claude Code |
Thread-Id | Agent | OpenAI Codex CLI |
X-Codex-Parent-Thread-Id | 啟動它的 agent 或工作階段 | OpenAI Codex CLI |
baggage: gen_ai.agent.id=… | Agent | W3C Baggage (OpenTelemetry) |
X-Parent-Session-Id | 啟動它的 agent 或工作階段 | OpenCode, Kilo Code |
X-Opencode-Parent-Session-Id | 啟動它的 agent 或工作階段 | OpenCode |
X-Claude-Code-Request-Class | 呼叫類型 | Claude Code |
X-Claude-Code-Agent-Type | 呼叫類型 | Claude Code |
X-Claude-Code-Compaction | 呼叫類型 | Claude Code |
X-Claude-Code-Context-Compacted | 呼叫類型 | Claude Code |
X-Openai-Subagent | 呼叫類型 | OpenAI Codex CLI |
baggage: gen_ai.agent.name=… | 呼叫類型 | W3C Baggage (OpenTelemetry) |
X-Claude-Code-Prev-Tool-Durations | 工具耗時 | Claude Code |
Claude Code 預設會傳送 agent ID。呼叫類型和工具耗時只有在它執行的環境裡設定了 CLAUDE_CODE_GATEWAY_HINT_HEADERS=1 才會傳送。
會保存:這些 ID、agent 的類型、呼叫類型,以及工具名稱和以毫秒計的耗時。不會保存:你給 agent 取的名稱,以及工具讀取或寫入的任何內容。
為請求加上標籤
在 X-Synthorai-Metadata 裡放一個值都是字串的 JSON 物件,說明這個請求是做什麼用的:哪個功能、哪個環境、你的哪位使用者。這些標籤會顯示在日誌的請求詳情裡,可以用來篩選日誌、鏈路和工作階段(metadata.feature = checkout),在 Analytics 的 Breakdown 裡依標籤彙總花費,也會隨匯出一起送出。
curl https://synthorai.io/v1/chat/completions \
-H "Authorization: Bearer $SYNTHORAI_API_KEY" \
-H 'X-Synthorai-Metadata: {"feature":"checkout","env":"prod","_user":"u_123"}' \
-d '{"model":"claude-sonnet-5","messages":[{"role":"user","content":"Hi"}]}' 最多 10 個標籤。鍵由 1 到 32 個 a-z 0-9 _ . - 組成;值是不超過 128 位元組的字串。_user 表示這個請求是為哪位終端使用者發起的;其他以底線開頭的鍵是保留的。不符合規則的標籤會被捨棄,請求照常執行:回應標頭 X-Synthorai-Metadata-Dropped 會列出被捨棄的鍵。
標籤隨請求一起保存,保存時間與請求日誌相同;依標籤篩選涵蓋最近 90 天。不要在標籤裡放提示詞內容、個人資料或金鑰;看起來像憑證的值會存成 [redacted]。標籤不會送給供應商。
工作階段親和
快取項目是綁在供應商側的某一把 API key 上的。一條通道可能配了多把 key,沒有提示時閘道會把請求分散到這些 key 上,於是一段工作階段的第二輪可能落到一把從沒見過你前綴的 key 上,又付一次寫入費用。
同一段工作階段的每次呼叫都帶同一個值,不同工作階段用不同的值。伺服器端不存任何工作階段表、也沒有過期:這個值經雜湊決定選哪條通道、哪把 key,因此同一個值永遠解析到同一處。
在 /v1/chat/completions 與 /v1/responses 這兩個 OpenAI 相容端點上,可以完全不帶這個標頭:如果請求主體裡有 prompt_cache_key,閘道就用它作為親和鍵。/v1/messages 與 Gemini 端點請用這個標頭。
親和是 best-effort:它只決定在同等優先的候選裡選誰。故障轉移、限流冷卻、重試以及你顯式指定的路由偏好都優先於它。同一個模型在我們可能選中的每條通道上,刊例價都是一樣的。
限制
- 每個標頭上限 128 位元組。超出會被截斷。含控制字元或可列印 ASCII 之外字元的值會被整體丟棄 —— 既不回顯也不記日誌。這些值會進入回應標頭和日誌行,這條限制用於擋住標頭注入與日誌偽造。
- 它們止步於閘道。三個標頭都不會轉發給模型供應商:出站請求只帶鑑權、Content-Type 和各供應商專有標頭。
- 三個標頭都不是計費身分。計費、去重和對帳一律以閘道自己產生的
X-Request-ID為準,絕不使用由你控制的值。
出問題時
- 報上你發的
X-Trace-Id,或回應裡的X-Request-ID—— 任一個都能讓我們定位到那一次具體呼叫。 - 你的 trace ID 會顯示在日誌和鏈路追蹤裡對應的呼叫上,提工單前可以自己先查。
- 想確認親和是否生效:用同一個
X-Session-Id把同一段長前綴連發幾次,看日誌裡的快取欄位。第一次是寫入快取,後面幾次應當變成讀取;換一個值,寫入應當重新出現。
延伸閱讀: Prompt Caching · 鏈路追蹤與工作階段