
Bedrock の Structured outputs が Claude 全モデルでサポート、最新 Haiku 5.5 で試してみた
はじめに
2026年10月20日、Amazon Bedrock の Structured outputs が、bedrock-runtime と bedrock-mantle(Messages API)の両エンドポイントで、Claude の全モデルに対応したと案内されました。
Structured outputs 自体は 2026年2月4日に一般提供されている機能でしたが、今回、Haiku 5.5 など、最新のモデルカードにも対応が明記されました。
この記事では、bedrock-runtime のエンドポイントを対象に、InvokeModel、Converse、strict tool use の3つのリクエスト形を Claude Haiku 5.5 で実行し、応答の構造を確認しました。
検証環境
| 項目 | 値 |
|---|---|
| リージョン | ap-northeast-1 |
| Haiku 5.5 のモデル ID | global.anthropic.claude-haiku-5-5、jp.anthropic.claude-haiku-5-5 |
| boto3 | 1.43.116 |
3つのリクエスト形の動作確認
CSV 集計、sentiment 分類、商品検索のツール呼び出しを、global プロファイルで実行しました。
| リクエスト形 | 入力 | 結果 |
|---|---|---|
| InvokeModel の output_config.format | CSV 集計 | total_sales 746000、total_items 36 |
| Converse の outputConfig.textFormat | CSV 集計 | 同じ値 |
| Converse の outputConfig.textFormat | sentiment 分類 | {"sentiment": "positive"} |
| Converse の strict tool use | 商品検索 | {"category": "electronics", "price_limit": 50000} |
| InvokeModel の strict tool use | 商品検索 | 同じ引数 |
5つの応答はすべてスキーマ通りでした。
InvokeModel で CSV 集計を実行するコード全体
text の取り出し方は、次の節で説明します。
import json
import boto3
MODEL_ID = "global.anthropic.claude-haiku-5-5"
client = boto3.client("bedrock-runtime", region_name="ap-northeast-1")
CSV_INPUT = """date,product,quantity,price,region
2026-01-15,ノートPC,5,89800,東京
2026-01-15,マウス,20,2980,大阪
2026-01-16,キーボード,8,12800,東京
2026-01-16,モニター,3,45000,福岡"""
CSV_SCHEMA = {
"type": "object",
"properties": {
"total_sales": {"type": "integer", "description": "総売上金額"},
"total_items": {"type": "integer", "description": "総販売数"},
"by_region": {
"type": "array",
"items": {
"type": "object",
"properties": {"region": {"type": "string"}, "sales": {"type": "integer"}},
"required": ["region", "sales"],
"additionalProperties": False,
},
},
"top_product": {"type": "string", "description": "最も売上が高い商品"},
},
"required": ["total_sales", "total_items", "by_region", "top_product"],
"additionalProperties": False,
}
response = client.invoke_model(
modelId=MODEL_ID,
body=json.dumps({
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 1024,
"messages": [{"role": "user", "content": f"以下のCSVデータを集計してください:\n\n{CSV_INPUT}"}],
"output_config": {"format": {"type": "json_schema", "schema": CSV_SCHEMA}},
}),
)
result = json.loads(response["body"].read())
text = next(b["text"] for b in result["content"] if b["type"] == "text")
print(json.dumps(json.loads(text), indent=2, ensure_ascii=False))
実行結果は次のとおりでした。
{
"total_sales": 746000,
"total_items": 36,
"by_region": [
{
"region": "東京",
"sales": 551400
},
{
"region": "大阪",
"sales": 59600
},
{
"region": "福岡",
"sales": 135000
}
],
"top_product": "ノートPC"
}
Converse では、呼び出しと text の取り出しが次のように変わります。client、MODEL_ID、CSV_INPUT、CSV_SCHEMA は共通です。
response = client.converse(
modelId=MODEL_ID,
messages=[{"role": "user", "content": [{"text": f"以下のCSVデータを集計してください:\n\n{CSV_INPUT}"}]}],
inferenceConfig={"maxTokens": 1024},
outputConfig={"textFormat": {
"type": "json_schema",
"structure": {"jsonSchema": {"schema": json.dumps(CSV_SCHEMA), "name": "sales_summary"}},
}},
)
blocks = response["output"]["message"]["content"]
text = next(b["text"] for b in blocks if "text" in b)
print(json.dumps(json.loads(text), indent=2, ensure_ascii=False))
strict の位置は、Converse では toolSpec の中、InvokeModel ではツール定義の直下です。TOOL_SCHEMA は両方で共通です。
TOOL_SCHEMA = {
"type": "object",
"properties": {
"category": {"type": "string", "enum": ["electronics", "books"]},
"price_limit": {"type": "integer"},
},
"required": ["category", "price_limit"],
"additionalProperties": False,
}
# Converse
tool_config = {
"tools": [{
"toolSpec": {
"name": "search_products",
"description": "商品を検索する",
"strict": True,
"inputSchema": {"json": TOOL_SCHEMA},
}
}],
"toolChoice": {"auto": {}},
}
# InvokeModel
tools = [{
"name": "search_products",
"description": "商品を検索する",
"strict": True,
"input_schema": TOOL_SCHEMA,
}]
JP プロファイルでは、CSV 集計(InvokeModel、Converse)と sentiment 分類(Converse)を実行しました。結果は global と同じで、集計は total_sales 746000、total_items 36、sentiment は positive でした。
CSV 集計の応答に含まれる thinking ブロック
Haiku 5.5 の既定は adaptive thinking で、effort は medium です。
thinking を指定していないのに、CSV 集計の応答には thinking ブロックが含まれていました。content の並びは、InvokeModel では thinking、text の順、Converse では reasoningContent、text の順でした。global、JP ともに thinking の本文は空で、signature だけが入っていました。
一方、sentiment 分類の応答は text ブロックだけ、strict tool use の応答は toolUse(Converse)または tool_use(InvokeModel)のブロックだけでした。
result["content"][0]["text"] のように先頭のブロックから text を読むコードでは、先頭が thinking ブロックの場合に JSON を取り出せません。InvokeModel では type が text のブロックを、Converse では text キーを持つブロックを選ぶようにします。
# InvokeModel
text = next(b["text"] for b in result["content"] if b["type"] == "text")
data = json.loads(text)
# Converse
blocks = response["output"]["message"]["content"]
text = next(b["text"] for b in blocks if "text" in b)
data = json.loads(text)
まとめ
2026年10月7日にリリースされた Claude Haiku 5.5 で、Structured outputs によりスキーマ通りの JSON を取得できました。
Bedrock の Claude モデルで応答の形式が安定しない場合や、最新モデルのモデルカードにサポートの記載がなく Structured outputs を見送っていた場合は、今回正式にサポートされた Structured outputs での JSON 取得を試してみてください。









