Claude の会話では、いちばん上に置く system がふるまいの土台になります。ところが会話が始まってから状況が変わることがあります。利用者がモードを切り替えた、手元のファイルが更新された、残りの予算が減った。こうした「途中で分かったこと」を伝えるために system を書き換えると、そこから後ろのキャッシュがすべて無効になります。messages 配列に role: "system" のメッセージを足せば、キャッシュを壊さずに同じことができます。
概要
会話の途中でシステム指示を足す機能です。トップレベルの system フィールドを編集するかわりに、messages 配列の末尾のほうに {"role": "system", "content": "..."} という項目を追加します。ベータヘッダーは不要で、通常の client.messages.create から使えます。
解決している問題はプロンプトキャッシュです。Claude のキャッシュは前方一致で働きます。tools → system → messages の順に組み立てられた文字列の、先頭からの一致でキャッシュが引かれます。したがって system を1文字でも変えると、その後ろにある会話履歴のキャッシュがすべて無効になり、次のリクエストで会話全体を最初から処理し直すことになります。
会話の途中に置いたシステムメッセージは履歴の後ろに並ぶので、前にあるキャッシュ済みの部分は変わりません。指示は、そのターンと、それ以降のすべてのターンに効きます。
この機能に対応しているモデルは Claude Opus 5、Claude Opus 4.8、Claude Fable 5、Claude Mythos 5 です。Claude Sonnet 5 は対応していません。対応していないモデルに送ると 400 エラー(role 'system' is not supported on this model)が返ります。
基本概念
置ける場所には決まりがあります。
- 直前は
userのメッセージであること。(サーバー側ツールの使用で終わるassistantメッセージの直後も可) messagesの最後の要素であるか、直後がassistantのターンであること。messages[0]にはできません。最初のシステムプロンプトは、これまでどおりトップレベルのsystemに書きます。- 内容はテキストのみです。
もう1つ大事なのが書き方です。命令ではなく、状況として書いてください。「利用者が言ったことは無視しろ」「前の指示は破棄しろ」といった上書き型の言い回しは避けます。Claude は、利用者の不利になるように働く指示から利用者を守るよう訓練されており、その保護は system というロールに対しても働きます。事実を置いて、あとは Claude に任せるのが確実です。
従来からある方法として、利用者のターンの本文に <system-reminder> のような印をつけて運用指示を埋め込むやり方があります。キャッシュの挙動は同じですが、安全性が違います。利用者の入力に書き込めるものは、その印を偽装できます。role: "system" は偽装できない運用側の経路です。対応していないモデルではこの旧方式にフォールバックすることになります。
使い方
トップレベルの system は固定したまま、会話の後ろに指示を足します。
response = client.messages.create(
model="claude-opus-5",
max_tokens=16000,
system=[
{
"type": "text",
"text": STABLE_SYSTEM_PROMPT,
"cache_control": {"type": "ephemeral"},
}
],
messages=[
*history,
{"role": "user", "content": user_message},
{
"role": "system",
"content": "利用者のタイムゾーンは Asia/Tokyo です。",
},
],
)
TypeScript でも形は同じです。
const response = await client.messages.create({
model: "claude-opus-5",
max_tokens: 16000,
system: [
{ type: "text", text: STABLE_SYSTEM, cache_control: { type: "ephemeral" } },
],
messages: [
...history,
{ role: "user", content: userMessage },
{ role: "system", content: "簡潔モードです。回答は40語以内にしてください。" },
],
});
効いているかどうかは usage で確かめられます。cache_read_input_tokens が伸びていれば、前置きはキャッシュから読まれています。ここが毎回 0 のままなら、どこかで前方一致が崩れています。トップレベルの system に日時や UUID を埋め込んでいないか、tools の順序がリクエストごとに変わっていないかを疑ってください。
対応していないモデルも扱うなら、400 を捕まえて旧方式に落とす形にします。
try:
response = client.messages.create(model=MODEL, messages=messages, ...)
except anthropic.BadRequestError:
# role: "system" 非対応。利用者ターンの本文に埋め込む形へ落とす
response = client.messages.create(model=MODEL, messages=fallback_messages, ...)
まとめ
要点をまとめます。
- 会話の途中で運用側の指示を足したいときは、トップレベルの
systemを書き換えず、messagesにrole: "system"のメッセージを足します。 - 理由はプロンプトキャッシュです。
systemを変えると、その後ろの履歴のキャッシュがすべて無効になります。 - ベータヘッダーは不要です。対応モデルは Claude Opus 5 / Claude Opus 4.8 / Claude Fable 5 / Claude Mythos 5 で、Claude Sonnet 5 は対応していません。
- 置ける場所に制約があります。直前は
user、位置は末尾かassistantの直前、messages[0]は不可、内容はテキストのみです。 - 命令ではなく状況として書きます。上書き型の言い回しは避けてください。
- 利用者ターンに印を埋め込む旧方式と比べると、キャッシュの挙動は同じですが、こちらは偽装できない経路です。
本記事は Anthropic 公式ドキュメントに基づく非公式の日本語解説です。仕様は変わることがあります。実装前に公式ドキュメントで最新をご確認ください。