Amazon Bedrock 上の OpenAI モデル(GPT-5.5)をプロダクトに組み込んでみて得た知見
こんにちは、@TakaakiKakei です。
自社プロダクトに、Amazon Bedrock 経由で OpenAI モデル(GPT-5.5)を組み込みました。他の Bedrock モデルとは異なる点があり、実装の途中でいくつかハマりどころに遭遇しました。この記事では、実際に組み込んでみて分かった知見を共有します。
結論(先にまとめ)
- Bedrock 上の OpenAI モデルは OpenAI SDK の
BedrockOpenAIクライアントで呼び出せる - 認証は
@aws/bedrock-token-generatorを使い、AWS クレデンシャルからベアラートークンを都度生成できる - リージョンは Regional availability by models - Amazon Bedrock のとおり、US リージョンのみで提供されている(2026 年 6 月 27 日時点)
- ストリームの delta セマンティクスが純正 OpenAI と異なるため、専用の変換処理が必要
- 他の Bedrock モデルとデータの取り扱いが異なるため、abuse detection のドキュメントの確認が必要
1. クライアントの作成
OpenAI SDK が公式に BedrockOpenAI クライアントを提供しているため、これを利用します。認証は @aws/bedrock-token-generator で AWS クレデンシャルから Bedrock 用のベアラートークンを都度生成する方式です。
import { getTokenProvider } from "@aws/bedrock-token-generator";
import { BedrockOpenAI } from "openai";
const BEDROCK_OPENAI_REGION = "us-east-2";
export async function getBedrockOpenAIClient(): Promise<OpenAI> {
const { credentials } = await getAwsSecretAndCredentials();
if (!credentials) {
throw new Error("AWS credentials are not available for Bedrock OpenAI");
}
return new BedrockOpenAI({
awsRegion: BEDROCK_OPENAI_REGION,
// 専用 API キーではなく、AWS クレデンシャルからトークンを都度生成する
bedrockTokenProvider: getTokenProvider({
credentials,
region: BEDROCK_OPENAI_REGION,
}),
});
}
ポイントは次の 2 点です。
- API キーが不要:
bedrockTokenProviderを渡すと、SDK が呼び出しのたびに@aws/bedrock-token-generatorでベアラートークンを生成します。OpenAI プラットフォーム用の API キーは不要です。 - baseURL は自動導出:
awsRegionを指定するとhttps://bedrock-mantle.{region}.api.aws/openai/v1が自動で組み立てられます。baseURL を手動で設定する必要はありません。
2. 最大のハマりどころ: delta が「累積スナップショット」で返る
Bedrock の OpenAI 互換エンドポイントは Responses API を提供しているため、メッセージ整形やストリーム呼び出しは、既存の OpenAI Responses 向けヘルパーをほぼそのまま再利用できました。一方でストリーム変換だけは Bedrock 専用の処理が必要で、ここが最大のハマりどころでした。
純正 OpenAI の Responses API では、response.output_text.delta に文字どおり増分(差分)が届きます。そのため、素直に連結すれば回答が組み上がります。
ところが Bedrock 経由(内部では Bedrock Mantle と呼ばれる OpenAI 互換レイヤー)では、特に reasoning の effort を有効にした場合、新しい item の delta に累積スナップショット(それまでの全文の再送)が混ざる挙動がありました。
つまり、増分 delta と「全文再送」が混在して届きます。これをそのまま連結すると、同じ文章が二重・三重に表示されてしまいます。
そこで、現在保持しているテキストと新しく届いた delta を比較して正規化する処理を挟みました。以下はその判定ロジックの要点です。
/**
* 累積スナップショットと増分 delta が混在するテキストを正規化する。
*/
private emitCumulativeText(text: string, blockType: ContentBlockType, controller) {
if (!text) return;
const current =
blockType === 'TEXT' ? this.#currentText : this.#currentReasoningText;
// (1) 再送(完全一致)は無視
if (text === current) return;
// (2) current より短ければ増分 delta
// (累積スナップショットなら current 以上の長さになるため、startsWith 判定を省略できる)
if (text.length < current.length) {
// text を送出して current に追記
return;
}
// (3) current より長く current を prefix に持てば累積スナップショット
if (text.length > current.length && text.startsWith(current)) {
const suffix = text.slice(current.length);
if (!suffix) return;
// suffix だけを送出して current を全文で置き換え
return;
}
}
ロジックの要点は次のとおりです。
- (1) 完全一致: 届いた
textが現在保持中のcurrentと同一なら、再送とみなして無視する - (2)
currentより短い: 累積スナップショットであればcurrent以上の長さになるはずなので、短いものは増分 delta とみなす。先に長さで判定することでstartsWithを省略でき、currentには追記する - (3)
currentより長くstartsWithする: 累積スナップショット(全文再送)なので、差分(suffix)だけを送出し、currentは届いた全文で置き換える
この「累積か増分かを判定し、差分だけを流す」変換を挟むことで、UI 側に重複なくストリーミング表示できるようになりました。
3. その他の制約
組み込みの際に確認した、その他の制約もまとめます。
| 項目 | 内容 |
|---|---|
| PDF 入力 | Responses API の input_file コンテンツとして、file_data に data:application/pdf;base64,<base64文字列> 形式で base64 をインライン埋め込むことで可能 |
| hosted tools | web_search / file_search / code_interpreter は Bedrock エンドポイントで未サポート |
| データの取り扱い | 他の Bedrock モデルと異なる扱いになるため、abuse detection のドキュメントを要確認 |
特にデータの取り扱いについては、ユーザーへの注意喚起も必要だと考え、アシスタントの説明文に上記ドキュメントへのリンクを記載しました。
OpenAI models in Amazon Bedrock にも、純正 OpenAI との違いに関する記述があります。組み込みの際はあわせて目を通すことをおすすめします。
おわりに
Amazon Bedrock 上の OpenAI モデルは、「OpenAI 互換エンドポイントを AWS クレデンシャルで叩く」という性格のものでした。OpenAI SDK の BedrockOpenAI クライアントと Responses API 向けヘルパーを再利用できたため、土台の構築はスムーズに進められました。
一方で、ストリームの delta が累積スナップショットとして返る(→ 専用変換で重複を排除)という、純正 OpenAI とは異なる挙動が実装上のハマりどころでした。同様の組み込みを検討している方の参考になれば幸いです。








