会話途中のツール変更
Claude がツール使用を持って以来ずっと、tools配列は会話の寿命の間凍結されてきた — 正確には、キャッシュエントリの寿命の間凍結されてきた。それを変えれば、プロンプトキャッシュは溶ける。
これは、プロンプトキャッシングがリクエストプレフィックスを固定された順序でハッシュ化するためだ:tools → system → messages。ツール一覧は送信する他のあらゆるものより先に置かれる。ツールを一つ追加したり、説明を一つ書き換えたりしただけで、そのポイント以降のキャッシュされたすべてのターンがミスする。数十万トークンのキャッシュ入力を持つ長時間のエージェンティックセッションでは、その「小さな編集」が実際のお金と数秒の新しいコールドスタートを要求する。
会話途中のツール変更は、会話途中のシステムメッセージのツール配列版だ。ツールの全宇宙は依然としてtoolsで一度、事前に宣言する。しかし今後は、role: "system"メッセージ内にtool_additionとtool_removalブロックを追加することで、任意のターンでモデルに実際にどのサブセットが提供されるかを決められる。tools配列そのものは変わらないので、キャッシュされたプレフィックスはバイト単位で同一のままだ。
- なぜ tools[] を編集すると、ツールセクションだけでなくキャッシュ全体が吹き飛んでいたのか
- defer_loading、tool_addition、tool_removal がどのように宣言と可用性を分割するか
- これらのブロックを運ぶシステムメッセージの正確な配置ルール(会話途中のシステムメッセージからルールを継承)
- MCP ツールを個別(mcp_tool_reference)またはサーバー全体(mcp_toolset_reference)で参照する方法
- このベータが代替手段(独自の tool_choice を持つサブエージェント、ターンごとの再送、外側のルータ)に勝つ場面
★ Insight ─────────────────────────────────────
2つの点がこの機能を静かに重要にしている。第一に、Opus 5 ではキャッシュ可能な最小プロンプトが1,024から512トークンに下がったので、小規模なセッションでもキャッシュの恩恵を受ける — これは小規模なセッションでも今後、無効化したときの痛みを受けることを意味する。第二に、toolsがsystemより先にハッシュされることは、今日、mid-conversation-system-messagesを使って新しい指示を差し込むとき、新しいツールを導入する必要が生じた日にはやはり全額を支払うことを意味する。このベータが最後の穴を塞ぐ。
─────────────────────────────────────────────────
キャッシュハッシュ問題を一枚の絵で
リクエストのキャッシュキーはプレフィックスのローリングハッシュで、この順序だ:
[ tools ][ system ][ messages…, up to the breakpoint ]
キャッシュヒットには、ブレークポイント前のすべてのバイトが最近のリクエストとマッチする必要がある。だから:
| 何を変えるか | 何がキャッシュヒットするか | 何を再支払いするか |
|---|---|---|
末尾に新しいuserターンを追加 | そのターンまでのプレフィックス全体 | 新しいターンだけ |
新しい会話途中systemメッセージを追加 | その前のすべて | 新しいシステムメッセージ |
トップレベルsystemフィールドを編集 | toolsだけ | system + すべてのメッセージ |
toolsに新しいツールを1つ追加 | 何もない | system + すべてのメッセージ |
その最後の行こそが、会話途中のツール変更が書き換える部分だ。
3つの可動部品
1. defer_loading: true — tools内のツール宣言に付けると、そのツールを宣言されたまま保留する。キャッシュプレフィックスにはハッシュされる(それが全ポイント)が、あなたが浮上させるまで Claude はそれを呼び出し可能とは見ない。
2. tool_addition — role: "system"メッセージ内のコンテンツブロック。そのターン以降、defer_loadingツールを浮上させる。以前のtool_removalが撤回したツールを再提供することもできる。
3. tool_removal — 鏡像。そのターン以降、現在提供中のツールを撤回する。以降のすべてのターンはキャッシュにヒットするが、そのツールは Claude の選択肢集合にもう存在しない。
tool_additionとtool_removalの両方は、toolフィールドを介してツールを参照する。3つの参照形状が合法だ:
{"type": "tool_reference", "name": "get_forecast"}—toolsで宣言された通常のツール。{"type": "mcp_tool_reference", "server_name": "linear", "name": "create_issue"}— 単一のMCPコネクタツール。{"type": "mcp_toolset_reference", "server_name": "linear"}— MCPサーバーが公開するすべてのツールを一つのブロックで。
toolsで宣言されていない名前を参照すると、400が返る。
最小動作例
このベータはmid-conversation-tool-changes-2026-07-01ヘッダと、Fable 5、Mythos 5、Opus 4.8、Opus 5 のいずれかを要求する。下記:「読み取り」ツールと「書き込み」ツールを事前に宣言し、delete_fileを保留し、ユーザーが破壊的意図を確認した後にのみ浮上させる。
import anthropic
client = anthropic.Anthropic()
TOOLS = [
{
"name": "read_file",
"description": "Read a file from disk.",
"input_schema": {
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"],
},
},
{
# Declared but withheld. Hashed into the cache prefix so we can
# surface it later without invalidating anything.
"name": "delete_file",
"description": "Permanently delete a file from disk.",
"defer_loading": True,
"input_schema": {
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"],
},
},
]
messages = [
{"role": "user", "content": "Read notes.md and summarize it."},
# ...several tool_use / tool_result turns...
{"role": "user", "content": "OK, I confirm: delete notes.md."},
# Surface delete_file from this point onward. The cached prefix
# (tools + all earlier turns) still matches byte-for-byte.
{
"role": "system",
"content": [
{
"type": "tool_addition",
"tool": {"type": "tool_reference", "name": "delete_file"},
}
],
},
]
response = client.beta.messages.create(
model="claude-opus-5",
max_tokens=1024,
betas=["mid-conversation-tool-changes-2026-07-01"],
cache_control={"type": "ephemeral"},
tools=TOOLS,
messages=messages,
)
次のリクエストは:
tools(変わらず)をハッシュ → キャッシュヒット。- 以前のすべてのターン(変わらず)をハッシュ → キャッシュヒット。
- 新しいユーザーターン + システムロールの tool_addition ブロックだけが新規入力。
これを「旧来のやり方」と対比せよ — その瞬間だけtoolsにdelete_fileを落とす。その単一の変異はプレフィックス全体を無効化したはずだ。
エージェンティックループでの採用
- 後で浮上させる予定のツールも含めよ。ただし defer_loading: true をつけること。要点は、キャッシュプレフィックスのツールセクションを今凍結することだ。
- 会話途中のツール変更は、プレフィックスが実際にキャッシュされている場合にのみお金を節約する。トップレベルで cache_control: {type: ephemeral} を使うか、最後の安定ブロックで明示的なブレークポイントを使え。ブレークポイントがなければ何もキャッシュされず、保存すべきものがない。
- アプリケーションが新しい機能を利用可能にすべきと決めたとき — ログイン後、計画が承認された後、モード切り替え後 — role: system メッセージを tool_addition ブロックとともに追加する。ユーザーターンまたは tool_result ターンの直後に配置し、tool_use と対応する tool_result の間には配置しないこと。
- 同じ理由:落とすとプレフィックスが変異する。tool_removal は追加のみのシステムロールブロックだ。よくあるトリガー:読み取り専用モードへの移行、タスクフェーズの終了、レート制限が特定の統合をロックした後。
- 会話途中のシステムメッセージが履歴に入れば、それ自体がキャッシュ可能だ。次のリクエストで、自動キャッシングを使うか、明示的なブレークポイントをそれ以降に動かして、追加・削除された機能がそこからキャッシュに焼き付けられるようにせよ。
- それはプレフィックス変異であり、それ以降すべてを無効化する。心変わりする必要があれば、新しいシステムメッセージを追加せよ(直前に追加したものを撤回するには tool_removal、再提供するには新しい tool_addition)。
リファレンスパターン
ユーザーが確認するまで破壊的ツールを保留する
system:
<tool_addition tool={type: "tool_reference", name: "delete_project"}>
Only append this after a user turn where the user explicitly confirmed destruction.
Never place before a "clarify what you want to delete?" turn.計画 → 実行 → レビューループのためにツールセットをフェーズ分けする
Phase 1 (plan): tools[] visible = { read_repo, search_web } — everything else defer_loading: true.
Phase 2 (execute): append system-role tool_addition for { edit_file, run_tests }.
Phase 3 (review): append system-role tool_removal for { edit_file }, tool_addition for { post_review_comment }.
The tools[] array never changes; only the offered set does. Cache is preserved across all three phases.レート制限後に MCP サーバーを引退させる
On 429 from the Linear MCP connector, append:
system:
<tool_removal tool={type: "mcp_toolset_reference", server_name: "linear"}>
One block retires every tool that server exposed. Re-offer with a matching tool_addition once your backoff window expires.サンドボックス:サブエージェントに厳密なサブセットを与える
When you dispatch a subagent, do NOT create a new conversation with a smaller tools[]. Instead reuse the same tools[] (cache hit!) and open the subagent turn with a system-role tool_removal for every capability that subagent should not touch. The parent conversation can restore them on return with a matching tool_addition.
配置ルール(重要 — 非常に)
tool_addition / tool_removalブロックを運ぶrole: "system"メッセージは通常の会話途中システムメッセージであり、その配置ルールを継承する:
- 決して最初ではない。
systemメッセージはmessagesの最初のエントリになれない。初期ツールセットはトップレベルのsystemフィールドとtoolsで宣言せよ。 - ユーザーターンまたはサーバーツール・アシスタントターンの直後でなければならない。
tool_resultブロックを運ぶuserメッセージも該当する — これがまさにツールが返したものに反応するスロットだ。 - アシスタントターンの直前か、最後のエントリでなければならない。
tool_useと対応するtool_resultの間には決して置かない。 それは400だ。
連続するsystemメッセージは合法で、一つのセクションとして扱われる。同じcontent配列内でtool_addition、tool_removal、通常のtextブロックを混在させられる。
これがプロンプトキャッシングとどう相互作用するか
- キャッシングを明示的に有効化せよ。どこかに
cache_controlフィールドが必要だ。トップレベルでの自動キャッシングが最もシンプルだ。 - いつものように安定プレフィックスをキャッシュせよ — リクエスト間で変わらない最後のブロックまで。
- 追加されたシステムメッセージはキャッシュされたプレフィックスの後に来るため、プレフィックスハッシュを変えない。
- システムメッセージが会話に入れば、それは安定履歴となり、次のターンでキャッシュ可能だ。
tools内のすべてのツールは、defer_loading: trueのツールも含めて、最小キャッシュ可能プロンプト長にカウントされる — Opus 5 では512トークン、ほとんどの他のモデルでは1,024トークン。
★ Insight ─────────────────────────────────────
この設計はエージェント作者を特定の規律に押しやる:セッションの野望を事前に宣言し、ランタイム信号を使ってアクセスを調節せよ。 これは古典的な関数呼び出しAPIの形よりも、OSプロセスケイパビリティのモデル方式(あなたが持つケイパビリティ vs 今行使できるケイパビリティ)に近い。これを軸にエージェントを設計すると、「このエージェントはどんなツールを持つか?」は2つの答えを持つ質問になる — 宣言された宇宙と提供されるサブセット — そしてキャッシュは温かいままだ。
─────────────────────────────────────────────────
これがしないこと
toolsに全く入っていなかったツールを導入することはできない。 モデルにいずれ提供される可能性のあるすべてのツールは、最初のリクエストからtoolsに存在しなければならない。それは制限ではなく機能だ — ハッシュを安定に保つのはまさにそれだ。- ツールの
input_schemaやdescriptionを会話途中で変更することはできない。 どちらもtoolsの変異でありキャッシュミスを引き起こす。ツールのスキーマが進化する必要があれば、異なる名前で2つのツールを宣言せよ。 - 今日、Claude Sonnet 5には適用されない。 Sonnet 5 は会話途中のシステムメッセージを一切サポートしないので、このベータはその上に乗れない。動的ツールセットが必要な Sonnet 層のターンは、外側のルータ経由でルーティングせよ。
他のプロバイダは同じ問題をどう扱うか
| プロバイダ | 全プレフィックス再処理なしの動的ツールセット? |
|---|---|
| Anthropic Claude Opus/Fable/Mythos | このベータ経由で可能。 |
| Anthropic Claude Sonnet 5 | 不可 — toolsを再送(キャッシュミス)するか、外側スーパーバイザ経由でルーティング。 |
| OpenAI GPT-5/6 | 実質的に不可。Responses/Chat Completions API でtools配列を変更するのはプレフィックス変更だ。ツール一覧で切れる自動キャッシングのプレフィックスマッチに依存する。一般的な回避策は、子が範囲付きの tools 配列を持つ親/子エージェント。 |
| Google Gemini 3 | OpenAI と類似。tools config はリクエストの一部で、実用的パターンはフェーズごとの Function Declaration セットで、再宣言のコストを受け入れる。 |
| MCP サーバー一般 | 一部のホスト(Claude Code、Cursor)はホスト内で「オンデマンドツールロード」を実装するが、それはトランスポートレベルだ:このベータがプロバイダ側に着地するまで、基盤モデルは再送されたツール一覧を受け取り続ける。 |
クロスモデルハーネスを構築しているなら、「動的ツール」動作を、どこでも仮定するのではなく、モデルごとに特徴検出するケイパビリティとして構造化せよ。
よくある失敗モード
- ベータヘッダを忘れた。 リクエストは受け入れられ、
tool_addition/tool_removalブロックはシステムメッセージ内の未知のコンテンツとして扱われ、挙動は未定義 — しばしばブロックは静かに無視され、Claude は新しいツールを見ることがない。 tool_useとtool_resultの間にシステムメッセージを置いた。400 invalid_request_error。tool_result を運ぶ次のユーザーターンの後に動かせ。toolsで宣言されていないツールを参照した。400。defer_loading: trueで宣言してから再試行せよ。- 「明確化のためだけに」ツール説明を編集した。 会話全体のキャッシュミス。ツールをセッション途中で進化させるには、新しい名前で v2 ツールを追加し、v1 に
tool_removal、v2 にtool_additionを使え。 - Sonnet 5 でなぜ動かないか困っている。 Sonnet 5 では動かない。別のティアか外側ルータを使え。
Check yourself
0/5出典と参考文献
- Mid-conversation system messages and tool changes — Claude Platform Docs(決定的リファレンス、8つのSDKで完全なコードサンプル付き)
- What's new in Claude Opus 5(ベータの発表、および512トークンのキャッシュ最小値)
- Prompt caching — Claude Platform Docs(
tools → system → messagesハッシュの構築方法とブレークポイントの配置場所) - Claude Platform release notes — July 24, 2026(
mid-conversation-tool-changes-2026-07-01ベータヘッダの初回リリース) - MCP connector docs(
mcp_tool_referenceとmcp_toolset_referenceブロック形状) - Cache diagnostics — Claude Platform Docs(期待したキャッシュヒットが起きなかったとき、二つのリクエストがどこで分岐したかを正確に見つける)