首页 / 文章

通过请求头而非读取提示语来路由 Claude 代码流量

了解 Claude Code 的可选网关提示标头如何让 LLM 网关无需解析提示内容,即可根据请求元数据来优先处理、控制缓存使用量并管理缓存。

发布者:REACTAPP.TOP阅读约 9 分钟1990 词

来源: Your LLM Gateway Reads Your Prompt to Decide Where to Send It. Claude Code Just Shipped Five Headers That Make That Unnecessary.

当一家公司通过大语言模型网关发送 Claude Code 的请求时,该网关在转发请求之前必须对每个请求做出决策:使用哪条上游通道、加入哪个队列、适用何种速率限制规则以及分配多高的优先级。直到最近,了解这些决策依据的唯一途径还是请求体,也就是你的提示词、文件内容、工具输出结果以及 CLAUDE.md 文件。在最近一次的 Claude Code 更新中添加的一组可选请求头将这些信息移到了请求封包的外层。下面你会了解到每个请求头能用于决策什么、代理在可以使用它们时会发生怎样的变化,以及哪些情况下仍不可依赖它们。

为何网关最终会读取你的提示词

内容检查并非理论上的问题,而是当今许多路由器的实际运作方式。诸如vLLM Semantic Router这样的语义路由器会提取用户输入的提示语,用BERT风格的模型对其进行嵌入处理,再将生成的向量与参考任务的向量进行比对以对请求进行分类。那些为降低数据传输延迟而优化的路由器可以从大约第一个千字节处识别出模型名称并流式传输剩余数据,但任何需要执行分类任务的路由器都必须对整个内容进行缓冲和处理。

这种检查功能正是该产品的特色,同时也使得该网关成为比其名称所暗示的更为敏感的信任边界——它能够查看并可能存储开发者代理发送的所有内容。

在2026年9月15日的更新日志中,Claude Code 2.1.273版本为网关操作员添加了五个头部信息。总体而言,这些内容与其说是可观测性功能,不如说是网关作为调度器而非读取器时所需的最低限度元数据。

原有标识符头部的局限性

在2.1.273版本之前,网关兼容性指南中仅记载了三个Claude Code头部信息,它们均为标识符:

+--------------------------------+---------------------------------+
| HEADER                         | WHAT IT IS                      |
+--------------------------------+---------------------------------+
| x-claude-code-session-id       | which session                   |
| x-claude-code-agent-id         | which subagent (if any)         |
| x-claude-code-parent-agent-id  | which agent spawned it          |
+--------------------------------+---------------------------------+
                  |
                  v
        good for GROUPING after the fact
        useless for DECIDING before forwarding

文档中将它们设计为用于聚合处理:会话ID允许你在无需解析请求体内容的情况下,将某个会话发送的所有数据归类在一起;而代理ID与会话ID结合使用,则可帮助为并行运行的各个代理分配成本。这属于会计核算范畴,足以用于生成成本仪表板,但无法用于调度决策。仅知道某个请求来自会话abc和代理def,并不能说明该请求的成本高低、处理所需时间,或是系统是否能够承受等待。因此,任何希望具备比简单计数器更强大功能的网关都必须打开请求体内容。

五个新的提示标头

根据更新日志的记载,2.1.273版本新增了五个头部字段,这些字段均需通过CLAUDE_CODE_GATEWAY_HINT_HEADERS=1一同启用。其中两个用于描述调用方信息(x-claude-code-agent-type和x-claude-code-request-class),一个用于记录最近的耗时情况(x-claude-code-prev-tool-durations),还有两个用于描述上下文重写信息(x-claude-code-context-compacted与x-claude-code-compaction)。

一个重要的注意事项:在撰写本文时,网关兼容性指南中的请求头表格仍仅显示三个标识符头部,而该变量并未出现在环境变量参考中。这些头部名称来自变更日志,但其值格式目前尚未有文档说明。以下内容仅描述每个头部能让网关做出何种决策,并非固定的数据格式规范。在依赖具体值之前,请先检查自己的流量数据,并查阅最新的文档,因为相关内容可能已得到更新。

按每个头部所能支持的决策进行分类:

+---------------------------+------------------------------------------+
| HEADER                    | DECISION IT UNBLOCKS AT THE EDGE         |
+---------------------------+------------------------------------------+
| request-class             | is this a real turn, or a small          |
|                           | internal call? -> priority, cheap pool   |
+---------------------------+------------------------------------------+
| agent-type                | which kind of subagent is asking         |
|                           | -> per-agent-type budgets and quotas     |
+---------------------------+------------------------------------------+
| prev-tool-durations       | how long the last tools took             |
|                           | -> timeout and retry window sizing       |
+---------------------------+------------------------------------------+
| compaction                | this turn IS the summarization call      |
|                           | -> never count it as user latency        |
+---------------------------+------------------------------------------+
| context-compacted         | the prefix just changed under you        |
|                           | -> do not expect a cache hit this turn   |
+---------------------------+------------------------------------------+

简而言之:

  • request-class 可以区分真正的对话轮次与内部的小规模调用,从而让系统优先处理前者,并将后者发送到成本更低的处理池中。
  • agent-type 用于标识发起调用的子代理类型,便于为不同类型的代理设置预算和配额。
  • prev-tool-durations 会记录最近工具运行的时长,有助于确定超时时间和重试间隔。
  • compaction 用于标记执行摘要生成的请求,这类请求不应计入面向用户的延迟时间。
  • context-compacted 表示对话开头部分刚刚被重写,因此无法通过缓存获取相关内容。

这些都不是新信息。网关已经可以通过区分完整的 Claude Code 系统提示与精简后的分类器提示来推断请求类型,从系统提示的代理部分判断代理类型,并通过消息数组的结构识别是否经过压缩处理。头部信息无法透露关于你的任何额外内容。变化的是知识获取的成本:网关不再需要解析高达一兆字节的源代码,只需从头部信息中读取一段简短的字符串即可。

相同的路由规则,变化之前与之后

以一个具体目标为例:将背景总结任务路由到成本更低的处理池。在没有额外提示的情况下,代理必须先缓存并解析对话内容,再根据其内容进行推断:

// BEFORE: the router has to read the conversation to classify it
const body = await readEntireBody(req);        // buffers the whole prompt
const parsed = JSON.parse(body);               // your files are now in gateway memory
const sys = flattenSystemBlocks(parsed.system);
const isBackground =
  sys.includes("summarize the conversation") ||
  parsed.messages.length > COMPACT_THRESHOLD;

之后才将缓存的对话内容转发出去:

route(isBackground ? "cheap-pool" : "interactive-pool", body);

这种方法存在三个问题,严重程度由轻到重依次为:对整个请求体进行缓冲会破坏流式处理的延迟特性;仅依据提示词文字进行匹配的可靠性较低,因为不同版本的 Claude Code 中该文字表述会有所变化;最严重的是,你的代码仓库的完整内容现在存储在路由层的进程内存中、其堆转储文件里,甚至还可能出现在日志中——这些地方并未出现在任何架构图中,也无人考虑对其进行保护。

借助提示词标题,路由器会从请求包中读取两个值:

// AFTER: the router decides on the envelope and never touches the body
const cls  = req.headers["x-claude-code-request-class"];
const comp = req.headers["x-claude-code-compaction"];

然后原封不动地传输请求流。如代码片段中的注释所述,这些字面值字符串仅用于说明,因为相关格式尚未有正式文档记载:

// value strings below are illustrative: confirm them against your own
// traffic, since the wire format is not documented yet
const isBackground = Boolean(comp) || cls === "background";route(isBackground ? "cheap-pool" : "interactive-pool", req);  // streams through

没有缓冲机制,也没有JSON解析,更不会在内存中保留提示词。兼容性指南已经允许这种模式:除了anthropic-workspace-id、anthropic-beta和anthropic-version之外,其他标头被视作网关可用于追踪、归因或路由的输入,无需传递给模型。这些提示词的设计初衷就是停留在网关层面;由于模型根本看不到它们,因此可以保持简单且成本低廉。

压缩标记是对缓存状态的指示

有两个标头看似重复,但实际上回答的是不同的问题。第一个x-claude-code-compaction是在生成摘要的请求中设置的;而第二个x-claude-code-context-compacted则出现在后续已经完成压缩的请求中。

一旦开始使用缓存,这种区别就非常重要。提示词的缓存键是由工具名称、系统提示词以及消息内容组合而成的前缀,因此只要修改了前面的任何部分,后面的所有内容都会失效。此外,缓存写入的操作成本也高于读取操作,不必要的写入只会带来额外开销。在普通会话中,压缩操作是导致内容失效的最主要因素:它会用摘要替换累积的历史记录,从而按定义生成全新的前缀。

turn N        turn N+1 (compaction)      turn N+2
+---------+     +------------------+     +------------------+
| prefix  |     | compaction: 1    |     | context-         |
| stable  | --> | rewrites history | --> | compacted: 1     |
| cache   |     | EXPECT A MISS    |     | new prefix is    |
| hits    |     | bill it as       |     | now the stable   |
+---------+     | background       |     | one, re-warm it  |
                +------------------+     +------------------+

那种拥有自己缓存却对数据压缩一无所知的网关,在进行数据压缩时会表现得极为糟糕:它会执行完整的查找操作,未能找到所需数据,随后为某个稍后就会过时的前缀存储一条记录,最后报告命中率下降,这一现象会被视为缺陷。而借助这些头部信息,它就可以跳过压缩请求时的查找操作,将此次调用视为后台任务处理,并在下一轮该前缀成为稳定状态时刻意对其“预热”。

提示信息是客户端的声明,而非凭证

变更日志和变量名都会使用这些提示头部信息,而且用词很关键。提示信息是客户端做出的未经验证的声明。x-claude-code-request-class 的值由 Claude Code 自行决定发送什么,没有上游组件会对它进行验证。

假设你的网关会对标记为后台的请求给予折扣费率、更大的配额或更宽松的速率限制。但实际上,是由客户端提供的字符串在悄悄决定授权事宜。那些修改过自身Shell环境的开发者,或是任何在网关前运行小型本地代理的人,都可以选择那种能获得最优惠待遇的标记。这些提示信息可用于排队管理、缓存决策以及延迟测量,但绝不能用来决定调用者应享有何种权限。

第二个实际问题是采用问题。这些标头默认是禁用的,除非明确启用,而这正适合那些需要向中间件暴露会话结构的元数据。加之在撰写本文时环境变量参考中并未提及它们,因此很少有安装会启用这些标头。在组织内部,通常需要由管理员通过管理设置来逐步推广,而管理员往往只是注意到日志中的某一行变更记录。

网关操作员的推广计划

如果您的团队负责管理 Claude Code 流量的网关,那么一个谨慎的采用路径如下:

  • 为一个小规模测试组启用 CLAUDE_CODE_GATEWAY_HINT_HEADERS=1。
  • 记录这五个标头的所有值约一周时间,但不采取任何操作。
  • 将其数值与当前用于解析内容的分类器所做出的决策进行对比。
  • 如果他们同意,就将路由切换到请求头,停止缓冲请求体,并移除解析器。
  • 权限判定应基于已验证的身份,绝不能依据提示值来决定。
  • 其好处是影响范围显著缩小:开发人员的源代码不再需要经过路由层的内存处理,而且只需一个环境变量即可。

    总结

    这些标题本身是 Claude Code 所特有的,但其中的设计原则适用于任何智能体平台。置于智能体之前的任何层都需要在传递请求之前对其进行分类,而实现这一目标只有两种方式:从请求内容中推断答案,或以显式元数据的形式接收答案。虽然通过推断总能得到结果,但这种方式总会将敏感内容留在本不应存储它的组件中。而使用显式元数据通道虽然需要更多的前期设计工作,却是唯一能让中间节点正确路由请求而不直接接触数据的方案。如需了解网关的更多功能,可参阅 Claude Code 文档中的LLM 网关概述。