
こんにちは、サーバーワークスで生成AIの活用推進を担当している針生です。
Amazon Bedrock 経由で Claude を使って JSON を生成するとき、レスポンスに余計な説明文やマークダウンが混ざって困った経験はないでしょうか。Bedrock の Converse API には outputConfig パラメータがあり、JSON Schema を渡すことで API レベルでスキーマへの準拠が保証されます。
問題:デフォルトでは余計なものが返ってくる
AWS EventBridge のルールを JSON 形式で生成する Web アプリを例に考えます。ユーザーが説明を入力して「生成」ボタンを押すと、コピーしてすぐ使える JSON が表示されるといったアプリです。
Claude にそのままリクエストを投げると、返ってくるのは次のようなレスポンスです。
# EventBridge Rule
以下の JSON は EC2 インスタンスの状態変化を検知するルールです。
\`\`\`json
{
"source": ["aws.ec2"],
"detail-type": ["EC2 Instance State-change Notification"],
"detail": {
"state": ["running"]
}
}
\`\`\`
このルールは EC2 インスタンスが起動したときにイベントを捕捉します。
アプリでそのまま使える JSON データが必要な場合、コードブロックや説明文を取り除く処理を追加する必要があります。正規表現で対処することもできますが、Claude のレスポンス形式が変わると壊れるリスクもあり、堅牢とは言えません。
解決策:Converse API の outputConfig
Converse API は Amazon Bedrock が提供する推論 API です。Claude をはじめとする基盤モデルをモデルごとの差異を意識せず統一されたインターフェイスで呼び出せます。boto3 の bedrock-runtime クライアントから converse() メソッドで利用します。
この API には outputConfig パラメータがあり、textFormat に JSON Schema を渡すことで Claude が返すテキストをスキーマに準拠した JSON のみに制限できます。なお、InvokeModel でも構造化出力は利用できますが、パラメータ名が異なります(Converse API: outputConfig.textFormat / InvokeModel: output_config.format)。
import boto3 import json client = boto3.client("bedrock-runtime", region_name="us-east-1") # Structured Outputs の対応リージョンを確認してください schema = { "type": "object", "properties": { "source": { "type": "array", "items": {"type": "string"} }, "detail-type": { "type": "array", "items": {"type": "string"} }, "detail": { "type": "object", "additionalProperties": False } }, "required": ["source", "detail-type"], "additionalProperties": False } response = client.converse( modelId="us.anthropic.claude-sonnet-4-6", messages=[ { "role": "user", "content": [ {"text": "EC2 インスタンスの状態変化を検知する EventBridge ルールを JSON で生成してください"} ], } ], outputConfig={ "textFormat": { "type": "json_schema", "structure": { "jsonSchema": { "name": "eventbridge_rule", "description": "EventBridge ルールの JSON 定義", "schema": json.dumps(schema), # JSON Schema は文字列で渡す } } } }, ) text = response["output"]["message"]["content"][0]["text"] parsed = json.loads(text) print(json.dumps(parsed, indent=2))
実行すると、次のように JSON テキストのみが返ってきます。
{ "source": [ "aws.ec2" ], "detail-type": [ "EC2 Instance State-change Notification" ], "detail": {} }
説明文もコードブロックの区切り文字も含まれず、json.loads() に直接渡せます。スキーマに違反する出力はそもそも生成されないため、パースエラーも発生しません。
スキーマを定義する際、type: "object" のフィールドにはネストされたオブジェクトも含めてすべて additionalProperties: False を明示する必要があります。省略するとバリデーションエラーになるため注意してください。
なお、スキーマの初回コンパイルに数分かかる場合があります。2 回目以降は 24 時間キャッシュされるため、通常のレスポンス速度で返ってきます。
まとめ
Bedrock の Converse API に outputConfig を指定すると、Claude からクリーンな JSON を確実に取得できます。正規表現による後処理も、プロンプトで「JSON だけ返して」と指示する手間も不要です。
outputConfig={
"textFormat": {
"type": "json_schema",
"structure": {
"jsonSchema": {
"name": "スキーマ名",
"schema": json.dumps(schema),
}
}
}
}
JSON を扱う Bedrock アプリの定番パターンとして、ぜひ活用してください。
参考
針生 泰有(執筆記事の一覧)
サーバーワークスで生成AIの活用推進を担当