メインコンテンツまでスキップ

Message Threads — 新しいターンだけを送る

上級
What you'll learn
  • スレッドが節約するもの(リクエストのバイト数と time to first token)と、節約しないもの(トークン、つまりお金)を正確に言える
  • create、continue、replay の 3 状態ライフサイクルを回し、リプレイ経路をリリース後ではなくリリース前に作る
  • すべてのパラメータを create-only、fixed、free のいずれかに分類し、thread_fingerprint_mismatch を一目で見分ける
  • 4 ブレークポイントの予算内で、スレッド上でもプロンプトキャッシュを機能させ続ける
  • ストリーミングされたターンを安全に continue し、message_stop より前に continue すると 404 が返る理由を理解する
  • 5 つのスレッドエラーと、復旧せずに新しいスレッドを始めるべき 2 つのケースを扱う
  • 自分のワークロードがスレッドの恩恵を受けるタイプかどうかを判断する

長いエージェントループには、モデルとは無関係の静かな税金があります。40 ターン目のリクエストは、またしても会話全体です。読んだすべてのファイル、撮ったすべてのスクリーンショット、すべてのツール結果。モデルが 1 トークンを出力する前に、API はそのすべてを受信し、検証し、トークナイズしなければなりません。会話は入力ではなくアップロードになってしまったのです。

メッセージスレッドはアップロードをなくします。会話全体を 1 回だけ送ります。それ以降、各リクエストは新しいメッセージと直前のレスポンスの id だけを運び、API はモデルを呼び出す前に自分の側で会話を再構築します。

スレッドが実際に節約するもの​

移行を計画する前にこのセクションを読んでください。この機能について人づてに書かれた記事は、ほぼ例外なくここを間違えています。

Watch out
  • スレッドはトークン課金を減らしません。Anthropic は明言しています: Claude は毎ターン会話全体を読むため、トークン使用量は同じです。料金とレート制限も変わりません。
  • 引用されているのを見かけるかもしれない劇的な数字 — 数百キロバイトから数キロバイトへ — はリクエストのペイロードサイズであり、トークンではありません。アップロードが小さくなるのはレイテンシにとって本物の利点ですが、請求書が小さくなるわけではありません。
  • コストを下げるレバーは依然としてプロンプトキャッシュであり、スレッドがキャッシュを代わりに有効にしてくれるわけではありません。下の「スレッド上のプロンプトキャッシュ」を参照してください。

得られるのはレイテンシと帯域です。Anthropic が公表している実測値: 50 万〜100 万トークンのリクエストでは、time to first token の中央値が約 35% 短縮されます。

したがって、正直な判断ルールはこうです:

ワークロードスレッドは有効か?
長いコーディングエージェント — ツール結果やファイル編集のたびに会話が膨らむ**はい。**想定されている主要ケースです。
毎ターンスクリーンショットをアップロードする computer-use エージェント**はい。**スクリーンショットは再アップロードされず、スレッドに残ります。
ドキュメントや画像が多いマルチターンのワークフロー**はい。**ペイロード削減が最大になります。
短いターンが数回のチャット**いいえ。**取り除くものがほとんどなく、リプレイの複雑さだけを背負うことになります。
バッチジョブ、トークンカウント、オンデマンドのコンパクション不可能です。スレッドが対応しない機能を参照。
このトラフィックでゼロデータ保持(ZDR)が必要**いいえ。**スレッドは ZDR の対象外です。データ保持を参照。
スレッドとセッションは別の機能です

Message threads は Messages API 上にあります。セッションスレッドは Claude Managed Agents 内の別の概念です。同じ言葉でもレイヤーが違います — 一方の検索結果でもう一方に迷い込まないようにしてください。

3 状態ライフサイクル​

Guided walkthrough1 of 3
  1. 最初のリクエストは今と全く同じように送ります — 会話全体、system、tools — そこに type 'create' の thread パラメータを追加します。API は会話と Claude の返信をレスポンス id のもとに保存します。このスレッドのこのリクエストと以降のすべてのリクエストで、message-threads-2026-08-12 ベータヘッダーを送ってください。

Create、そして continue​

curl — create a thread, then send only the new turn

# Turn 1 — full conversation, system, tools, and thread: create
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 support agent for Acme. Keep replies short.",
  "messages": [{"role": "user", "content": "Hi! I have a question about an order."}],
  "thread": {"type": "create"}
}'

# Turn 2 — only the new message. No system, no tools.
# message_id is the id from the turn-1 response.
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": "How long does standard shipping usually take?"}],
  "thread": {"type": "continue", "previous_message_id": "'"$message_id"'"}
}'

公式 SDK には、これらのリクエストを組み立ててくれるスレッドヘルパーが付属しています — ベータヘッダーを追加し、最初のターンで create、以降は continue を行い、system と tools を省略し、404 のときは 1 回だけリプレイします。これが適切なデフォルトです。上の素の形式が必要になるのは、ヘルパーのない言語から、ゲートウェイ経由で、あるいは curl から API を直接使う場合です。

3 つのフィールドグループ​

机の上に貼っておきたい表です。スレッドリクエストのすべてのパラメータはちょうど 1 つのグループに属し、そのグループが、送ってよいか、送らなければならないか、変更してよいかを決めます。

グループフィールドルール
Create-onlysystem, toolscreate リクエストでのみ送ります。それらに付ける cache_control ブレークポイントも同様です。continue リクエストで送ると — null であっても — 400 が返ります。
Fixedmodel, tool_choice, thinking, mcp_servers(各サーバーの認証トークンを除く)、context_management, output_config, container 内の skills, トップレベルの cache_control, および anthropic-beta の値の集合すべてのリクエストで、create リクエストと同じ値を送ります。異なる値だと 400 thread_fingerprint_mismatch が返り、エラーメッセージに異なるフィールド名が示されます。
Freemax_tokens, stop_sequences, stream, metadata, service_tier, および上に挙げていないすべてターンごとに自由に変更できます。

覚えておくべき例外が 2 つあります:

  • **MCP サーバーの URL、MCP の認証トークン、コンテナの id は保存されません。**これらを使うリクエストでは毎回 mcp_servers と container を送ってください。認証トークンはリクエスト間で変えても構いません。
  • fallbacks はオールオアナッシングです。サーバーサイドフォールバックを使うなら、スレッドのすべてのリクエストで fallbacks を送るか、まったく送らないかのどちらかです。スレッドの途中で追加したり外したりすると thread_fingerprint_mismatch になります。ただし値は変更できます。

create-only や fixed のフィールドを変えたいときは、スレッドを変更するのではなく、新しい値を載せた create リクエストで新しいスレッドを始めます。

system や tools を変えると、保持された thinking が無効になることがあります

Claude Fable 5.1、Mythos 5.1、Opus 5.5、Sonnet 5.5、Haiku 5.5 では、system または tools を変えて新しいスレッドを始めると、会話にすでに含まれている thinking と redacted_thinking ブロックが無効になり、create リクエストが 400 を返すことがあります。対処法: 送る会話からすべての thinking と redacted_thinking ブロックを取り除きます。するとモデルは、以前の推論なしで回答します。Thinking と effortを参照してください。

リプレイ経路は省略できません​

Watch out
  • 真実の源はあくまでアプリケーションです。すべてのメッセージを自分で保持してください。
  • どのレスポンスも — create も continue も — API がそのターンを保存したかどうかを教えてくれません。それは次の continue リクエストで、成功するか 404 になるかで初めて分かります。
  • SDK のスレッドヘルパーは、ヘルパーオブジェクトが生きている間だけ自分のコピーをメモリに保持し、他のどこにも保存しません。会話をプロセスより長く残す必要があるなら、自分で永続化してください。
  • リプレイは初日から作ってください。開発中に一度も失効しなかったスレッドは、本番で最初の遅いツール呼び出しの間に失効します。

error.details.error_code が thread_not_found の 404 は「これは continue できない」という意味で、レスポンスは意図的に理由を教えません。文書化されている原因:

  • スレッドが失効した(5 分間の無操作)。
  • API は直前のターンを処理したが保存しなかった — たとえば、max_tokens で打ち切られてツール呼び出しで終わったレスポンス。
  • フォールバックモデルが直前のレスポンスを書いた。
  • 直前のレスポンスがまだ生成中である。
  • リクエストが別の組織、ワークスペース、ユーザー、またはサービスアカウントから来ている。ワークスペースキーで作成されたスレッドは、そのキーに紐づいたままです。
  • 新しいメッセージが、保存済みの会話が受け付けられない内容を追加している — 典型例は、画像がなかった会話に追加される最初の画像。
リプレイする前にエラーコードを確認する

存在しないモデルへのリクエストも 404 を返します。ステータスだけで判断せず、必ず error.details.error_code で分岐してください。

復旧するには、自分のコピーから会話全体を、system と tools を付けて新しい create リクエストで送り、新しいレスポンスの id から continue を続けます。先に、以前のメッセージから cache_control ブレークポイントを取り除いてください(下の 4 ブレークポイント予算を参照)— SDK ヘルパーはこれを自動で行います。

リプレイした create が成功したのに、直後の continue がまた 404 になる場合、API がその会話をそもそも保存できない可能性があります — たとえば非常に大きな会話です。その会話には thread を使うのをやめて、通常のリクエストを送ってください。SDK ヘルパーは、unsupported_request_behavior を continue_unthreaded に設定して構築するとこれを自動化します。この設定がないと毎ターンリプレイしてしまい、スレッドを使わないより厳密に悪い結果になります。

Append-only、そして id ごとに continue は 1 回だけ​

スレッドは増える一方です。各レスポンス id はちょうど 1 回だけ continue でき、常に最新の id から continue します。そこから 3 つの帰結が生じます:

  • **編集も分岐もできません。**以前のメッセージを変更したり別の案を試したりするには、新しいスレッドを始めます。
  • **その場での要約はできません。**スレッドの初期のターンを要約に置き換えることはできません。コンパクトにしたいなら、自分のコードで要約を書き、新しい create リクエストの通常のメッセージとして送ります。オンデマンドのコンパクションはスレッドでは一切使えません。
  • **continue のリトライは冪等ではありません。**同じ continue を 2 回送ると、2 回目は thread_already_continued か thread_continue_in_flight を受け取ります。

API は continue で送ったメッセージの重複排除を行いません。メッセージを再送すると、会話に 2 回現れることもあれば、エラーになることもあります。直前のレスポンスから始まる continue リクエストは、即座にエラーになります。

pause_turn の特殊ケース​

レスポンスが stop_reason: "pause_turn" で止まったら、そのレスポンスの id からの continue リクエストと空の messages リストで再開します:

Resume a paused turn — empty messages, same thread

{
"model": "claude-opus-5-5",
"max_tokens": 1024,
"cache_control": {"type": "ephemeral"},
"messages": [],
"thread": {"type": "continue", "previous_message_id": "msg_01..."}
}

ターンが一時停止している間は、新しいユーザーメッセージを送らないでください — API は拒否します。その再開が 404 を返したら、一時停止したレスポンスを含めた会話全体を新しい create リクエストでリプレイします。SDK ヘルパーでは、メッセージなしで send() を呼びます。

拒否の後は、最初からやり直す​

レスポンスが stop_reason: "refusal" なら、**スレッドを continue しないでください。**API はすでに拒否されたターンを保持している可能性があり、continue ではそれを取り除けません。拒否を引き起こしたターンを省くか言い換えた create リクエストで、新しいスレッドを始めます。拒否と安全性を参照してください。SDK ヘルパーがこれを処理します: 拒否されたターンを破棄し、次の呼び出しで新しいスレッドを始めます。

スレッド上のプロンプトキャッシュ​

スレッドとキャッシュは直交しています。スレッドがプロンプトを代わりにキャッシュしてくれることはありません — プロンプトキャッシュはスレッドなしの場合とまったく同じ挙動で、ブレークポイントをどこに置くかはあなたが決めます。これを飛ばすと、アップロードは速くなったのにコストは以前と同じ、というスレッドになります。

簡単なレシピ: スレッドのすべてのリクエストでトップレベルの cache_control を送ります。API はそれをそのリクエストの messages の最後のキャッシュ可能ブロックに適用するので、各ターンは以前の会話をキャッシュから読み、新しいターンをそこに書き込みます。トップレベルの cache_control は fixed フィールドであることを忘れずに — すべてのリクエストで同じ値でなければ、フィンガープリント不一致になります。

ブロックに自分でブレークポイントを置く場合は、4 つのルールがあります:

  1. create リクエストで送ったブレークポイントは、tools、system、または最後ではないメッセージブロックに置かれているなら、後のターンでも機能し続けます。リクエストの最後のブロックにあるブレークポイントはそのリクエストでキャッシュを書き込みます — したがって新しいターンごとにキャッシュしたいなら、リクエストごとに 1 つ送ります。
  2. 送ったブレークポイントを移動も削除もできません。continue リクエストは以前のメッセージを二度と再送できないからです。
  3. **予算はスレッドリクエストあたり 4 ブレークポイントです。**トップレベルの cache_control が 1 つ分を消費します。create リクエストの system と tools に置いたブレークポイントは、continue リクエストのたびにまた数えられます。以前の保存済みメッセージのブレークポイントは数えられません。超過すると、unsupported: ["cache_control"] を伴う 400 thread_unsupported_request になります。
  4. リプレイするときは、以前のメッセージからブレークポイントを取り除き、最後のブロックに 1 つだけ残します — そうしないと、リプレイ自体が予算を超えます。
計算は一度だけやっておく

トップレベルの cache_control(1)+ system のブレークポイント(2)+ tools のブレークポイント(3)で、すべての continue リクエストにちょうど1 つの余裕が残ります。設計の余地はありますが、気軽に使える予算ではありません。

プロバイダー横断で見たこのトレードオフについては、プロバイダー間のプロンプトキャッシュを参照してください。

ストリーミング​

スレッドはストリーミングと併用できますが、間違えやすいタイミングのルールが 1 つあります。

レスポンスの id は、message_start イベントで早い段階に届きます。**message_stop を受信するまで、その id から continue しないでください。**それより前に送った continue は 404 を返すことがあります。直前のレスポンスがまだ生成中だからです。

continue リクエストのストリームが message_stop の前に切れたら、同一の continue リクエストを再送します。それが thread_already_continued や thread_continue_in_flight を返したら、新しい create リクエストで会話全体をリプレイします。この曖昧さは本質的なものです: 接続がサーバーのレスポンス保存の前に切れたのか、後に切れたのか分かりません。リプレイは常に安全ですが、リトライはそうとは限りません。

SDK ヘルパーはストリームが切れた後の continue をリトライしません。完了したブロックと届いたテキストを保持し、次のターンで会話全体をリプレイします。中断したターンを代わりに再送するには、メッセージなしで send() を呼びます。また、すべてのストリームを最後まで読んでください — ヘルパーは message_stop でレスポンスを記録するので、ストリームを途中で放棄すると、閉じるまでスレッドが占有されたままになります(Python では with ブロックを抜ける、TypeScript では abort()、それ以外では Close/close())。

エラー​

この 5 つはすべてモデルが実行される前に返されます: リクエストはトークンを消費せず、スレッドも有効期限も変わりません。

ステータスerror_code意味対処
404thread_not_foundAPI がスレッドを continue できない。リプレイ: 新しい create リクエストで会話全体を送る。
400thread_fingerprint_mismatchfixed フィールドが保存された値と異なる。メッセージにどれかが示される。fixed フィールドを同一に保つか、変更したければ新しいスレッドを始める。
400thread_already_continuedそのレスポンス id はすでに continue 済み。最新の id から continue する。クライアントがそのレスポンスを見ていないなら、リプレイする。
400thread_continue_in_flightこの id からの別の continue がまだ実行中 — たいていは自分自身のリトライ。終わるのを待ってそのレスポンスから continue するか、リプレイする。
400thread_unsupported_requestリクエストがスレッド非対応の機能または組織設定を使っている。該当するとき error.details.unsupported がそれを示す。その機能を外すか、thread なしでリクエストを送る。

error.type は 400 では invalid_request_error、404 では not_found_error です。スレッドリクエストの 400 の中には、error_code がまったくないものもあります — previous_message_id のない continue や、system あるいは tools を含む continue がそうです。

A thread error body — note where error_code actually lives

{
"type": "error",
"error": {
  "type": "invalid_request_error",
  "message": "thread: a maximum of 4 blocks with cache_control may be provided when `thread` is set. Found 5.",
  "details": {
    "error_code": "thread_unsupported_request",
    "unsupported": ["cache_control"]
  }
},
"request_id": "req_011CSHoEeqs5C35K2UUqR7Fy"
}

error.details.unsupported に文書化されている値は context_management、cache_control、fallbacks、hipaa、inference_hooks です。Anthropic はこの一覧に追加することがあるので、未知の値はクライアントのバグではなく「これは thread なしで送る」と扱ってください。複数の条件に該当するリクエストでも、通知されるのは 1 つだけです。

スレッドが対応しない機能​

  • Message Batches。 thread を含むバッチ項目は、結果の中で invalid_request_error として失敗します。
  • トークンカウント。 thread 付きのトークンカウントリクエストは 400 です。代わりに system と tools を付けた会話全体をカウントしてください。トークンと料金を参照。
  • コンパクション。 オンデマンドのコンパクションはスレッドでは使えません。
  • コンテキスト編集 — ただし 1 つ例外があります: keep: "all" の clear_thinking_20251015。
  • HIPAA readiness が有効な組織。
  • **Inference hooks がオンの組織。**組織を管理している人に伝えておく価値があります: 他の誰かが有効にしたプラットフォームレベルの設定によって、チーム全体でスレッドが使えなくなることがあります。

厳格な制限がさらに 2 つ: スレッドを一覧、読み取り、削除する API は存在せず、スレッドは Amazon Bedrock では利用できません。

データ保持​

Message threads は ZDR の対象外です。使っても、他の ZDR 対象機能の利用には影響しません — 範囲はこのトラフィックであり、アカウント全体ではありません。

スレッドが保持するもの: モデルに送られた会話、create リクエストの system と tools、および fixed フィールドのフィンガープリント。これはメモリ上で、サーバー管理の鍵で暗号化され、永続ストレージには一切書き込まれず、スレッドの最後の使用から遅くとも 1 時間以内に削除されます。ファイルは参照として保持されるため、会話が使うすべてのファイルは、スレッドを使っている間ずっと利用可能でなければなりません。

コンプライアンス上ワークロードに ZDR が必要なら、そのワークロードにスレッドは使えません。プライバシーを参照してください。

よくある間違い​

間違い症状対処
請求額が減ると期待する請求書が変わらないスレッドが削るのはバイト数と TTFT であり、トークンではない。コストにはプロンプトキャッシュを追加する。
continue で system を再送する400、error_code なしcreate-only。省略する — null でも失敗する。
スレッドの途中で thinking や tool_choice を少し変えるthread_fingerprint_mismatchfixed フィールド。変えるなら新しいスレッドを始める。
レスポンスを保存の証拠とみなす後で突然の 404保存を確認するものは何もない。リプレイを作る。
message_start から continue する負荷時に断続的な 404message_stop を待つ。
切れた continue を闇雲にリトライするthread_already_continued1 回リトライし、それからリプレイ。リプレイは常に安全。
拒否の後に continue する会話が汚染されるそのターンを除いて新しいスレッドを始める。
system + tools + トップレベル + ターンごとにブレークポイントthread_unsupported_request予算は 4 で、system/tools のブレークポイントは毎ターン数え直される。
pause_turn 中に新しいユーザーメッセージを送る拒否される"messages": [] で continue する。
ヘルパーが毎ターンリプレイしているスレッドなしより遅延が悪化unsupported_request_behavior を continue_unthreaded に設定する。

Check yourself

0/4
  1. 40 ターンのコーディングエージェントをメッセージスレッドに移行したところ、リクエストのペイロードが約 600 KB から約 2 KB に減りました。月々の API 請求額はどうなりますか?
  2. スレッドの 12 ターン目で、error_code が thread_not_found の 404 が返りました。正しい復旧方法はどれですか?
  3. create リクエストで system に cache_control ブレークポイントを 1 つ、tools に 1 つ置き、さらにトップレベルの cache_control を送りました。各 continue リクエストで使えるブレークポイントはいくつ残りますか?
  4. 既存のスレッドに対してできることはどれですか?
Key takeaways
  • スレッドはアップロードと引き換えに状態を持ちます: 会話は 1 回だけ送り、その後は新しいメッセージと previous_message_id だけを送る。ベータヘッダー message-threads-2026-08-12 をすべてのリクエストに付ける。
  • 得られるのはバイト数とレイテンシ — 50 万〜100 万トークンのリクエストで TTFT 中央値が約 35% 低下 — だけです。トークン使用量、料金、レート制限は変わりません。
  • すべてのパラメータを分類する: system と tools は create-only、model、thinking、tool_choice、context_management、output_config、トップレベルの cache_control は fixed、max_tokens などは free。
  • アプリが真実の源です。どのレスポンスも保存を確認せず、スレッドは 5 分の無操作で失効し、唯一の復旧は新しい create リクエストで会話全体をリプレイすること。
  • Append-only で、レスポンス id ごとに continue は 1 回: 編集も分岐もその場でのコンパクションもなし。fixed なものを変えるなら新しいスレッド。
  • キャッシュは依然としてあなたの仕事です。すべてのリクエストにトップレベルの cache_control を付けるのが簡単なレシピで、4 ブレークポイントという厳格な予算の中で system と tools は毎ターン数え直される。
  • ストリーミング: continue は message_stop の後だけ。切れた continue は 1 回リトライし、その後はリプレイ — リプレイは常に安全。
  • バッチ、トークンカウント、コンパクション、ほとんどのコンテキスト編集、HIPAA 有効の組織、inference hooks 有効の組織、Amazon Bedrock、ZDR が必要なワークロードには使えません。

出典とさらなる読み物​

次へ​