# Claude Message Threads API 指南

> Claude API 的 message threads：beta 头、create 与 continue 请求、必须重发的字段、过期时间，以及 404 之后怎么重放。

- Canonical HTML: https://omniakey.com/zh/blog/claude-message-threads-api-guide
- Markdown: https://omniakey.com/zh/blog/claude-message-threads-api-guide.md
- Published: 2026-11-04 (updated 2026-11-04)
- Author: Kiki, COO at OmniaKey
- Language: zh
- Category: API setup
- Reading time: 9 min
- All posts: https://omniakey.com/blog/llms.txt

---

Claude message threads API 让长对话只上传一次。之后每轮只发新消息（Anthropic 的 Message threads 页面，2026 年 11 月 4 日核对）。第一次的 Messages 请求里加 `"thread": {"type": "create"}` 和 `message-threads-2026-08-12` beta 头，API 就把这段对话留在内存里，下一次请求要传的内容少很多。

所有模型上它都是 beta，17 个模型 ID 可以用。下面代码调用的 [Claude Opus 5.5](https://omniakey.com/zh/claude-opus-5-5) 就在其中。beta 头要加在线程的每个请求上。

## 一条线程省掉什么

没有线程时，每一轮都要带上整段记录：系统提示词、工具定义、之前每条消息和每个工具结果。线程把这段对话，包括 Claude 的回复，存在响应的 `id` 下面。continue 请求只发之后新增的内容（Message threads 页面，2026 年 11 月 4 日核对）。每轮要传的东西就少了。

提示缓存从另一个方向帮同一段对话。缓存命中让重复的输入更便宜。Anthropic 价格页（2026 年 11 月 4 日核对）的数字：Opus 5.5 每百万 token 0.20 美元，新输入 4 美元。请求里仍然要带这份输入。线程减少的是上传量，缓存降低的是重复部分的单价。长会话两样都要。上下文这一侧，看 [减少 Claude Code token 消耗](https://omniakey.com/zh/blog/reduce-claude-code-token-usage)。

线程不改价格，也不改速率限制（Message threads 页面，2026 年 11 月 4 日核对）。现行价格看 [价格页](https://omniakey.com/zh/pricing)。

缓存要自己加。每个请求都在顶层发 `cache_control`，API 就把它用到这个请求 `messages` 里最后一个可缓存的块上。4 个断点是平台级的提示缓存规则。自动缓存占掉其中一个（Anthropic 提示缓存页面，2026 年 11 月 4 日核对）。可以这样分配：

1. 顶层 `cache_control`，线程的每个请求占一个；
2. 一个放在 `system` 上，随 create 请求发；
3. 一个放在 `tools` 上，也是 create 时；
4. 一个放在最新的消息上。

更早的已存消息上的断点不算在这 4 个里，因为 continue 请求只发新消息。断点发出去就留在原处。重放对话时，把早先消息上的断点去掉。最后一个块上留一个（Message threads 页面，2026 年 11 月 4 日核对）。

## 先建第一轮，再续传

三步：

1. 把 Anthropic key 放进 `ANTHROPIC_API_KEY`。
2. 发完整对话，用 `system` 和 `tools` 就一起带上，再加 `"thread": {"type": "create"}`。
3. 只发上次响应之后的新消息，加 `"thread": {"type": "continue", "previous_message_id": "<previous id>"}`，`system` 和 `tools` 都不要带。

每个请求都带 beta 头和 `anthropic-version: 2026-10-21`。这是当前版本（Anthropic 的 API 版本说明，2026 年 11 月 4 日核对）。

```bash
curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2026-10-21" \
  -H "anthropic-beta: message-threads-2026-08-12" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5-5",
    "max_tokens": 1024,
    "system": "You are a careful backend reviewer.",
    "messages": [
      {"role": "user", "content": "Read this design and list the risks."}
    ],
    "thread": {"type": "create"}
  }'
```

把响应里的 `id` 存下来。下一个请求只发新消息：

```bash
PREVIOUS_ID="<id from the previous response>"

curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2026-10-21" \
  -H "anthropic-beta: message-threads-2026-08-12" \
  -H "content-type: application/json" \
  -d @- <<JSON
{
  "model": "claude-opus-5-5",
  "max_tokens": 1024,
  "messages": [
    {"role": "user", "content": "Turn those risks into a checklist."}
  ],
  "thread": {"type": "continue", "previous_message_id": "$PREVIOUS_ID"}
}
JSON
```

每次回复都把最新的 `id` 留着。线程只追加，每个响应的 `id` 只能续传一次，下一个请求要用最新的那个（Message threads 页面，2026 年 11 月 4 日核对）。

需要另一种拿 API key 的方式，OmniaKey 提供同样模型 ID 的 Claude API 接入，请求体不用改。OmniaKey 支持支付宝和微信支付，人民币充值。线程在 Anthropic 那边是 beta，正式依赖之前，先确认你用的中转把 `thread` 字段原样传下去。

Python SDK 会替你记 id。用 `client.beta.messages.thread(...)` 建一个 helper，它只准备调用，每轮用 `thread.send()` 发出。这个 helper 加上 beta 头，第一轮做 create，之后只带新消息续传（Message threads 页面，2026 年 11 月 4 日核对）。continue 返回 `thread_not_found`、`thread_already_continued` 或 `thread_continue_in_flight` 时，它用一个新的 create 请求把整段对话重放一次。

```python
import anthropic

client = anthropic.Anthropic()  # 读取 ANTHROPIC_API_KEY

# helper 此时还没发出请求，它保存每轮都要复用的字段。
thread = client.beta.messages.thread(
    model="claude-opus-5-5",
    max_tokens=1024,
    system="You are a careful backend reviewer.",
)

first = thread.send({"role": "user", "content": "Read this design and list the risks."})
print(first.id, first.stop_reason)

second = thread.send({"role": "user", "content": "Turn those risks into a checklist."})
print(second.id)  # 下次从这个 id 续传
```

我们没有拿这些请求跑过 Anthropic 的 API。写法来自 Anthropic 的 Message threads 页面。要做流式，`thread.stream(...)` 加 `stream.get_final_message()` 是同一套流程；想自己记 id，`thread.previous_message_id` 会返回当前值。

## 哪些字段要重发，哪些不要

固定字段每个请求都发同样的值，`system` 和 `tools` 只在 create 请求里发，自由字段每轮都可以变。

|分组|字段|规则|
|-|-|-|
|仅 create|`system`、`tools`|只在 create 请求里发；在 continue 请求里带上其中任何一个，哪怕是 `null`，都返回 400。|
|固定|`model`、`tool_choice`、`thinking`、除各服务器授权 token 之外的 `mcp_servers`、`context_management`、`output_config`、`container` 里的 `skills`、顶层 `cache_control`，以及 `anthropic-beta` 的取值集合|每次同值。值不同就返回 400，并指出是哪个字段不一样。|
|自由|`max_tokens`、`stop_sequences`、`stream`、`metadata`、`service_tier`，以及没有列名的字段|随时可以改。|

改了固定字段的 400 会带 `thread_fingerprint_mismatch`，消息里列出不一样的字段。要改其中某个值，就用完整对话和新值开一条新线程。

漏掉 `previous_message_id`，continue 返回 400，body 里有 message，没有 `error_code`；带上 `system` 或 `tools` 也一样。continue 只该携带上次响应之后的消息。

`mcp_servers` 是固定字段，但服务器 URL 和授权 token 只存在你的请求里。用 MCP 的每个请求都要发这个列表，token 可能在两次请求之间变化。容器 `id` 也只存在你的请求里，所以每次都要发 `container`（Message threads 页面，2026 年 11 月 4 日核对）。

服务端 fallback 在 create 时就定下来：`fallbacks` 要么在线程的每个请求里都发，要么都不发。值可以每轮变，加上或去掉这个字段会返回 `thread_fingerprint_mismatch`。

## 过期、重放和 404

线程在最后一次请求后 5 分钟过期，API 在最后一次使用后最多 1 小时删除它。每个存储的响应都会重新开始算这 5 分钟。线程不适用零数据保留。对话、系统提示词和工具在内存里加密保存，从不写进持久存储（Message threads 页面，2026 年 11 月 4 日核对）。

响应里没有字段说明这一轮是否存下，create 和 continue 都一样。要到下一次 continue 才知道。所以应用自己存好每条消息，以自己为准（Message threads 页面，2026 年 11 月 4 日核对）。

continue 返回 404 `thread_not_found` 时，用完整对话发一个新的 create 请求，再从那个响应的 `id` 续传。下面这些情况，API 都给同样的 404：

- 线程过期了；
- 上一轮已经处理过又被丢弃，比如被 `max_tokens` 截断的工具调用；
- 上一轮的响应由 fallback 模型写出；
- 上一条响应还在生成；
- 请求来自另一个组织、工作区、用户或工作区密钥；
- 新消息带来了已存对话里没有的内容，比如一段对话里的第一张图片。

重放完整对话总是安全的，上面每种情况都能修好。

流式多一条规则：等 `message_stop` 之后再用响应的 `id` 续传，早发的 continue 可能返回 404。continue 的流在 `message_stop` 之前断了，就再发同一个 continue 请求；返回 `thread_already_continued` 或 `thread_continue_in_flight` 就重放。流断掉时，SDK helper 把重试留给你。

另外三种情况各有各的处理：

- `pause_turn`：用那个响应的 `id` 续传，`"messages": []`。如果 404，就带上暂停的响应重放。
- `stop_reason: "refusal"`：去掉引起它的那一轮，开新线程；API 可能已经保存了被拒的那一轮。
- `usage.iterations` 里或流里最后的 `message_delta` 里出现 `fallback_message` 条目，就是 fallback 回答：重放。

## 错误

5 个线程错误都在模型运行前返回：请求不消耗 token，线程的过期时间也不变。400 的 `error.type` 是 `invalid_request_error`，404 是 `not_found_error`（Message threads 页面，2026 年 11 月 4 日核对）。

- **404，`thread_not_found`**：API 无法继续这个线程。用完整对话发一个新的 create 请求。
- **400，`thread_fingerprint_mismatch`**：固定字段和创建时存下的值不一致。保持一致，或者开新线程。
- **400，`thread_already_continued`**：这个响应 id 已经用过一次。用最新的 id 续传，没拿到就重放。
- **400，`thread_continue_in_flight`**：同一个 id 上还有 continue 在跑，通常是你自己前一次尝试。等它结束，或者重放。
- **400，`thread_unsupported_request`**：请求用到线程不支持的功能或组织设置。`error.details.unsupported` 会指出 `context_management`、`cache_control`、`fallbacks`、`hipaa`、`inference_hooks` 中的一个。这个列表会增加；遇到不认识的值，就不带 `thread` 发完整对话。

Message Batches 不接受 `thread` 字段，带它的批次项会以 `invalid_request_error` 失败。token 计数和压缩返回 400；除 `clear_thinking_20251015` 配 `keep: "all"` 之外的上下文编辑也返回 400。HIPAA 就绪的组织和开了 Inference hooks 的组织同样被排除。列表、读取、删除线程，这些 API 都没有。

## 常见问题

### 什么是 Claude message threads API？

它是 Messages API 的一个 beta 功能，把对话存在 Anthropic 一侧，之后每轮只发新消息。beta 头是 `message-threads-2026-08-12`，每个请求加一个 `thread` 对象，`type` 设为 `create` 或 `continue`。

### message threads 能和提示缓存一起用吗？

能。线程不替你缓存提示词：每个请求在顶层发 `cache_control`，断点不超过 4 个。已存消息上的断点不占这 4 个名额，顶层那个和 `system`、`tools` 上的要算。

### 线程中途能换模型吗？

换模型要开新线程。`model` 是固定字段，换个值就返回 400 `thread_fingerprint_mismatch`；用完整对话和新模型开一条新线程。

### 为什么 continue 返回 400 却没有 error\_code？

continue 少了 `previous_message_id`，或者带了 `system`、`tools`，哪怕是 `null`。这类 400 有 message、没有 `error_code`，看文本就知道。

### 线程能存多久，过期后会怎样？

线程在最后一次请求后 5 分钟过期，最后一次使用后最多 1 小时被删除。之后 continue 返回 404 `thread_not_found`；用完整对话发新的 create 请求，再从它的 `id` 续传。

## 核对的来源

- [Anthropic：message threads](https://platform.claude.com/docs/en/build-with-claude/message-threads)
- [Anthropic：提示缓存](https://platform.claude.com/docs/en/build-with-claude/prompt-caching)
- [Anthropic：Claude API 价格](https://platform.claude.com/docs/en/about-claude/pricing)
- [Anthropic：API 版本](https://platform.claude.com/docs/en/api/versioning)
