構造化出力は巧妙なパースに勝る
まだモデルの文章から正規表現でJSONを抜き出しているのか。やめよう。Bedrockの構造化出力はデコード時にJSON Schemaを強制するので、レスポンスは構造上正しくなる。

アプリケーションがいまだにモデルの散文から正規表現でJSONを抜き出しているなら、それはAmazon Bedrockがすでにデコード層で解決している問題を解こうとしていることになる。2026年2月からBedrockで一般提供されている構造化出力は、トークンを生成している最中にモデルをJSON Schemaに制約するので、レスポンスは希望通りの形に、期待ではなく構造として一致する。正規表現は本来の解決策ではなかった。それは「JSONを返してください」とモデルにお願いし、うまくいかなかったときに後始末をするという症状だった。
パース方式は、稀であるがゆえに厄介な失敗の仕方をする。9割数パーセントのレスポンスはパースできる。残りはJSONをmarkdownのフェンスで囲んだり、その前に愛想の良い一文を添えたり、末尾にカンマを残したり、フィールドを幻覚させたりする。そしてパーサーは、テストしていなかった入力に対して、最悪のタイミングで本番環境で例外を投げる。制約付きデコードはこの失敗クラスをまるごと取り除く。無効なトークンがそもそも生成されないからだ。
構造を得る3つの方法、強度の順に
プロンプトして祈る
システムプロンプトでJSONを求め、おそらく例も示し、テキストをパースする。これはうまくいく、うまくいかなくなるまでは。保証も強制力もなく、その失敗率はまさにデモには決して現れないロングテールそのものだ。パーサーを堅牢化するために費やす1時間はすべて、プラットフォームが排除できる問題に費やされた1時間だ。
スキーマとしてのツール利用
ネイティブな構造化出力が登場するずっと前から、信頼できる手法は、入力スキーマを欲しい形に定義したツールを1つ用意し、メッセージテキストではなくツール呼び出しの引数を読むことだった。モデルがツール入力を埋め、こちらは構造化されたオブジェクトを得る。同じ呼び出しで実際にツールを呼び出す必要がある場合、これは今でも良いパターンだ。Bedrockでは、ツール定義にstrict: trueを追加できるようになり、ツール名と入力が単に示唆されるのではなくスキーマに対して検証されるようになった。
ネイティブな構造化出力
直接的な方法は、レスポンスの形をJSON Schemaとして宣言し、生成中にBedrockに強制させることだ。JSON Schema Draft 2020-12と制約付きデコードを使うため、モデルはスキーマを破壊するトークンを物理的に生成できない。Converse APIではこのフィールドはoutputConfig.textFormatで、スキーマにはnameが必要になる。Converse、ConverseStream、InvokeModel、InvokeModelWithResponseStreamにわたって、Anthropic Claude 4.5と一部のオープンウェイトモデル群で利用できる。
// Converse: enforce the shape instead of parsing for it
outputConfig: {
textFormat: {
jsonSchema: {
name: "extraction",
schema: {
type: "object",
properties: {
invoice_id: { type: "string" },
total_cents: { type: "integer" },
currency: { type: "string", enum: ["USD", "EUR", "GBP"] }
},
required: ["invoice_id", "total_cents", "currency"],
additionalProperties: false
}
}
}
}強制することで得られるもの
明白な利点は出力がパース可能になることだ。より大きな利点は削除できるものにある。無効時のリトライループ、JSON修復ライブラリ、「無効なJSONが返されました、もう一度試してください」と伝える防御的な再プロンプト、そしてそれでも失敗したときに鳴るアラート。これらはすべて、確率的な出力を補うためのものだった。デコード時にスキーマが強制されれば、その補償はデッドコードになる。リトライが減れば、トークン数も減り、レイテンシも下がる。最初の呼び出しを修正するための2回目の呼び出しに料金を払わなくて済むからだ。
additionalProperties: falseを設定し、フィールドをrequiredとマークして、スキーマを厳密にしておこう。追加のキーを許す緩いスキーマは、せっかく手に入れた保証の一部を手放すことになる。モデルはこちらのコードが想定していないフィールドを依然として付加できてしまうからだ。
適用できない場面
構造化出力は、抽出、分類、ルーティング、関数の引数など、下流のシステムが消費する機械可読なレスポンスのためのものだ。人間が読む散文には向いていない。硬直したスキーマはその目的と衝突する。そして構造化出力が制約するのは形であって真実ではない。スキーマはtotal_centsが整数であることを保証するが、それが正しい整数であることは保証しない。値、範囲、ビジネスルールの検証は依然としてこちら側の仕事だ。スキーマはパースの失敗モードを取り除くが、モデルが正しい答えを出したかを確認する必要性までは取り除かない。
まとめ
巧妙なパースは、モデルが決して与えてくれなかった保証を回避するために費やす労力だ。Bedrockの構造化出力はその保証をデコーダーの中に移す。JSON Schemaを宣言すれば、レスポンスは構造上正しくなる。純粋なデータにはネイティブな構造化出力を、同じ呼び出しで実際に行動もする場合はツール利用スキーマを使い、いずれにせよ値レベルの検証は維持しよう。そのうえで正規表現、修復ループ、リトライを削除すればいい。それらは、もう存在しない問題のために組んだ足場だったのだから。
次に読むべき記事
- Stop Fine-Tuning. You Need RAG, a Cache, and Better Prompts、学習ではなくプロンプトとプラットフォームの機能でフォーマットと振る舞いを解決する記事。
- Streaming Responses Are a UX Decision, Not a Performance One、ストリーミングと構造化出力がなぜ逆方向に引っ張り合うのかについての記事。
これを確実に届けるためのプラットフォームと配信の側面については、クラウド分野のフィールドノートをercan.cloudで、ハブをercanermis.comで公開している。
Ercan の他のサイト
同じ著者、別の領域のサイトが2つ。