Skip to content

Claude Message Threads 经过网关会断在哪 ​

最后核实:2026-11-03。对照 Anthropic 的 API release notes 和 Message threads 文档。功能还在 beta,字段和行为可能会改,动手前请以官方文档为准。

直接回答

2026-11-02,Anthropic 在 Messages API 公测 Message Threads(beta 头 message-threads-2026-08-12):第一轮发完整对话,之后每轮只发新消息加上一轮回复的 id,由服务端拼回整段对话。它对直连很友好,但对网关和代理有几个硬约束:同一个 thread 必须始终落在同一个 Key / workspace 上,否则返回 404;请求体里的 thread 字段必须原样转发,丢了不会报错,只会让模型在没有前文的情况下回答;model、thinking、anthropic-beta 等“固定字段”在整条 thread 里不能变。下面逐条拆开,并给出网关侧和客户端侧的修法。

30 秒看懂 Message Threads ​

步骤你发什么要点
创建完整对话 + system + tools,加 "thread": {"type": "create"}服务端把对话连同这轮回复存在回复的 id 下
续接只发上一轮之后的新消息,加 "thread": {"type": "continue", "previous_message_id": "<上一轮 id>"}不能再带 system / tools(带了哪怕是 null 也报 400)
重放续接返回 404 thread_not_found 时,用自己保存的完整对话重新发一个创建请求thread 是“尽力而为”:最后一次请求 5 分钟后过期,最迟 1 小时删除

官方的定位是让长对话和智能体循环请求更小、首字更快:不用每轮都重新上传、校验和分词整段对话。有三点容易误解:

  • 对话的“真相”仍在你这边。 文档要求客户端保留每一条消息的副本,并且从一开始就写好重放路径。任何响应都不会告诉你这一轮有没有被存下来,要等下一次续接才知道。
  • thread 不等于缓存。 文档原话的意思是:thread 不会替你缓存提示,缓存断点仍由你决定(见下文“thread 和提示缓存”)。
  • 计费规则文档里没有单独写。 本文不对“用 thread 是否省钱”下结论,以响应里的 usage 为准。

另外,Anthropic 开发者账号 @ClaudeDevs 在 X 上称 Claude Code 已开始使用 threads,并给出了长上下文请求首字时间明显下降的数字。截至 11-03,官方文档里没有对应说明,这一条请按单一来源看待。可以确定的是:文档写明 SDK 的 beta tool runner 默认就会使用 thread(可在创建时关闭)。也就是说,你没主动开启 thread,请求里也可能已经带着它了。

字段分三类 ​

这张表是排查网关问题的核心,整理自官方文档的 “What to send on each request”:

类别字段规则
仅创建时发送system、tools只在创建请求里发;续接请求里出现即 400
固定字段model、tool_choice、thinking、mcp_servers(各服务器的授权 token 除外)、context_management、output_config、container 里的 skills、顶层 cache_control、anthropic-beta 的取值集合每轮都要发,值必须和创建时一致;不一致返回 400 thread_fingerprint_mismatch,报错信息会写出哪些字段不同
可变字段max_tokens、stop_sequences、stream、metadata、service_tier 等每轮可以不同

网关和代理最容易踩的 7 个坑 ​

直连 Anthropic 的用户只要用官方 SDK 的 thread helper,基本不用操心。问题集中在“客户端 → 网关 → 上游”这条链路上。网关为了负载均衡、容灾和计费所做的那些常规动作,恰好和 thread 的约束冲突。

#网关的常规行为对 thread 的影响客户端看到的现象
1在多个上游 Key / workspace 之间负载均衡续接请求落到另一个 Key 上,文档列明“请求来自不同的 organization、workspace、用户或服务账号”会 404频繁 404 thread_not_found;SDK 自动重放后能用,但每轮都在全量重发,等于没开 thread
2按固定结构体解析再序列化请求体结构体里没有 thread 字段,就会被静默丢弃见下文“最危险的一种”
3改写或追加 anthropic-beta,或做模型映射 / 自动降级beta 取值集合和 model 都是固定字段,前后两轮不一致就会冲突400 thread_fingerprint_mismatch
4上游出错时换渠道 / 换 Key 重试每个回复 id 只能续接一次;第一次请求可能其实已经在上游跑完或还在跑400 thread_already_continued 或 thread_continue_in_flight;换了 Key 还会叠加第 1 条
5把上游错误统一改写成自己的格式error.details.error_code 丢失,SDK 判断不出是 thread_not_found,无法自动重放。注意“模型不存在”也返回 404,只看状态码会误判404 直接抛给业务代码,或被当成“模型不存在”
6只提供 OpenAI 兼容端点,内部再转成 Anthropic 格式Chat Completions 里没有能表达 thread 的字段无法使用;只有 Anthropic 原生 /v1/messages 才能用 thread
7上游没回用量时按请求体本地估算计费续接请求只有新消息,体积可能只是原来的零头,本地估算会严重偏低账单和实际用量对不上;网关应以上游返回的 usage 为准

最危险的一种:thread 字段被静默丢掉 ​

前 6 种坑大多会报错,第 2 种却往往不报错。

续接请求的设计就是“只发新消息、不带 system / tools”。如果网关把请求体反序列化成一个预先定义好的结构体,结构体里又没有 thread,这个字段会在转发前被丢掉。上游看到的就是一个普通请求,内容只有最后一两条消息:

  • 新消息以 tool_result 开头(智能体循环里最常见):前面没有对应的 tool_use,上游返回 400,至少能发现。
  • 新消息是一条普通 user 消息:上游照常回答,只是没有系统提示、没有工具、也没有之前的任何上下文。回答看起来“有点怪”,但没有任何报错。

这不是假设。以开源网关 new-api 为例,v1.0.0-rc.46 的源码里:anthropic-beta 请求头会被合并后原样转发,但 ClaudeRequest 结构体里没有 thread 字段。没开启请求体透传的渠道,thread 就会在这一步被丢掉。其他按结构体实现 Anthropic 接口的代理也值得自查一遍。

客户端怎么自检: 续接回合如果明显“忘了前文”,或者 usage 里的输入规模(input_tokens + cache_read_input_tokens + cache_creation_input_tokens)只和你这一轮发的几条新消息差不多大,基本可以判断 thread 没有生效、上游只看到了片段。文档没有逐项说明 thread 下 usage 的口径,这条判断属于经验推断,用来报警足够,不要拿来对账。

网关侧怎么改 ​

如果你维护的是网关或代理,最小改动清单如下:

  1. thread 原样透传。 结构体加上这个字段,或者对 /v1/messages 走请求体透传。上游如果不支持 thread,宁可返回 400 并带上 thread_unsupported_request 这类明确的错误码,也不要静默丢掉。这样官方 SDK 设了 unsupported_request_behavior: continue_unthreaded 时就能自动退回不带 thread 的全量请求。
  2. 按 previous_message_id 粘滞。 创建请求成功后,记录“回复 id → 上游渠道和 Key”的映射;续接请求按 previous_message_id 查表,路由回同一个 Key。映射的 TTL 取 1 小时就够,对应 thread 的最长保留时间。每一轮的新回复 id 都要写入映射,因为下一轮会从它续接。
  3. 续接请求不跨 Key 重试。 原 Key 失败时,把错误原样返回给客户端,让客户端用完整对话重新创建。不要自作主张换一个 Key 重发续接请求,那只会把错误变成 404 或 thread_already_continued。
  4. 错误原样返回。 保留上游的 HTTP 状态码、error.type 和 error.details(尤其是 error_code 和 unsupported)。SDK 的自动重放完全依赖这些字段。
  5. 固定字段别按请求变。 不要按请求动态追加 beta 值;模型映射在整条 thread 内保持一致;带 thread 的请求不要做跨模型降级。
  6. 计费以上游 usage 为准。 对带 thread 的请求关掉本地估算,或者在上游没有返回用量时明确记为异常。
bash
# 创建:完整对话 + system + tools
curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: message-threads-2026-08-12" \
  -H "content-type: application/json" \
  -d '{"model":"claude-opus-5-5","max_tokens":1024,
       "cache_control":{"type":"ephemeral"},
       "system":"You are a code reviewer.",
       "messages":[{"role":"user","content":"Review this diff: ..."}],
       "thread":{"type":"create"}}'

# 续接:只发新消息;不带 system / tools;固定字段(model、顶层 cache_control、beta 头)保持一致
curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: message-threads-2026-08-12" \
  -H "content-type: application/json" \
  -d '{"model":"claude-opus-5-5","max_tokens":1024,
       "cache_control":{"type":"ephemeral"},
       "messages":[{"role":"user","content":"Now check the error handling."}],
       "thread":{"type":"continue","previous_message_id":"'"$MESSAGE_ID"'"}}'

经过网关时,把 https://api.anthropic.com 换成网关的地址,请求本身保持不变。网关要做的是让第二个请求落到和第一个请求同一个 Key 上。

客户端侧怎么防 ​

  • 优先用官方 SDK 的 thread helper。 它会自动加 beta 头,续接时自动省掉 system / tools,遇到 thread_not_found、thread_already_continued、thread_continue_in_flight 时用完整对话重放一次,并在降级模型回答或被拒答之后自动开新 thread。
  • 把 unsupported_request_behavior 设为 continue_unthreaded。 网关或组织设置不支持 thread 时,自动退回普通请求,而不是每轮都重放。
  • 自己保留完整对话。 helper 只把对话保存在内存里,进程退出就没了。需要跨进程的会话请自己落盘。
  • 流式响应等到 message_stop 再续接。 id 在 message_start 里就能拿到,但提前续接可能得到 404。流中途断开时,先用同一个续接请求重发一次;如果返回 thread_already_continued 或 thread_continue_in_flight,再走全量重放。
  • pause_turn 用空消息续接。 回复以 stop_reason: "pause_turn" 结束时,用 "messages": [] 续接;不要在暂停期间插入新的 user 消息。

thread 和提示缓存 ​

很多人看到“请求只发新消息”,就以为不用再管缓存了。文档写得很明确:thread 不替你缓存。

  • 最简单的做法是每个请求都带同样的顶层 cache_control。API 会把断点打在本轮最后一个可缓存的块上,于是每轮都从缓存读取前文,并把新一轮写进缓存。顶层 cache_control 本身也是固定字段。
  • 一个带 thread 的请求最多 4 个缓存断点,顶层 cache_control 算 1 个;创建请求里 system / tools 上的断点,在每个续接请求里都要再算一次。超出返回 400 thread_unsupported_request。
  • 断点发出去以后就不能移动或删除,因为续接请求无法重发之前的消息。重放时要先去掉旧消息上的断点,只在最后一块保留一个。

缓存的经济账没有变:Anthropic 的缓存写入是输入价的 1.25 倍(5 分钟 TTL)或 2 倍(1 小时 TTL),读取是 0.1 倍。断点放错位置、命中率又低,费用反而比不缓存更高。原理和算账方法见提示缓存详解。对网关来说还有一层:缓存也是按上游账号维度存的,第 2 条的 Key 粘滞同样决定缓存命中率,一次做好,两个问题一起解决。

不能用 thread 的场景 ​

按官方文档(2026-11-03):

  • Message Batches:带 thread 的条目会在结果里以 invalid_request_error 结束。
  • Token 计数:带 thread 返回 400,需要用完整对话(含 system / tools)计数。
  • Compaction:按需压缩不能和 thread 一起用;要压缩,就自己写摘要,用摘要开一个新 thread。
  • Context editing:只支持 clear_thinking_20251015 配 keep: "all"。
  • 组织设置:开启了 HIPAA readiness 或 Inference hooks 的组织不能用;功能本身不符合 ZDR。
  • 平台:支持 Claude API、Claude Platform on AWS、Google Cloud(Anthropic 托管)和 Microsoft Foundry;Amazon Bedrock 不在列表里。
  • 没有管理接口:不能列出、读取或删除 thread。

来源 ​

“Claude Code 已接入 thread”和具体的首字时间提升数字只出自官方 X 账号的帖子,文档中尚无对应说明,本文不把它当作定论。

常见问题 ​

Claude Message Threads 是什么? ​

Anthropic 于 2026-11-02 在 Messages API 公测的功能,beta 头为 message-threads-2026-08-12。第一轮带完整对话并加 thread: {type: "create"},之后每轮只发新消息并加 thread: {type: "continue", previous_message_id},由服务端拼回整段对话。thread 在最后一次请求 5 分钟后过期。

为什么经过网关调用时,续接请求会返回 404 thread_not_found? ​

文档列出的原因包括:thread 已过期、上一轮没有被存储、上一轮由降级模型回答、上一轮还在生成,以及请求来自不同的 organization、workspace、用户或服务账号。网关在多个上游 Key 之间轮换,就会命中最后一条。修法是按 previous_message_id 把续接请求粘到同一个 Key 上。

用了 thread 还需要提示缓存吗? ​

需要。thread 不替你缓存;每个请求带同样的顶层 cache_control 是最简单的做法。一个带 thread 的请求最多 4 个断点。

网关把 thread 字段丢掉会发生什么? ​

上游收到的是一段没有前文、没有系统提示和工具的普通请求。以 tool_result 开头时会报 400;以 user 消息开头时会照常回答,但已经丢了上下文,而且不会报任何错误。

哪些情况不能用 Message Threads? ​

Message Batches、Token 计数、按需压缩、大部分 context editing;开启了 HIPAA readiness 或 Inference hooks 的组织;需要 ZDR 的场景;以及 Amazon Bedrock。

遇到问题?联系客服或加交流群。