この記事では、Windows環境のClaude Codeで実際に試した結果をもとにまとめています。
AIにJSONを出力させたとき、形式が崩れたり想定したフィールドが抜けたりした経験はないだろうか。Structured Outputは、こうしたブレを減らす仕組みだが、APIの知識が必要そうに見えて手を出しにくいかもしれない。今回はClaude Codeのターミナル画面から、JSON Schemaを指定して頼むだけ——この方法でJSON出力がどこまで安定するかを、シンプルなスキーマから複雑なスキーマまで3段階で確かめた。結果として、シンプルなスキーマなら初回から高い安定性が確認できた。だが一見シンプルなinteger型の指定が反映されない場面もあった。
この記事で確かめること
Claude CodeにJSON Schemaを渡して「この形で出力して」と頼むだけで、JSON出力がどれくらい安定するかを検証する。具体的には、次の3段階で負荷を上げていく。
- Step 1–2:文字列・数値・配列だけのシンプルなスキーマで、初回出力と複数回の安定性を確認
- Step 3–4:ネストしたオブジェクト、enum制約、必須フィールドを含む複雑なスキーマに挑戦し、修正指示の効果も見る
- Step 5:ブログ記事のメタデータのような実務的なスキーマで、実用レベルの安定性を試す
「頼むだけでどこまでできて、どこから人が確認すべきか」の境界線が、読者にも伝わる構成にした。Windows 11環境、ターミナルから自然文で依頼する前提で進める。
前提——今回の検証条件
検証は次の条件で行った。
- 環境:Windows 11
- 依頼方法:ターミナルでClaude Codeを開き、自然文で指示を出す
- スキーマの指定方法:プロンプト内にインラインでJSON Schemaを貼り付ける(ファイルパスを渡す方法もあるが、今回は直書き)
- 範囲:Claude Code上でのプロンプト指示による検証。Python SDK等のAPI直接利用は対象外
- データ:すべて架空のダミーデータを使う
この記事で言う「頼むだけ」とは、Claude Codeのチャット画面に自然文とJSON Schemaを書き込む操作を指す。API経由でtool useを使う方法(公式docsで推奨されているStrict tool use等)は別のアプローチであり、今回の検証範囲には含めない。両者の違いは「この検証の振り返り」で少し触れる。
Claude Codeを開いてチャットできる状態なら、特別な準備は不要だ。以降の依頼文は、そのままコピペして試せる形にしてある。
Step 1: シンプルなスキーマで最初の依頼
まずは文字列・数値・配列だけのシンプルなスキーマを指定し、初回出力の様子を見る。依頼文は次の通り。
次のJSON Schemaに従って、架空の書籍データを1件出力して。
Schema:
{
"type": "object",
"properties": {
"title": { "type": "string" },
"author": { "type": "string" },
"price": { "type": "number" },
"tags": { "type": "array", "items": { "type": "string" } }
},
"required": ["title", "author", "price"]
}
スキーマの中身をざっくり説明すると、titleとauthorが文字列、priceが数値、tagsが文字列の配列だ。requiredでtitle・author・priceの3つを必須に指定している。
結果はこう返ってきた。
{
"title": "深夜のコンビニ",
"author": "佐藤さくら",
"price": 1400,
"tags": ["小説", "ミステリー"]
}
requiredで指定した3項目はすべて揃っている。tagsは任意フィールドだが、こちらも正しく配列で出力された。型もtitleが文字列、priceが数値と、スキーマ通りの形だ。
JSONに慣れていない方向けに補足すると、ここで確認しているのは主に2点——「必要な項目が抜けていないか」と「それぞれの型(文字列か数値か配列か)が合っているか」だ。この2点が合っていれば、とりあえず「スキーマに準拠している」と言える。
確認したのはフィールドの有無、データ型、配列の中身の3点。シンプルなスキーマなら、1回の依頼でスキーマに準拠した出力が得られた。
Step 2: シンプルなスキーマでの安定性テスト
同じスキーマで何回か出力させ、結果がブレないかを確かめた。具体的には、先ほどの依頼文をそのままもう4回実行し、計5回分の出力を比較する。
安定していた例:
requiredフィールドや型は毎回揃っていた。たとえば次の出力では、tagsも3要素でバランスよく返ってきている。
{
"title": "星屑のレストラン",
"author": "田中ひろし",
"price": 1100,
"tags": ["SF", "短編集", "恋愛"]
}
ブレた例:
一方で、tagsの要素数にばらつきが出た回もあった。次の出力ではtagsが空配列になっている。
{
"title": "雨上がりの午後",
"author": "中村あおい",
"price": 980,
"tags": []
}
傾向のまとめ:
– requiredフィールド(title, author, price)は5回すべてで欠落なし
– priceが毎回数値型で返ってきた(文字列になったことはない)
– tagsの要素数が0〜4個で変動し、空配列になることもあった
依頼文を「tagsは必ず3つ含めて」と追記したところ、5回中5回とも3要素の配列になった。指示を具体化するだけで安定性が上がる場面がある。
シンプルなスキーマでは、初回からほぼスキーマに準拠した出力が続いた。不安定性の原因はスキーマの曖昧さ(要素数の指定がない等)であり、依頼文で補える範囲だった。
Step 3: 複雑なスキーマで挑む
次に、ネストしたオブジェクト・enum制約・必須/任意フィールド・配列長制限を含む複雑なスキーマを試す。
次のJSON Schemaに従って、架空のオンラインショップの商品データを2件出力して。
Schema:
{
"type": "object",
"properties": {
"products": {
"type": "array",
"minItems": 2,
"maxItems": 2,
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"category": { "enum": ["家電", "文具", "食品"] },
"price": { "type": "number" },
"stock": { "type": "integer" },
"details": {
"type": "object",
"properties": {
"weight": { "type": "string" },
"color": { "type": "string" }
},
"required": ["weight"]
}
},
"required": ["name", "category", "price"]
}
}
},
"required": ["products"]
}
先ほどと比べると、構造が一段ネストしている点(productsの中にオブジェクトが入る)、enumで選択肢を限定している点、minItems/maxItemsで配列の長さを固定している点が違う。
初回結果の評価:
– products配列は2件でminItems/maxItemsに準拠
– categoryのenumも”家電”と”文具”で正しく収まった
– details.weightは2件とも含まれていた
気になった点:
– stockの型が1件だけ小数付きで返ってきた——integer指定だったのに
– details.colorが1件でしか出力されなかった(任意フィールドなので違反ではないが、バラつきがある)
複雑なスキーマでも大部分は準拠していたが、integer型の制約が一部で反映されなかった。ネストが深くなると、内側の型制約を見落とす場面があるようだ。
Step 4: 修正指示でどう変わるか
Step 3で気になった点に対して、修正指示を出した。
出力のstockが小数になっている箇所があったよ。
stockはinteger型なので、必ず整数にして出力し直して。
また、details.colorも2件とも含めて。
修正前後の比較:
修正前(Step 3の該当部分):
{
"products": [
{
"name": "ワイヤレスイヤホン",
"category": "家電",
"price": 4980,
"stock": 12.5,
"details": { "weight": "35g" }
},
{
"name": "ノートパソコンスタンド",
"category": "文具",
"price": 3200,
"stock": 8,
"details": { "weight": "650g", "color": "シルバー" }
}
]
}
修正後:
{
"products": [
{
"name": "ワイヤレスイヤホン",
"category": "家電",
"price": 4980,
"stock": 12,
"details": { "weight": "35g", "color": "ブラック" }
},
{
"name": "ノートパソコンスタンド",
"category": "文具",
"price": 3200,
"stock": 8,
"details": { "weight": "650g", "color": "シルバー" }
}
]
}
stockの12.5が12に直り、details.colorが両方に含まれるようになった。それ以外のフィールドに影響は出ていない。
修正は1回で済んだ。「整数にして」「2件とも含めて」と指示内容を具体化したことが効いたようだ。
フィールド名がずれることもあった:
別の実行では、detailsの中にweightではなくweight_kgというフィールド名が返ってくることがあった。
{
"details": { "weight_kg": "0.65", "color": "シルバー" }
}
スキーマにはweightと書いたのにweight_kgが返ってきた——単位を補って親切にしてくれたつもりかもしれないが、プログラムでdetails.weightを参照しているとキーが見つからずエラー修正になる。この種の「意味的には正しいが、キー名がずれる」問題は、複雑なスキーマで時々観察された。人の目による確認が欠かせない。
他の関連記事も参照したい場合、タイトル案の作成やテキスト整形、見出し構成の検証など、JSON出力と同じく「構造化された出力」の事例として参考になります。また、ブログ記事メタデータの実務例については記事下書きの記事でも触れています。
Step 5: 実務的なスキーマで試す
最後に、ブログ記事のメタデータを想定した実務的なスキーマで検証する。
次のJSON Schemaに従って、架空のテックブログ記事のメタデータを3件出力して。
各フィールドが空でないこと。
Schema:
{
"type": "object",
"properties": {
"articles": {
"type": "array",
"items": {
"type": "object",
"properties": {
"slug": { "type": "string", "pattern": "^[a-z0-9-]+$" },
"title": { "type": "string" },
"status": { "enum": ["draft", "published", "archived"] },
"tags": {
"type": "array",
"items": { "type": "string" },
"minItems": 1
},
"author": { "type": "string" },
"published_at": { "type": "string" }
},
"required": ["slug", "title", "status", "tags"]
}
}
},
"required": ["articles"]
}
出力結果:
– slugは3件とも半角小文字とハイフンの形式を満たしていた(例:ai-writing-tips)
– statusのenumも正しく、draft/published/archivedのいずれか
– tagsは1件以上含まれておりminItems: 1に準拠
人が確認したポイント:
– published_atの日付形式が混在していた。たとえば、一般的に使いやすいYYYY-MM-DD形式と、YYYY/MM/DD形式が混ざっていた
– slugのpattern制約は満たしていたが、実際のURLとして使える自然なslugかどうかは別の話——内容との整合性は人が判断する領域
実務的なスキーマでも、基本的な構造は頼むだけで安定していた。日付フォーマットの統一等、細かい表記揺れには追加の指示か事後確認が必要だった。
やり取りの回数:シンプルなスキーマは依頼1回。複雑なスキーマは依頼1回+修正指示1回。実務スキーマも依頼1回で基本構造は揃い、表記揺れの修正に1回程度だった。
この検証の振り返り
いくつか気づいた点を書き出してみる。
依頼文のコツ:スキーマの曖昧さを依頼文で補うと安定性が上がる。「tagsは3つ」「stockは整数」といった具体指示を添えることで、ブレが減った。スキーマだけでは伝わらない条件は、自然文で補うのが効く。
スキーマの複雑さと安定性の傾向:シンプルなスキーマではほぼ安定。複雑なスキーマでは「大枠は合うが細部がずれる」傾向があった。ネストが深くなるほど、内側の制約が反映されにくくなる印象だ。
向いている用途・向かない用途:JSONのたたき台生成やバリデーション対象のサンプルデータ作成には向いている。一方、エラーハンドリングを含む本番パイプラインへの直接組み込みには不向き——出力後にバリデーション層を挟む方が安心だ。
プロンプト指示とAPIの違いについて:今回の検証はプロンプト指示の範囲に限ったものだ。API側のStructured OutputやStrict tool useを使うと、JSON Schemaに沿った出力・ツール入力をより厳密に扱える。特にStrict tool useでは、ツール入力がJSON Schemaに合うよう制御されるため、今回のようなプロンプト指示だけの方法より安定しやすい。ただし、意味的に正しい値かどうかは別問題なので、実務ではバリデーションや人の確認も併用したい。プロンプト指示は手軽さの反面、厳密さの面でAPIに分がある。用途に合わせて選ぶと良い。
なお、環境やバージョンによっても結果は変わりうるので、あくまで今回の検証での傾向として読んでほしい。
次に試すなら
読者が次にやるとよいことを整理する。
- まず試す:この記事のStep 1の依頼文をコピペして、自分の環境で同じ検証を再現する。シンプルなスキーマであれば、特別な準備なしに動くはずだ
- 次に読む:API経由のStructured Output(tool use)に興味があるなら、公式docsの該当ページを眺めてみると、プロンプト指示との違いが腑に落ちる。また、他の構造化出力の事例としてタイトル案や見出し構成の検証記事も参考になります
- さらに進めるなら:Pythonの
jsonschemaライブラリ等を使って、Claude Codeが出力したJSONを自動バリデーションする流れを組むと、プロンプト指示のブレを二重に検証できる。コマンド1行でスキーマとの整合性を確かめられるので、実務で使うならこの組み合わせがおすすめだ。テキスト整形の自動化と組み合わせると、より強力なワークフローになります
骨組みが短時間でできるのが、この方法の利点だ。合わない部分は後から直せばいい。自分の作業で使いそうなスキーマを1つ作って、どこまで安定するか試してみるところから始めたい。
まとめ
今回の検証範囲(Claude Codeのプロンプト指示、シンプル〜実務レベルの3スキーマ)では、シンプルなスキーマなら初回から高い安定性が確認できた。複雑なスキーマでも大枠は合い、修正指示1回で実用的なレベルに到達する。ただし、フィールド名の表記揺れ(weightがweight_kgになる等)や日付フォーマットなど、細部の正確さは人の確認が必要だ。完全な自動化にはもう一段階バリデーションの仕組みが必要だが、たたき台の生成やバリデーション補助としては頼むだけで実用的な水準に達したと言えそうだ。ブログ記事の実務的な応用例については、記事下書きの記事も参考にしてください。
今回の結果は、利用モデル、接続先、時期、環境によって変わる可能性があります。再現する時は検証条件もあわせて確認してください。

