Skip to main content

概要

リアルタイム会話は、WebSocket(クライアントとサーバー間で長時間接続を維持し、双方向にデータをプッシュできるプロトコル)で永続的な接続を確立し、音声またはテキストの入力をリアルタイムで対話モデルに送り、モデルがテキストと音声の返答をインクリメンタルにプッシュします。音声アシスタント、リアルタイムQ&A、会話練習など、やり取りを繰り返すシーンに適しています。 リアルタイム音声認識と同じく WebSocket を使用しますが、用途が異なります: 利用可能なモデル:
  • gpt-realtime-2.1:音声対話モデル。音声とテキストの入力に対応し、テキストと音声の返答をリアルタイムで出力します。
本APIはサーバーサイド統合向けであり、ブラウザから直接接続することはできません。 セキュリティ上の理由から、ゲートウェイは Origin ヘッダー付きの接続を検証して拒否し、openai-insecure-api-key サブプロトコルを拒否します。APIキーは標準の Authorization ヘッダーのみを受け付けます。ブラウザから発起される WebSocket は自動的に Origin ヘッダーを付加するため、拒否されます。フロントエンドでリアルタイム会話を行う必要がある場合は、自身のサーバーサイドでゲートウェイへの接続を確立し、音声と結果をフロントエンドとサーバーサイドの間で転送してください。

クイックスタート

接続エンドポイント

  • model=gpt-realtime-2.1必須、モデルは接続時に URL パラメータで固定され、セッション中は変更できません(下記の制約を参照)。
  • 転写との違いに注意:会話エンドポイントには intent=transcription付けません

認証

ハンドシェイク時に標準の HTTP ヘッダーでAPIキーを渡します:

オーディオ形式の要件

現在、音声の入力と出力はそれぞれ1つの形式のみに対応しています。送信前に音声を次に変換してください:
  • エンコード:PCM16(16ビット符号付き整数、リトルエンディアン)
  • サンプルレート:24000 Hz
  • チャンネル:モノラル(mono)
つまり audio/pcm@24000 です。入力または出力で他の形式(G.711/µ-law など)を宣言すると拒否され、セッションが閉じられます。
転写と異なり、会話セッションは音声活動検出(turn_detection / VAD)に対応しています。有効にするとモデルが発話の終わりを自動判定して返答をトリガーします。無効(null を設定)にすると、音声のコミットと返答要求のタイミングを手動で制御します。必要に応じて選択してください。

セッション構成(session.update)

接続確立後、クライアントは session.update を1フレーム送信して会話パラメータ(音声のトーン、システム指示、VAD の有効化など)を構成できます。会話セッションのモデルは接続 URL で固定されているため、session.update を送信しなくてもそのまま会話できます。音色や指示をカスタマイズしたい場合に送信してください。

構成パラメータ

string
必須
セッションタイプ。会話では realtime です。
string
システム指示。モデルの役割、トーン、回答の制約を設定します。
string[]
出力モダリティ。["audio"] または ["text"] を指定します。["audio"](デフォルト)では音声を出力し、返答テキストは response.output_audio_transcript.delta イベントで返ります。["text"] ではテキストのみを出力し、テキストは response.output_text.delta イベントで返ります。その他の組み合わせ(["audio", "text"] など)は拒否され error イベントが返ります。
object
必須
入力オーディオ形式。{ "type": "audio/pcm", "rate": 24000 } で固定です。
object | null
音声活動検出。{ "type": "server_vad" } を渡すと自動ターン検出を有効化し、null を渡すと無効化してクライアントから手動でコミットと返答要求を行います。
object
必須
出力オーディオ形式。{ "type": "audio/pcm", "rate": 24000 } で固定です。
string
返答音声のトーン。最初の返答が始まると変更できません:セッションが生成状態に入った後に再度送信された voice は無視されます(その他の設定は通常どおり反映されます)。音色を指定する場合は、最初の返答要求の前に設定してください。
以下の機能は本リリースでは未対応で、構成するとセッションが閉じられます(クローズコード 1008):会話セッション内でのインライン転写の有効化(audio.input.transcription、理由 input_transcription_not_supported)、会話アイテムによる音声(理由 item_audio_not_supported)または画像(理由 image_input_not_supported)の注入、テキスト以外のコンテンツアイテム種別(理由 unsupported_content_part)。音声入力はすべて input_audio_buffer.append チャネルから送信してください。

入力の送信

音声の送信

PCM16 音声を小さなチャンク(例:100ms ごとに1チャンク)に分割し、base64 エンコードして input_audio_buffer.append イベントで継続的に送信します:
VAD を有効にすると、モデルが発話の終わりを自動判定して返答をトリガーします。VAD を無効にすると、一定の音声を送信した後に手動でコミットし返答を要求します:

テキストの送信

テキストメッセージを直接注入して返答を要求することもできます:

返答の受信

サーバーはイベントを継続的にプッシュします。主要なイベントタイプ:
event
セッション作成と構成更新の確認。session.created を受け取ると、音声とテキストを送信できます。
event
会話アイテムの書き込み完了。ユーザー入力とモデルの返答がそれぞれ1つのアイテムになります。
event
VAD 有効時、ユーザーが話し始めたこと、話し終えたことをサーバーが検出した通知。speech_started は通常ユーザーがモデルに割り込んだことを意味します。処理方法は割り込みと切り詰めを参照してください。
event
1 ターンの返答の生成が開始されました。
event
このターンの出力アイテムの開始と終了。added イベントの item.id は、後で音声を切り詰めるときに参照する会話アイテム ID です。
event
返答音声の増分チャンク(base64 エンコードされた PCM16)と終了マーカー。受信しながら再生できます。
event
返答音声に対応する文字起こしの増分と終了マーカー。delta フィールドに今回追加されたテキストが入ります。音声出力を有効にした場合、返答テキストはこのイベントから取得します。音声の再生に合わせて字幕を表示できます。
event
テキストのみの返答の増分と終了マーカー。出力モダリティをテキストのみ(output_modalities: ["text"])に設定した場合にのみ発生します。
event
1 ターンの返答が完了。このイベントはこのターンの token 使用量(usage)を伴い、課金の根拠になります。
event
切り詰め要求が反映されたことの確認。割り込みと切り詰めを参照してください。
event
エラーイベント。エラーコードと説明を含みます。リクエスト自体の問題(output_modalities の値が不正など)では error イベントが 1 件返るだけでセッションは継続して利用できます。ポリシーに関わる場合(モデル変更、残高枯渇など)はセッションが閉じられます。
返答テキストはイベントを間違えないこと。 デフォルト(出力に音声を含む)では、モデルは response.output_audio_transcript.delta のみをプッシュし、response.output_text.deltaプッシュしません。出力モダリティをテキストのみに設定すると、テキストは response.output_text.delta に移ります。どちらのモードでも両方を監視して、文字の取りこぼしを避けてください(下記の実行結果を参照)。

割り込みと切り詰め

モデルが話している途中でユーザーが話し始めると、生成済みでまだ再生していない内容がユーザーの次の発話とずれます。WebSocket 接続では再生をクライアントが担うため、割り込み後の後処理もクライアントが行います。 VAD 有効時、サーバーはユーザーが話し始めたことを検出すると input_audio_buffer.speech_started を送信します。このイベントを受信したクライアントは次の処理を行います:
  1. ローカル再生を直ちに停止し、このターンの返答をどこまで再生したか(ミリ秒)を記録します。
  2. conversation.item.truncate を送信し、再生していない音声を会話から取り除きます。これにより、次のターンでモデルがその内容を発話済みとして扱うことを防ぎます。
  • item_id:このターンの返答の会話アイテム ID。response.output_item.added イベントの item.id から取得します。
  • content_index:音声コンテンツパートのインデックス。常に 0 です。
  • audio_end_ms:保持する音声の長さ(ミリ秒)。クライアントが実際に再生した位置を指定します。
サーバーが処理を終えると conversation.item.truncated が返ります。切り詰めはこのターンの音声と対応する文字起こしにのみ影響し、セッション自体には影響しないため、そのまま次のターンへ進めます。OpenAI SDK では conn.conversation.item.truncate(item_id=..., content_index=0, audio_end_ms=...) に対応します。 VAD を無効にする場合(プッシュトゥトークなど)は、ボタンを押した時点が割り込みです。押下時に response.cancel を送信して進行中の返答をキャンセルし、上記の手順で切り詰めます。離したら input_audio_buffer.appendinput_audio_buffer.commitresponse.create の順に送信します。

完全なサンプル

以下に3つの書き方を示します。いずれかを選んでください:
  • OpenAI 公式 SDK(推奨):WebSocket を手書きする必要がなく、websocket_base_url(SDK の WebSocket ベース URL パラメータ)をゲートウェイに向けるだけで公式ライブラリを再利用できます。
  • OpenAI Agents SDK:公式 agent フレームワークのリアルタイム音声形態。model_configurl をゲートウェイのアドレスに差し替えるだけです。
  • 生の websockets:SDK をインストールせず、プロトコルに従ってフレームを直接送受信します。依存が最も少なく、調査が容易です。
なぜ接続時にモデル名を渡すのか? AiHubMix ゲートウェイは WebSocket ハンドシェイクの時点で model を使い、モデルプロバイダーの選択、認証、割り当ての予約を行う必要がありますが、session.update はハンドシェイク完了後に到着するため間に合いません。したがって SDK を使う場合は connect()model を明示的に渡してください(SDK が URL クエリに組み込みます)。これがないとゲートウェイはハンドシェイク時に拒否し、接続は確立できません。転写と異なり、会話エンドポイントに intent=transcription不要です。
任意の音声を本APIが要求する生 PCM 形式に変換するには ffmpeg を使います:

公式サンプルの再利用

OpenAI が公開しているリアルタイム会話のサンプルの多くは SDK のベース URL パラメータにしか依存しないため、アドレスを AiHubMix のエンドポイントに変えるだけで再利用できます:

実行結果(本番実測)

以下は OpenAI SDK サンプルを aihubmix.com 本番環境(モデル gpt-realtime-2.1)で実行した実際の結果です。セッションは server_vad を有効にし、prompt と音声はいずれも英語です。 テキスト入力
音声入力
2つの構成の実測差
実測での確認事項:デフォルト(出力に音声を含む)では返答テキストは response.output_audio_transcript.delta からのみ文字単位で届き、response.output_text.delta は出現しません。response.done はそのターンの usage を運びます。ハンドシェイクには約2〜4秒かかり、これはセッション確立時の割り当て予約のコストです。

課金

  • token 課金:会話セッションは各ターンの返答完了時に response.done イベントでそのターンの token 使用量(usage)を返し、これを基に課金されます。使用量は音声入力 / 音声出力 / テキスト入力 / テキスト出力などのコンポーネントごとに個別に計量され、各コンポーネントの単価はモデル詳細ページのリアルタイム表示価格に準じます。
  • 使った分だけ随時決済:これは長時間接続であり、料金はセッション中に各ターンの返答ごとにリアルタイムで差し引かれ、セッション終了時にまとめて決済する必要はありません。セッション確立時には約1分相当の使用量の割り当て予約を先に行い(入場審査のみで実際の課金ではありません)、セッション終了後に残りの予約を解放します。そのため、アカウントの利用可能残高が約1分相当の使用量を賄える場合にのみセッションを確立できます。
  • 使用量と請求の利用明細で、リアルタイム会話ごとの課金記録を1件ずつ確認できます。

制限と制約

  1. セッションの最大時間:1つの WebSocket 接続は最長 62 分で、到達するとサーバーが切断します(クローズコード 1000、理由 session_duration_limit)。より長く必要な場合は分割して再接続してください。
  2. アイドル切断:クライアントとモデルの双方に 5 分間まったく活動がない場合、サーバーがセッションを閉じます(クローズコード 1008、理由 idle_timeout)。どちらかの活動でタイマーはリセットされるため、出力を続ける長い返答が途中で切れることはありません。
  3. 残高不足セッション確立時に、利用可能残高が約1分の予約分を賄えない場合、ハンドシェイクは直接拒否され(HTTP 403)、セッションは確立されません。セッション中に残高が尽きると、確立済みの接続は即座に閉じられます。
  4. サーバーサイド専用:ブラウザからの直接接続は非対応です(Origin ヘッダーを検証)。サーバーサイドで統合してください。
  5. モデル固定:モデルは接続 URL で固定され、セッション中に session.update で変更すると拒否されセッションが閉じられます。
  6. 形式固定:音声の入力と出力はいずれも audio/pcm@24000 モノラルのみに対応し、他の形式は拒否されます。
  7. 音色固定voice は最初の返答開始後は変更できません。最初の返答要求の前に設定してください。
  8. 未対応:会話セッション内のインライン転写、会話アイテムによる音声または画像コンテンツの注入、テキスト以外のコンテンツアイテム種別。
  9. 1 ターン 1 返答:同一セッションで同時に進行できる返答は 1 つだけです。前のターンが完了する前に response.create を再度送信すると拒否され、セッションが閉じられます(クローズコード 1008、理由 response_already_active)。

よくあるエラー


更新日:2026-09-21