仕様変更の記録・2026年8月15日時点

思考の指定が400で弾かれる|世代で受け付ける形が変わったところ

動いていたコードのモデルIDだけを新しくしたら、400が返って止まりました。私たちの場合、原因は思考(拡張思考)のパラメータでした。世代で受け付ける形が変わっていて、書き換え自体は1行で済みます。ただし、直し方を間違えると静かに劣化します。

このサイトはAnthropicの公式サイトではありません。モデルの提供状況・仕様・料金は変わる可能性があるため、実装や契約の前には公式のドキュメントとご自身のアカウント設定を確認してください。

先に結論

  1. 数値指定(budget_tokens)を { "type": "adaptive" } に書き換える。新しい世代は数値指定を400で弾きます
  2. 「モデルIDだけ差し替える」移行は通らない。IDと一緒に思考の指定も書き換える必要があります
  3. エラーを避けるために thinking ごと削るのは一番悪い逃げ方。リクエストは通りますが、思考なしで動き続けます。エラーにならないので気づけません
  4. 新旧の世代を併用しているなら、分岐はモデルIDの列挙ではなく世代で書く。IDの列挙は新しいモデルが増えるたびに壊れます

何が変わったか

拡張思考をどれだけ使うかの指定は、以前はトークン数を数値で渡す形でした。

// 以前の書き方
"thinking": { "type": "enabled", "budget_tokens": 10000 }

新しい世代では、必要な量をモデル側が決める形になりました。

// 新しい書き方
"thinking": { "type": "adaptive" }

問題は移行のしかたです。単に非推奨になっただけのモードと、送ると400を返すモードがあります。

世代数値指定(budget_tokens)
4.6 より前これを使う
Opus 4.6 / Sonnet 4.6非推奨。動くが新しい形が推奨
Opus 4.7 / 4.8 / Opus 5 / Sonnet 5 / Fable 5400で弾かれる

つまり「モデルIDだけ差し替える」という移行は通りません。新しい世代に上げるときは、思考の指定も一緒に書き換える必要があります。

なぜ生成内容の話になるのか

これは一見、単なるAPIの互換性の話です。ただ、気づかずに素通りしている場合があります。

思考の指定は必須ではないので、書かなければリクエストは通ります。エラーを避けるためにパラメータごと削ってしまうと、思考が有効になっていない状態で動き続けます。これはエラーになりません。ただ、複雑な指示に対する出力が浅くなります。

「モードを上げたのに結果が変わらない」という症状が出たとき、原因がこれだったことがあります。上位のモードを、思考なしで使っていたわけです。単価だけ上がって、得られるものは増えていませんでした。

確認は簡単で、リクエストのボディに thinking が入っているかを見るだけです。ログに残していない場合は、まずログに残すところからです。

書き換えるときの手順

  1. いま何を送っているかを確認する。コードに書いてある値ではなく、実際に送信しているボディを1回ログに出します。ラッパーが値を書き換えていることがあります
  2. 対象のモデルがどちらの形を受け付けるかを確認する。上の表で世代を見ます
  3. 書き換えたら、1本だけ実際に投げる。400が返らないことと、応答に思考の痕跡があることの両方を見ます
  4. 複数のモデルを切り替えて使っている場合は、モデルごとに形を分岐させる。古い世代も併用しているなら、1つの形では両方を賄えません

4番目は見落としやすいところです。新しい世代だけを使うつもりでも、費用を抑えるために古い世代を混ぜていることがあります。分岐の条件はモデルIDではなく世代で書いてください。モデルIDの列挙は、新しいモデルが増えるたびに壊れます。

他にも世代で分かれているところ

思考の指定と同じ種類の落とし穴が、いくつかあります。移行のときにまとめて確認してください。

  • サーバー側で動くツールの版。ウェブ検索などは版が分かれていて、新しい世代では絞り込みに対応した版が使えます。古いモデルでは基本の版のままです
  • クラウド事業者の基盤経由の場合の制限。同じモデルでも、経路によって使えるツールが減ることがあります
  • 長い入出力でのタイムアウト。文脈が1Mになったことで、1回のやり取りが長くなりました。応答を待ち切れずに切れる場合は、逐次受け取る形に変えると通ります

最後の点は、「モデルを上げたら不安定になった」の正体だったことがあります。モデルの問題ではなく、長い応答を一括で待っていたことが原因でした。

移行のときに壊れやすいコードの形

世代を上げるときに落ちやすいのは、次のような書き方をしているコードです。心当たりがあれば先に直しておくと楽になります。

モデルIDを条件に分岐している

// 新しいモデルが増えるたびに壊れる
if (model === 'claude-opus-4-6') { ... }

列挙は必ず漏れます。世代で判定するか、対応表を1か所にまとめて、そこだけを更新する形にしてください。分岐が散らばっていると、移行のたびに全部を探すことになります。

リクエストのボディを組み立てる場所が複数ある

ラッパー関数がいくつもあって、それぞれがパラメータを足している状態だと、実際に何が送られているかが読めなくなります。1回ログに出して、思っていたものと一致するか確認してください。ここが合っていないまま調べても、原因にたどり着けません。

エラーの内容を捨てている

try { ... } catch (e) { console.log('失敗'); }

400なのか429なのか、どのフィールドが問題なのかが分からなくなります。返ってきたメッセージはそのまま残してください。「動かない」だけでは切り分けができません。

成功したことしか確認していない

リクエストが通ったことと、意図した設定が効いていることは別です。思考を有効にしたつもりで、実際には入っていなかった、というのはこの形の失敗です。送ったボディと受け取った応答の両方を見てください。

よくある質問

思考は常に有効にすべきですか?

複雑な指示では効きます。単純な分類や整形では差が出にくく、そのぶん時間と費用がかかります。用途で分けてください。

400のメッセージには何と出ますか?

受け付けないパラメータが含まれている旨が返ります。メッセージをそのまま読めば、どのフィールドが問題かは分かります。読まずに「動かない」で止めないでください。

古い世代を使い続ける選択肢は?

あります。ただし利用できる期間は保証されていません。分岐を世代で書いておけば、どちらでも動く形にできます。

次に読むページ