Bedrock Structured Outputs がGAに — Anthropic APIと同時リリース、ベータから3ヶ月で正式版へ

AWSAmazon BedrockStructured OutputsClaudeAnthropicJSON Schema

Amazon Bedrock で Structured Outputs(構造化出力)がGAになりました。JSON Schemaを指定すると、モデルの出力が100%そのスキーマに準拠することが保証される機能です。

AnthropicのClaude APIでは2025年11月にパブリックベータとして先行公開されていて、自分もベータの段階から使っていました。100%スキーマに準拠したレスポンスが得られるので、バリデーションやリトライのコードが丸ごと不要になります。そして2026年2月4日、AnthropicのGA(2026年2月4日 Anthropic Blog)と同日にBedrockでもGAとなりました。

Structured Outputs以前の苦しみ

LLMにJSONを出力させたい場面は多いです。APIレスポンスの生成、データ抽出、ツール呼び出しのパラメータ構築など。ただ、これまではプロンプトで「JSONで返してください」とお願いするしかなく、実運用では以下のような問題が頻発していました。

  • json.loads() でパースに失敗する(Markdownのコードブロックで囲んでくる、余計なテキストが混ざる)
  • 必須フィールドが欠けている
  • 型が合わない(数値が期待されるところに文字列 "2" が返ってくる)
  • スキーマにないフィールドが勝手に追加される

これらに対処するために、バリデーション → リトライ → フォールバックのロジックを書くのが「LLM開発あるある」でした。Structured Outputsはこの問題を根本から解決します。

Constrained Decodingの仕組み

Structured Outputsは単なるプロンプトエンジニアリングではなく、**Constrained Decoding(制約付きデコーディング)**という仕組みで実現されています。SDKやアプリケーション側でバリデーションやリトライを行うのではなく、基盤モデル側で出力の準拠を保証する方法です。

AWSブログには以下のように記載されています(2026年2月6日 AWS Blog)。

This represents a paradigm shift in AI application development. Instead of validating JSON responses and writing fallback logic for when they fail, you can move straight to building with the data.

つまり、モデルがトークンを生成する際に、指定されたJSON Schemaに違反するトークンをそもそも生成候補から除外します。これにより、出力が確定的にスキーマに準拠します。「お願い」ではなく「強制」なので、100%保証されるわけです。

AWSの公式ドキュメントではこの内部処理を「grammar」と呼んでいます(AWS ドキュメント)。

First-time compilation – For new schemas, Amazon Bedrock compiles the grammar, which may take up to a few minutes.

Caching – Successfully compiled grammars are cached for 24 hours from first access. Cached grammars are encrypted with AWS-managed keys.

ここで言う「grammar」は形式言語理論における形式文法(Formal Grammar)のことで、JSON Schemaから「この構造に合致するトークン列のみを許可する」ルールセットを生成しています。初回リクエスト時にこの変換処理が走るため少し遅くなりますが、変換結果は24時間キャッシュされます。

スキーマの構造を変更するとキャッシュが無効になりますが、namedescription フィールドだけの変更ではキャッシュは維持されるとのことです(AWS Blog)。

Anthropic APIのベータから3ヶ月でGAへ

Structured Outputsのタイムラインを整理しておきます。

  • 2025年11月: Anthropic Claude APIでパブリックベータ公開(ベータヘッダー structured-outputs-2025-11-13 が必要)
  • 2026年2月4日: Anthropic Claude APIでGA + Amazon BedrockでもGA(同日リリース)

Anthropicのブログでは以下のように述べられています(2026年2月4日 Anthropic Blog)。

GA adds support for more complex schemas.

ベータ期間中はBedrock経由では使えなかったため、Bedrockユーザーにとっては約3ヶ月待つ必要がありました。ただし、GAのタイミングではAnthropic APIとBedrockが同日リリースとなっており、AWS側がAnthropicと連携してGA日程を合わせたのだと思います。

なお、GAに伴いベータヘッダーは不要になり、パラメータも output_format から output_config.format に移行されています。

2つの利用方法

Bedrockでは2つの方法でStructured Outputsを利用できます。

JSON Schema Output Format

モデルのレスポンス全体を指定したJSON Schemaに準拠させる方法です。データ抽出やAPIレスポンスの生成に使います。

Converse APIでは outputConfig.textFormat に、InvokeModel API(Claude)では output_config.format にスキーマを指定します。

Strict Tool Use

ツール呼び出し時のパラメータをスキーマに準拠させる方法です。ツール定義に strict: true を追加するだけで有効になります。エージェントワークフローで外部関数を呼ぶ際に、パラメータの型や必須フィールドが保証されるので安心です。

自分の環境で試してみた

実際にBedrock Converse APIでStructured Outputsを試してみました。

  • AWS CLI: aws-cli/2.34.3
  • リージョン: us-east-1
  • モデル: us.anthropic.claude-sonnet-4-5-20250929-v1:0(クロスリージョン推論プロファイル)

JSON Schema Output Formatの検証

テキストからリード情報を抽出するスキーマを定義して試しました。--output-config に渡すJSON(整形版)は以下の通りです。

{
  "textFormat": {
    "type": "json_schema",
    "structure": {
      "jsonSchema": {
        "schema": "{...}",
        "name": "lead_extraction",
        "description": "Extract lead info from text"
      }
    }
  }
}

schema の中身(JSON文字列として渡す部分):

{
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "email": { "type": "string" },
    "plan": { "type": "string" },
    "demo_requested": { "type": "boolean" }
  },
  "required": ["name", "email", "plan", "demo_requested"],
  "additionalProperties": false
}

実際のCLIコマンド:

aws bedrock-runtime converse \
  --profile your-profile \
  --region us-east-1 \
  --model-id us.anthropic.claude-sonnet-4-5-20250929-v1:0 \
  --messages '[{"role":"user","content":[{"text":"以下のテキストから情報を抽出してください: 田中太郎さん(taro.tanaka@example.com)はProプランに興味があり、来週火曜にデモを希望しています。"}]}]' \
  --inference-config '{"maxTokens":1024}' \
  --output-config '{"textFormat":{"type":"json_schema","structure":{"jsonSchema":{"schema":"{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\"},\"email\":{\"type\":\"string\"},\"plan\":{\"type\":\"string\"},\"demo_requested\":{\"type\":\"boolean\"}},\"required\":[\"name\",\"email\",\"plan\",\"demo_requested\"],\"additionalProperties\":false}","name":"lead_extraction","description":"Extract lead info from text"}}}}'

CLIだとスキーマをJSON文字列としてエスケープする必要があるので見にくいですが、上の整形版と見比べれば構造は分かると思います。

レスポンス:

{
    "output": {
        "message": {
            "role": "assistant",
            "content": [
                {
                    "text": "{\"name\":\"田中太郎\",\"email\":\"taro.tanaka@example.com\",
                             \"plan\":\"Pro\",\"demo_requested\":true}"
                }
            ]
        }
    },
    "stopReason": "end_turn",
    "usage": {
        "inputTokens": 277,
        "outputTokens": 34,
        "totalTokens": 311
    },
    "metrics": {
        "latencyMs": 4370
    }
}

きちんとスキーマ通りのJSONが返ってきました。demo_requested が文字列ではなく true(boolean)で返っているのがポイントです。Structured Outputs以前は「true」(文字列)で返ってくることもあったので、この確実性はありがたいです。

Strict Tool Useの検証

ツール定義に strict: true を付けて試しました。

--tool-config に渡すツール定義:

{
  "tools": [{
    "toolSpec": {
      "name": "get_weather",
      "description": "指定された場所の天気を取得する",
      "strict": true,
      "inputSchema": {
        "json": {
          "type": "object",
          "properties": {
            "location": { "type": "string", "description": "都市名" },
            "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "温度の単位" }
          },
          "required": ["location", "unit"],
          "additionalProperties": false
        }
      }
    }
  }]
}

実行コマンド:

aws bedrock-runtime converse \
  --profile your-profile \
  --region us-east-1 \
  --model-id us.anthropic.claude-sonnet-4-5-20250929-v1:0 \
  --messages '[{"role":"user","content":[{"text":"東京の天気を教えて"}]}]' \
  --inference-config '{"maxTokens":1024}' \
  --tool-config '<上記のJSONを1行にしたもの>'

レスポンス:

{
    "output": {
        "message": {
            "role": "assistant",
            "content": [
                {
                    "toolUse": {
                        "toolUseId": "tooluse_AbCdEfGhIjKlMnOpQrStUv",
                        "name": "get_weather",
                        "input": {
                            "location": "東京",
                            "unit": "celsius"
                        }
                    }
                }
            ]
        }
    },
    "stopReason": "tool_use",
    "usage": {
        "inputTokens": 700,
        "outputTokens": 71,
        "totalTokens": 771
    },
    "metrics": {
        "latencyMs": 23762
    }
}

unit フィールドが enum で定義した "celsius""fahrenheit" のどちらかに必ずなること、未定義のフィールドが混入しないことが保証されます。enum の保証についてはAWSブログでも以下のように言及されています(2026年2月6日 AWS Blog)。

Use enum for constrained values. When a field has a limited set of valid values, use enum to constrain options. This improves accuracy and produces valid values.

Constrained Decodingの仕組み上、スキーマに準拠した値しか生成されないので、enum で定義した選択肢以外の値が返ることはありません。エージェントを組む上でこの信頼性は大きいです。

ただし、Strict Tool Useの初回リクエストでレイテンシが約24秒かかっている点は注意が必要です。これは前述の形式文法への変換処理が発生しているためで、同じスキーマでの2回目以降はキャッシュが効いて速くなります。

additionalProperties: false は必須

試しに additionalProperties: false を省略してリクエストしてみたところ、以下のエラーが返りました。

An error occurred (ValidationException) when calling the Converse operation:
The model returned the following errors: output_config.format.schema:
For 'object' type, 'additionalProperties' must be explicitly set to false

Bedrockでは additionalProperties: false の指定が必須です。省略するとバリデーションエラーになります。これはAnthropicのAPIでも同じ要件ですが、Bedrockではリクエスト時点で弾いてくれるのでわかりやすいと思います。

Converse APIの jsonSchema.name フィールド

Converse APIでStructured Outputsを使う際に気になるのが、jsonSchema の中に name フィールドが必須になっている点です。

{
  "jsonSchema": {
    "schema": "{...}",
    "name": "lead_extraction",
    "description": "Extract lead info from text"
  }
}

この name はJSON Schemaの仕様にはないフィールドで、Bedrock側の要件です。公式ドキュメントにはこのフィールドが何に使われるかの説明がありません。AWSブログでは「namedescription の変更ではキャッシュは維持される」と述べられているので、少なくとも形式文法のキャッシュキーではないようです。OpenAIのStructured Outputsでも同様に name フィールドが必要なので、スキーマの識別子として業界的な慣習になりつつあるのかもしれませんが、正直なところ何のために必須なのかはよく分かりません。

API間の実装差異

AWSブログでも説明されていますが(2026年2月6日 AWS Blog)、Converse APIとInvokeModel APIでパラメータの構造がかなり異なります。

項目Converse APIInvokeModel(Claude)InvokeModel(オープンウェイト)
スキーマ指定場所outputConfig.textFormatoutput_config.formatresponse_format
スキーマ形式JSON文字列JSONオブジェクトJSONオブジェクト
strict指定toolSpec.stricttools[].stricttools[].function.strict

特にConverse APIではスキーマをJSON文字列として渡す必要があるのに対し、InvokeModel APIではJSONオブジェクトとしてそのまま渡すという違いがあります。CLIで試す場合、Converse APIはエスケープが二重になって地獄なので、実際にはPython SDKなどを使うのが現実的です。

対応モデル

ここが重要なポイントですが、Bedrockの全モデルで使えるわけではありません。対応しているのは一部のモデルに限られます(AWS ドキュメント)。

Anthropic(Claudeモデル):

  • Claude Haiku 4.5(anthropic.claude-haiku-4-5-20251001-v1:0
  • Claude Sonnet 4.5(anthropic.claude-sonnet-4-5-20250929-v1:0
  • Claude Opus 4.5(anthropic.claude-opus-4-5-20251101-v1:0
  • Claude Opus 4.6(anthropic.claude-opus-4-6-v1

その他のプロバイダー:

  • DeepSeek: DeepSeek-V3.1
  • Google: Gemma 3 12B / 27B
  • MiniMax: M2
  • Mistral AI: Magistral Small、Ministral 3B / 8B / 14B、Mistral Large 3、Voxtral各種
  • Moonshot AI: Kimi K2 Thinking
  • NVIDIA: Nemotron Nano 12B / 9B v2
  • OpenAI: gpt-oss-120b / 20b
  • Qwen: Qwen3 235B / 32B、Coder 30B / 480B、Next 80B、VL 235B

注意すべきは、Claude 3.x系(Claude 3 Sonnet、Claude 3.5 Sonnet、Claude 3.7 Sonnet)はStructured Outputsの対象外ということです。Claude 4.5以降が必要になります。Claude 3.5 Sonnet v2をまだ使っているプロジェクトは多いと思いますが、Structured Outputsを使いたい場合はモデルのアップグレードが必要です。

制約事項とSDKの自動変換

公式ドキュメントに記載されている主な制約です(AWS ドキュメント)。

  • 再帰的スキーマはサポートされません(ツリー構造のような自己参照型)
  • 外部 $ref は使えません(内部参照の $ref / $def は可能)
  • 数値の制約はサポートされませんminimummaximummultipleOf
  • 文字列の制約はサポートされませんminLengthmaxLength
  • 配列の minItems は0と1のみ対応
  • Anthropicモデルでは Citations(引用)との併用ができません(400エラー)

特にCitationsとの非互換は、RAGパイプラインでStructured Outputsを使おうとした時にハマりそうなポイントです。

Anthropic SDKの自動変換とBedrock CLIの違い

ここで注意したいのが、AnthropicのPython/TypeScript SDKとBedrock CLIで挙動が異なる点です。

AnthropicのClaude APIドキュメントには以下のように記載されています(Anthropic ドキュメント)。

The Python and TypeScript SDKs automatically transform schemas with unsupported features:

  1. Remove unsupported constraints (e.g., minimum, maximum, minLength, maxLength)
  2. Update descriptions with constraint info (e.g., “Must be at least 100”), when the constraint is not directly supported with structured outputs
  3. Add additionalProperties: false to all objects
  4. Filter string formats to supported list only
  5. Validate responses against your original schema (with all constraints)

つまり、AnthropicのSDKを使えば非対応の制約が含まれていても自動的に削除・変換してくれます。さらに、削除した制約を description に転記してモデルに伝え、レスポンス受信後に元のスキーマでバリデーションまでしてくれるという親切設計です。具体例もドキュメントに記載されています(Anthropic ドキュメント)。

Example: A Pydantic field with minimum: 100 becomes a plain integer in the sent schema, but the description is updated to “Must be at least 100”, and the SDK validates the response against the original constraint.

一方、Bedrock側ではAWSのドキュメントにシンプルに以下のように記載されています(AWS ドキュメント)。

If the schema contains unsupported features, Amazon Bedrock returns a 400 error immediately.

ただし、自分の環境でCLIから試したところ、実際にはこのドキュメントの記載通りではなく、制約の種類によって挙動が異なりました。Bedrock側でもAnthropicのSDKと同様に、内部的に一部の制約を処理している可能性があります。

minimum / maximum(integer型) → バリデーションエラーで弾かれます。

An error occurred (ValidationException) when calling the Converse operation:
The model returned the following errors: output_config.format.schema:
For 'integer' type, properties maximum, minimum are not supported

minLength / maxLength(string型) → エラーにならずリクエストが通ります。しかも実際に検証してみると、Constrained Decodingで制約が効いているように見えます。

同じプロンプト(「1文字のユーザー名を生成してください」)で minLength: 10 の有無を比較しました。

minLength: 10 あり:

{"username":"abcdefghij"}

minLength なし:

{"username":"a"}

minLength: 10 を指定すると、「1文字にしろ」と強く指示しても10文字の文字列が返ってきます。ドキュメントには「サポートされていない」と書かれていますが、少なくともCLI経由のConverse APIでは minLength / maxLength はエラーにもならず、しかも実際に効いているように見えます。

ただし、これはドキュメントに記載のない挙動なので、本番環境で頼るのはおすすめしません。公式にサポートされていない以上、将来のアップデートで挙動が変わる可能性があります。SDKを使わずにAPIを直接叩く場合は、非対応の制約を自前で除去してからリクエストする方が安全です。

まとめ

Bedrock Structured Outputsは、LLMの出力を「お願い」から「保証」に変える機能です。自分としては、Anthropic APIで先に使えていたので待ちわびていたアップデートでした。

実運用での一番の恩恵は、バリデーション・リトライのコードが丸ごと不要になることだと思います。JSONパースに失敗して try/except でリトライするあのパターンとはおさらばです。

一方で、APIごとにパラメータ構造が異なる点、additionalProperties: false が必須な点、Citationsとの非互換など、使い始める前に知っておくべきことは結構あります。特にConverse APIとInvokeModel APIの差異は、ドキュメントをよく読まないとハマると思います。

Structured Outputsは全ての商用AWSリージョンで利用可能です。AWS CLI 2.33.14以降が必要です(CHANGELOG 2.33.14)。