◉CLAUDE LABEN
●2.1.302 — fork サブエージェントが claude -p・Agent SDK・デスクトップで既定オンになりました。無効化は CLAUDE_CODE_FORK_SUBAGENT=0(10/16)●2.1.301 — 使用量上限に達した無人実行の再試行を、待ち続けず自動で止めるようになりました(10/15)。最新は 2.1.303(10/18)●SONNET 4.5 — Claude API での提供終了は 11/30、残り41日。移行先は Sonnet 5.5●WINDOWS — 日本語の環境でだけフックが黙って壊れる報告があり、文字コードの確認が呼びかけられています●FAST MODE — Opus 5.5 の Fast mode がリサーチプレビューを終え、ベータヘッダーが不要になりました(10/15)●NEW — Cowork に渡したフォルダの先頭に、十行の約束ファイルを置いてみました●2.1.302 — fork サブエージェントが claude -p・Agent SDK・デスクトップで既定オンになりました。無効化は CLAUDE_CODE_FORK_SUBAGENT=0(10/16)●2.1.301 — 使用量上限に達した無人実行の再試行を、待ち続けず自動で止めるようになりました(10/15)。最新は 2.1.303(10/18)●SONNET 4.5 — Claude API での提供終了は 11/30、残り41日。移行先は Sonnet 5.5●WINDOWS — 日本語の環境でだけフックが黙って壊れる報告があり、文字コードの確認が呼びかけられています●FAST MODE — Opus 5.5 の Fast mode がリサーチプレビューを終え、ベータヘッダーが不要になりました(10/15)●NEW — Cowork に渡したフォルダの先頭に、十行の約束ファイルを置いてみました
記事一覧/API & SDK
⬡ API & SDK/2026-10-20中級

構造化出力を output_config.format へ移す — beta ヘッダーを外す前に確かめておきたい差分

output_format と beta ヘッダーで書いた構造化出力を output_config.format へ移すときの差分を、公式ドキュメントから洗い出しました。Python SDK の TypeError、プロンプトキャッシュの無効化、拒否や途中切れで崩れるスキーマまで、移行前に確かめる順番を整理します。

Claude API130構造化出力output_config移行11Python SDK

ストアのレビュー文を「不具合」「要望」「称賛」に分類して、JSON で受け取りたい——個人開発でアプリを運用していると、この手の処理はいつの間にか一つ二つ増えていきます。そして動いているコードほど、しばらく触らないままになりがちです。

私の手元のコードにも structured-outputs-2025-11-13 という beta ヘッダーが残っていないか、先日ふと気になりました。公式ページを読み直すと、構造化出力のパラメーターは output_format から output_config.format へ移り、beta ヘッダーも不要になった、と書かれています。

最初にお伝えしたいのは、この移行は「パラメーター名を一つ変えるだけ」では済まないという点です。名前の差し替えに加えて、SDK の挙動、プロンプトキャッシュ、スキーマの制約、そして「スキーマに合わない出力が返る場面」まで、確かめる箇所がいくつかあります。移す前に公式ページから洗い出した順番を、書き残します(2026 年 10 月 20 日に確認)。

まず、何が変わったのか

公式の「Migrating from the beta」の節は短く、要点は次の3つです。

項目書かれていること
パラメーターoutput_format は output_config.format へ移った。output_format は非推奨で、将来削除される
beta ヘッダーもう不要。古い書き方を使い続けるなら structured-outputs-2025-11-13 が必要で、付けないと API は 400 を返す
Python SDKv1.0 以降は、client.beta.messages.create() と count_tokens() に output_format={...} を渡すと TypeError になる。output_config を使う

つまり、古い書き方は「いつか止まる」だけでなく、SDK を上げた時点で先に止まる可能性があります。API は動いているのに、手元のコードが TypeError で落ちる——この順番で壊れると、原因が見えにくいのです。

書き換えの Before / After

何を解決するか: beta ヘッダー付きの呼び出しを、現行の形へ置き換えます。

# Before: output_format と beta ヘッダーに頼っていた書き方(非推奨)
response = client.beta.messages.create(
    model="claude-sonnet-5-5",
    max_tokens=1024,
    betas=["structured-outputs-2025-11-13"],
    messages=[{"role": "user", "content": review_text}],
    output_format={"type": "json_schema", "schema": REVIEW_SCHEMA},
)
# After: output_config.format に schema を渡す。beta は使わない
import json
 
response = client.messages.create(
    model="claude-sonnet-5-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": review_text}],
    output_config={
        "format": {"type": "json_schema", "schema": REVIEW_SCHEMA}
    },
)
 
# 結果は text ブロックの中に JSON 文字列として入っている
text = next(b.text for b in response.content if b.type == "text")
result = json.loads(text)

なぜこう書くか: client.beta.messages.create から client.messages.create に変わる点が、見落としやすい差分だからです。ヘッダーだけ消して beta のクライアントを使い続けると、SDK 側の挙動が変わった箇所でつまずきます。

もう一つ、少し紛らわしい点があります。Pydantic のモデルを渡す SDK ヘルパー client.messages.parse() では、引数名が今も output_format です。公式はこちらを推奨の方法としていて、SDK がスキーマを変換し、output_config.format として送り、応答を検証して parsed_output に詰めてくれます。

from pydantic import BaseModel
 
class Review(BaseModel):
    category: str
    confidence: float
    summary: str
 
parsed = client.messages.parse(
    model="claude-sonnet-5-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": review_text}],
    output_format=Review,   # ヘルパーの引数名は output_format のまま
)
print(parsed.parsed_output)

「output_format を探して全部消す」という置換は、このヘルパーまで壊してしまいます。検索するときは、messages.create 系の生の呼び出しと、parse() を分けて見るのが安全だと感じました。

移したあとに変わること

名前を直しても、挙動の側にはまだ確かめる点が残ります。

確かめる点公式の記述手元で見るもの
初回のレイテンシ新しいスキーマの最初のリクエストは、文法のコンパイルで遅くなる。コンパイル済みの文法は最後の使用から 24 時間キャッシュされるデプロイ直後の最初の呼び出しだけ遅くないか
キャッシュの無効化スキーマの構造を変えると無効になる。name や description だけの変更は無効にならないスキーマを頻繁に差し替えていないか
プロンプトキャッシュoutput_config.format を変えると、その会話スレッドのプロンプトキャッシュが無効になるキャッシュヒット率が移行の前後で落ちていないか
入力トークン出力形式を説明する追加のシステムプロンプトが自動で入り、わずかに増える請求の入力トークンの変化

特に、プロンプトキャッシュは見落としやすいと思います。ユーザーごとにスキーマを動的に組み立てている場合、リクエストのたびにキャッシュが切れてしまうかもしれません。スキーマはできるだけ固定し、変えたい部分は description ではなくメッセージ側に寄せる、という線引きが現実的です。

スキーマに書けないもの

移行のついでに、スキーマの制約も読み直しておきます。公式が挙げている「サポートされない」ものは次のとおりです。

  • 再帰的なスキーマ、enum の中の複雑な型、外部の $ref
  • minimum / maximum / multipleOf といった数値の制約
  • minLength / maxLength といった文字列の制約
  • minItems が 0 か 1 以外の配列の制約
  • additionalProperties を false 以外にすること

使うと 400 エラーが返ります。Pydantic で Field(ge=0, le=1) のように範囲を書いている場合、SDK ヘルパーは制約を取り除いて説明文に書き足し、応答は元のスキーマで検証し直してくれます。生のスキーマを渡す場合は、anthropic パッケージの transform_schema() で同じ変換ができます。

さらに、複雑さの上限があります。

上限値
strict: true のツール数(1 リクエストあたり)20
省略可能なパラメーターの合計24
ユニオン型(anyOf や型の配列)を使うパラメーターの合計16

超えると 400 で「Schema is too complex for compilation」と返ります。省略可能な項目が多いスキーマは、「必須にして、無いときは明示的な値を返してもらう」形へ寄せると収まりやすい、と公式も勧めています。

「スキーマどおり」にならない 4 つの場面

構造化出力は、ほとんどの場合スキーマに沿った JSON を返します。ただし公式は、例外もはっきり書いています。移行後のテストには、次の4つを入れておくと安心です。

  1. 拒否(stop_reason: "refusal"): ステータスは 200 で、課金もされますが、出力はスキーマに合わないことがあります。拒否のメッセージが優先されるためです。
  2. 途中切れ(stop_reason: "max_tokens"): 出力が不完全な JSON になります。max_tokens を上げて再試行します。
  3. enum の大文字小文字: 空白の後ろの単語の頭文字などが、スキーマとは大文字小文字だけ違う値で返ることがあります。エラーにはなりません。比較は大文字小文字を区別せずに行います。
  4. 併用できない機能: 引用(citations)と一緒に使うと 400 になります。プリフィル(prefill)とも併用できません。
# 何を解決するか: スキーマ外の出力を、パースする前に弾く
def read_structured(response):
    if response.stop_reason in ("refusal", "max_tokens"):
        raise RuntimeError(f"schema を満たさない可能性: {response.stop_reason}")
    text = next(b.text for b in response.content if b.type == "text")
    return json.loads(text)

なぜこう書くか: json.loads が失敗してから原因を探すより、stop_reason を先に見るほうが、はるかに早く手がかりに辿り着けるからです。分類の結果を category == "bug" のように比較しているなら、enum の値は .lower() してから突き合わせる一行も足しておきます。

移行の最小手順

私が今回まとめた順番は、次のとおりです。個人開発の小さなリポジトリでも、この5段で足ります。

  1. コード全体から output_format と structured-outputs-2025-11-13 を検索し、生の呼び出しと parse() を分けて数えます。
  2. 生の呼び出しだけ messages.create と output_config.format へ書き換え、beta ヘッダーを外します。
  3. スキーマを transform_schema() か SDK ヘルパーに通し、400 が出る制約を洗い出します。
  4. stop_reason の確認と、enum の比較を大文字小文字無視にする一行を足します。
  5. 移行の前後で、初回レイテンシ・キャッシュヒット率・入力トークンを一度だけ見比べます。

名前を直す前に、壊れ方を先に知っておきます。 移行では、この順番だけは崩さないようにしています。

まずは、お手元のリポジトリで structured-outputs-2025-11-13 を検索するところから始めていただければと思います。一件も出なければ、この移行は済んでいます。出てきたら、その行がいちばん先に止まる場所です。

シェア

お読みいただきありがとうございます

Claude Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

⬡ API & SDK2026-10-13
Haiku 5.5 は単価が下がるのに、4.5 換算で約 7.7 万トークンを超えると請求が跳ねます
Haiku 4.5 から 5.5 へ移すと、価格表だけ見れば約 1/10 です。ただし同じ文章が約 30% 多いトークンになり、入力が 10 万を超えた時点で単価が 5 倍になります。境目を 4.5 換算で計算し、実際に動かした出力と、移行時に直す 400 の項目を並べました。
⬡ API & SDK2026-10-08
Sonnet 4.5 の退役前に、コードに残った「400 になる引数」を一本のスクリプトで拾いました
claude-sonnet-4-5 は 11 月 30 日に退役します。モデル ID を書き換えるだけでは、budget_tokens・prefill・tool_choice などで 400 が返ります。公式の移行ガイドの該当箇所を正規表現にしたスキャナと、実行結果、直し方の順番をまとめました。
⬡ API & SDK2026-08-30
モデルIDを一括置換する前に、手元の CLI が抱えるカタログを引きます
新しい世代が出ると、前の世代のモデルIDが急に古く見えます。インストール済みの実行ファイルに埋め込まれたカタログを引き、一致・日付不一致・書式破損の三段に仕分けてから直す手順を、実測値とスクリプトつきでまとめました。