現役のITエンジニアが、 システム開発の現場で求められる知識を発信
記事検索
公開

Structured Outputsの基本と実践

生成AI関連

はじめに

生成AIは、チャットボットや文章生成ツールにとどまらず、業務システムの中核に組み込まれるケースが急速に増えています。しかし、生成AIを「システムの部品」として利用しようとした瞬間に直面するのが、出力の不安定さです。
自由文として読む分には自然で問題のない出力も、APIレスポンスやデータベース保存を前提とすると、一気に扱いづらくなります。
本記事では、

  • Structured Outputs(構造化出力)とは何か
  • なぜ生成AIシステムにおいてほぼ不可欠と言えるのか
  • Amazon Bedrock における実装方法

について整理します。

構造化出力とは

構造化出力とは、LLMの出力を、あらかじめ定義した構造に従わせる仕組みです。
従来は、

  • 「JSONで返してください」とプロンプトで指示する
  • JSONの例を提示する
  • スキーマを文章で説明する

といった方法が一般的でした。
しかしこれらはあくまで“お願い”に過ぎません。モデルの性質上、フォーマットが崩れることは避けられません。
構造化出力では、スキーマを明示的に指定することで、モデル出力を構造化データとして取得できるようになります。

なぜ生成AIシステムに不可欠なのか

生成AIはしばしば“賢いAPI”のように扱われますが、本質は確率的な文章生成モデルです。
過去の記事でも触れた通り、LLMは意味を理解しているというよりも、文脈に対して“それらしい単語列”を確率的に生成しています。

生成AI関連
生成AIのテキスト生成のしくみとパラメータ

そのため、

  • 指示が曖昧なら出力も曖昧になる
  • フォーマット指定を破ることがある
  • JSONの前後に説明文を付ける

といった挙動は、バグではなくモデルの性質です。
しかし、システム連携では事情が異なります。

  • データベース保存
  • APIレスポンス
  • ワークフローの制御

これらはすべて構造化データが前提です。
LLMの不確実性をそのまま流し込むと、

  • JSONDecodeError
  • 型エラー
  • 想定外キーの混入
  • ワークフロー誤分岐

といった問題が発生します。
構造化出力は、この“文章生成エンジン”をシステム部品として扱える状態に近づけるための仕組みです。生成AIを単なるチャットではなく、業務システムとして構築するのであれば、構造化出力はほぼ不可欠な要素と言えます。

Amazon Bedrockでの構造化出力対応

2026年2月、Amazon Bedrock にて構造化出力が利用可能になりました。これにより、JSON Schemaを指定してモデル出力を構造化データとして取得できます。
Amazon Bedrock で構造化出力が利用可能に

※ もちろん、OpenAI、Anthropic、Googleなどの生成AIでも構造化出力を使用することは出来ますが、今回はBedrockが対応したこともあり、Bedrockの紹介をします。

対応状況について

現時点では、すべてのモデルが対応しているわけではありません。

  • Anthropic Claude 4.5 モデルおよび一部のオープンウェイトモデルで一般公開(GA)
  • Novaなど一部モデルは未対応

Novaを利用する場合は、公式ドキュメントで紹介されているプロンプトベース、もしくはツールベースの構造化出力手法を採用する必要があります。
つまり、

  • 対応モデル → JSON Schema指定で安定取得
  • 未対応モデル → プロンプト設計+アプリ側バリデーション

という使い分けが必要です。

実装例

以下は、BedrockのConverse APIでClaude系モデルを利用した例です。

python

import json

import boto3
from dotenv import load_dotenv
from pydantic import BaseModel, Field

load_dotenv()


class UserInfo(BaseModel):
    name: str = Field(..., description="氏名")
    age: int = Field(..., description="年齢")


def main():
    session = boto3.Session()
    bedrock_runtime_client = session.client(
        "bedrock-runtime", region_name="ap-northeast-1"
    )

    request_dict = UserInfo.model_json_schema()
    request_dict["additionalProperties"] = False  # additionalProperties項目の設定は必須

    res = bedrock_runtime_client.converse(
        modelId="jp.anthropic.claude-haiku-4-5-20251001-v1:0",
        messages=[
            {
                "role": "user",
                "content": [{"text": "ダミーのユーザー情報をJSONで出力して。"}],
            }
        ],
        outputConfig={
            "textFormat": {
                "type": "json_schema",
                "structure": {
                    "jsonSchema": {
                        "name": "user_info",
                        "schema": json.dumps(request_dict),
                        "description": "ユーザー情報",
                    }
                },
            }
        },
    )

    output = res["output"]["message"]["content"][0]["text"]
    user_info = UserInfo.model_validate_json(output)

    print(user_info)
    print(type(user_info))


if __name__ == "__main__":
    main()
実行結果

name='田中太郎' age=30
<class '__main__.UserInfo'>

実装のポイント

  • jsonSchema.schema は手書きする必要はない
  • Pydanticの model_json_schema() から生成可能
  • アプリケーションの型定義と完全に一致させられる

これにより、

アプリケーションの型定義
→ JSON Schema
→ LLM出力
→ 型安全に受け取る

という流れが実現できます。
これは、生成AIを“実験ツール”から“システムコンポーネント”へと引き上げる重要なステップです。

実務での活用例

構造化出力の価値は、「JSONがきれいに返ること」そのものではありません。実務においては、システム設計を安定させる点に本質があります。
ここでは、弊社がシステム作成で利用した代表的な活用パターンを紹介します。

1. RAG回答の構造化

RAG(Retrieval-Augmented Generation)では、単に回答を生成するだけでなく、

  • 回答本文
  • 参照したドキュメントID
  • 信頼度

といった情報も扱いたくなります。構造化出力を利用すれば、これらを明示的な構造として取得できます。

※ ここでは、生成AIに生成させた信頼度が果たして信頼できるのか、といった議論はおいておきます。

スキーマ例(Pydantic)
python

from pydantic import BaseModel, Field

class RAGResponse(BaseModel):
    answer: str = Field(..., description="回答")
    source_ids: list[str] = Field(..., description="参考にした出典ID")
    confidence: float = Field(..., description="信頼度")
出力例
json

{
  "answer": "社内規程では〇〇と定められています。",
  "source_ids": ["doc_123", "doc_456"],
  "confidence": 0.82
}

このように構造化されていれば、

  • source_ids からさらにデータを取得し、加工した回答を作成できる
  • confidence が閾値未満なら未回答フォーラムへ送る
  • 出典が空ならエスカレーションする

といった分岐処理を実装できます。自由形式での回答から情報を抽出する設計に比べ、実装と運用の安定性は大きく向上します。

弊社で作成した下記システムでは、この方式を応用して出典にリンクをつけて、ユーザーがファクトチェックを行いやすいようにしています。
RAG(検索拡張生成)と生成AIを活用したAIチャットボットの開発

2. ワークフロー分岐

生成AIを業務フローの一部として使う場合、分類器として使用するケースも少なくありません。
例えば、受信するメールの自動分類です。構造化出力を利用すれば、分類結果を明示的な値として取得できます。

スキーマ例(Pydantic + Literal)
python

from pydantic import BaseModel, Field
from typing import Literal

class ClassificationResult(BaseModel):
    category: Literal["案件紹介メール", "人材紹介メール", "その他"] = Field(
        ..., description="メールの分類"
    )
出力例
json

{
  "category": "案件紹介メール"
}

Literal を使うことで、許可された値以外は型エラーになります。
これにより、案件紹介メールだったらその後処理を行う。人材紹介メールやその他のメールだったら処理をしない、などのワークフローを実現できます。
弊社で作成した下記システムでは、この方式を応用してメールを分類しています。
生成AIによる構造化出力とベクトル検索で、SES案件探索を効率化

それでも注意すべき点

構造化出力は非常に強力ですが、

  • 完全保証ではない
  • モデル対応状況は要確認
  • アプリ側バリデーションは依然としてあったほうが安心

といった前提は変わりません。
しかし、従来の「プロンプトで祈る」方式と比べると、安定性は大きく向上します。

まとめ

生成AIは本質的に不確実なモデルです。それをシステムに組み込む以上、出力構造の制御は避けて通れません。
構造化出力は、

  • 不確実性を抑制し
  • 型安全な連携を可能にし
  • 実装と運用の安定性を高める

ための重要な仕組みです。生成AIを“チャットツール”として使う段階であれば必須ではないかもしれません。
しかし、業務システムの一部として設計するのであれば、構造化出力はすでに標準的な構成要素のひとつと言ってよいでしょう。

もはや「あると便利な機能」ではなく、生成AIを安全に組み込むための前提条件に近い存在になっています。

生成AI活用支援サービスのご紹介

Tech Funでは、お客様のフェーズに合わせ、生成AI活用に向けた支援を3つのパックでご提供しています。

  1. 無料診断パック:業務・プロセスの現状を無料で診断し、生成AI活用の可能性をレポートします。
  2. 検証(PoC)パック:診断で有効性が確認された業務を対象に、プロトタイプ構築を支援します。
  3. コンサルティングサービス:生成AI導入戦略の策定から運用体制構築までを包括的に支援します。

生成AIに限らず、Web・業務システム開発やインフラ設計など、技術領域を問わずご相談を承っています。「何から始めれば良いか分からない」という段階でも構いませんので、ぜひお気軽にお問い合わせください。

執筆・編集

Tech Fun Magazine R&Dチーム
Tech Funの生成AI研究に携わるエンジニアが、最新のAIモデル動向やプロンプト設計、実業務への応用手法など、生成AIに特化した知見を執筆・編集しています。
モデル評価や業務シナリオに応じたAI活用設計など、日々のR&D活動で得られる実践的なノウハウをわかりやすく紹介します。

ARTICLE
生成AI関連記事一覧

生成AI関連

Structured Outputsの基本と実践

生成AI関連

AI最新情報のキャッチアップ術

生成AI関連

Knowledge Base × S3 Vectors In…

生成AI関連

RAGの評価指標 ─ 何を・どう測るかを整理する

生成AI関連

Guardrails for Amazon Bedrock …

生成AI関連

生成AIに精度を求めすぎない

生成AI関連

Amazon Bedrock Knowledge Baseで…

生成AI関連

はじめての人のための:AI・機械学習・統計・生成AI・AGI…

生成AI関連

Amazon Bedrock Knowledge Base …

記事一覧を見る