Claudeのstructured outputs — output_config.formatでJSONを固定する
ClaudeのJSONモードに当たる機能がstructured outputsです。output_config.formatの書き方、structured_outputフィールドとの違い、Claude Codeでの指定方法を扱います。
ClaudeでJSON出力を確実に固定する公式機能は、「JSONモード」という名前ではなくstructured outputsです。リクエストにoutput_config.formatを1つ加えるだけで、JSON Schemaに沿った応答が返ります。この記事ではAPIでの実装手順、tool useとの使い分け、Agent SDK・Claude Code CLIでの指定方法の違い、そしてスキーマに従わないまま返ってくる場面までを扱います。
structured outputsとは — ClaudeのJSONモードに当たる機能
structured outputsとは、Claudeの応答を指定したJSON Schemaへ強制的に一致させる機能です。中身はJSON outputs(output_config.format)とStrict tool use(strict: true)という2つの独立した仕組みの総称で、片方だけでも両方同時にでも使えます。
プロンプトでいくら「JSON形式で返して」と頼んでも、Claudeは崩れたJSONや必須フィールド欠けを返すことがあります。structured outputsは制約付きデコーディングでこれを構造から防ぎ、JSON.parse()失敗のリトライ処理そのものを不要にします。同じプロンプトでも毎回微妙に違う応答が返る非決定性の発生源(サンプリング・推論サービス側・エージェントの経路分岐)はClaudeの回答が毎回違うのはなぜか — 非決定性の3つの発生源と対処で扱っています。再現性が要る場面での対処の1つがstructured outputsです。
対応モデルはClaude 4.5以降の各モデル(Fable 5.1を含む)とClaude Mythos Previewです。提供面はClaude API、Claude Platform on AWS、Amazon Bedrock、Google Cloud、Microsoft Foundryです。モデル一覧の注記ではAmazon Bedrockの対応が絞られています。Opus 4.6・Sonnet 4.6・Sonnet 4.5・Opus 4.5・Haiku 4.5の5モデルです。Bedrockで新しいモデルを使うときは、そのモデルが対応に含まれるかを先に確かめます。
旧来はoutput_formatという単体パラメータでしたが、現在はoutput_config.formatに統合され、ベータヘッダーも不要です。旧パラメータは非推奨で、使い続けるにはstructured-outputs-2025-11-13ベータヘッダーを付けます。ヘッダーなしで送ると400エラーです。Python SDK(v1.0以降)のclient.beta.messages.create()はoutput_format={...}を受け付けず、TypeErrorになります。
structured_output・output_formatとの呼び分け
検索では似た綴りが混在しますが、指すものは別です。structured outputsは機能の総称、output_config.formatはAPIのリクエストパラメータ、structured_outputはAgent SDKの結果メッセージに入るフィールドです。output_formatは旧単体パラメータの名前ですが、Python SDKのclient.messages.parse()では引数名として今も使います。Agent SDKのquery()のオプション名も、Pythonはoutput_format、TypeScriptはoutputFormatです。
output_config.formatでJSON出力を固定する手順
前提はAnthropic APIキーと、対応モデル(Opus 5・Sonnet 5・Haiku 4.5・Fable 5.1など)です。SDKは最新版を使います。
JSON出力を固定する3手順
- 1
スキーマを定義する
JSON Schemaを直接書くか、PythonならPydantic、TypeScriptならZodの型で表します。
- 2
output_config.formatに渡す
type: "json_schema"とスキーマを組にして送ります。SDKのヘルパーを使えば、この形はSDKが組み立てます。 - 3
返ってきたテキストを読む
応答の
textブロックにスキーマ通りのJSONが入ります。ヘルパーは、これを型付きの値にまで変換します。
素のHTTPで送ると次のようになります。
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",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "田中太郎(tanaka@example.com)がEnterpriseプランのデモを来週火曜14時に希望しています"}
],
"output_config": {
"format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string", "format": "email"},
"plan_interest": {"type": "string"},
"demo_requested": {"type": "boolean"}
},
"required": ["name", "email", "plan_interest", "demo_requested"],
"additionalProperties": false
}
}
}
}'応答のcontentにあるtextブロックには、スキーマに一致する値だけが入ります。requiredとadditionalPropertiesはどちらもサポート対象で、additionalPropertiesだけがfalse固定です。false以外を指定すると400エラーになります。
PythonやTypeScriptではPydantic・Zodをそのまま使えます。client.messages.parse()にモデルを渡すと、SDKがJSON Schemaへの変換とレスポンスの検証を自動でやってくれます。Python SDKのclient.messages.parse()はoutput_format引数を受け付け、内部でoutput_config.formatへ変換します。他のSDKではoutput_configを直接指定します。Ruby・Javaでの型定義の書き方はStructured outputsの型定義パターンをPython・Ruby・Javaで実装比較で比較しています。
from pydantic import BaseModel
from anthropic import Anthropic
class ContactInfo(BaseModel):
name: str
email: str
plan_interest: str
demo_requested: bool
client = Anthropic()
response = client.messages.parse(
model="claude-opus-5",
max_tokens=1024,
messages=[{"role": "user", "content": "田中太郎(tanaka@example.com)がEnterpriseプランのデモを来週火曜14時に希望しています"}],
output_format=ContactInfo,
)
print(response.parsed_output.name, response.parsed_output.email)TypeScriptならzodOutputFormat()でZodスキーマをそのまま渡せます。生のJSON Schemaを直接書きたいときはjsonSchemaOutputFormat()を使います。スキーマをas constで宣言すれば、parsed_outputの型がスキーマどおりに推論されます。インポートや生成したスキーマではunknown型です。Zodと違い、応答の検証まではしません。
structured outputsとtool useの使い分け
目的が違う2つの仕組みなので、どちらを使うかは「何を固定したいか」で決まります。同じリクエストで両方を有効にすることもできます。
固定する対象で選ぶ
JSON outputs
Claudeが最後に返す応答全体をスキーマに固定します。指定はoutput_config.formatです。
Strict tool use
ツール呼び出しの引数(input)をスキーマ通りに保証します。指定はツール定義のstrict: trueです。
ツールを使った後の最終成果物まで固定したいときは、output_config.formatとツールのstrict: trueを同一リクエストに含めます。たとえば旅行計画エージェントなら、search_flightsツールの引数をstrict: trueで保証しつつ、Claudeが最後にまとめるsummaryとnext_stepsはoutput_config.formatで固定できます。Claudeは先にツールを呼ぶことも、直接JSONで応答することもあり、stop_reasonを見てどちらが起きたかを判定します。
グラマーによる制約はClaudeの直接出力にだけかかり、ツール呼び出し・ツール結果・思考(thinking)の中身には及びません。セクションが切り替わるたびにグラマーの状態はリセットされます。そのためClaudeは自由に考えてから、最後だけ構造化して答えられます。
Strict tool useが実際に保証するのはinput_schema準拠とツール名の妥当性までで、それ以外は保証しません。保証の境界線はClaudeのstrict tool useが保証すること・しないことで個別に確認できます。
複数のツールを持つエージェントでは、全部をstrict: trueにしないほうが通りやすくなります。複雑さの上限を超えたときの対処順として、リファレンスの先頭に挙がるのが「スキーマ違反が実害になるツールだけをstrictにする」ことです。
API・Agent SDK・Claude Code CLIで指定方法が異なる
同じstructured outputsでも、呼び出す面によって指定方法とふるまいが変わります。単発リクエストのAnthropic API、ツール呼び出しを挟む複数ターンのAgent SDK、ターミナルから叩くClaude Code CLIの3つで、それぞれ違います。
| 面 | 指定方法 | 特徴 |
|---|---|---|
| Anthropic API(単発) | 指定方法output_config.format | 特徴Messages APIへの1リクエストで完結する |
Agent SDK query() | 指定方法TS: outputFormat / Python: output_format | 特徴複数ターンのツール呼び出しを経て、完了時にstructured_outputフィールドで返る。検証失敗時は自動で再プロンプトする |
Claude Code CLI(-p) | 指定方法--json-schema | 特徴print modeでの単発実行。v2.1.205以降はスキーマが不正だと起動時にエラーで停止する(それ以前はエラーを出さずに無構造の出力に戻っていた) |
手元のClaude Code(v2.1.287)でclaude --helpを引くと、2つのフラグは次のように並びます。--output-formatの行に「only works with --print」とあるのに対し、--json-schemaの行は書式の例が付くだけです。
claude --help | grep -A3 -e "--json-schema" -e "--output-format <format>"出力の該当部分は次のとおりです。
--json-schema <schema> JSON Schema for structured output
validation. Example:
{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}
--
--output-format <format> Output format (only works with --print):
"text" (default), "json" (single
result), or "stream-json" (realtimeヘルプにprint modeの限定は書かれていませんが、CLIリファレンスの--json-schemaの説明には「print mode only」とあります。ヘルプの表示だけで使える範囲を判断せず、リファレンスと突き合わせる必要があるフラグです。
Agent SDKとCLIの検証はAPIより緩い部分がある
APIのグラマーはString formatsを強制します。一方、Agent SDKとCLI側のバリデーターは、formatキーワード("format": "email"など)を注釈として受け入れるだけで強制しません。v2.1.205より前は、formatを含むスキーマ自体が不正扱いでした。メールアドレスの形式を厳密に守らせたいなら、受け取った値をアプリ側でも検証します。
Agent SDKはスキーマに合わない出力を検証で見つけると、再プロンプトしてやり直します。リトライ上限に達するとstructured_outputを返さず、error_max_structured_output_retriesというsubtypeで終了します。モデルフォールバックが出力を取り消し、代わりのリトライが無かった場合も同じsubtypeになるため、原因を切り分けるには結果メッセージのerrors一覧を見ます。
もう1つ見落としやすいのが、subtypeがsuccessでもstructured_outputが空のまま終わる場合です。エージェントが構造化出力を作らずに完了したときがその例で、分岐はsubtype === "success"だけで済ませず、structured_outputが入っているかも条件に含めます。公式のエラー処理の例も、この2つを合わせて判定しています。
Agent SDKでZodスキーマを渡すときはz.toJSONSchema(schema, { target: "draft-7" })でdraft-07に変換してから渡します。Zodは既定でdraft 2020-12形式のスキーマを出力するため、変換しないままだとAgent SDK側のバリデーションで弾かれます。
JSON Schemaの落とし穴とエラー処理
structured outputsが対応するJSON Schemaは標準の一部だけです。送る前に次の点を確認します。
additionalPropertiesはfalse固定で、オブジェクトにそれ以外の値を指定すると400エラーになる- String formatsは
date-time・time・date・duration・email・hostname・uri・ipv4・ipv6・uuidに対応する。メールアドレスのような項目にはformatで指定できる - 数値・文字列の制約は非対応。
minimum・maximum・multipleOf・minLength・maxLengthはそのまま送ると拒否される - 再帰スキーマ・外部
$ref・enum内の複合型は非対応。$ref・$defはスキーマ内で完結する参照のみ使える - 配列の
minItemsは0か1のみ対応する。2件以上を必須にしたいときはdescriptionで指示するか、応答後にアプリ側で件数を検証する pattern(正規表現)は単純なものなら通る。後方参照・先読み/後読み・単語境界\b・範囲の大きな{n,m}は非対応で、複雑なパターンは400エラーになることがある- enum・constの文字列は大文字小文字がずれることがある。スペースに続く単語の先頭文字で起きやすい既知の挙動で、比較は大文字小文字を無視して行い、大文字小文字だけが違うenum値は避ける
- プロパティの並び順は
requiredが先になる。スキーマでnotesを先に書いても、requiredに含めたname・emailが先に出力されるため、順序に依存するならすべてrequiredにする
数値・文字列の制約は、Pydantic・Zod経由なら手作業が要りません。SDKが制約を取り除いてdescriptionへ言い換えて送り、レスポンスは元のスキーマで検証します。たとえばminimum: 100の項目は、送られるスキーマでは単なる整数になり、descriptionに{minimum: 100}が足されます。それでもヘルパーが検証するなら、返ってきた値が100未満のときはコード側で弾かれます。SDKがどの制約をどう変換しているかはStructured outputsのJSON Schema制限をSDKが自動変換する仕組みで追っています。
スキーマの外側で壊れる2つの場合
スキーマ準拠は「ほぼ常に」の保証で、構造そのものが崩れる例外は2つあります。いずれもエラーにならず、200で返ります。
スキーマに従わない応答
拒否(refusal)
安全上の理由でClaudeが拒否すると
stop_reasonはrefusalになります。課金は発生し、本文はスキーマに従いません。推論過程そのものを入れるプロパティを定義するとreasoning_extractionの拒否を招くことがあり、公式は短い説明を求める形を勧めています。max_tokens超過
max_tokensで切れると出力が不完全なまま終わります。上限を上げて再試行します。入力サイズが事前に分からない場合の実装は入力サイズ不明でも最大出力を引き出す実装パターンで扱っています。
スキーマの複雑さと上限
複雑さには上限があります。strict: trueのツールは1リクエストあたり20個まで、required外の任意プロパティは全スキーマ合計で24個まで、anyOfや型配列を使う項目は16個までです。この合計は1つのスキーマごとではなくリクエスト全体で数えます。任意プロパティ6個のstrictツールが4本あれば、どれも単体では小さく見えても24個に届きます。
超えると「Schema is too complex for compilation」という400エラーになります。明示の上限を守っていても、ネストや任意項目の組み合わせでコンパイル後のグラマーが大きくなりすぎると同じエラーです。最終的な歯止めとしてコンパイルのタイムアウトは180秒あります。
対処は公式が示す順に、strictにするツールを絞る、任意プロパティをrequiredに寄せる、ネストを浅くする、複数リクエストやサブエージェントに分ける、の4段階です。任意プロパティは1つ増えるごとに、グラマーの状態空間の一部がおよそ2倍になるとされています。常に妥当な既定値があるなら、任意にせずrequiredにしてClaudeにその値を明示させるほうが軽くなります。
初回の遅延・コスト・併用できない機能
初回はコンパイルで少し遅くなります。コンパイル済みグラマーは最終利用から24時間キャッシュされ、スキーマ構造やツール構成を変えると無効になります(name・descriptionだけの変更では無効になりません)。structured outputsは出力形式を説明する追加のシステムプロンプトを挿入するため、入力トークンがわずかに増えます。output_config.formatを変更すると、その会話のプロンプトキャッシュも無効になります。
Citationsとは併用できず、有効にすると400エラーになります。Message prefillingとも併用できません。一方でストリーミング・トークンカウント・50%割引のBatch処理とは組み合わせられます。
まとめ
Claudeに「json mode」という機能名はありません。output_config.formatでスキーマを渡せば最終応答が固定され、ツールの引数まで保証したいときだけStrict tool useを重ねます。refusalとmax_tokens超過はstop_reasonで、enumの大文字小文字のずれは比較側で受ける前提で組みます。既存のtool useの実装は高度なTool use、Anthropic API全体の構造はAnthropic API完全ガイド、Agent SDKでの組み方はAgent SDKクイックスタートにまとめています。
よくある質問
JSON以外の形式(YAMLやXMLなど)も強制できますか
できません。structured outputsが保証するのはJSON Schemaに一致するJSONだけです。他形式が必要なら、JSONで受け取ってからアプリ側で変換します。
HIPAA環境でPHI(医療に関する個人情報)を扱えますか
structured outputs自体はHIPAA適格ですが、JSON Schemaの定義にPHIを含めてはいけません。スキーマはグラマーとしてメッセージ本文とは別にキャッシュされ、本文と同じ保護を受けないためです。プロパティ名・enum値・const値・patternの正規表現もスキーマの一部なので、PHIはプロンプトと応答の中身だけに置きます。スキーマ自体は最終利用から最大24時間キャッシュされます。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。