Preserved thinkingとは — Messages APIのthinking block保護策
Claude Fable 5.1・Opus 5.5・Sonnet 5.5・Haiku 5.5で効く蒸留対策の仕組みと、システムプロンプトやツールの書き換えで起きる400エラーの切り分けと置き換え方を扱います。
Preserved thinkingとは
Preserved thinkingは、Messages APIが送り返されたthinkingブロックを再利用してよいかを検証する仕組みです。Claude Fable 5.1から始まり、現在はClaude Fable 5.1・Claude Opus 5.5・Claude Sonnet 5.5・Claude Haiku 5.5が対象です。APIはthinkingブロックのsignatureを見て、そのブロックより前のsystemプロンプト・tools・messagesが、生成したときと一字一句変わっていないかを確認します。変わっていれば、そのブロックとそれ以降のthinkingブロックは無効です。
狙いは蒸留(distillation)対策です。会話の前の部分を書き換え、暗号化されたthinkingブロックをClaudeに復号・出力させる手口が、他モデルの学習用データを不正に抽出する蒸留キャンペーンで使われてきました。書き換えそのものを検知して弾けば、この手口は通りません。10月12日に公表された防御の全体像と抽出量の変化は、蒸留対策の強化にまとめています。
Claudeのthinkingブロックは暗号化されて返ります。抽出した推論で別モデルを訓練すると、そのモデルはClaudeの能力の一部を引き継ぎつつ、サイバー攻撃や兵器開発のような悪用を防ぐ安全策は引き継ぎません。サポート記事はこの手口を、数千の偽アカウントを使う産業規模のものと説明しています。既存の対策である蒸留検知の分類器や、上位モデルから下位モデルへのセッション・推論の転送制限に、Preserved thinkingが加わった形です。
書き換えると無効になるもの、ならないもの
APIが比較するprefixは3つだけです。直近のリクエストで送ったsystemプロンプト、toolsの一覧、そのブロックより前にある全messageです。サーバー側のcompaction(コンパクション)を使う場合は、直近のcompactionブロック以降がprefixになります。
変えるとthinkingが無効になるか
無効になる
systemの文字列やブロックの変更toolsの追加・削除・改名・スキーマ変更(まだ誰も参照していないdefer_loading: trueのツールの追加は例外で、prefixを変えません)- 過去の
user・assistantメッセージの編集・並べ替え・削除 - 過去の
tool_resultの削除・短縮、画像の再エンコード - 呼ぶたびに中身が変わる画像・文書のURL
無効にならない
- 末尾へのメッセージ追加
effort・max_tokens・output_config・tool_choice・metadataの変更cache_controlマーカーの追加・移動・削除- 同じバイト列を返す署名付きURLの入れ替え
- サーバー側のcompactionやcontext editingによる削除(APIは送った内容と比べる)
thinkingブロック自体は、先頭から・末尾から・全部を削除しても構いません。ただし途中の1件だけを抜くと、それ以降のブロックはすべて無効です。一度抜いたブロックを後から戻すと、抜けていた間に生成されたブロックが無効になります。
400が出たとき、何をどの順で確かめるか
無効なブロックへの対応はthinking.block_binding.prefix_mismatch_behaviorで選びます。
"error"(既定): 最初に失敗したブロックを名指しした400のinvalid_request_errorで拒否します"drop_block": 失敗したブロックとそれ以降のthinkingブロックを落とし、リクエストは成立させます。落とされた分は課金されませんが、モデルはそのターンを過去の推論なしで答え、プロンプトキャッシュも編集地点から作り直されます
フィールドも、落としたブロックを示す応答のinput_transformations配列も、thinking-binding-controls-2026-08-01ベータヘッダーが必要です。ヘッダーなしでblock_bindingを送ると、block_binding: Extra inputs are not permittedで始まる別の400になります。Message Batches APIでは、フィールドを省いた項目は失敗せず、既定で強制される環境ではブロックが落とされます。バッチ項目を失敗させたいときは"error"を明示します。
400を受けてから原因の編集を直すまで
- 1
エラー文を読む
400は
messages.1.content.0: ...のように、失敗したブロックのパスで始まり、thinkingブロックのsignatureが別の会話に紐づいていると伝えます。末尾の1文が、systemかtoolsの差分のように何が変わったかを名指しします。署名が壊れている場合は同じ400でも会話への言及がなく、prefix_mismatch_behaviorは効きません。 - 2
同じボディを再送しない
何度送っても同じ理由で失敗します。ベータヘッダーと
prefix_mismatch_behavior: "drop_block"を付けて1回だけリトライし、この選択をセッションに保存して、再起動後のリクエストにも付けます。ヘッダーを送れないときは、履歴のthinking・redacted_thinkingブロックを一度すべて外して続けます。 - 3
連続2回のリクエストを突き合わせる
直近数ターンの送信ボディを記録しておき、名指しされた位置までの
system・tools・messagesを前後で比べます。追記した分を除いて一致していなければ、差の出た箇所が原因の編集です。 - 4
編集を置き換える
差が見つかったら、次節の表で同じ効果を出す書き方に直します。
drop_blockを外すのは、input_transformationsのprefix_binding_mismatchが出なくなってからです。
"drop_block"はエラーを隠すだけで、編集は直りません。落とされた推論をClaudeが再構築しようとして、トークン消費が増えることがあります。落とされたブロックが多いほど、また長いセッションの多くのターンで落とされるほど、増え方は大きくなります。セッションごとにprefix_binding_mismatchを含む応答を数え、通知を出しておく運用が公式ドキュメントで勧められています。
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: thinking-binding-controls-2026-08-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-fable-5-1",
"max_tokens": 16000,
"thinking": {
"type": "adaptive",
"block_binding": {
"prefix_mismatch_behavior": "drop_block"
}
},
"messages": [
{"role": "user", "content": "1071と462の最大公約数は?"}
]
}'最初のターンでは再送するthinkingブロックがないので、input_transformationsは空の配列で返ります。2ターン目以降に、pathがmessages.3.content.0のような形で落とされたブロックを列挙します。
どの編集を、何に置き換えるか
公式ドキュメントは、実装で踏みやすい編集それぞれに、prefixを変えずに同じ効果を出す書き方を挙げています。共通の前提は、アシスタントターンをAPIが返した通りに一字一句送り返すことです。
| 踏みやすい編集 | 置き換え | ベータヘッダー |
|---|---|---|
systemに現在時刻・モードフラグ・再読み込みした指示を毎回埋め直す | 置き換えsystemは固定し、変わった時点にrole: "system"のメッセージを追加する | ベータヘッダー不要 |
| 最初のuserメッセージの環境情報(ブランチ・日付・メモリ)を毎回描き直す | 置き換え一度だけ描画して使い回し、変化は直近のuserターンにテキストで足す | ベータヘッダー不要 |
古いtool_resultをその場で削る・縮める | 置き換え送る前に縮める。後から消すならclear_tool_uses_20250919でサーバー側に任せる | ベータヘッダーcontext-management-2025-06-27 |
toolsの追加・削除 | 置き換えtool_addition・tool_removalを載せたsystemメッセージを足す | ベータヘッダーinline-tools-2026-09-15 |
| ターンごとの注意書きを足しては消す | 置き換えclear_at: "next_user_message"を付けたターン限定のsystemメッセージ | ベータヘッダーmid-conversation-system-clear-at-2026-08-21 |
| クライアント側で古いターンを要約・間引く | 置き換えon-demand compactionでAPIに要約させ、署名付きブロックで差し替える | ベータヘッダーcompact-2026-09-04 |
toolsの置き換えには、旧名のmid-conversation-tool-changes-2026-07-01も使えます。これはツールを名前で参照する変更に限られます。MCPコネクタ経由のツールはmcp-client-2026-09-15を加えます。会話の途中のsystemメッセージは、Fable 5.1・Mythos 5.1・Fable 5・Mythos 5・Opus 5.5・Opus 4.8・Opus 5・Sonnet 5.5で使えます。Sonnet 5は対象外なので、モデルを切り替える実装では、対応しないモデル向けに従来どおりsystemを編集する経路を残します。
effortを途中で変えるのは、prefixの外なのでthinkingを無効にしません。ただしトップレベルのoutput_config.effortを変えるとキャッシュは作り直しです。Claude APIとGoogle Cloudでは、Fable 5.1・Mythos 5.1・Opus 5.5・Opus 5・Sonnet 5.5が対象です。中身が空のsystemメッセージにoutput_configを載せれば、キャッシュを保てます(ベータ、ヘッダーはmid-conversation-output-config-2026-07-01)。対応しないモデル(Fable 5を含む)に送ると400です。メッセージはoutput_config.effort requires a model that supports per-turn effort; this model does notです。Sonnet 5.5でbetween_toolsを使う場合は、effortを途中で変えると400になります。
一番確実な圧縮は、会話全体を1つのuserメッセージに要約し、それより前のターンを送り返さないことです。送り返すthinkingがないのでチェック自体が起きず、Claudeは要約から推論をやり直します。自前で書いた要約に直近ターンを残す方式は、残したターンのthinkingが要約前の履歴で作られているため失敗します。残すならthinkingブロックを外し、textとtool_useだけを持ち越します。
見落としやすい3つの落とし穴
エラーにならずに推論が消える実装
空のthinkingを捨てるシリアライザ
Claude Fable 5.1では
thinkingフィールドが既定で空で、推論の本体はsignatureに入っています。空のブロックを読み飛ばす実装だと、エラーは出ないまま毎ターンの推論が失われます。ストリームを自前で解析するなら、テキストが来なくてもsignature_deltaを受けて閉じるまでブロックを保持します。中身が変わるURL
imageやdocumentのURL指定では、チェックの対象はURL文字列でなく取得したバイト列です。「最新のスクリーンショット」を返すエンドポイントや、会話の途中で編集された文書は、次のターンのthinkingを無効にします。Files APIのfile_idかbase64なら避けられます。プロキシが動かす`system`
ゲートウェイが呼び出し元の
role: "system"メッセージをトップレベルのsystemへ移すと、その時点で全thinkingが無効です。ツールを止めたいときも、toolsを消さずにtool_choice: {"type": "none"}を送ります。anthropic-betaの値とblock_bindingは、呼び出し元のままそのまま転送します。未知のキーを弾くオプション定義だと、利用者が"drop_block"を選べません。
対象のモデルとアカウント
サポート記事の表では、2026年8月31日(UTC)より前のアカウントは「適用外」です。一方、APIドキュメントでは、古いアカウントでもprefix_mismatch_behaviorを設定したリクエストは強制を受けます。設定せずベータヘッダーだけ送ると、リクエストは失敗しません。失敗したブロックはモデルに渡り、input_transformationsにthinking_mismatch_allowedとして記録されます。
| モデル | 8月31日(UTC)以降のアカウント | それより前のアカウント |
|---|---|---|
| Claude Fable 5.1 | 8月31日(UTC)以降のアカウント既定で強制("error") | それより前のアカウントフィールドを設定したリクエストのみ |
| Claude Opus 5.5 | 8月31日(UTC)以降のアカウント既定で強制("error") | それより前のアカウントフィールドを設定したリクエストのみ |
| Claude Sonnet 5.5 | 8月31日(UTC)以降のアカウント既定で強制("error") | それより前のアカウントフィールドを設定したリクエストのみ |
| Claude Haiku 5.5 | 8月31日(UTC)以降のアカウント既定で強制("error") | それより前のアカウントフィールドを設定したリクエストのみ |
| Claude Mythos 5.1・Fable 5.1より前のモデル | 8月31日(UTC)以降のアカウントprefixチェックなし | それより前のアカウントprefixチェックなし |
対象はClaude Platform組織のほか、Amazon Bedrock・Google Cloud Vertex AI・Microsoft Foundryの各アカウントです。手元のアカウントがどちらかは、thinkingブロックを含むFable 5.1の会話でブロックより前を書き換え、ヘッダーもblock_bindingも付けずに送ると分かります。ヘッダーを名指しした400なら既定で強制される側、200なら強制されない側です。
自分のキーで400が出ていないことは、コードが安全な証拠になりません。ツールを他人が自分のAPIキーで動かす場合、新しいアカウントの利用者が先に400を受けます。ヘッダーだけ付けて応答のthinking_mismatch_allowedを数えると、挙動を変えずに編集箇所を洗い出せます。
Claude Codeの最新版なら、点検を自動化できます。リクエストを組み立てるリポジトリを開き、同梱のClaude APIスキルで/claude-api preserved-thinking-migrationを実行します。数回分のマルチターンのセッションを記録して連続するリクエストを比べ、コードがprefixを書き換えている箇所を探します。続けて"drop_block"付きでセッションを再生し、APIが落としたthinkingブロックを数えます。token countingのエンドポイントも同じprefixチェックを走らせ、同じ400を返します。
モデルをまたぐと推論はどうなるか
読めるブロックはモデルごとに決まっています。Claude Fable 5.1とMythos 5.1は、互いと、それより前のモデルのブロックを読みます。Claude Opus 5.5はOpus 5以前のOpus・Sonnet・Haikuのブロックを読み、Claude APIとGoogle CloudではSonnet 5.5のブロックも読みます。FableやMythosのブロックは読みません。Claude APIでは、Fable 5.1とMythos 5.1がOpus 5.5のブロックを読めるので、Opus 5.5からFable 5.1への切り替えでは推論が残ります。
逆向きは落ちます。Fable 5.1・Mythos 5.1からOpus 5.5へ、またOpus 5.5からその2つ以外のモデルへ切り替えたターンは、それまでの推論なしで実行されます。ルーターが安いモデルへ振る場合や、分類器の拒否後のフォールバックも同じです。読めないブロックはAPIが黙って落とし、課金もinput_tokensへの計上もありません。
履歴は毎回すべて、thinkingブロック込みで送ります。APIはmessages配列を書き換えないので、Fable 5.1に戻した次のリクエストではブロックがまた読めます。推論が戻らなくなるのは、クライアントがモデル切替時にthinkingを自分で外した場合です。ベータヘッダーを付けると、落とされたブロックはreason: "model_binding_mismatch"で報告されます。
Claude Sonnet 5.5では、thinkingがモデルと会話に紐づく
2026年9月28日発表のClaude Sonnet 5.5にも、同じ紐づけが入っています。Sonnet 5.5は、Sonnet 5・Opus 4.8・Haiku 4.5以前のモデルが出したブロックを読みます。Opus 5・Opus 5.5・Fable・Mythosのブロックは読めません。Sonnet 5.5のブロックを読めるのは、Claude APIとGoogle CloudのOpus 5.5だけです。
Sonnet 5.5のthinkingは、作成したアカウントと、そのアカウントに紐づくアカウントでしか読めません。別アカウントが送ってきたブロックはAPIが落とし、リクエストは成功します。ベータヘッダー付きのClaude APIとGoogle Cloudでは、organization_binding_mismatchで報告されます。
同じClaude Platformの親組織、または同じGoogle Cloud組織のアカウントは、自動で紐づきます。Claude PlatformとAmazon Bedrockの間でフェイルオーバーするような構成は自動では紐づかず、サポート記事はアカウントチームへの連絡を案内しています。落とされた次の応答は遅くなり、トークンも増えることがあります。Claude Codeでセッションの途中にアカウントを切り替えた場合も同じです。
block_bindingはSonnet 5.5ではthinking: {"type": "adaptive"}でのみ使えます。between_toolsとの組み合わせは400になります。この制約がenabledとadaptiveの両方を受け付けるモデルとの違いで、1つのリクエストボディをモデル間で共用するときの注意点です。
Claude Code・Agent SDK利用者への影響
変更が要らないかどうかは、リクエストを誰が組み立てるかで決まります。サポート記事はClaude Code・Claude Cowork・Claude.aiを、APIドキュメントはClaude Managed AgentsとClaude Agent SDKを挙げています。影響を受けるのは、自前のハーネスやMCPサーバー統合でMessages APIを直接叩く実装です。
| 利用形態 | 影響度 | 分かれ目 |
|---|---|---|
| Claude Code・Claude Cowork・claude.ai・Managed Agents・Agent SDKをそのまま使う | 影響度ほぼ影響なし | 分かれ目各製品がthinkingの送り返しを処理する |
自前ハーネスでsystem・toolsを固定しmessagesに追記するだけ | 影響度ほぼ影響なし | 分かれ目prefixを変えない |
| クライアント側で要約・間引きをしている | 影響度条件次第 | 分かれ目古いターンの書き換えがthinkingを無効にする |
| プロキシ・ゲートウェイとして間に立つ | 影響度条件次第 | 分かれ目呼び出し元のrole: "system"をsystemへ移す実装は、全thinkingが無効になる |
| 8月31日以降のアカウントでFable 5.1・Opus 5.5・Sonnet 5.5を使う | 影響度条件次第 | 分かれ目既定の"error"で400を受ける |
thinkingの一貫性はプロンプトキャッシュにも効く
systemとtoolsを固定してmessagesを追記だけにする規律は、プロンプトキャッシュのヒット率を保つ規律でもあります。thinkingを無効にする編集は、そのままキャッシュを作り直させる編集です。
thinking blocks cannot be modifiedエラーも、履歴のthinkingブロックを書き換えると起きます。ただしこちらは最後のassistantターンのブロックを直接改変したときのエラーで、prefixチェックとは別の仕組みです。
まとめ
system・tools・過去のmessagesに追記しかしない実装は、何も直さずに済みます。前方を書き換えている場合の進め方は、利用者のアカウントで分かれます。新しいアカウントの利用者には既定の"error"で400が返るので、エラー文が名指しした編集を直します。ツールを他人のキーで動かすなら、"drop_block"で止血しつつprefix_binding_mismatchが出なくなるまで観測する選択肢があります。Extended thinkingとAdaptive thinkingの違いを先に押さえると、どのモデルでどの方式が動くかが読みやすくなります。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。