请求头

网关认三个可选请求头:两个把你自己的链路上下文带进我们的日志,一个让同一段会话固定落在同一个上游,使 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(提示词缓存) · 链路追踪与会话