入力サイズ不明でも最大出力を引き出す実装パターン — model_context_window_exceeded
入力トークン数を事前計算せずに、コンテキストウィンドウが許す最大量までClaudeに出力させる実装パターンをmodel_context_window_exceededで組む方法を解説します。
入力が毎回違うとき、max_tokensはどう決めるか
要約や変換のバッチで、入力の長さが呼び出しごとに数千トークンから数十万トークンまでばらつくことがあります。「残りの枠を計算してmax_tokensを決める」のは手間なので、max_tokensを大きめの固定値にして、足りなくなったらウィンドウ側に止めてもらうのが本記事のパターンです。止まった理由はstop_reasonで区別できます。
Messages APIの成功レスポンスには必ずstop_reasonが入ります。エラーとは別物で、「なぜ生成が終わったか」を示す値です。
| stop_reason | 発生条件 | 次の一手 |
|---|---|---|
end_turn | 発生条件自然に応答が完結した | 次の一手そのまま使う |
max_tokens | 発生条件指定したmax_tokensに到達した | 次の一手値を上げるか、続きを生成させる |
model_context_window_exceeded | 発生条件モデルのコンテキストウィンドウを使い切った | 次の一手打ち切りとして扱う |
tool_use | 発生条件Claudeがtoolを呼び出した | 次の一手toolを実行して結果を返す |
max_tokensは呼び出し側が決めた上限、model_context_window_exceededはモデル自体の容量に達した合図です。どちらに先に達したかがstop_reasonに出ます。
実装パターン
次の関数は、入力サイズを計算せずに取れるだけ出力を得ます。stop_reasonを3通りに分けて、どの上限で止まったかをログに残します。
この値はSDKのbeta名前空間にだけ型定義されているので、呼び出しはclient.beta.messages経由で、型もBeta接頭辞のものを使います。Sonnet 4.5以降のモデルでは、ヘッダーなしでこの値が返ります。
def get_max_possible_tokens(client, prompt):
"""
Get as many tokens as possible within the model's context window
without needing to calculate input token count
"""
response = client.beta.messages.create(
model="claude-opus-5-5",
messages=[{"role": "user", "content": prompt}],
max_tokens=20000,
)
if response.stop_reason == "model_context_window_exceeded":
# 入力サイズに応じた最大限のトークンを取得できた
print(f"Generated {response.usage.output_tokens} tokens (context limit reached)")
elif response.stop_reason == "max_tokens":
# 要求どおりのトークン数を取得できた
print(f"Generated {response.usage.output_tokens} tokens (max_tokens reached)")
else:
# 自然な完了
print(f"Generated {response.usage.output_tokens} tokens (natural completion)")
return next((block.text for block in response.content if block.type == "text"), "")async function getMaxPossibleTokens(client: Anthropic, prompt: string): Promise<string> {
const response = await client.beta.messages.create({
model: "claude-opus-5-5",
max_tokens: 20000,
messages: [{ role: "user", content: prompt }]
});
const tokens = response.usage.output_tokens;
if (response.stop_reason === "model_context_window_exceeded") {
console.log(`Generated ${tokens} tokens (context limit reached)`);
} else if (response.stop_reason === "max_tokens") {
console.log(`Generated ${tokens} tokens (max_tokens reached)`);
} else {
console.log(`Generated ${tokens} tokens (natural completion)`);
}
const textBlock = response.content.find(
(block): block is Anthropic.Beta.BetaTextBlock => block.type === "text"
);
return textBlock?.text ?? "";
}呼び出す側は、入力の長さを気にせずmax_tokensに同じ値を渡すだけです。ロジックを1本にまとめられる点が、事前計算方式に対する最大の利点になります。
入力サイズ別に何が起きるか
ウィンドウが1Mトークンのモデル(Claude Opus 5.5)にmax_tokens: 20000を渡した場合の分岐です。
入力サイズ別の止まり方
入力 10,000
max_tokens
残り枠は十分。20,000に先に届いて停止
入力 990,000
context_window_exceeded
残り約10,000で停止。max_tokensには届かない
入力も出力も短い
end_turn
どちらの上限にも触れず自然に完了
ウィンドウが200Kのモデルでも考え方は同じです。入力が190,000トークンなら、出力は約10,000トークンでウィンドウに当たります。Claude Haiku 4.5(ウィンドウ200K・最大出力64K)のような4.5世代以降のモデルでは、ヘッダーなしでこの動作になります。
取れるだけ出力させる設計は、そのぶん出力トークンの課金も増やします。Claude Opus 5.5の出力単価は100万トークンあたり$20で、128Kトークンを使い切ると1回あたり約$2.56です。大量のドキュメントを流すバッチでは、件数を掛けた額で見積もります。
「入力が大きすぎる」はどこで止まるか
このパターンが救えるのは、入力は収まったが、出力の途中でウィンドウが尽きる場合だけです。入力そのものがウィンドウを超えていると、生成の前に別の経路で止まります。
リクエストが止まる3つの地点
- 1
入力だけでウィンドウを超える
どのモデルでも、生成は始まらず400の
invalid_request_error(prompt is too long)が返ります。 - 2
入力+max_tokensがウィンドウを超える
Claude 4.5世代以降のモデルでは、リクエストは受理されます。それより前のモデルは検証エラーを返します。
- 3
生成の途中でウィンドウに達する
受理されたあとに上限へ当たると、
stop_reason: "model_context_window_exceeded"で止まります。生成済みの分は有効な応答として返ります。
4.5世代より前のモデルでも、model-context-window-exceeded-2025-08-26ベータヘッダーを付ければ、2番目の地点を通すことができます。ヘッダーなしでは、入力+max_tokensがウィンドウを超えた時点で検証エラーになります。
入力が明らかに上限を超えていないかだけは、トークンカウンティングAPIで見積もれます。細かい残量計算は省けても、この1点の確認までは省けません。
ウィンドウ超過の挙動全体はコンテキストウィンドウがオーバーフローしたときのClaude APIの挙動で扱っています。
curlで挙動を確かめる
長い入力と短い入力の両方で試し、stop_reasonがどう変わるかを見ておくと、分岐の実装ミスに早く気づけます。
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-opus-5-5",
"max_tokens": 20000,
"messages": [{"role": "user", "content": "Large input that uses most of context window..."}]
}' | jq '.stop_reason'大量の入力でmodel_context_window_exceeded、短い入力でmax_tokensかend_turnが返れば、想定どおりです。
max_tokensはどこまで上げてよいか
max_tokensの上限はモデルごとに決まっています。Claude Opus 5.5とSonnet 5.5は128Kトークン、Haiku 4.5は64Kトークンです。Message Batches APIでは、Opus 5.5やSonnet 5.5などでoutput-300k-2026-03-24ベータヘッダーを付けると、最大300Kトークンまで出力できます。
上限に近い値を同期のMessages APIで使うときは、別の制約があります。長い生成は通信が長引き、ネットワークによってはアイドル接続が切られて、レスポンスを受け取れないまま失敗することがあります。Python SDKは、10分を超えそうな非ストリーミングのリクエストでValueErrorを投げます。公式のPython例にも、max_tokensが約21kを超えるとストリーミングが必要だというコメントがあります。大きなmax_tokensにするなら、ストリーミングかMessage Batches APIを使う前提で組みます。
逆に、極端に小さいmax_tokensでは、ウィンドウに届く前にmax_tokensで止まるため、このパターンを使う意味がなくなります。サンプルの20000は、公式の例に合わせた中間的な値です。
ストリーミングで使うときのstop_reasonの読み方
大きなmax_tokensをストリーミングで受けるなら、stop_reasonの出どころが変わります。最初のmessage_startイベントではnullで、値はmessage_deltaイベントに入り、ほかのイベントには現れません。
イベントを逐次処理する必要がなければ、SDKのストリームに最終的なMessageを組み立てさせるのが簡単です。返ってくるオブジェクトは非ストリーミングの呼び出しと同じ形なので、前の節の分岐をそのまま使えます。
with client.beta.messages.stream(
model="claude-opus-5-5",
max_tokens=100000,
messages=[{"role": "user", "content": prompt}],
) as stream:
message = stream.get_final_message()
print(message.stop_reason, message.usage.output_tokens)思考とtool useが出力量に与える影響
Claude Opus 5.5では、適応型思考(adaptive thinking)が常にオンで、無効にできません。思考トークンはmax_tokensの一部として数えられ、出力トークンとして課金されます。思考が長くなるタスクでは、テキストに回せる分が減り、ウィンドウの残りも早く消えます。
過去のターンの思考ブロックの扱いはモデルで分かれます。Opus 4.5以降のOpus、Sonnet 4.6以降のSonnetなどでは、前のターンの思考ブロックが既定で履歴に残り、次のリクエストの入力としてウィンドウを占めます。それより前のOpus・SonnetとすべてのモデルのHaikuでは、API側が自動で取り除きます。続きを書かせる多ターンの設計では、どちらのモデルかで入力の膨らみ方が変わります。
Sonnet 5・Sonnet 4.6・Sonnet 4.5とHaiku 4.5には、残りのウィンドウを自分で追う仕組み(context awareness)があります。自動で働き、利用側が有効にする操作はありません。残量を把握したうえで長い作業を進めるモデルでも、上限に当たればmodel_context_window_exceededで止まります。
tool useを使うエージェントループでは、ツール呼び出しとその結果が毎ターンの入力に積み上がり、ウィンドウを消費します。長いツール結果を何度も往復させるループでは、model_context_window_exceededが早い段階で出ます。単発の呼び出しとループの中では、得られる出力量の目安を分けて見積もります。
事前に数えるなら、トークンカウンティングAPIの制約
事前計算の側を選ぶときは、カウント用のエンドポイントに癖があります。返る値は推定で、実際の入力トークン数と少しずれることがあります。利用は無料ですが、ユーザーティアに応じた毎分リクエスト数の制限があり、メッセージ作成とは別枠で数えられます。
数える対象にも制約があります。ウェブ検索・ウェブフェッチ・コード実行などのサーバーツール(advisorツールを除く)とMCPコネクターは、カウント側がinvalid_request_errorを返します。urlやfileを指す画像・文書ブロックも同じです。画像やPDFはbase64で渡して数えます。
もう1つ、トークナイザーの世代差があります。Claude Opus 4.7より前のモデルで測った値を、新しいモデルの見積もりに流用することはできません。実際に使うモデルIDを指定して数え直します。サーバーツールを使う呼び出しでは、使用量はMessages APIの応答のusageで確かめることになります。
向く場面と向かない場面
このパターンと事前計算の使い分け
このパターン
入力の長さが呼び出しごとに大きく変わるバッチ処理が向きます。ドキュメントごとにトークン数を数えず、「取れるだけ要約させる」実装にでき、分岐はstop_reasonの判定だけで済みます。多少の打ち切りを許せる、要約や下書き生成が主な用途です。
事前計算
出力が途中で切れると成果物として成立しない用途は、入力を事前に数え、余裕を持ったmax_tokensを明示的に決めるほうが安全です。入力がほぼ一定の場合や、すでに見積もりの仕組みがある場合、入力段階で400エラーを避けたい場合も、こちらが合います。
model_context_window_exceededは「ウィンドウが尽きた」ことを示すだけで、出力を途中で切る動作は変わりません。文の途中で終わったテキストが返りうるため、切れ目のない出力が必須なら後処理が要ります。
打ち切られた出力の後処理
最低限の対処は、切れていることを後段の処理や読者に伝えることです。max_tokens到達とウィンドウ超過は原因が違うので、通知も分けておくと、ログから原因を切り分けられます。
def handle_truncated_response(response):
text = next((block.text for block in response.content if block.type == "text"), "")
if response.stop_reason in ["max_tokens", "model_context_window_exceeded"]:
if response.stop_reason == "max_tokens":
note = "[max_tokens到達により応答が途中で切れています]"
else:
note = "[コンテキストウィンドウの上限により応答が途中で切れています]"
return f"{text}\n\n{note}"
return text完結した出力が必要なら、通知ではなく続きを生成させる設計にします。公式の「Ensuring complete responses」のサンプルは、max_tokensで止まったときだけ続きを書かせます。次のコードは、その条件にmodel_context_window_exceededも加えた変形です。
def get_complete_response(client, prompt, max_attempts=3):
messages = [{"role": "user", "content": prompt}]
full_response = ""
for _ in range(max_attempts):
response = client.beta.messages.create(
model="claude-opus-5-5", messages=messages, max_tokens=20000
)
full_response += next(
(block.text for block in response.content if block.type == "text"), ""
)
if response.stop_reason not in ("max_tokens", "model_context_window_exceeded"):
break
messages = [
{"role": "user", "content": prompt},
{"role": "assistant", "content": full_response},
{"role": "user", "content": "続きから書いてください。"},
]
return full_responseウィンドウ超過で止まった場合は、続きを頼む次のリクエストの入力に、元のプロンプトと生成済みの全文が入ります。入力がすでにウィンドウ近くまで埋まっているため、再び同じ停止に当たりやすくなります。この状態が続く長時間のエージェントループでは、サーバーサイドCompactionで古い部分を要約し、実効的な容量を延ばす設計のほうが扱いやすくなります。
Compactionとプロンプトキャッシュを両立させる設計はCompactionとプロンプトキャッシュのCache Control両立設計にあります。発動しきい値の考え方はCompactionの発動しきい値の決め方で扱っています。