AAIF に加わった Open Responses を Amazon Bedrock で試す
先端技術開発グループ(WAND)の伊賀です。
2026年10月22日、Linux Foundation 傘下の Agentic AI Foundation(AAIF)に、Open Responses が新しいプロジェクトとして加わりました(Linux Foundation のプレスリリース)。
Open Responses は複数の LLM プロバイダーを同じ形で呼び出すためのオープンな仕様です。
同じ日に AWS も Open Responses への関わりと Amazon Bedrock での対応を紹介しています(Open Responses Joins AAIF with AWS Support)。
内容は 2026年10月時点のものです。
Open Responses とは
Open Responses は OpenAI の Responses API をもとにした LLM API の共通仕様です(openresponses.org)。
主に次の 3 つを定めています(README)。
リクエストとレスポンスの共通スキーマ
ストリーミングで流れるイベントの種類と順序
ツール呼び出しの手順
仕様は日付つきのバージョンとして公開されていて、最新は 2026-04-24 です。
GitHub リポジトリには仕様の本文と OpenAPI 定義に加えて、API が仕様に沿っているかを確かめる準拠テストが入っています。
プロジェクトは OpenAI が寄贈したもので、今後は AAIF の下で運営されます。
同じ日に Anthropic の Agent Skills、Hugging Face の OpenEnv、Alibaba の OpenSandbox も加わり、AAIF のプロジェクトは MCP や A2A、AGENTS.md などとあわせて 10 個になりました。
共通の仕様が必要な理由
LLM の API はメッセージ、関数呼び出し、ツール利用、マルチモーダル入力といった似た概念に収束してきました。
しかし OpenAI の Responses API、Anthropic の Messages API、Google の Gemini API では、同じことをするにもプロバイダーごとに書き方が異なります。
そのため、複数のモデルを比べたり切り替えたりするには、プロバイダーごとの変換コードを書いて保守する必要がありました。
Open Responses に沿ったエンドポイントであれば、クライアントのコードはそのままで、接続先とモデル名を変えるだけで済みます。
AAIF のブログではこれを「エンドポイントとモデル識別子が、コードの分岐ではなく設定値になる」と表現しています(What Open Responses Changes for AI Developers)。
この形式のエンドポイントは Bedrock のほかにもあり、AWS のブログは Ollama、vLLM、OpenRouter、Hugging Face を、AAIF のブログは Databricks Model Serving と Vercel AI Gateway を挙げています。
各社が独自の機能を出す余地も残されています。
仕様にないツールやアイテムは、openai:web_search_call のようにプロバイダー名を前に付けて定義します。
クライアントは知らない種類のアイテムやイベントを無視しても、標準の部分はそのまま処理できます。
仕様の考え方
仕様の本文は次の 4 つの考え方を軸にしています(Specification)。
エージェントループ:モデルがツール呼び出しを出力し、結果を受け取って推論を続ける流れを、共通の形で扱います
アイテム:メッセージ、関数呼び出し、その実行結果、推論などを、type で区別するアイテムとして表します。アイテムは入力にも出力にも使えます
状態機械:レスポンスやアイテムは in_progress、completed、incomplete などの決まった状態を持ちます
意味のあるイベント:ストリーミングではテキストの差分をそのまま流さず、response.output_item.added や response.completed のように何が起きたかを表すイベントを流します
ツールには関数や MCP サーバーのように実装がプロバイダーの外にあるものと、プロバイダーの中で実行が完結するものの 2 種類があります。
2026-04-24 バージョンでは接続を張ったまま使う WebSocket での呼び出しと、長い会話の文脈を圧縮する /responses/compact エンドポイントが追加されました(Changelog)。
AWS の関わり
AWS は AAIF の Governing Board に加わっています。
Amazon Bedrock は 2025年12月から Responses API に対応していて、AWS のブログではその実装を「仕様が大規模な環境でどう動くかを示す、本番品質のリファレンス」とも紹介しています。
Bedrock で Responses API を使えるエンドポイントは 2 つあります(Responses API - Amazon Bedrock User Guide)。
エンドポイント
ベース URL
位置づけ
bedrock-runtime
bedrock-runtime.{region}.amazonaws.com
推奨
bedrock-mantle
bedrock-mantle.{region}.api.aws
GPT OSS の Responses API など、mantle でしか使えないモデルや機能向け
Responses API に対応しているのは、OpenAI の GPT 系と GPT OSS、xAI の Grok、Moonshot AI の Kimi K3、Google の Gemma 4、Z.AI の GLM 5.3 です(API compatibility)1。
Amazon Bedrock で動かしてみる
us-east-1 の bedrock-runtime に、Open Responses の形でリクエストを送ってみます。
認証には Bedrock の API キーを使います。有効期限の短いキーは AWS の認証情報から aws-bedrock-token-generator で発行できます。
基本のリクエストとレスポンス
curl で 1 回呼び出します。
store: false は会話を Bedrock 側に保存しないための指定です。
export OPENAI_BASE_URL=bedrock-runtime.us-east-1.amazonaws.com
export OPENAI_API_KEY=<Bedrock の API キー>
curl -sS "$OPENAI_BASE_URL/responses" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "global.openai.gpt-5.6-sol",
"input": "Open Responses を一文で説明してください。",
"store": false
}'
レスポンスから主要な部分を抜き出すと、次のようになります。
{
"id": "resp_fjc2ydhllu3zscoaqtqem3wpley2fas4u7dghhguugzipayo3pcq",
"object": "response",
"status": "completed",
"model": "global.openai.gpt-5.6-sol",
"output": [
{
"type": "reasoning",
"id": "rs_8c6287cb8f975b00bebcc6c9a47a5487",
"encrypted_content": "(省略)",
"content": [],
"summary": []
},
{
"type": "message",
"id": "msg_4629e516851f545db7fd68af8e29b3fd",
"role": "assistant",
"status": "completed",
"phase": "final_answer",
"content": [
{
"type": "output_text",
"text": "Open Responsesは、異なるAIモデルやエージェントの応答APIを共通形式で扱えるようにするオープンな標準仕様です。",
"annotations": []
}
]
}
],
"usage": {
"input_tokens": 15,
"output_tokens": 219,
"output_tokens_details": { "reasoning_tokens": 177 },
"total_tokens": 234
}
}
output はアイテムの配列で、推論の reasoning アイテムと、回答の message アイテムが順に並んでいます。
回答の本文は message アイテムの content にある output_text に入ります。
phase は 2026-04-24 バージョンで追加された任意のフィールドで、最終回答(final_answer)か途中の説明(commentary)かを表します。
レスポンス全体{
"background": false,
"billing": {
"payer": "developer"
},
"completed_at": 1792717922,
"created_at": 1792717919,
"error": null,
"frequency_penalty": 0.0,
"id": "resp_fjc2ydhllu3zscoaqtqem3wpley2fas4u7dghhguugzipayo3pcq",
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
"max_tool_calls": null,
"metadata": {},
"model": "global.openai.gpt-5.6-sol",
"object": "response",
"output": [
{
"content": [],
"encrypted_content": "(省略)",
"id": "rs_8c6287cb8f975b00bebcc6c9a47a5487",
"summary": [],
"type": "reasoning"
},
{
"content": [
{
"annotations": [],
"logprobs": [],
"text": "Open Responsesは、異なるAIモデルやエージェントの応答APIを共通形式で扱えるようにするオープンな標準仕様です。",
"type": "output_text"
}
],
"id": "msg_4629e516851f545db7fd68af8e29b3fd",
"phase": "final_answer",
"role": "assistant",
"status": "completed",
"type": "message"
}
],
"parallel_tool_calls": true,
"presence_penalty": 0.0,
"previous_response_id": null,
"prompt_cache_key": null,
"prompt_cache_retention": "in_memory",
"reasoning": {
"summary": null,
"effort": "medium",
"context": "all_turns"
},
"safety_identifier": null,
"service_tier": "default",
"status": "completed",
"store": false,
"temperature": 1.0,
"text": {
"format": {
"type": "text"
},
"verbosity": "medium"
},
"tool_choice": "auto",
"tools": [],
"top_logprobs": 0,
"top_p": 0.98,
"truncation": "disabled",
"usage": {
"input_tokens": 15,
"input_tokens_details": {
"cache_write_tokens": 0,
"cached_tokens": 0
},
"output_tokens": 219,
"output_tokens_details": {
"reasoning_tokens": 177
},
"total_tokens": 234
},
"user": null,
"moderation": null
}
ストリーミングのイベント
"stream": true を付けると、Server-Sent Events でイベントが順に届きます。
「Say hello in three words.」と送ったときのイベントの並びは次のとおりでした。
response.created
response.in_progress
response.output_item.added reasoning アイテムの開始
response.output_item.done reasoning アイテムの完了
response.output_item.added message アイテムの開始
response.content_part.added
response.output_text.delta "Hello"
response.output_text.delta " there"
response.output_text.delta ","
response.output_text.delta " friend"
response.output_text.delta "!"
response.output_text.done
response.content_part.done
response.output_item.done message アイテムの完了
response.completed
レスポンス全体の状態の変化(response.created → response.in_progress → response.completed)の中に、アイテムごとの「追加 → 差分 → 完了」が入れ子になっています。
文字を順に画面へ表示したいだけなら、response.output_text.delta の delta をつなげれば足ります。
受信したイベント(一部のフィールドを省略)data: {"type": "response.created", "response": {"id": "resp_f4wjs5jc4gfuomhyckvwc2o7zlrmxx4e2ohxyyupkph7imdpjhaq", "status": "in_progress", "output": []}, "sequence_number": 0}
data: {"type": "response.in_progress", "response": {"id": "resp_f4wjs5jc4gfuomhyckvwc2o7zlrmxx4e2ohxyyupkph7imdpjhaq", "status": "in_progress", "output": []}, "sequence_number": 1}
data: {"type": "response.output_item.added", "item": {"content": [], "encrypted_content": "(省略)", "id": "rs_949298e3722a55969bf6fb415bcc857e", "summary": [], "type": "reasoning"}, "output_index": 0, "sequence_number": 2}
data: {"type": "response.output_item.done", "item": {"content": [], "encrypted_content": "(省略)", "id": "rs_949298e3722a55969bf6fb415bcc857e", "summary": [], "type": "reasoning"}, "output_index": 0, "sequence_number": 3}
data: {"type": "response.output_item.added", "item": {"content": [], "id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "phase": "final_answer", "role": "assistant", "status": "in_progress", "type": "message"}, "output_index": 1, "sequence_number": 4}
data: {"type": "response.content_part.added", "content_index": 0, "item_id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "output_index": 1, "part": {"annotations": [], "text": "", "type": "output_text"}, "sequence_number": 5}
data: {"type": "response.output_text.delta", "content_index": 0, "delta": "Hello", "item_id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "output_index": 1, "sequence_number": 6}
data: {"type": "response.output_text.delta", "content_index": 0, "delta": " there", "item_id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "output_index": 1, "sequence_number": 7}
data: {"type": "response.output_text.delta", "content_index": 0, "delta": ",", "item_id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "output_index": 1, "sequence_number": 8}
data: {"type": "response.output_text.delta", "content_index": 0, "delta": " friend", "item_id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "output_index": 1, "sequence_number": 9}
data: {"type": "response.output_text.delta", "content_index": 0, "delta": "!", "item_id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "output_index": 1, "sequence_number": 10}
data: {"type": "response.output_text.done", "content_index": 0, "item_id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "output_index": 1, "sequence_number": 11, "text": "Hello there, friend!"}
data: {"type": "response.content_part.done", "content_index": 0, "item_id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "output_index": 1, "part": {"annotations": [], "text": "Hello there, friend!", "type": "output_text"}, "sequence_number": 12}
data: {"type": "response.output_item.done", "item": {"content": [{"annotations": [], "text": "Hello there, friend!", "type": "output_text"}], "id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "phase": "final_answer", "role": "assistant", "status": "completed", "type": "message"}, "output_index": 1, "sequence_number": 13}
data: {"type": "response.completed", "response": {"id": "resp_f4wjs5jc4gfuomhyckvwc2o7zlrmxx4e2ohxyyupkph7imdpjhaq", "status": "completed", "output": "(省略)"}, "sequence_number": 14}
data: [DONE]
ツール呼び出しとエージェントループ
関数ツールを渡すと、モデルはツールが必要と判断したときに、回答の代わりに function_call アイテムを返します。
開発者側で関数を実行し、その結果を function_call_output アイテムとして返すと、モデルは結果を踏まえて回答します。
この流れが仕様でいうエージェントループです。
東京の天気を聞いたときのやり取りは、次のようになりました。
1 回目のリクエスト input : message(ユーザーの質問)
1 回目のレスポンス output: function_call get_weather {"city":"東京"}
2 回目のリクエスト input : message, function_call, function_call_output
2 回目のレスポンス output: message 「東京の現在の天気は晴れ、気温は18℃です。」
1 回目に出力された function_call アイテムは、そのまま 2 回目の入力に積んでいます。
アイテムが入力にも出力にも使えるので、クライアントは出力を加工せずに会話を続けられます。
今回は store: false にしているため、2 回目では 1 回目の入出力をすべて送り直しています。
store: true にすれば、previous_response_id で前のレスポンスを指定して、新しい入力だけを送る形にもできます。
Python と httpx で書いたコードは次のとおりです。
エージェントループのコード(Python)import json
import os
import httpx
BASE_URL = os.environ["OPENAI_BASE_URL"]
API_KEY = os.environ["OPENAI_API_KEY"]
MODEL = os.environ.get("MODEL", "global.openai.gpt-5.6-sol")
TOOLS = [
{
"type": "function",
"name": "get_weather",
"description": "指定した都市の現在の天気を返す",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}
]
def get_weather(city: str) -> dict:
return {"city": city, "weather": "晴れ", "temperature_c": 18} # 検証用の固定値
items = [{"type": "message", "role": "user", "content": "東京の天気を教えて"}]
while True:
r = httpx.post(
f"{BASE_URL}/responses",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"model": MODEL, "input": items, "tools": TOOLS, "store": False},
timeout=120,
)
r.raise_for_status()
output = r.json()["output"]
items += output # 出力されたアイテムを、そのまま次の入力に積む
calls = [i for i in output if i["type"] == "function_call"]
if not calls:
break
for call in calls:
result = get_weather(**json.loads(call["arguments"]))
items.append(
{
"type": "function_call_output",
"call_id": call["call_id"],
"output": json.dumps(result, ensure_ascii=False),
}
)
message = next(i for i in output if i["type"] == "message")
print(message["content"][0]["text"])
ループの最後に積み上がったアイテム[
{
"type": "message",
"role": "user",
"content": "東京の天気を教えて"
},
{
"arguments": "{\"city\":\"東京\"}",
"call_id": "call_899965767e1351f0ad9eedeee141713c",
"id": "fc_8bae4d13c8af5eeabe698af52b86aafd",
"name": "get_weather",
"status": "completed",
"type": "function_call"
},
{
"type": "function_call_output",
"call_id": "call_899965767e1351f0ad9eedeee141713c",
"output": "{\"city\": \"東京\", \"weather\": \"晴れ\", \"temperature_c\": 18}"
},
{
"content": [
{
"annotations": [],
"logprobs": [],
"text": "東京の現在の天気は晴れ、気温は18℃です。",
"type": "output_text"
}
],
"id": "msg_67235d5875da5839bc139e6e43cdecb5",
"phase": "final_answer",
"role": "assistant",
"status": "completed",
"type": "message"
}
]
モデルを差し替える
上のコードの MODEL だけを変えて、ベンダーの違うモデルで同じループを動かしました。
GPT OSS は bedrock-mantle でしか Responses API を使えないため、接続先も切り替えています。
モデル
ベンダー
エンドポイント
1 回目の output
2 回目の output
global.openai.gpt-5.6-sol
OpenAI
bedrock-runtime
function_call
message
global.xai.grok-4.7
xAI
bedrock-runtime
reasoning, function_call
reasoning, message
global.moonshotai.kimi-k3
Moonshot AI
bedrock-runtime
reasoning, function_call
reasoning, message
openai.gpt-oss-120b
OpenAI
bedrock-mantle
reasoning, function_call
message
4 モデルとも、コードを変えずに get_weather を呼び、その結果から晴れで気温は 18℃ と答えました。
推論の返し方はモデルによって違いました。
Grok 4.7 は推論を暗号化した encrypted_content として返し、Kimi K3 は推論のテキストを content に入れて返しました。
基本のリクエストで GPT-5.6 Sol が返した reasoning アイテムも、Grok 4.7 と同じ暗号化した形でした。
仕様では、生の推論、暗号化した推論、推論の要約のどれを返すかはプロバイダーが選べます。
どのモデルでもアイテムの種類は reasoning なので、クライアントは中身を表示するかどうかだけを決めればよく、次の入力にはアイテムをそのまま積み直せます。
reasoning アイテムの違い(Grok 4.7 と Kimi K3)
Grok 4.7 の reasoning アイテムです。
{
"content": [],
"encrypted_content": "(省略)",
"id": "rs_ce42a11eac045f66a050ac8795cd2dee",
"summary": [],
"type": "reasoning"
}
Kimi K3 の reasoning アイテムです。
{
"content": [
{
"text": "The weather tool returned:\n- City: 東京 (Tokyo)\n- Weather: 晴れ (Sunny/Clear)\n- Temperature: 18°C\n\nI should respond in Japanese since the user asked in Japanese. I'll give a natural, friendly response with the weather information.",
"type": "reasoning_text"
}
],
"id": "rs_2709a15cc20a5e899c4d2d98d2c61d68",
"summary": [],
"type": "reasoning"
}
準拠テストを Bedrock に向けて動かす
リポジトリの準拠テストは Web 版(openresponses.org)と同じもので、CLI からも実行できます。
ただし WebSocket のテストは、ブラウザから認証ヘッダーを付けられないため、CLI でしか動きません。
実行には Bun が必要で、ベース URL、API キー、モデル名を渡せば動きます。
git clone openresponses/openresponses
cd openresponses
bun install
bun run test:compliance \
--base-url "$OPENAI_BASE_URL" \
--api-key "$OPENAI_API_KEY" \
--model global.openai.gpt-5.6-sol
テストは 17 件あり、基本の応答、ストリーミング、システムプロンプト、ツール呼び出し、画像入力、複数ターンの会話に加えて、2026-04-24 バージョンで追加された WebSocket、compact、phase も対象です。
GPT-5.6 Sol では 17 件中 15 件が通り、WebSocket での応答や会話の継続も確認できました。
実行結果(GPT-5.6 Sol、途中経過の表示を省略)Running compliance tests against: bedrock-runtime.us-east-1.amazonaws.com
Model: global.openai.gpt-5.6-sol
✓ Response Output Phase Schema
✓ Compaction Missing Required Model (676ms)
✗ WebSocket Missing Previous Response (992ms) [1 events]
✗ Expected previous_response_not_found but got model_not_found
✓ Basic Text Response (1139ms)
✓ Image Input (1150ms)
✓ Multi-turn Conversation (1156ms)
✓ Tool Calling (1158ms)
✓ Streaming Response (1290ms) [22 events]
✓ System Prompt (1283ms)
✓ Assistant Message Phase (1311ms)
✓ Compaction Endpoint (1435ms)
✓ WebSocket Continuation (2336ms) [19 events]
✓ WebSocket Response (2359ms) [13 events]
✓ WebSocket Sequential Responses (2397ms) [18 events]
✗ WebSocket Failed Continuation Evicts Cache (2637ms) [22 events]
✗ Expected previous_response_not_found after failed continuation eviction but got no error code
✓ WebSocket Compact New Chain (3024ms) [10 events]
✓ WebSocket Store False Reconnect Recovery (4157ms) [20 events]
==================================================
Results: 15 passed, 2 failed, 0 skipped, 17 total
結果はモデルによって変わり、たとえば Grok 4.7 と Kimi K3 は compact に対応していませんでした。
準拠テストは、使いたいモデルが仕様のどこまでに対応しているかを手元で手軽に確かめる道具として使えます。
まとめ
Open Responses は OpenAI の Responses API をもとにした LLM API の共通仕様で、2026年10月22日に AAIF のプロジェクトになりました。
アイテム、状態、イベントの形が決まっているので、接続先とモデル名を変えるだけで、同じクライアントのコードを使い回せます。
Amazon Bedrock では bedrock-runtime と bedrock-mantle の 2 つのエンドポイントで使え、今回は 3 ベンダー・4 モデルで同じエージェントループが動きました。
リポジトリの準拠テストを使えば、モデルごとの対応範囲も手元で確かめられます。
複数のモデルを比べたり切り替えたりする場面で、まず試してみる価値のある仕様だと感じました。
脚注
AWS のブログには Amazon、Anthropic、Meta、Mistral、DeepSeek などのモデルも Responses API で使えると書かれています。ただし Bedrock のドキュメントの対応表では、これらのモデルは Responses API に対応していません。今回の検証でも、Claude Sonnet 5.5、Llama 4 Maverick、Nova Pro、Mistral Large 3、DeepSeek V3.2 への Responses API のリクエストは受け付けられませんでした。Claude Sonnet 5.5 は Messages API で、Mistral Large 3 と DeepSeek V3.2 は Chat Completions で呼び出せます。 ↩︎
Mentions
AAIF に加わった Open Responses を Amazon Bedrock で試す 先端技術開発グループ(WAND)の伊賀です。 2026年10月22日、Linux Foundation 傘下の Agentic AI Foundation(AAIF)に、Open Responses が新しいプロジェクトとして加わりました(Linux Foundation のプレスリリース)。 Open Responses は複数の LLM プロバイダーを同じ形で呼び出すためのオープンな仕様です。 同じ日に AWS も Open Responses への関わりと Amazon Bedrock での対応を紹介しています(Open Responses Joins AAIF with AWS Support)。 内容は 2026年10月時点のものです。 Open Responses とは Open Responses は OpenAI の Responses API をもとにした LLM API の共通仕様です(openresponses.org)。 主に次の 3 つを定めています(README)。 リクエストとレスポンスの共通スキーマ ストリーミングで流れるイベントの種類と順序 ツール呼び出しの手順 仕様は日付つきのバージョンとして公開されていて、最新は 2026-04-24 です。 GitHub リポジトリには仕様の本文と OpenAPI 定義に加えて、API が仕様に沿っているかを確かめる準拠テストが入っています。 プロジェクトは OpenAI が寄贈したもので、今後は AAIF の下で運営されます。 同じ日に Anthropic の Agent Skills、Hugging Face の OpenEnv、Alibaba の OpenSandbox も加わり、AAIF のプロジェクトは MCP や A2A、AGENTS.md などとあわせて 10 個になりました。 共通の仕様が必要な理由 LLM の API はメッセージ、関数呼び出し、ツール利用、マルチモーダル入力といった似た概念に収束してきました。 しかし OpenAI の Responses API、Anthropic の Messages API、Google の Gemini API では、同じことをするにもプロバイダーごとに書き方が異なります。 そのため、複数のモデルを比べたり切り替えたりするには、プロバイダーごとの変換コードを書いて保守する必要がありました。 Open Responses に沿ったエンドポイントであれば、クライアントのコードはそのままで、接続先とモデル名を変えるだけで済みます。 AAIF のブログではこれを「エンドポイントとモデル識別子が、コードの分岐ではなく設定値になる」と表現しています(What Open Responses Changes for AI Developers)。 この形式のエンドポイントは Bedrock のほかにもあり、AWS のブログは Ollama、vLLM、OpenRouter、Hugging Face を、AAIF のブログは Databricks Model Serving と Vercel AI Gateway を挙げています。 各社が独自の機能を出す余地も残されています。 仕様にないツールやアイテムは、openai:web_search_call のようにプロバイダー名を前に付けて定義します。 クライアントは知らない種類のアイテムやイベントを無視しても、標準の部分はそのまま処理できます。 仕様の考え方 仕様の本文は次の 4 つの考え方を軸にしています(Specification)。 エージェントループ:モデルがツール呼び出しを出力し、結果を受け取って推論を続ける流れを、共通の形で扱います アイテム:メッセージ、関数呼び出し、その実行結果、推論などを、type で区別するアイテムとして表します。アイテムは入力にも出力にも使えます 状態機械:レスポンスやアイテムは in_progress、completed、incomplete などの決まった状態を持ちます 意味のあるイベント:ストリーミングではテキストの差分をそのまま流さず、response.output_item.added や response.completed のように何が起きたかを表すイベントを流します ツールには関数や MCP サーバーのように実装がプロバイダーの外にあるものと、プロバイダーの中で実行が完結するものの 2 種類があります。 2026-04-24 バージョンでは接続を張ったまま使う WebSocket での呼び出しと、長い会話の文脈を圧縮する /responses/compact エンドポイントが追加されました(Changelog)。 AWS の関わり AWS は AAIF の Governing Board に加わっています。 Amazon Bedrock は 2025年12月から Responses API に対応していて、AWS のブログではその実装を「仕様が大規模な環境でどう動くかを示す、本番品質のリファレンス」とも紹介しています。 Bedrock で Responses API を使えるエンドポイントは 2 つあります(Responses API - Amazon Bedrock User Guide)。 エンドポイント ベース URL 位置づけ bedrock-runtime bedrock-runtime.{region}.amazonaws.com 推奨 bedrock-mantle bedrock-mantle.{region}.api.aws GPT OSS の Responses API など、mantle でしか使えないモデルや機能向け Responses API に対応しているのは、OpenAI の GPT 系と GPT OSS、xAI の Grok、Moonshot AI の Kimi K3、Google の Gemma 4、Z.AI の GLM 5.3 です(API compatibility)1。 Amazon Bedrock で動かしてみる us-east-1 の bedrock-runtime に、Open Responses の形でリクエストを送ってみます。 認証には Bedrock の API キーを使います。有効期限の短いキーは AWS の認証情報から aws-bedrock-token-generator で発行できます。 基本のリクエストとレスポンス curl で 1 回呼び出します。 store: false は会話を Bedrock 側に保存しないための指定です。 export OPENAI_BASE_URL=bedrock-runtime.us-east-1.amazonaws.com export OPENAI_API_KEY=<Bedrock の API キー> curl -sS "$OPENAI_BASE_URL/responses" \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "global.openai.gpt-5.6-sol", "input": "Open Responses を一文で説明してください。", "store": false }' レスポンスから主要な部分を抜き出すと、次のようになります。 { "id": "resp_fjc2ydhllu3zscoaqtqem3wpley2fas4u7dghhguugzipayo3pcq", "object": "response", "status": "completed", "model": "global.openai.gpt-5.6-sol", "output": [ { "type": "reasoning", "id": "rs_8c6287cb8f975b00bebcc6c9a47a5487", "encrypted_content": "(省略)", "content": [], "summary": [] }, { "type": "message", "id": "msg_4629e516851f545db7fd68af8e29b3fd", "role": "assistant", "status": "completed", "phase": "final_answer", "content": [ { "type": "output_text", "text": "Open Responsesは、異なるAIモデルやエージェントの応答APIを共通形式で扱えるようにするオープンな標準仕様です。", "annotations": [] } ] } ], "usage": { "input_tokens": 15, "output_tokens": 219, "output_tokens_details": { "reasoning_tokens": 177 }, "total_tokens": 234 } } output はアイテムの配列で、推論の reasoning アイテムと、回答の message アイテムが順に並んでいます。 回答の本文は message アイテムの content にある output_text に入ります。 phase は 2026-04-24 バージョンで追加された任意のフィールドで、最終回答(final_answer)か途中の説明(commentary)かを表します。 レスポンス全体{ "background": false, "billing": { "payer": "developer" }, "completed_at": 1792717922, "created_at": 1792717919, "error": null, "frequency_penalty": 0.0, "id": "resp_fjc2ydhllu3zscoaqtqem3wpley2fas4u7dghhguugzipayo3pcq", "incomplete_details": null, "instructions": null, "max_output_tokens": null, "max_tool_calls": null, "metadata": {}, "model": "global.openai.gpt-5.6-sol", "object": "response", "output": [ { "content": [], "encrypted_content": "(省略)", "id": "rs_8c6287cb8f975b00bebcc6c9a47a5487", "summary": [], "type": "reasoning" }, { "content": [ { "annotations": [], "logprobs": [], "text": "Open Responsesは、異なるAIモデルやエージェントの応答APIを共通形式で扱えるようにするオープンな標準仕様です。", "type": "output_text" } ], "id": "msg_4629e516851f545db7fd68af8e29b3fd", "phase": "final_answer", "role": "assistant", "status": "completed", "type": "message" } ], "parallel_tool_calls": true, "presence_penalty": 0.0, "previous_response_id": null, "prompt_cache_key": null, "prompt_cache_retention": "in_memory", "reasoning": { "summary": null, "effort": "medium", "context": "all_turns" }, "safety_identifier": null, "service_tier": "default", "status": "completed", "store": false, "temperature": 1.0, "text": { "format": { "type": "text" }, "verbosity": "medium" }, "tool_choice": "auto", "tools": [], "top_logprobs": 0, "top_p": 0.98, "truncation": "disabled", "usage": { "input_tokens": 15, "input_tokens_details": { "cache_write_tokens": 0, "cached_tokens": 0 }, "output_tokens": 219, "output_tokens_details": { "reasoning_tokens": 177 }, "total_tokens": 234 }, "user": null, "moderation": null } ストリーミングのイベント "stream": true を付けると、Server-Sent Events でイベントが順に届きます。 「Say hello in three words.」と送ったときのイベントの並びは次のとおりでした。 response.created response.in_progress response.output_item.added reasoning アイテムの開始 response.output_item.done reasoning アイテムの完了 response.output_item.added message アイテムの開始 response.content_part.added response.output_text.delta "Hello" response.output_text.delta " there" response.output_text.delta "," response.output_text.delta " friend" response.output_text.delta "!" response.output_text.done response.content_part.done response.output_item.done message アイテムの完了 response.completed レスポンス全体の状態の変化(response.created → response.in_progress → response.completed)の中に、アイテムごとの「追加 → 差分 → 完了」が入れ子になっています。 文字を順に画面へ表示したいだけなら、response.output_text.delta の delta をつなげれば足ります。 受信したイベント(一部のフィールドを省略)data: {"type": "response.created", "response": {"id": "resp_f4wjs5jc4gfuomhyckvwc2o7zlrmxx4e2ohxyyupkph7imdpjhaq", "status": "in_progress", "output": []}, "sequence_number": 0} data: {"type": "response.in_progress", "response": {"id": "resp_f4wjs5jc4gfuomhyckvwc2o7zlrmxx4e2ohxyyupkph7imdpjhaq", "status": "in_progress", "output": []}, "sequence_number": 1} data: {"type": "response.output_item.added", "item": {"content": [], "encrypted_content": "(省略)", "id": "rs_949298e3722a55969bf6fb415bcc857e", "summary": [], "type": "reasoning"}, "output_index": 0, "sequence_number": 2} data: {"type": "response.output_item.done", "item": {"content": [], "encrypted_content": "(省略)", "id": "rs_949298e3722a55969bf6fb415bcc857e", "summary": [], "type": "reasoning"}, "output_index": 0, "sequence_number": 3} data: {"type": "response.output_item.added", "item": {"content": [], "id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "phase": "final_answer", "role": "assistant", "status": "in_progress", "type": "message"}, "output_index": 1, "sequence_number": 4} data: {"type": "response.content_part.added", "content_index": 0, "item_id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "output_index": 1, "part": {"annotations": [], "text": "", "type": "output_text"}, "sequence_number": 5} data: {"type": "response.output_text.delta", "content_index": 0, "delta": "Hello", "item_id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "output_index": 1, "sequence_number": 6} data: {"type": "response.output_text.delta", "content_index": 0, "delta": " there", "item_id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "output_index": 1, "sequence_number": 7} data: {"type": "response.output_text.delta", "content_index": 0, "delta": ",", "item_id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "output_index": 1, "sequence_number": 8} data: {"type": "response.output_text.delta", "content_index": 0, "delta": " friend", "item_id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "output_index": 1, "sequence_number": 9} data: {"type": "response.output_text.delta", "content_index": 0, "delta": "!", "item_id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "output_index": 1, "sequence_number": 10} data: {"type": "response.output_text.done", "content_index": 0, "item_id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "output_index": 1, "sequence_number": 11, "text": "Hello there, friend!"} data: {"type": "response.content_part.done", "content_index": 0, "item_id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "output_index": 1, "part": {"annotations": [], "text": "Hello there, friend!", "type": "output_text"}, "sequence_number": 12} data: {"type": "response.output_item.done", "item": {"content": [{"annotations": [], "text": "Hello there, friend!", "type": "output_text"}], "id": "msg_d2cdd7ec87f75d3bbf1bb4ae2effb0b6", "phase": "final_answer", "role": "assistant", "status": "completed", "type": "message"}, "output_index": 1, "sequence_number": 13} data: {"type": "response.completed", "response": {"id": "resp_f4wjs5jc4gfuomhyckvwc2o7zlrmxx4e2ohxyyupkph7imdpjhaq", "status": "completed", "output": "(省略)"}, "sequence_number": 14} data: [DONE] ツール呼び出しとエージェントループ 関数ツールを渡すと、モデルはツールが必要と判断したときに、回答の代わりに function_call アイテムを返します。 開発者側で関数を実行し、その結果を function_call_output アイテムとして返すと、モデルは結果を踏まえて回答します。 この流れが仕様でいうエージェントループです。 東京の天気を聞いたときのやり取りは、次のようになりました。 1 回目のリクエスト input : message(ユーザーの質問) 1 回目のレスポンス output: function_call get_weather {"city":"東京"} 2 回目のリクエスト input : message, function_call, function_call_output 2 回目のレスポンス output: message 「東京の現在の天気は晴れ、気温は18℃です。」 1 回目に出力された function_call アイテムは、そのまま 2 回目の入力に積んでいます。 アイテムが入力にも出力にも使えるので、クライアントは出力を加工せずに会話を続けられます。 今回は store: false にしているため、2 回目では 1 回目の入出力をすべて送り直しています。 store: true にすれば、previous_response_id で前のレスポンスを指定して、新しい入力だけを送る形にもできます。 Python と httpx で書いたコードは次のとおりです。 エージェントループのコード(Python)import json import os import httpx BASE_URL = os.environ["OPENAI_BASE_URL"] API_KEY = os.environ["OPENAI_API_KEY"] MODEL = os.environ.get("MODEL", "global.openai.gpt-5.6-sol") TOOLS = [ { "type": "function", "name": "get_weather", "description": "指定した都市の現在の天気を返す", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"], }, } ] def get_weather(city: str) -> dict: return {"city": city, "weather": "晴れ", "temperature_c": 18} # 検証用の固定値 items = [{"type": "message", "role": "user", "content": "東京の天気を教えて"}] while True: r = httpx.post( f"{BASE_URL}/responses", headers={"Authorization": f"Bearer {API_KEY}"}, json={"model": MODEL, "input": items, "tools": TOOLS, "store": False}, timeout=120, ) r.raise_for_status() output = r.json()["output"] items += output # 出力されたアイテムを、そのまま次の入力に積む calls = [i for i in output if i["type"] == "function_call"] if not calls: break for call in calls: result = get_weather(**json.loads(call["arguments"])) items.append( { "type": "function_call_output", "call_id": call["call_id"], "output": json.dumps(result, ensure_ascii=False), } ) message = next(i for i in output if i["type"] == "message") print(message["content"][0]["text"]) ループの最後に積み上がったアイテム[ { "type": "message", "role": "user", "content": "東京の天気を教えて" }, { "arguments": "{\"city\":\"東京\"}", "call_id": "call_899965767e1351f0ad9eedeee141713c", "id": "fc_8bae4d13c8af5eeabe698af52b86aafd", "name": "get_weather", "status": "completed", "type": "function_call" }, { "type": "function_call_output", "call_id": "call_899965767e1351f0ad9eedeee141713c", "output": "{\"city\": \"東京\", \"weather\": \"晴れ\", \"temperature_c\": 18}" }, { "content": [ { "annotations": [], "logprobs": [], "text": "東京の現在の天気は晴れ、気温は18℃です。", "type": "output_text" } ], "id": "msg_67235d5875da5839bc139e6e43cdecb5", "phase": "final_answer", "role": "assistant", "status": "completed", "type": "message" } ] モデルを差し替える 上のコードの MODEL だけを変えて、ベンダーの違うモデルで同じループを動かしました。 GPT OSS は bedrock-mantle でしか Responses API を使えないため、接続先も切り替えています。 モデル ベンダー エンドポイント 1 回目の output 2 回目の output global.openai.gpt-5.6-sol OpenAI bedrock-runtime function_call message global.xai.grok-4.7 xAI bedrock-runtime reasoning, function_call reasoning, message global.moonshotai.kimi-k3 Moonshot AI bedrock-runtime reasoning, function_call reasoning, message openai.gpt-oss-120b OpenAI bedrock-mantle reasoning, function_call message 4 モデルとも、コードを変えずに get_weather を呼び、その結果から晴れで気温は 18℃ と答えました。 推論の返し方はモデルによって違いました。 Grok 4.7 は推論を暗号化した encrypted_content として返し、Kimi K3 は推論のテキストを content に入れて返しました。 基本のリクエストで GPT-5.6 Sol が返した reasoning アイテムも、Grok 4.7 と同じ暗号化した形でした。 仕様では、生の推論、暗号化した推論、推論の要約のどれを返すかはプロバイダーが選べます。 どのモデルでもアイテムの種類は reasoning なので、クライアントは中身を表示するかどうかだけを決めればよく、次の入力にはアイテムをそのまま積み直せます。 reasoning アイテムの違い(Grok 4.7 と Kimi K3) Grok 4.7 の reasoning アイテムです。 { "content": [], "encrypted_content": "(省略)", "id": "rs_ce42a11eac045f66a050ac8795cd2dee", "summary": [], "type": "reasoning" } Kimi K3 の reasoning アイテムです。 { "content": [ { "text": "The weather tool returned:\n- City: 東京 (Tokyo)\n- Weather: 晴れ (Sunny/Clear)\n- Temperature: 18°C\n\nI should respond in Japanese since the user asked in Japanese. I'll give a natural, friendly response with the weather information.", "type": "reasoning_text" } ], "id": "rs_2709a15cc20a5e899c4d2d98d2c61d68", "summary": [], "type": "reasoning" } 準拠テストを Bedrock に向けて動かす リポジトリの準拠テストは Web 版(openresponses.org)と同じもので、CLI からも実行できます。 ただし WebSocket のテストは、ブラウザから認証ヘッダーを付けられないため、CLI でしか動きません。 実行には Bun が必要で、ベース URL、API キー、モデル名を渡せば動きます。 git clone openresponses/openresponses cd openresponses bun install bun run test:compliance \ --base-url "$OPENAI_BASE_URL" \ --api-key "$OPENAI_API_KEY" \ --model global.openai.gpt-5.6-sol テストは 17 件あり、基本の応答、ストリーミング、システムプロンプト、ツール呼び出し、画像入力、複数ターンの会話に加えて、2026-04-24 バージョンで追加された WebSocket、compact、phase も対象です。 GPT-5.6 Sol では 17 件中 15 件が通り、WebSocket での応答や会話の継続も確認できました。 実行結果(GPT-5.6 Sol、途中経過の表示を省略)Running compliance tests against: bedrock-runtime.us-east-1.amazonaws.com Model: global.openai.gpt-5.6-sol ✓ Response Output Phase Schema ✓ Compaction Missing Required Model (676ms) ✗ WebSocket Missing Previous Response (992ms) [1 events] ✗ Expected previous_response_not_found but got model_not_found ✓ Basic Text Response (1139ms) ✓ Image Input (1150ms) ✓ Multi-turn Conversation (1156ms) ✓ Tool Calling (1158ms) ✓ Streaming Response (1290ms) [22 events] ✓ System Prompt (1283ms) ✓ Assistant Message Phase (1311ms) ✓ Compaction Endpoint (1435ms) ✓ WebSocket Continuation (2336ms) [19 events] ✓ WebSocket Response (2359ms) [13 events] ✓ WebSocket Sequential Responses (2397ms) [18 events] ✗ WebSocket Failed Continuation Evicts Cache (2637ms) [22 events] ✗ Expected previous_response_not_found after failed continuation eviction but got no error code ✓ WebSocket Compact New Chain (3024ms) [10 events] ✓ WebSocket Store False Reconnect Recovery (4157ms) [20 events] ================================================== Results: 15 passed, 2 failed, 0 skipped, 17 total 結果はモデルによって変わり、たとえば Grok 4.7 と Kimi K3 は compact に対応していませんでした。 準拠テストは、使いたいモデルが仕様のどこまでに対応しているかを手元で手軽に確かめる道具として使えます。 まとめ Open Responses は OpenAI の Responses API をもとにした LLM API の共通仕様で、2026年10月22日に AAIF のプロジェクトになりました。 アイテム、状態、イベントの形が決まっているので、接続先とモデル名を変えるだけで、同じクライアントのコードを使い回せます。 Amazon Bedrock では bedrock-runtime と bedrock-mantle の 2 つのエンドポイントで使え、今回は 3 ベンダー・4 モデルで同じエージェントループが動きました。 リポジトリの準拠テストを使えば、モデルごとの対応範囲も手元で確かめられます。 複数のモデルを比べたり切り替えたりする場面で、まず試してみる価値のある仕様だと感じました。 脚注 AWS のブログには Amazon、Anthropic、Meta、Mistral、DeepSeek などのモデルも Responses API で使えると書かれています。ただし Bedrock のドキュメントの対応表では、これらのモデルは Responses API に対応していません。今回の検証でも、Claude Sonnet 5.5、Llama 4 Maverick、Nova Pro、Mistral Large 3、DeepSeek V3.2 への Responses API のリクエストは受け付けられませんでした。Claude Sonnet 5.5 は Messages API で、Mistral Large 3 と DeepSeek V3.2 は Chat Completions で呼び出せます。 ↩︎