0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude API の拡張思考(Extended Thinking)を実装する — budget_tokens の下限、tool use で thinking ブロックを返し忘れて 400、temperature 非対応の3つのハマりどころ【2026】

0
Posted at

はじめに / 対象と前提

複雑な推論タスク(コードレビュー・数学・多段の意思決定)で 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 です。計算の工夫としては...

contentthinking ブロック → 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_tokensmax_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 未満
  • レスポンスは thinkingtext の 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 時点の仕様。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?