構造化出力(Structured Output)とは?LLMの型安全な応答設計と実装ガイド

リード文
構造化出力(Structured Output)とは、LLM(大規模言語モデル)が JSON スキーマなど事前に定義した型に従って応答を生成する仕組みです。自然言語の自由回答とは異なり、出力の形式と値の型を強制することで、後続システムへ安全にデータを受け渡せます。本記事は、AI エージェントや自動化パイプラインを構築する開発者・アーキテクト向けに、構造化出力が必要な理由から JSON スキーマの設計、LLM への強制方法、よくある失敗の回避策、応用パターンまでを体系的に解説します。
結論: LLM の出力をそのまま業務システムに渡すと、形式のゆらぎ一つでパース失敗や障害を招きます。構造化出力はこのリスクを設計段階で抑える手法です。 自由形式テキストの問題、ハルシネーションとの関係、エージェント連携での重要性から、その必要性を整理します。
自由形式テキストがシステム連携で引き起こす問題
LLM に「この問い合わせから顧客名と希望日を抜き出して」と頼むと、ある時は「顧客名: 山田太郎」、別の時は「お客様のお名前は山田太郎様です」と返ってきます。人間には同じ意味でも、後続プログラムにとっては別物です。
自由形式テキストをシステムで扱おうとすると、次のような壁にぶつかります。
- 出力の文言や順序が毎回変わり、正規表現や文字列分割が安定しない
- 余計な前置き(「承知しました」「以下が結果です」)が混ざる
- 数値や日付の表記がゆれ、型変換に失敗する
こうした処理は最初は動いても、入力パターンが増えるほど例外対応が膨らみ、いずれ保守できなくなります。出力の「意味」だけでなく「形」をあらかじめ固定しておくことが、安定した連携の前提になります。
ハルシネーションと型エラーの関係
ハルシネーションには、大きく分けて「内容のハルシネーション(事実と異なる回答)」と「形式のハルシネーション(想定外の構造で返す)」の 2 種類があります。構造化出力が直接抑えられるのは後者です。
型を強制すると、存在しないキーを勝手に追加したり、数値であるべき欄に文章を入れたりといった形式崩れを防げます。一方で、スキーマに従っていても値の中身が事実と異なる可能性は残ります。「型が正しい」ことと「内容が正しい」ことは別物だと捉えてください。
ただし、列挙値(enum)で取り得る値を限定したり、必須項目を明示したりすると、LLM が選択肢の外の値をでっち上げる余地は狭まります。スキーマ設計は、形式の安定化と一部の内容ハルシネーションの抑制に効く、いわばガードレールの役割を果たします。
AIエージェント・マルチエージェントシステムでの重要性
単発の質問応答であれば、多少出力がゆれても人間が読んで補正できます。しかし AI エージェントが複数のツールを連鎖的に呼び出す構成では、あるステップの出力が次のステップの入力になります。ここで形式が崩れると、連鎖全体が途中で停止します。
当初は「プロンプトを工夫すれば JSON で返るはず」と考えがちですが、実際にはステップ数が増えるほど、どこか一箇所で形式が崩れる確率が積み上がります。エージェント間の受け渡しでは、出力を厳密な型で固定し、契約(インターフェース)として扱うほうが安定します。
マルチエージェントでは特に、各エージェントの責務をスキーマで定義しておくと、担当範囲の境界が明確になり、デバッグやリプレイもしやすくなります。構造化出力は、エージェント同士をつなぐ共通言語だと考えると設計の見通しが良くなります。
構造化出力を実装する前に確認すべき前提条件は?

結論: 構造化出力は「使う LLM が対応しているか」と「適切なスキーマを設計できるか」で成否が決まります。実装前に前提を確認すると手戻りを防げます。 対応プロバイダーと API モード、JSON スキーマの基礎、チューニングとの使い分けの 3 点を押さえます。
対応している LLM プロバイダーと API モードの確認
構造化出力の実現手段は、使う LLM や API モードによって異なります。実装前に、利用予定のモデルがどの方式に対応しているかを確認してください。
代表的な方式は次のとおりです。
- スキーマを直接渡し、その型を保証するモード(厳密なスキーマ準拠)
- 関数定義(Function Calling / Tool Use)の引数として構造化データを受け取る方式
- 「JSON で返す」とだけ指定する緩い JSON モード
厳密なスキーマ準拠に対応していれば、それを使うのが最も堅牢です。対応していない場合は Function Calling を、それも難しい場合はプロンプト指示とバリデーションで補う、というように方式を段階的に選びます。モデルやバージョンによって対応状況は変わるため、最新の公式ドキュメントを確認することをおすすめします。
JSON スキーマの基礎知識と設計スキル
構造化出力の品質は、渡す JSON スキーマの質に大きく左右されます。最低限、次の要素を扱えると設計がスムーズです。
type(string / number / boolean / object / array など値の型)properties(オブジェクトの各フィールド定義)required(必須フィールドの指定)enum(取り得る値の限定)description(各フィールドの意味を自然言語で補足)
特に description は軽視されがちですが、LLM はこの説明文を手がかりにフィールドの意味を解釈します。「日付」とだけ書くより「予約希望日。YYYY-MM-DD 形式」と書くほうが、期待する出力に近づきます。
スキーマは一度書いて終わりではなく、実際の出力を見ながら制約を足したり緩めたりして調整するものだと捉えてください。
ファインチューニングとプロンプトエンジニアリングの使い分け
「構造化出力のためにファインチューニングが必要か」とよく聞かれますが、多くのケースではスキーマ強制とプロンプト設計で十分です。まずは追加学習なしで試すのが現実的です。
判断の目安は次のとおりです。
- 汎用的な抽出・分類なら、スキーマとプロンプトで対応できることが多い
- 独自の業務用語や、プロンプトでは説明しきれない複雑な判断が絡む場合は、ファインチューニングが選択肢に入る
ファインチューニングは精度を高める余地がある一方で、データ整備や再学習の運用コストがかかります。プロンプトエンジニアリングで目標精度に届かないことを確認してから検討するほうが、投資対効果を見極めやすくなります。まずは軽い手段から始め、限界が見えた段階で重い手段へ移る順序をおすすめします。
どのように JSON スキーマを設計するか?

結論: 良いスキーマは「ユースケースから必要な項目を洗い出し、型と制約を明示し、構造をできるだけ平らに保つ」の 3 ステップで作れます。複雑すぎるスキーマは LLM が従いきれず逆効果です。 具体的な設計手順を順に見ていきます。
Step 1: ユースケースから必要フィールドを洗い出す
スキーマ設計は、いきなり JSON を書き始めるのではなく、「この出力を受け取った後、システムは何をするのか」から逆算します。後続処理が使わない項目は、そもそも出力させる必要がありません。
たとえば問い合わせ分類で、後続が「担当チームへの振り分け」と「優先度設定」を行うなら、必要なのはカテゴリと緊急度の 2 項目で足ります。ここで「要約」や「感情スコア」まで欲張ると、出力が重くなり精度も分散します。
洗い出しのコツは、フィールドごとに「これは誰がいつ使うのか」を一言で説明できるかを確認することです。説明できない項目は、たいてい不要か、別のタイミングで取得すべきものです。必要十分な項目に絞ることが、安定した構造化出力への近道になります。
Step 2: 型・必須項目・列挙値を定義する
必要な項目が決まったら、それぞれに型と制約を与えます。ここを曖昧にすると、せっかく項目を絞っても出力がゆれます。
ポイントは 3 つです。
- 型を具体的に: 「数値」なら整数か小数か、範囲はあるかまで示す
- 必須と任意を分ける: 後続処理が必ず使う項目は
requiredに入れ、欠損を防ぐ - 取り得る値は
enumで固定: カテゴリや状態のように選択肢が決まっている項目は列挙する
特に enum は効果が大きく、「緊急」「通常」「低」の 3 値に限定すれば、LLM が「やや緊急」のような中間表現を勝手に作る余地がなくなります。自由入力にすべき箇所と、選択肢に閉じるべき箇所を見極めることが、後続処理の堅牢さに直結します。
Step 3: ネストと再帰を最小化してトークン効率を高める
スキーマは深くネストするほど LLM が従いにくくなり、トークン消費も増えます。3 階層を超える入れ子や再帰的な構造は、可能な限り平らに展開することをおすすめします。
最初は現実の階層をそのまま写し取りたくなりますが、実際にはフラットな配列とキーの組み合わせで表現したほうが、出力が安定するケースが多くあります。たとえば「部署・チーム・メンバー」を深くネストするより、メンバーの配列に部署名とチーム名を持たせるほうが、LLM にとって扱いやすくなります。
トークン効率の観点でも、ネストが浅いほどスキーマ自体の記述量が減り、コンテキストを圧迫しません。複雑な構造が本当に必要かを問い直し、後続処理側で組み立て直せる部分は LLM に任せない、という割り切りが有効です。
LLM に構造化出力を強制する方法は?

結論: 構造化出力を確実に得るには、Function Calling などのモードで出力を拘束し、プロンプトでスキーマと例を示し、受け取った後にバリデーションと再試行を組み合わせる多層防御が有効です。 どれか一つではなく、3 つを重ねることが安定運用の鍵です。
Step 4: Function Calling / Tool Use モードで出力を拘束する
最も確実な方法は、API の Function Calling(Tool Use)モードを使い、関数の引数として構造化データを受け取ることです。このモードでは、LLM の出力が定義したスキーマに沿うよう API 側で誘導されます。
使い方の流れはシンプルです。
- 受け取りたいデータ構造を関数の引数スキーマとして定義する
- LLM にその関数を「呼び出す」形で応答させる
- 返ってきた引数を構造化データとして受け取る
単に「JSON で返して」とプロンプトで頼む方式に比べ、前置きの文章が混ざりにくく、形式崩れも起きにくくなります。スキーマ準拠を保証する専用モードがあればそれを優先し、なければ Function Calling を第一候補にすると、後段のエラー処理を大きく減らせます。
Step 5: システムプロンプトでスキーマと例を提示する
モードで拘束したうえで、システムプロンプトにもスキーマの意図と具体例を添えると、出力の質がさらに安定します。モデルは「形式」だけでなく「何を入れるべきか」の判断材料を必要とするためです。
効果的なのは、期待する入出力のペアを 1〜2 例示すこと(few-shot)です。文章で「丁寧に書いて」と説明するより、実際の出力例を 1 つ見せるほうが、フィールドの粒度やトーンが伝わります。
ただし例を増やしすぎるとコンテキストを圧迫し、かえって例に引きずられた出力になることがあります。代表的なケースと、間違えやすい境界ケースを 1 つずつ示す程度に抑えるのが、コストと効果のバランスを取りやすい構成です。例は固定せず、失敗した出力を見ながら差し替えていくと精度が上がります。
Step 6: レスポンスをバリデーションして再試行ループを実装する
モードとプロンプトで拘束しても、出力が完全にスキーマへ一致する保証はありません。受け取ったデータは必ずスキーマで検証し、不正なら再試行する仕組みを用意してください。
基本的なループは次のようになります。
- 出力をスキーマでバリデーションする
- 失敗したら、エラー内容を添えてもう一度生成を依頼する
- 規定回数で成功しなければ、フォールバック処理に切り替える
再試行時に「どのフィールドがなぜ不正だったか」をプロンプトに渡すと、次の試行で修正されやすくなります。無限ループを避けるため、試行回数の上限とタイムアウトは必ず設けてください。検証と再試行をアプリ側の責務として実装しておくことが、本番環境で構造化出力を安定させる最後の砦になります。
よくある失敗パターンとその回避策は?

結論: 構造化出力の失敗の多くは「スキーマが複雑すぎる」「出力の扱いが雑でセキュリティ穴になる」「コンテキスト不足でスキーマが切れる」の 3 つに集約されます。いずれも設計段階で予防できます。 それぞれの兆候と回避策を見ていきます。
スキーマが複雑すぎて LLM が従えないケース
構造化出力がうまくいかないとき、原因の多くはモデルではなくスキーマ側にあります。フィールドが多すぎる、ネストが深い、制約が矛盾している、といった複雑さが従いにくさを生みます。
よくある兆候は次のとおりです。
- 必須項目の一部が欠ける
- 深い階層の値だけが崩れる
- 出力が途中で切れる
対処は「分割」と「簡素化」です。一度にすべてを出力させず、段階的に複数回へ分けて取得する、あるいはスキーマを平らにして項目を減らす、といった調整が効きます。最初は理想形のスキーマを書いてしまいがちですが、実際には LLM が安定して従える複雑さの範囲に収めるほうが、結果的に精度も保守性も高くなります。スキーマは「正しさ」だけでなく「従いやすさ」も設計対象だと考えてください。
不正な出力ハンドリング(インプロパーアウトプットハンドリング)によるインジェクションリスク
構造化出力で見落とされがちなのが、受け取ったデータをそのまま信用してしまうリスクです。LLM の出力は、たとえ JSON として正しくても、内容まで安全とは限りません。
危険なのは、出力値を検証せずに後続処理へ渡すケースです。たとえば出力に含まれる文字列を、そのまま SQL やシェルコマンド、HTML に埋め込むと、インジェクションの入口になり得ます。これは「不正な出力ハンドリング(Improper Output Handling)」と呼ばれ、LLM アプリの代表的なリスクとして知られています。
対策は、外部入力と同じ姿勢で出力を扱うことです。
- 型と値の範囲をスキーマで検証する
- データベースやコマンドへ渡す際は必ずエスケープ・パラメータ化する
- 想定外の値はフォールバックで弾く
「構造化されている=安全」ではありません。形式の検証と、内容の無害化は別の対策として、両方を講じてください。
コンテキストウィンドウ不足でスキーマが切り捨てられる問題
スキーマや入力データが大きいと、コンテキストウィンドウの上限に達し、出力が途中で切れることがあります。長い JSON の末尾が欠けて、パースに失敗するのは典型的な症状です。
最初は「モデルの不調」に見えますが、実際にはトークン不足が原因のことが少なくありません。次の点を確認してください。
- スキーマ定義や few-shot 例が長すぎないか
- 入力テキストを丸ごと渡していないか(必要部分だけに絞れないか)
- 出力の最大トークン数が、想定する JSON の長さに対して足りているか
対策としては、スキーマの簡素化、入力の要約や分割、出力項目の削減が有効です。一度に大量のデータを処理させるより、適切な単位へ分けて複数回呼び出すほうが、結果的に安定し、コストの見通しも立てやすくなります。
構造化出力を応用した高度な設計パターンは?

結論: 構造化出力は単体の抽出にとどまらず、RAG の回答整形やマルチエージェントの型安全な連携といった高度な設計の土台になります。出力を型で固定することが、複雑なパイプラインの信頼性を支えます。 代表的な 2 つの応用を紹介します。
RAG パイプラインでの構造化レスポンス活用
RAG(検索拡張生成)では、検索した文書をもとに LLM が回答を生成します。この回答を自由形式テキストではなく構造化出力にすると、後続の表示や検証が格段に扱いやすくなります。
たとえば回答本文に加えて、次の項目を構造化して返す設計が考えられます。
- 回答の根拠として参照した文書 ID
- 回答の確信度や、情報が不足している旨のフラグ
根拠を構造化して持たせると、UI で出典を提示したり、確信度が低い回答を人手レビューへ回したりといった制御がしやすくなります。
自由文だけの回答では「どこを根拠にしたか」が曖昧になりがちですが、構造化することで回答と根拠を機械的に結びつけられます。RAG の品質改善は、出力の形を整えるところから始めると進めやすくなります。
マルチエージェントシステムでの型安全なメッセージパッシング
複数のエージェントが協調するシステムでは、エージェント間でやり取りするメッセージの形式が崩れると、連鎖全体が不安定になります。ここで構造化出力を「エージェント間の契約」として使うと、型安全な受け渡しが実現します。
設計の勘どころは次のとおりです。
- 各エージェントの入力・出力スキーマを事前に定義する
- 受け取ったメッセージは、処理前に必ずスキーマで検証する
- スキーマ違反は、その場で検出して再生成やエラー処理へ回す
こうしておくと、あるエージェントの出力が次のエージェントの入力要件を満たしているかを機械的に保証できます。問題が起きた箇所も特定しやすく、個々のエージェントを差し替えても、契約さえ守られていれば全体は動き続けます。
マルチエージェントの複雑さを飼いならす鍵は、自由なやり取りを許さず、型で縛ることにあります。構造化出力は、その規律を支える基盤技術です。
著者・監修者
Yusuke Ishihara
13歳でMSXに触れプログラミングを開始。武蔵大学卒業後、航空会社の基幹システム開発や日本初のWindowsサーバホスティング・VPS基盤構築など、大規模システム開発に従事。 2008年にサイトエンジン株式会社を共同創業。2010年にユニモン株式会社、2025年にエニソン株式会社を設立し、業務システム・自然言語処理・プラットフォーム開発をリード。 現在は生成AI・大規模言語モデル(LLM)を活用したプロダクト開発およびAI・DX推進を手がける。


