For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
メインナビゲーション

WebSockets

サーバーが管理する音声ストリームを WebSockets 経由で接続します。

アプリケーションで使用する API を選択します。認証、セッションの作成、イベントの仕様は API ごとに異なります。

サーバーと GPT-Live の接続

サーバーで音声を取り込む場合や、クライアントの音声ストリームを中継する場合は、プライマリ WebSocket を使用します。この接続では、音声と JSON イベントを双方向に送受信します。プロジェクトの API キーは、この信頼できるサーバー上で保管してください。ブラウザやモバイルアプリケーションでは、まず WebRTC を使用してください。

このガイドでは、プライマリ音声接続について説明します。サイドバンド接続を使用すると、サーバーから既存の Live セッションを監視、制御できます。Responses WebSocket は、推論やツールを利用するためにバックエンドを Responses API に接続します。どちらもプライマリ音声接続の代わりにはなりません。

認証とセッションの開始

  1. クエリパラメーターを付けずに wss://api.openai.com/v1/live/sessions に接続します。Authorization: Bearer $OPENAI_API_KEY で認証し、例に示す接続ヘッダーを含めます。
  2. 最初のメッセージとして session.start を送信します。モデル、会話の指示、音声形式、ボイス、委任の設定は、session オブジェクト内に指定します。
  3. session.started を受信してから、音声やアプリケーションのコマンドを送信します。このイベントには、確定したセッション設定とセッション ID が含まれます。

以下の例では、Marin、24 kHz の PCM16 音声、ウェブ検索を備えた Responses バックエンドを使用します。会話の指示は短くしてください。バックエンドの指示、ツール、ツールの権限は、委任とツールを参照して設定してください。

SDK による音声ストリーミング

Node.js では、npm install openai wsopenaiws をインストールし、JavaScript のサンプルを client.mjs として保存します。macOS または Linux 上の Python では、openai[realtime] をインストールし、Python のサンプルを client.py として保存します。サーバー環境に OPENAI_API_KEY を設定してください。これらのサンプルには、Live に対応したバージョンの SDK が必要です。サンプルは、24 kHz のモノラル PCM16 の生音声データを標準入力から読み取り、返された音声を同じ形式で標準出力に書き込みます。これらのストリームをアプリケーションの音声取り込みと再生に接続してください。ログと文字起こしのイベントは、音声ストリームに混入しないよう標準エラー出力に送られます。

import OpenAI from "openai";
import { LiveWS } from "openai/resources/live/ws";

// stdin and stdout carry raw mono PCM16 audio at 24 kHz, not WAV files.
// Supply microphone bytes continuously and play stdout in the same format.
process.stdin.pause();
const ws = new LiveWS(new OpenAI());
let started = false;
let closing = false;
let finalized = false;
let pendingByte = Buffer.alloc(0);

let closeTimeout;

ws.socket.on("open", () => {
  ws.send({
    type: "session.start",
    event_id: "event_start",
    session: {
      model: "gpt-live-1",
      instructions:
        "Be concise. Delegate requests needing current information to the backend, which can search the web.",
      audio: {
        format: { type: "audio/pcm", rate: 24000 },
        output: { voice: "marin" },
      },
      delegation: {
        type: "responses",
        responses: {
          model: "gpt-5.6-luna",
          tools: [{ type: "web_search" }],
          tool_choice: "auto",
        },
      },
    },
  });
});

process.stdin.on("data", (chunk) => {
  if (!started || closing || ws.socket.readyState !== 1) return;
  const bytes = Buffer.concat([pendingByte, chunk]);
  const completeLength = bytes.length - (bytes.length % 2);
  pendingByte = bytes.subarray(completeLength);
  if (completeLength) {
    ws.send({
      type: "session.input_audio.append",
      audio: bytes.subarray(0, completeLength).toString("base64"),
    });
  }
});

// Register the final-event handler before any close command can be sent.
ws.on("event", (event) => {
  if (event.type === "session.started") {
    started = true;
    console.error("Session ready", event.session.id);
    process.stdin.resume();
  } else if (event.type === "session.output_audio.delta") {
    process.stdout.write(Buffer.from(event.delta, "base64"));
  } else if (event.type === "session.closed") {
    finalized = true;
    clearTimeout(closeTimeout);
    process.stdin.pause();
    console.error("Final session usage", event.usage);
    ws.close();
  } else {
    // Includes transcript deltas and nested response.event usage.
    console.error(JSON.stringify(event));
  }
});

process.on("SIGINT", () => {
  if (closing) return;
  if (!started || ws.socket.readyState !== 1) {
    ws.socket.platformSocket.terminate();
    return;
  }
  closing = true;
  process.stdin.pause();
  ws.send({ type: "session.close" });
  closeTimeout = setTimeout(() => {
    console.error("Incomplete finalization: session.closed was not received");
    process.exitCode = 1;
    ws.socket.platformSocket.terminate();
  }, 15_000);
});
ws.on("error", (error) => {
  console.error(error.message);
  process.exitCode = 1;
});
ws.socket.on("close", () => {
  clearTimeout(closeTimeout);
  process.stdin.pause();
  if (!finalized) {
    console.error("Connection closed without final session usage");
    process.exitCode = 1;
  }
});

音声ソースとプレーヤーを接続した状態で、node client.mjs または python client.py を実行します。Session ready が表示されたら、録音時のサンプルレートに合わせた速度で、マイクからのストリームを連続して供給します。ファイル全体を一度にパイプで渡しても、ライブのマイク入力を再現することにはなりません。音声ソースが EOF に達しても会話は終了しません。正常な終了処理を要求するには、プロセスに SIGINT を送信します。

このサンプルは音声ストリームを接続するものです。音声の取り込み、バッファリング、再生、必要に応じたリサンプリングはアプリケーション側で処理します。モデルの動作を評価する前に、使用するデバイスとネットワークでこれらの処理をテストしてください。

音声形式の選択

開始時に session.audio.format を設定します。入力と出力の両方に同じ形式が適用され、セッション中は変更できません。

  • {"type":"audio/pcm","rate":24000}:24 kHz、モノラル、符号付き 16 ビットのリトルエンディアン PCM。デフォルトの形式
  • {"type":"audio/pcm","rate":16000}:16 kHz、モノラル、符号付き 16 ビットのリトルエンディアン PCM
  • {"type":"audio/pcmu","rate":8000}:8 kHz の G.711 μ-law。1 サンプルあたり 1 バイト
  • {"type":"audio/pcma","rate":8000}:8 kHz の G.711 A-law。1 サンプルあたり 1 バイト

WAV などのコンテナーヘッダーを含めず、生のバイト列を Base64 エンコードします。PCM チャンクには完全な 16 ビットサンプルを格納する必要があるため、バイト長は偶数でなければなりません。このサンプルでは、末尾に余った 1 バイトを次の入力チャンクに繰り越します。それ以外のチャンクの区切り方は任意ですが、ストリームの連続性と順序は維持してください。

音声のサンプルレートが設定値と異なる場合は、リサンプリングしてください。形式の設定を変更しても、入力バイト列は変換されません。このサンプルを G.711 に対応させるには、PCM 固有の 2 バイト境界に揃える処理を使わずに各チャンクのコーデックのバイト列を転送し、出力プレーヤーにも同じコーデックを設定します。設定と一致する G.711 ストリームは、PCM に変換せずにそのまま通過させられます。電話通話の接続については、電話連携を参照してください。

イベントの送受信

各イベントを JSON テキストメッセージとして送信します。音声は、これらのメッセージ内で Base64 形式で転送されます。

  • 音声の送信: 生のバイト列を Base64 エンコードして audio に格納し、session.input_audio.append を送信します。音声の追加に対する受信確認は返されません。
  • 音声の受信:session.output_audio.delta イベントの delta をデコードし、設定した形式で順番に再生できるよう、音声をキューに追加します。
  • 文字起こしの受信: session.input_transcript.deltasession.output_transcript.deltadelta に含まれるテキストを、それぞれ対応する文字起こしに追加します。
  • バックエンドイベントの受信: Responses への委任を使用する場合は、各 response.event エンベロープ内にネストされた event を処理します。
  • エラー処理: error イベントを基に、拒否されたコマンドやセッションエラーに対処します。error.client_event_id が含まれている場合は、それを使って該当するコマンドを特定します。

出力音声イベントにはタイミングを示すフィールドがなく、GPT-Live は出力音声の完了イベントも発行しません。受信した音声のどこまで再生されたかは、再生キューを追跡して把握してください。文字起こしのタイムスタンプはセッションのタイムライン上の区間を示すものであり、音声の再生完了を示すものではありません。バックエンドのレスポンスが完了しても、アシスタントの発話が終わったことにはなりません。

GPT-Live は、音声のストリーミング中に、いつ聞き、いつ話すかを管理します。Realtime のように入力バッファーのコミットと response.create を繰り返して音声ターンを処理する仕組みは使用しません。Live では、response.create によって、委任されたバックエンド処理を開始または継続します。このワークフローについては、委任とツールを参照してください。

進行中のセッションの設定

Live モデル、初期の会話の指示、音声形式、ボイス、委任モードは開始時に固定されます。現在の委任モードでサポートされている設定を変更するには、session.update を使用します。省略した設定は現在の値を維持します。更新に成功すると、確定したセッション設定を含む session.updated が返されます。

会話の指示を追加するには session.instructions.append を、入力音声を制御するには session.input_audio.mute または session.input_audio.unmute を使用します。入力をミュートしても、バックエンド処理はキャンセルされず、生成された音声も停止しません。コンテキストの更新、文字起こし、入力の制御、使用量については、セッションの管理を参照してください。

セッションの終了

会話が終わったら session.close を送信します。あらかじめ session.closed のリスナーを登録し、そのイベントが届くまで受信を続けてから接続を解放します。このサンプルでは最大 15 秒間待機し、終了イベントが届かなかった場合は、終了処理が完了していないことを報告します。

session.closed に含まれる最終的な音声使用量と、すでに受信したバックエンドの使用量イベントを保持してください。音声時間の更新値は累積値のスナップショットなので、合算しないでください。session.closed を受信する前に通信障害やタイムアウトが発生した場合、最終的な使用量は未確認のままです。ライフサイクル全体については、セッションの管理を参照してください。