Claude 4.6でprefillが廃止された理由と5つの移行先
Claude 4.6以降とClaude Mythos Previewは最後のassistantターンへのprefillを400エラーで拒否する。5つの用途別に公式が示す移行先を整理する。
400エラーが出たとき、まず何を見るか
症状は1つです。最後のメッセージがassistantロールのリクエストをClaude 4.6以降のモデルに送ると、400が返ります。Claude Mythos Previewも同じです。種別はinvalid_request_errorです。エラーページに載っている本文は次のとおりです。
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "This model does not support assistant message prefill. The conversation must end with a user message."
}
}メッセージは「会話はuserメッセージで終わる必要がある」と言っているので、直し方の入口は決まります。messages配列の末尾を確認し、assistantロールなら、その中身が何をしていたかで移行先が変わります。会話の途中にあるassistantメッセージ(few-shot例など)は対象外です。旧世代のClaude 4.5以前は、引き続きprefillを受け付けます。
400が出たときの切り分け
- 1
末尾のメッセージを見る
messagesの最後がassistantロールか確認します。userで終わっているなら、原因はprefillではありません。 - 2
何のためのprefillだったかを分類する
形式の固定、前置きの排除、拒否の回避、応答の再開、文脈の再注入のどれかに当てはめます。
- 3
用途別の移行先に置き換える
userメッセージ、system prompt、
output_config.formatのいずれかへ内容を移し、末尾のassistantメッセージを削ります。 - 4
併用している機能の制約を確認する
thinkingや
tool_choiceを併用している場合は、後半の「prefillを外しても残る制約」を確認します。
公式が挙げる理由は「prefillがほぼ要らなくなった」だけ
prefillは、Claudeの出力を狙った形へ寄せる力技でした。「途中まで自分で書いてしまい、続きをClaudeに書かせる」ことで、JSON形式や前置きの省略を実現していたわけです。
同じエラーページは、代わりの手段としてstructured outputs、system promptの指示、output_config.formatの3つを挙げています。廃止の理由として公式が書いているのは、モデルの知能と指示追従力が上がり、ほとんどの用途でprefillが要らなくなったという点です。thinkingについては、別のページに「thinkingが有効な間はアシスタント応答をprefillできない」という制約が書かれています。
また、thinkingを使う会話では、直前のassistantターンのthinkingブロックを編集・並べ替え・削除せずに送り返す必要があります。改変するとやはり400エラーになります。
自分のprefillはどの用途か:5つの移行パターン
公式ドキュメントは、prefillの典型的な用途を5つに分けて移行先を示しています。
パターン1: JSON/YAML形式を強制していた場合
出力フォーマットをprefillで固定していたケースは、Structured Outputsへの置き換えが第一候補です。JSON Schemaを渡してClaudeの応答をそのスキーマに拘束する機能で、prefillより堅牢に構造を強制できます。リクエストに載せるのはoutput_config.formatで、typeはjson_schemaです。SDKのヘルパーはこの指定を代わりに組み立てます。分類タスクでは、有効なラベルを入れたenumフィールド付きのツールか、structured outputsを使うよう案内されています。ただし前者でツールの強制呼び出しを使う場合は、後述の制約に当たります。スキーマが複雑になりがちなら、SDKがJSON Schemaをどう自動変換するかも合わせて確認すると制限を避けやすくなります。
公式は、まず出力構造に従うよう頼んでみることを勧めています。新しいモデルは、とくにリトライと組み合わせれば、頼むだけで複雑なスキーマにも安定して合わせられるためです。
これまでのprefillでは、リクエストの最後に{"role": "assistant", "content": "{"のような部分JSONを積んで応答を強制していました。Structured Outputsではこの行を削り、代わりにリクエストへoutput_config.format(JSON Schema)を渡す形に変えます。
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-5-5",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "この製品レビューを分類してください"}
],
"output_config": {
"format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"label": {"type": "string", "enum": ["positive", "negative", "neutral"]}
},
"required": ["label"],
"additionalProperties": false
}
}
}
}'messages配列の末尾がassistantロールで終わっていない点が、prefill運用との構造上の違いです。
生のリクエストでは、スキーマのrequiredと"additionalProperties": falseを自分で書きます。SDKのヘルパーは、このfalseを全オブジェクトに自動で足してくれます。curlで直接送るときは書き忘れると400になります。
Structured Outputsへ移したあとは、失敗の出方がprefillと変わります。安全上の理由でClaudeが拒否すると、stop_reasonがrefusalになり、ステータスは200のままです。拒否メッセージが優先されるので、出力はスキーマに合いません。max_tokensに達した場合も出力が途中で切れ、スキーマに合わないことがあります。この場合はmax_tokensを上げて再試行します。
また、同じスキーマを初めて使うときだけ、文法のコンパイルで遅延が出ます。コンパイル済みの文法は、最後に使ってから24時間キャッシュされます。
パターン2: 前置き(preamble)を排除したかった場合
「Here is the requested summary:」のような前置きをスキップさせるためのprefillは、system prompt側の明示指示に移行します。「前置きなしで直接答えてください。『こちらが〜』『〜に基づいて』のようなフレーズで始めないでください」と明記する方法です。
XMLタグの中に出力させる、structured outputsを使う、ツール呼び出しの形で結果を受け取る、といった代替も有効です。たまに前置きが紛れ込む場合は、後処理で取り除くよう案内されています。
system prompt側の指示は次のような形になります。
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-5-5",
"max_tokens": 1024,
"system": "前置きなしで直接答えてください。「こちらが〜」「〜に基づいて」のようなフレーズで始めないでください。",
"messages": [
{"role": "user", "content": "この記事を要約してください"}
]
}'assistantロールのメッセージを一切含まない点が、prefillベースの実装との最大の違いです。prefillを外すだけでは前置きをスキップさせる手段が無くなるので、system側の指示を足す必要があります。
パターン3: 不要な拒否(refusal)を回避したかった場合
不要な拒否を避けるためのprefillは、公式が「Claudeは適切な拒否がずっと上手くなった」とし、userメッセージ内の明確な指示だけで足りるとしている用途です。具体的な書き換え例は示されていません。
パターン4: 中断した応答の続き(continuation)を書かせていた場合
途中で切れた生成の続きをprefillで再開させていた運用は、続きの依頼をuserメッセージに移す形へ変更します。「直前の応答は[前回の応答]で中断されました。続きを書いてください」という形で、中断箇所のテキストをuserメッセージに含めて渡す方法です。
エラーハンドリングの一環で、UX上のペナルティが無いのであれば、prefillで繋ぐより素直にリクエストをリトライする方が簡潔になるケースもあります。
パターン5: 文脈を定期的に再注入(context hydration)していた場合
長い会話でコンテキストが薄れないよう、定期的にprefillで文脈を再注入していた運用は、その内容をuserターンに移します。文脈の補給が複雑なエージェントの一部なら、代替は2つあります。1つはツール経由の補給で、ターン数などの目安に応じてツールの利用を促します。もう1つは、context compactionのタイミングで文脈を再構成する方法です。ツール経由で構造化データを渡す実装は、Agent SDKの構造化出力入門が参考になります。
prefillを外しても残る制約:thinking・強制ツール呼び出し・引用
prefillを消しただけでは、別の400が出る組み合わせがあります。いずれも公式ドキュメントに書かれている制約です。
prefillの代替手段ごとの注意点
Structured Outputs
output_config.formatは、メッセージのprefillと併用できません(Incompatible with JSON outputs)。引用(citations)を有効にした状態で使うと400になります。
ツールの強制呼び出し
tool_choiceをanyまたはtoolにする強制呼び出しは、Claude Opus 5.5・Sonnet 5.5・Fable 5.1・Mythos 5.1では毎回400で拒否されます。これらのモデルではautoとstrict tool useかstructured outputsを組み合わせます。
つまり「分類はenum付きツールで」という公式の案内も、モデルによっては強制呼び出しを外す必要があります。
この400は、メッセージ本文で見分けられます。Opus 5.5ではtool_choice: type "tool" and "any" are not supported for this model.が返ります。トークンカウントのエンドポイントでも同じく400になるので、count_tokensだけ失敗する場合はtool_choiceを疑う手があります。
強制呼び出しは、実はAPI側のprefillでもあります。anyやtoolを指定すると、APIがassistantメッセージをprefillしてツールを使わせます。そのため、明示的に頼んでも、tool_useブロックの前に自然言語の説明は出ません。代替として公式が示すのは、tool_choiceをauto(既定)のままにして、userメッセージで「天気はget_weatherツールを使って調べて」のように指示する形です。
移行先が使えない面もあります。Amazon Bedrock上のSonnet 5.5では、strict tool useを含むstructured outputsが提供されていません。その場合は、strictなしでautoを送り、いつツールを呼ぶかをプロンプトに書き、ツール入力を自分のコードで検証します。CMEK(顧客管理の暗号鍵)の組織では、Fableモデルでstructured outputsとstrictが使えず、指示文だけで制御します。強制呼び出しがthinkingとどう噛み合うかは、thinkingの種類でも変わります。手動の拡張thinkingでは強制呼び出しがエラーになり、適応型thinkingでは上記4モデルを除いて使えます。
Claude 4.5以前を併用する場合の分岐
Claude 4.5以前はこの変更の対象外で、従来どおりprefillを送れます。複数モデルを併用する実装では、モデルによってprefillの可否が分かれるため、モデルIDで分岐する必要があります。4.6系以降へ切り替える予定があるなら、先に上の5パターンでprefillに依存しない形へ寄せておくと、切り替え時の手戻りが減ります。
Opus系は事情が少し違います。prefillは4.6以降のOpus(5.5を含む)で400になるため、対応が要るのはOpus 4.5以前から移る場合だけです。4.6から来るなら、prefillの対応はすでに済んでいます。
書き換えを機械的に進める手段もあります。Claude Codeで/claude-api migrateを実行すると、同梱のClaude APIスキルが呼ばれます。モデルIDの置き換えに加えて、必要に応じてprefillの置き換えも行い、手で確認する項目のチェックリストを出します。
400エラーのハンドリングを入れる場合は、invalid_request_errorと上記のメッセージで判別できます。エラーページが載せているのは上のJSONの形だけで、400以外のステータスで返るという記載はありません。
まとめ
判断の順序は3段です。まず末尾のassistantメッセージが何を担っていたかを見て、次に移行先を決め、最後に自分の構成が移行先の制約に当たらないかを確かめます。移行先を決めただけで終えると、thinkingやtool_choice、citations、Bedrock、CMEKのどれかで別の400に当たります。