はじめに / 対象と前提
複雑な推論タスク(コードレビュー・数学・多段の意思決定)で Claude の回答精度を上げたいとき、拡張思考(Extended Thinking)を有効にすると「回答前に内部で考える」ステップを挟める。ただし自分が実装したとき、パラメータ制約と tool use 併用まわりで 3 回ハマったので、動く実装と一緒にまとめる。
- 想定読者:Claude API を Python から叩いたことがある人
- 環境:Python 3.13 / anthropic SDK 0.7 系 / モデル
claude-sonnet-5 - 拡張思考は Sonnet / Opus 系の対応モデルで利用可(モデルごとの対応状況は公式 docs の Models 表を確認)
TL;DR
-
thinking={"type": "enabled", "budget_tokens": N}を渡すだけで有効化。ただし N は 1024 以上、かつmax_tokens未満が必須 - レスポンスに
thinkingブロック(signature付き)が増える。tool use 併用時はこのブロックを丸ごと会話履歴に戻さないと 400 - 有効化中は
temperature/top_p/top_kの指定不可、tool_choiceの強制指定(any/tool)も不可
手順 / 動かし方
1. 最小実装
import anthropic
client = anthropic.Anthropic() # ANTHROPIC_API_KEY は環境変数から
res = client.messages.create(
model="claude-sonnet-5",
max_tokens=16000,
thinking={"type": "enabled", "budget_tokens": 8000},
messages=[{"role": "user", "content": "39.7 * 41.3 を暗算の工夫を使って計算して"}],
)
for block in res.content:
print(f"--- {block.type} ---")
if block.type == "thinking":
print(block.thinking[:200]) # 思考過程(先頭だけ表示)
elif block.type == "text":
print(block.text)
実行結果(抜粋):
--- thinking ---
39.7 * 41.3 を計算する。(40 - 0.3)(40 + 1.3) と分解すると...
--- text ---
39.7 × 41.3 = 1639.61 です。計算の工夫としては...
content が thinking ブロック → text ブロックの 2 段構成になるのがポイント。従来どおり res.content[0].text で取り出しているコードは、thinking ブロックに text 属性が無いため AttributeError で落ちる。block.type で分岐する実装に直す必要がある。
2. ストリーミング対応
思考中も進捗を出したい場合は thinking_delta を拾う。
with client.messages.stream(
model="claude-sonnet-5",
max_tokens=16000,
thinking={"type": "enabled", "budget_tokens": 8000},
messages=[{"role": "user", "content": "..."}],
) as stream:
for event in stream:
if event.type == "content_block_delta":
if event.delta.type == "thinking_delta":
print(event.delta.thinking, end="", flush=True)
elif event.delta.type == "text_delta":
print(event.delta.text, end="", flush=True)
thinking_delta の後に signature_delta という署名専用イベントも流れてくるが、SDK の stream.get_final_message() を使えば結合済みの完全なブロックが手に入るので、自前で連結する必要はない。
ハマりどころ
その1:budget_tokens の下限は 1024、max_tokens 未満も必須
「軽く考えさせたいだけだから」と budget_tokens: 500 を渡すと即 400:
anthropic.BadRequestError: Error code: 400 -
{"error": {"type": "invalid_request_error",
"message": "thinking.enabled.budget_tokens: Input should be greater than or equal to 1024"}}
さらに budget_tokens は max_tokens より小さくないといけない(思考も出力トークンの一部として消費されるため)。max_tokens=4096, budget_tokens=8000 のような指定も 400 になる。**「max_tokens は思考+本文の合計枠」**と覚えておくと事故らない。課金も思考トークン込みなので、res.usage.output_tokens を見ると体感より大きい値が返ってくる。
その2:tool use 併用時、thinking ブロックを返し忘れて 400
一番ハマったのがこれ。tool use と併用する場合、tool_result を返すターンで直前の assistant メッセージに thinking ブロックを丸ごと(signature 含め無改変で)含める必要がある。トークン節約のつもりで tool_use ブロックだけ返すと:
anthropic.BadRequestError: Error code: 400 -
{"error": {"type": "invalid_request_error",
"message": "messages.1.content.0.type: Expected `thinking` or `redacted_thinking`, but found `tool_use`..."}}
正しくは res.content をそのまま入れる:
messages.append({"role": "assistant", "content": res.content}) # thinking ごと戻す
messages.append({"role": "user", "content": [
{"type": "tool_result", "tool_use_id": tool_use.id, "content": result_text},
]})
signature はサーバー側で思考の改竄を検証するためのフィールドなので、thinking の中身を編集・要約して返すのも NG。「content は無改変で丸ごと戻す」が唯一の正解。なお稀に redacted_thinking(内容が暗号化されたブロック)が返ることがあるが、これも同じ扱いで丸ごと戻せばよい。
その3:temperature を指定していると 400
既存コードに拡張思考を後付けすると、だいたいここで一度落ちる。temperature(1 以外)や top_p / top_k は thinking 有効時に指定できない:
"message": "`temperature` may only be set to 1 when thinking is enabled."
生成の揺らぎを抑える用途で temperature=0 を入れているコードは多いので、thinking の有無でパラメータを分岐させる。あわせて tool_choice={"type": "any"} などの強制ツール指定も非対応(auto のみ可)なので、フォールバック側の設計に注意。
まとめ
- 有効化は
thinking={"type": "enabled", "budget_tokens": N}の 1 行。ただし N ≥ 1024 かつ max_tokens 未満 - レスポンスは
thinking→textの 2 ブロック構成。block.typeで分岐する - tool use 併用時は thinking ブロックを signature ごと無改変で会話履歴に戻す
-
temperature/top_p/top_k/ 強制tool_choiceは併用不可。既存コードの後付けは分岐必須 - 思考トークンは出力課金に乗るので、
usage.output_tokensを見てコスト監視を
バージョン:anthropic SDK 0.7 系 / Python 3.13 / 2026-09 時点の仕様。