Amazon BedrockでStructured Outputs試してみた

に公開

はじめに

Fusicのレオナです。今回は2026年2月にAmazon BedrockにStructured Outputsが追加されたので試してみます。

Structured Outputsとは

基盤モデルからの応答を、ユーザー定義のJSONスキーマに厳密に準拠させることが可能になりました。
従来のプロンプトベースのアプローチでは、モデルにユーザーの望むJSON形式で出力させることが不確実であり、バリデーション処理などの工夫が必要でした。Structured Outputsはこの問題を解決してくれます。

https://aws.amazon.com/jp/about-aws/whats-new/2026/02/structured-outputs-available-amazon-bedrock/

また対応されてるAPIは以下の通りです。本ブログではClaude Sonnet 4.5を用い、ConverseAPIで試してみます。

  • Converse/ConverseStream API
  • InvokeModel/InvokeModelWithResponseStream API
  • Cross-Region Inference
  • Batch Inference

実装

0. 環境構築

AWS認証情報が設定されていることを確認してください。
また、今回はuvboto3をインストールします。

Terminal
uv venv && source .venv/bin/activate && uv pip install boto3

1.実際に試してみる

サンプルテキストとして試験問題に対してAIが回答を生成する際に、固定の選択肢と自由記述を組み合わせた構造化された回答が得られるか確認します。

example_converse_api.py
"""
Amazon Bedrock Structured Outputs - ConverseAPI サンプル
JSON Schema出力フォーマットを使用した例
"""

import boto3
import json

# Bedrockクライアントの作成
session = boto3.Session(profile_name='お使いのAWS Profile名')
bedrock_runtime = session.client('bedrock-runtime', region_name='us-east-1')

# 試験問題
exam_question = """
【問題】
ある企業のWebアプリケーションで、ユーザーからの入力データをそのままSQLクエリに埋め込んでいる箇所が発見されました。

コード例:
query = "SELECT * FROM users WHERE username = '" + user_input + "'"

問1. このコードにはどのような脆弱性がありますか?
  A. クロスサイトスクリプティング(XSS)
  B. SQLインジェクション
  C. クロスサイトリクエストフォージェリ(CSRF)
  D. バッファオーバーフロー

問2. この脆弱性を悪用された場合、どのような被害が想定されますか?

問3. この脆弱性を修正するための適切な対策を説明してください。
"""

# 回答のJSON Schema定義
output_schema = {
    'type': 'object',
    'properties': {
        'question1': {
            'type': 'object',
            'properties': {
                'answer': {
                    'type': 'string',
                    'enum': ['A', 'B', 'C', 'D'],
                    'description': '選択肢の回答'
                },
                'explanation': {
                    'type': 'string',
                    'description': '選択した理由の説明'
                }
            },
            'required': ['answer', 'explanation'],
            'additionalProperties': False
        },
        'question2': {
            'type': 'object',
            'properties': {
                'potential_damages': {
                    'type': 'array',
                    'items': {'type': 'string'},
                    'description': '想定される被害のリスト'
                },
                'severity': {
                    'type': 'string',
                    'enum': ['低', '中', '高', '緊急'],
                    'description': '深刻度'
                }
            },
            'required': ['potential_damages', 'severity'],
            'additionalProperties': False
        },
        'question3': {
            'type': 'object',
            'properties': {
                'recommended_fix': {
                    'type': 'string',
                    'enum': ['プリペアドステートメント', 'エスケープ処理', '入力バリデーション', 'WAF導入'],
                    'description': '最も推奨される対策'
                },
                'code_example': {
                    'type': 'string',
                    'description': '修正後のコード例'
                },
                'additional_measures': {
                    'type': 'array',
                    'items': {'type': 'string'},
                    'description': '追加で検討すべき対策'
                }
            },
            'required': ['recommended_fix', 'code_example', 'additional_measures'],
            'additionalProperties': False
        }
    },
    'required': ['question1', 'question2', 'question3'],
    'additionalProperties': False
}


def answer_exam():
    """ConverseAPIを使用して試験問題に回答"""

    response = bedrock_runtime.converse(
        modelId='us.anthropic.claude-sonnet-4-5-20250929-v1:0',
        messages=[
            {
                'role': 'user',
                'content': [
                    {
                        'text': '以下の試験問題に回答してください。JSONスキーマに従って構造化された形式で回答してください。'
                    },
                    {
                        'text': exam_question
                    }
                ]
            }
        ],
        additionalModelRequestFields={
            'output_config': {
                'format': {
                    'type': 'json_schema',
                    'schema': output_schema
                }
            }
        }
    )

    # レスポンスから結果を取得
    result = response['output']['message']['content'][0]['text']
    parsed_result = json.loads(result)

    return parsed_result


if __name__ == '__main__':
    result = answer_exam()
    print(json.dumps(result, ensure_ascii=False, indent=2))

結果

Terminal
uv run python example_converse_api.py
{
  "question1": {
    "answer": "B",
    "explanation": "ユーザー入力を直接SQLクエリ文字列に連結しているため、SQLインジェクション脆弱性が存在します。攻撃者が特殊な文字列(例:' OR '1'='1)を入力することで、SQL文の構造を改変し、意図しないデータベース操作が可能になります。XSSはHTML出力時の問題、CSRFはリクエスト送信の問題、バッファオーバーフローはメモリ管理の問題であり、このケースには該当しません。"
  },
  "question2": {
    "potential_damages": [
      "データベース内の全ユーザー情報の不正取得",
      "機密データ(パスワード、個人情報、クレジットカード情報等)の漏洩",
      "データベースレコードの不正な変更・削除",
      "管理者権限の不正取得によるシステム全体の乗っ取り",
      "データベースサーバー上での任意のコマンド実行(権限次第)",
      "他のテーブルやデータベースへの不正アクセス",
      "サービス妨害攻撃によるシステムダウン"
    ],
    "severity": "緊急"
  },
  "question3": {
    "recommended_fix": "プリペアドステートメント",
    "code_example": "# Pythonの例(プリペアドステートメント)\nimport sqlite3\n\nconn = sqlite3.connect('database.db')\ncursor = conn.cursor()\n\n# プレースホルダを使用\nquery = \"SELECT * FROM users WHERE username = ?\"\ncursor.execute(query, (user_input,))\n\n# または名前付きパラメータ\nquery = \"SELECT * FROM users WHERE username = :username\"\ncursor.execute(query, {'username': user_input})",
    "additional_measures": [
      "入力値の厳格なバリデーション(ホワイトリスト方式による許可文字の制限)",
      "最小権限の原則に基づくデータベースユーザー権限の設定",
      "WAF(Web Application Firewall)の導入による攻撃パターンの検知・遮断",
      "データベースエラーメッセージの適切なハンドリング(詳細情報の非表示)",
      "定期的なセキュリティ監査とペネトレーションテスト",
      "ログ監視による不正アクセスの早期検知",
      "ORMフレームワークの活用によるSQL文の自動生成"
    ]
  }
}

最後に

Amazon BedrockのStructured Outputsは、本番環境でのAIアプリケーション開発にとても使えると思いました。JSONスキーマ準拠を保証することでシステムの信頼性を向上させることができます。

Fusic 技術ブログ

Discussion