会話途中のシステムメッセージとツール変更
会話の途中でシステム指示やツールの利用可否を変更しても、それ以前のキャッシュ済みプレフィックスを無効化しないようにします。
システム指示は通常、会話内のすべてのメッセージより前にあるトップレベルの system フィールドに置かれます。この位置は prompt caching(プロンプトキャッシング)にとって非常に都合が良いものです。システムプロンプトは安定したプレフィックスの一部となるため、後続のターンはキャッシュにヒットします。一方で、セッションの途中になって初めて必要だと気づいた指示にとっては不向きな位置です。トップレベルの system フィールドを編集するとプロンプトの冒頭そのものが変わり、それ以降のすべてについてキャッシュが無効化されるためです。
会話途中のシステムメッセージはこのギャップを埋めます。トップレベルの system フィールドを編集する代わりに、新しい指示が関連するようになった会話内の位置に {"role": "system"} メッセージを追加します。キャッシュ済みプレフィックスは変わらないため、次のリクエストでも引き続きキャッシュから読み取られ、新しい指示は通常のユーザーテキストとしてではなく、システム指示として適用されます。
会話途中のツール変更
tools 配列は、ハッシュ化されるリクエストプレフィックスの中でトップレベルの system フィールドよりもさらに前に位置するため、これを編集すると会話全体のプロンプトキャッシュが無効化されます。会話途中のツール変更は、会話途中のシステムメッセージのツール版にあたります。会話の存続期間中ツールリストを固定する代わりに、ターン間でモデルに提供するツールを変更します。最初に tools で完全なツールセットを宣言し、その後 tool_addition および tool_removal ブロックを使って、会話内の特定の位置以降でツールをモデルに提供したり取り下げたりします。tools 配列自体は決して変わらないため、キャッシュ済みプレフィックスはそのまま保たれます。
tool_addition と tool_removal は role: "system" メッセージの content 配列内のコンテンツブロックであり、同じメッセージ内で text ブロックと混在させることができます。このメッセージは他の会話途中のシステムメッセージと同じ配置ルールに従い(制限事項を参照)、変更は会話内のその位置以降に適用されます。各ブロックの tool フィールドはツールを定義するのではなく参照します。{"type": "tool_reference", "name": "..."} はリクエストの tools 配列で宣言されたツールを名前で指定し、MCP コネクタのツールは mcp_tool_reference(server_name と name)で個別に、または mcp_toolset_reference(server_name)でツールセット全体として参照できます。tools で宣言されていない名前を参照すると 400 エラーが返されます。
tools で宣言されたすべてのツールは、defer_loading: true を付けて宣言されていない限り、会話の開始時点からモデルに提供されます。defer_loading: true を付けると、tool_addition ブロックがそのツールを表に出すまで保留されたままになります。tool_addition は、以前の tool_removal で取り下げられたツールを再度提供することもできます。
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-opus-5",
max_tokens=1024,
betas=["mid-conversation-tool-changes-2026-07-01"],
# ツールセット全体は最初に宣言され、以降変更されないため、
# キャッシュされたプレフィックスはそのまま維持されます。
tools=[
{
"name": "get_weather",
"description": "Get the current weather for a location.",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"},
},
"required": ["location"],
},
},
],
messages=[
{
"role": "user",
"content": "Say OK.",
},
# この時点以降、get_weatherを取り下げます。このブロックは`tools`を編集する代わりに
# ツールを名前で参照するため、以前のターンはバイト単位で同一のまま保たれ、
# キャッシュは引き続きヒットします。
{
"role": "system",
"content": [
{
"type": "tool_removal",
"tool": {"type": "tool_reference", "name": "get_weather"},
},
],
},
],
)
for block in response.content:
if block.type == "text":
print(block.text)会話途中のツール変更はベータ版です。使用するには、リクエストにベータヘッダー mid-conversation-tool-changes-2026-07-01 を含めてください。
会話途中のシステムメッセージを使用するタイミング
プロンプトキャッシングは、リクエストプレフィックスを tools、次に system、次に messages の順にハッシュ化します。キャッシュヒットには、キャッシュブレークポイントまでのプレフィックスが最近のリクエストとバイト単位で完全に一致する必要があります。
この順序により、トップレベルの system フィールドはハッシュ化されるプレフィックスのほぼ先頭に位置します。一文を追加するだけであっても、これを変更すると異なるハッシュが生成され、リクエストはシステムプロンプトおよびそれ以降のすべてのキャッシュ済みメッセージについてキャッシュミスとなります。
会話途中のシステムメッセージを使えば、代わりにメッセージ履歴の末尾に指示を追加できます。新しい指示より前のすべては変更されないため、既存のキャッシュエントリは引き続き一致し、新しいメッセージだけが新規入力として処理されます。
これが重要になる状況をいくつか挙げます。
- セッション途中のポリシーやペルソナの変更。 長いエージェントセッションで、数十のキャッシュ済みターンの後に新しい制約(「今後はすべての SQL をパラメータ化クエリとして書くこと」)が必要になる場合。トップレベルの
systemフィールドに追加すると、履歴全体が再処理されます。 - 権威を持たせる必要があるターンごとのコンテキスト。 鮮度に関する注記、セッションの期限、ツールの利用可否の変更などをシステムレベルの重みで注入したいが、キャッシュ済みプレフィックスに置くには変更頻度が高すぎる場合。
- 積み重なってほしくないターンごとのリマインダー。 ハーネスがツール結果のバッチごとにモデルを促し(「独立した読み取りはまとめてリクエストすること」「ユーザーはしばらくあなたからの応答を受け取っていません」)、モデルには最新のコピーだけを見せたい場合。ターンスコープのシステムメッセージは 1 ターンだけレンダリングされ、その後は履歴から何も削除することなくコストがゼロになります。
- アプリケーションが観測した状態変化。 Claude がオペレーターレベルの事実として扱うべき事柄にアプリケーションが気づいた場合。たとえば、ディスク上のファイルが変更された、ユーザーが自動承認設定を切り替えた、利用可能なツールが変わった、残りのトークン予算がしきい値を下回った、などです。
- エージェントループを中断すべきでないユーザー入力。 Claude が前のリクエストのためにまだツールを実行している間に、ユーザーがフォローアップを入力した場合。次のツール結果の後にシステムメッセージとして中継すれば、Claude はそれを切り替え先の新しいリクエストとして扱うのではなく、すでに進行中の作業に新しい入力を組み込むことができます。ツール結果の後への配置を参照してください。
- 継続的な許可を与えるモード切り替え。 セッションレベルのモードでは、会話途中のシステムメッセージを使って、マルチエージェントワークフローの自動起動のような高コストな機能に対する継続的な同意を与えることができます。数ターンごとに短いリフレッシャーを送り、モードがオフになったときには終了通知を送ります。具体例については、オーケストレーションモードを構築するを参照してください。
これらすべてのケースで、指示を通常の user メッセージに入れることもでき、Claude はユーザーターンで届いた指示にも従います。違いは優先度です。user メッセージはエンドユーザーからのものとして扱われ、system メッセージはアプリケーションオペレーターであるあなたからのものとして扱われます。両者が矛盾する場合はシステム指示が優先されるため、エンドユーザーが別のことを求めた場合でも維持されるべきオペレーターレベルの事実や制約には system ロールを使用してください。会話途中のシステムメッセージは、トップレベルの system フィールドを編集することによるキャッシュミスのコストを払うことなく、そのオペレーターレベルの優先度を保ちます。
仕組み
messages 配列に "role": "system" を持つメッセージを追加します。content には、user や assistant ターンと同様に、プレーンな文字列またはコンテンツブロックを使用します。指示は会話内のその位置以降に適用されます。指示が矛盾する場合、後のシステムメッセージが前のものより優先され、会話途中のシステムメッセージはそれに続くターンについてトップレベルの system フィールドより優先されます。
会話全体に適用すべき指示については、引き続きトップレベルの system フィールドを設定できます。会話途中のシステムメッセージは、後になって初めて関連するようになる指示、またはキャッシュ済みプレフィックスを無効化せずに追加したい指示のために取っておいてください。
role: "system" メッセージは output_config.effort を持つこともでき、次の user ターン以降の effort(エフォート)レベルを変更できます。これは Claude API 上の Claude Fable 5.1、Claude Mythos 5.1、および Claude Opus 5 でベータ版として提供されており、mid-conversation-output-config-2026-07-01 ベータヘッダーが必要です。メッセージごとのエフォートを参照してください。
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
# 自動プロンプトキャッシング: 各リクエストはそれまでの会話をキャッシュし、
# 次のリクエストは変更されていないプレフィックスをキャッシュから読み取ります。
cache_control={"type": "ephemeral"},
system="You are a code review assistant. Be concise.",
messages=[
{
"role": "user",
"content": "Review process() in utils.py for performance issues.",
},
{
"role": "assistant",
"content": "The list comprehension is fine for small inputs. For large inputs, consider a generator to avoid materializing the full list.",
},
{
"role": "user",
"content": "Now review the calling code that invokes process().",
},
# レビュアーはセッションの途中で、すべての提案がチームの厳格な
# 型付けポリシーにも合格する必要があると気づきます。ここに指示を
# 追加すると以前のターンがバイト単位で同一に保たれるため、
# 前のリクエストでキャッシュされたプレフィックスは引き続きキャッシュから読み取られます。
{
"role": "system",
"content": "From now on, every suggestion must include explicit type annotations.",
},
],
)
for block in response.content:
if block.type == "text":
print(block.text)この例では、トップレベルの cache_control フィールドで自動キャッシングを有効にしています。プロンプトキャッシングはオプトインです。リクエストに cache_control フィールド(自動または明示的なブレークポイント)がない場合、何もキャッシュされず、すべてのリクエストが会話全体に対して通常の入力トークン料金を支払います。キャッシングが有効な場合、システムメッセージを追加してもすでにキャッシュ済みのターンは変更されないため、新しい指示を含むリクエストはそれらを再処理するのではなく、引き続きキャッシュから読み取ります。キャッシングには、会話がキャッシュ可能な最小プロンプト長を満たしている必要もあります。この例のように短いものはそれを下回るため、会話が長くなるまで cache_creation_input_tokens と cache_read_input_tokens は 0 のままです。
会話途中のシステムメッセージは、user ターン(またはサーバーツール結果で終わる assistant ターン)の直後に置く必要があり、messages の最後のエントリであるか、直後に assistant ターンが続く必要があります。tool_result ブロックを持つ user メッセージも該当します。エージェントループでは、ツール結果の直後、Claude の次のターンの前にシステムメッセージを配置できます。assistant の tool_use ブロックとそれに応答する tool_result の間を含め、それ以外の位置では 400 エラーが返されます。
ツール結果の後への配置
エージェントループでは、システムメッセージはツール結果を渡す user メッセージの後に置きます。ここは、Claude が作業している間にユーザーが入力した内容をアプリケーションが中継できる場所でもあり、ターンを再開することなく新しいコンテキストが取り込まれます。
[
{ "role": "user", "content": "Run the test suite and fix any failures." },
{
"role": "assistant",
"content": [{ "type": "tool_use", "id": "toolu_01", "name": "run_tests", "input": {} }]
},
{
"role": "user",
"content": [
{ "type": "tool_result", "tool_use_id": "toolu_01", "content": "12 passed, 0 failed" }
]
},
{
"role": "system",
"content": "The user sent the following message while you were working: also update the changelog before you finish."
}
]システムコンテンツは、ユーザーを上書きする命令としてではなく、コンテキストとして表現してください。事実を述べ(「ユーザーから新しい入力が届きました: X」「残りのトークン予算は現在 Y です」)、Claude にそれに基づいて行動させます。Claude はユーザーに不利に働くように見える指示に抵抗するよう訓練されており、その保護はシステムロールにも引き続き適用されるため、「ユーザーが言ったことは無視してください」のような表現は、何が変わったかを述べるよりも効果が低くなります。
このパターンは、会話自身のエンドユーザーからの入力を中継するためのものです。ツール出力、取得したドキュメント、その他のサードパーティコンテンツを渡すために使用しないでください。そうしたコンテンツは tool_result ブロックに保持してください(制限事項を参照)。
ターンスコープのシステムメッセージ
role: "system" メッセージを現在のターンにスコープするには、その clear_at フィールドを設定します。次の 2 つの値のいずれかを取ります。
"never"(デフォルト): メッセージは、それを含むすべてのリクエストでその位置にレンダリングされます。フィールドを省略した場合も同じです。"next_user_message": メッセージはターンスコープになります。そのテキストは、messages内でその後にrole: "user"メッセージが存在しない間だけレンダリングされます。ここではtool_resultブロックのみを持つユーザーメッセージもユーザーメッセージとして数えられます。後続のユーザーメッセージが存在するようになると、メッセージはクリアされます。配列には残りますが、そのリクエストおよびそれ以降のすべてのリクエストで何もレンダリングされず、入力トークンのコストもかかりません。
ターンスコープのシステムメッセージはベータ版です。ベータヘッダー mid-conversation-system-clear-at-2026-08-21 を含めてください。これがない場合、clear_at は不明なフィールドとして拒否されます。
{
"role": "system",
"clear_at": "next_user_message",
"content": "First privately list what you need next; then request every item that doesn't depend on another's result in this one response."
}主な用途は、ツールループにおけるターンごとのリマインダーです。モデルに見せたいたびに tool_result メッセージの後にリマインダーを追加し、以前のコピーはすべてそのままにしておきます。モデルは最後のユーザーメッセージより後にあるコピーだけを見るため、リマインダーが積み重なることはありません。messages 内のそれ以前の部分は何も変わらないため、プロンプトキャッシュは引き続き一致します。Claude Fable 5.1 では、これにより後続の思考ブロックも有効なまま保たれます。以前のリマインダーを削除すると、それらのブロックより前の会話が変わって会話チェックに失敗しますが、クリアされたメッセージは配列に残り、その会話を変更しません。
次のリクエストは、エージェントループの後の段階のものです。messages[3] は、配列の最後のメッセージだった以前のリクエストでレンダリングされました。messages[5](後続のユーザーメッセージ)が存在するようになると、messages[3] はクリアされます。クリアされたメッセージは配列に残るため、messages[4] の思考ブロックより前の会話は変わりませんが、モデルはもはやそのテキストを見ません。messages[6] と messages[7] はどちらも順番にレンダリングされます。
{
"model": "claude-fable-5-1",
"max_tokens": 16000,
"messages": [
{ "role": "user", "content": "Fix the failing test." },
{
"role": "assistant",
"content": [
{ "type": "thinking", "thinking": "", "signature": "..." },
{
"type": "tool_use",
"id": "toolu_01",
"name": "read_file",
"input": { "path": "test_auth.py" }
}
]
},
{
"role": "user",
"content": [{ "type": "tool_result", "tool_use_id": "toolu_01", "content": "..." }]
},
{
"role": "system",
"clear_at": "next_user_message",
"content": "Request independent reads in one turn."
},
{
"role": "assistant",
"content": [
{ "type": "thinking", "thinking": "", "signature": "..." },
{
"type": "tool_use",
"id": "toolu_02",
"name": "read_file",
"input": { "path": "auth.py" }
},
{
"type": "tool_use",
"id": "toolu_03",
"name": "read_file",
"input": { "path": "tokens.py" }
}
]
},
{
"role": "user",
"content": [
{ "type": "tool_result", "tool_use_id": "toolu_02", "content": "..." },
{
"type": "tool_result",
"tool_use_id": "toolu_03",
"content": "...",
"cache_control": { "type": "ephemeral" }
}
]
},
{
"role": "system",
"clear_at": "next_user_message",
"content": "Request independent reads in one turn."
},
{
"role": "system",
"clear_at": "next_user_message",
"content": "The shell exited with status 137."
}
]
}ターンスコープメッセージのルール:
- クリアされたメッセージはそのまま再送信する。 クリアされたメッセージも依然として会話履歴の一部です。現在の状態(最新のトークン数、タイムスタンプ)から再構築したり、冗長だとして削除したり、
clear_atの値を変更したりすることは、以前のメッセージの編集にあたります。プロンプトキャッシュはその位置からミスとなり、Claude Fable 5.1 ではそれ以降に生成されたすべての思考ブロックが会話チェックに失敗します。 - テキストのみ。
contentは 1 つ以上のtextブロック(または文字列)です。ターンスコープメッセージではtool_additionおよびtool_removalブロックは 400 エラーを返し、output_configも同様です。それらにはclear_atのない別のrole: "system"メッセージを使用してください。 - ブロックに
cache_controlを付けない。 クリアされたメッセージはキャッシュキーの一部になることがないため、そこに置いたブレークポイントは決して一致しません。例のように、代わりに直前のユーザーターンの最後のブロックにブレークポイントを置いてください。トップレベルの自動キャッシングフィールドは、ブレークポイントを選ぶ際にターンスコープメッセージをスキップします。メッセージをクリアするリクエストでは、再利用可能なキャッシュ済みプレフィックスはその前のユーザーターンで終わるため、そのメッセージと新しいユーザーメッセージの間にある 1 つのアシスタントターンだけが再処理されます。 - 配置ルールは引き続き適用される(クリアされているかどうかにかかわらず)。ターンスコープメッセージは、他の会話途中のシステムメッセージと同様に、
userターン(またはサーバーツール結果で終わるassistantターン)の後に置き、assistantターンの前に置くか配列の末尾にする必要があります。配列の末尾にあるものは常にレンダリングされます。直後に別のuserメッセージが続くものは、クリアされたメッセージではなく 400 エラーになります。ツールラウンドの結果はすべて 1 つのユーザーメッセージにまとめ、リマインダーはその後に置いてください。 - アシスタントターンではクリアされない。 メッセージの後のプリフィルされた、または一時停止されたアシスタントターン、あるいはサーバー側のツールループはユーザーメッセージを追加しないため、その継続ではメッセージは引き続きレンダリングされます。クライアント側のツールループを通じてリマインダーを表示し続けるには、各
tool_resultメッセージの後に再度追加してください。 - トークンカウントはレンダリングされる内容に従う。 クリアされたメッセージは
usage.input_tokensにもトークンカウントにも何も加算しません。 - インポートされた履歴。 一度に構築するトランスクリプト(few-shot の例、移行された会話)では、すでに後ろにアシスタントターンとユーザーメッセージがあるターンスコープメッセージは、最初のリクエストからクリアされ、決してレンダリングされません。これは、引き継ぐターンごとのリマインダーにとって正しい状態です。モデルがすべてのリクエストで見るべきメッセージについてのみ、
clear_atを未設定のままにしてください。
バリデーションエラーは次のとおりです。
messages.3.clear_at: Extra inputs are not permitted
messages.3.clear_at: clear_at is only permitted on role 'system' messages
messages.3.clear_at: Input should be 'next_user_message' or 'never'
messages.3: a turn-scoped system message supports text blocks only (clear_at: 'next_user_message')
messages.3: output_config is not permitted on a turn-scoped system message (clear_at: 'next_user_message')
messages.3.content.0: cache_control is not permitted on a turn-scoped system message (clear_at: 'next_user_message')1 つ目は、ベータヘッダーがない場合に返されるエラーです。Amazon Bedrock および Google Cloud では、ベータヘッダーで説明されているとおりにベータ値を渡してください。
SDK を通じて使用する場合は、messages 内の role: "system" エントリに clear_at を設定し、ベータヘッダーを送信します。次の例では、ユーザーターンの後にターンスコープのリマインダーを追加しています。次のリクエストで後続のユーザーメッセージが存在するようになると、リマインダーは配列に残りますがレンダリングされなくなります。
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-fable-5-1",
max_tokens=4096,
messages=[
{
"role": "user",
"content": "Draft a short status update on the database migration for the team channel.",
},
# ターンスコープのリマインダー: このターンで表示され、後続のユーザーメッセージが存在すると消去されます。
{
"role": "system",
"clear_at": "next_user_message",
"content": "The reader is on call: keep this reply under 50 words.",
},
],
betas=["mid-conversation-system-clear-at-2026-08-21"],
)
for block in response.content:
if block.type == "text":
print(block.text)プロンプトキャッシングとの組み合わせ
会話途中のシステムメッセージとプロンプトキャッシングは、一緒に使用するように設計されています。
- キャッシングを明示的に有効にする。 キャッシングは、リクエストに
cache_control(トップレベルの自動キャッシングフィールド、またはコンテンツブロック上の明示的なブレークポイント)が含まれている場合にのみ行われます。会話途中のシステムメッセージはそれ自体ではキャッシュエントリを作成せず、キャッシングが有効でなければ保持すべき節約もありません。 - 安定したプレフィックスを通常どおりキャッシュする。 リクエスト間で変わらない最後のブロックに
cache_controlを置きます。それがトップレベルのsystemフィールドの末尾であれ、ツール定義の末尾であれ、メッセージ履歴内の安定した位置であれ同様です。 - ブレークポイントの後にシステムメッセージを追加する。 キャッシュ済みプレフィックスの後に来るため、プレフィックスのハッシュは変わらず、キャッシュは引き続きヒットします。
- 会話途中のシステムメッセージ自体もキャッシュ可能。 会話に入った時点で、安定した履歴の一部になります。次のターンでは、キャッシュブレークポイントをその先に移動でき(または自動キャッシングに任せることもでき)、システムメッセージは他のターンと同様にキャッシュから読み取られます。
すでに送信した会話途中のシステムメッセージを編集または削除することは避けてください。以前のメッセージに対する他の変更と同様に、その位置以降のキャッシュが無効化されます。Claude Fable 5.1 では、それ以降のすべてのアシスタントターンの思考ブロックも無効化されます。1 ターンだけに適用すべきガイダンスには、ターンスコープのシステムメッセージを使用し、そのまま残しておいてください。指示を発展させる必要がある場合は、古いものを書き換えるのではなく、新しいシステムメッセージを追加してください。連続するシステムメッセージは受け入れられ、単一のシステムセクションとして扱われ、全体として同じ配置ルールに従います。
制限事項
- 最初のメッセージには使えない。 コンテンツを持つ
systemメッセージはmessagesの最初のエントリにはできません。最初から適用される指示にはトップレベルのsystemフィールドを使用してください。 - 配置に制約がある。 コンテンツ(
text、tool_addition、またはtool_removalブロック)を持つsystemメッセージは、userターン(tool_resultブロックを持つuserターンを含む)またはサーバーツール結果で終わるassistantターンの直後に置く必要があり、assistantターンの前に置くか配列の末尾にする必要があります。tool_useブロックとそのtool_resultの間に置くことはできません。それ以外の場所に置くと 400 エラーが返されます。output_config.effortのみを設定する空のcontentを持つメッセージは、その位置に何もレンダリングせず、先頭やassistantターンとuserターンの間を含め、messages内のどこにでも置くことができます。連続するsystemメッセージはまとめて判定されるため、エフォートのみのメッセージの隣にテキストを持つメッセージを追加すると、グループ全体がコンテンツのルールに従うことになります。 - ターンスコープメッセージはテキストのみで、そのまま再送信する。
clear_at: "next_user_message"メッセージはtool_addition、tool_removal、output_config、cache_controlを持たず、一度クリアされたら、以降のリクエストでバイト単位でそのままmessagesに残す必要があります。ターンスコープのシステムメッセージを参照してください。 - 信頼できないコンテンツを置く場所ではない。 Claude はシステムコンテンツをオペレーターの指示として扱い、それに従います。生のツール出力、取得したドキュメント、Web コンテンツなど、会話の外部からのテキストをシステムメッセージに直接置かないでください。そうすると、そのテキストにオペレーターレベルの権限を与えることになります。そうしたデータは
tool_resultブロックに保持し、引き続きジェイルブレイクとプロンプトインジェクションの軽減に従ってください。
関連情報
キャッシングの仕組み、ブレークポイントの配置場所、キャッシュ使用量フィールドの読み方。
期待したキャッシュヒットが起こらなかったときに、2 つのリクエストがどこで分岐したかを正確に突き止めます。
メッセージ構造、マルチターン会話、および system フィールド。
効果的なプロンプトとシステム指示の書き方。
messages 配列内で tool_use および tool_result ブロックがどのように構造化されるか。
Was this page helpful?