アプリケーションで使用する API を選択します。認証、セッションの作成、イベントの仕様は API ごとに異なります。
サーバーと GPT-Live の接続
サーバーで音声を取り込む場合や、クライアントの音声ストリームを中継する場合は、プライマリ WebSocket を使用します。この接続では、音声と JSON イベントを双方向に送受信します。プロジェクトの API キーは、この信頼できるサーバー上で保管してください。ブラウザやモバイルアプリケーションでは、まず WebRTC を使用してください。
このガイドでは、プライマリ音声接続について説明します。サイドバンド接続を使用すると、サーバーから既存の Live セッションを監視、制御できます。Responses WebSocket は、推論やツールを利用するためにバックエンドを Responses API に接続します。どちらもプライマリ音声接続の代わりにはなりません。
認証とセッションの開始
- クエリパラメーターを付けずに
wss://api.openai.com/v1/live/sessionsに接続します。Authorization: Bearer $OPENAI_API_KEYで認証し、例に示す接続ヘッダーを含めます。 - 最初のメッセージとして
session.startを送信します。モデル、会話の指示、音声形式、ボイス、委任の設定は、sessionオブジェクト内に指定します。 session.startedを受信してから、音声やアプリケーションのコマンドを送信します。このイベントには、確定したセッション設定とセッション ID が含まれます。
以下の例では、Marin、24 kHz の PCM16 音声、ウェブ検索を備えた Responses バックエンドを使用します。会話の指示は短くしてください。バックエンドの指示、ツール、ツールの権限は、委任とツールを参照して設定してください。
SDK による音声ストリーミング
Node.js では、npm install openai ws で openai と ws をインストールし、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.deltaとsession.output_transcript.deltaのdeltaに含まれるテキストを、それぞれ対応する文字起こしに追加します。 - バックエンドイベントの受信: 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 を受信する前に通信障害やタイムアウトが発生した場合、最終的な使用量は未確認のままです。ライフサイクル全体については、セッションの管理を参照してください。
WebSockets は、リアルタイムのデータ転送用として広くサポートされている API であり、サーバー間アプリケーションから OpenAI Realtime API に接続するのに適しています。ブラウザやモバイルクライアントでは、WebRTC 経由での接続を推奨します。
Realtime とサーバー間で連携する場合、バックエンドシステムは WebSocket 経由で Realtime API に直接接続します。トークンは安全なバックエンドサーバー上でのみ利用可能なため、この接続の認証には標準の API キーを使用できます。
WebSocket 経由の接続
以下に、WebSocket 経由で Realtime API に接続する例をいくつか示します。以下の WebSocket URL を使用するだけでなく、OpenAI API キーを使用した認証ヘッダーも渡す必要があります。アプリケーションで安全性識別子を割り当てている場合は、エンドユーザーを一貫して識別でき、プライバシーを保護する識別子を OpenAI-Safety-Identifier ヘッダーに渡してください。
WebRTC 接続ガイドに示すように、一時的な API トークンを使ってブラウザから WebSocket を利用することもできます。ただし、ブラウザやモバイルアプリなどのクライアントから接続する場合、ほとんどのケースでは WebRTC のほうが堅牢な接続方法です。
import WebSocket from "ws";
const url = "wss://api.openai.com/v1/realtime?model=gpt-realtime-2.1";
const ws = new WebSocket(url, {
headers: {
Authorization: "Bearer " + process.env.OPENAI_API_KEY,
"OpenAI-Safety-Identifier": "hashed-user-id",
},
});
ws.on("open", function open() {
console.log("Connected to server.");
});
ws.on("message", function incoming(message) {
console.log(JSON.parse(message.toString()));
});# example requires websocket-client library:
# pip install websocket-client
import os
import json
import websocket
OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]
url = "wss://api.openai.com/v1/realtime?model=gpt-realtime-2.1"
headers = [
"Authorization: Bearer " + OPENAI_API_KEY,
"OpenAI-Safety-Identifier: hashed-user-id",
]
def on_open(ws):
print("Connected to server.")
def on_message(ws, message):
data = json.loads(message)
print("Received event:", json.dumps(data, indent=2))
ws = websocket.WebSocketApp(
url,
header=headers,
on_open=on_open,
on_message=on_message,
)
ws.run_forever()必要な gem は
gem install openai async-websocket でインストールします。
require "openai"
client = OpenAI::Client.new(
default_headers: { "OpenAI-Safety-Identifier" => "hashed-user-id" }
)
client.realtime.connect(model: "gpt-realtime-2.1") do |connection|
puts("Connected to the Realtime API: #{connection.url.host}")
connection.each { |event| puts("Received event: #{event.type}") }
end/*
Note that in client-side environments like web browsers, we recommend
using WebRTC instead. It is possible, however, to use the standard
WebSocket interface in browser-like environments like Deno and
Cloudflare Workers.
*/
const ws = new WebSocket(
"wss://api.openai.com/v1/realtime?model=gpt-realtime-2.1",
[
"realtime",
// Use a short-lived token fetched from your application server.
"openai-insecure-api-key." + OPENAI_REALTIME_EPHEMERAL_KEY,
// Optional
"openai-organization." + OPENAI_ORG_ID,
"openai-project." + OPENAI_PROJECT_ID,
]
);
ws.addEventListener("open", function open() {
console.log("Connected to server.");
});
ws.addEventListener("message", function incoming(event) {
console.log(event.data);
});イベントの送受信
Realtime API のセッションは、開発者が送信するクライアントイベントと、セッションのライフサイクル上のイベントを通知するために Realtime API が生成するサーバーイベントを組み合わせて管理します。
WebSocket では、JSON にシリアライズしたイベントを文字列として送受信します。以下に Node.js の例を示します。他の WebSocket ライブラリでも同じ原則が適用されます。
import WebSocket from "ws";
const url = "wss://api.openai.com/v1/realtime?model=gpt-realtime-2.1";
const ws = new WebSocket(url, {
headers: {
Authorization: "Bearer " + process.env.OPENAI_API_KEY,
"OpenAI-Safety-Identifier": "hashed-user-id",
},
});
ws.on("open", function open() {
console.log("Connected to server.");
// Send client events over the WebSocket once connected
ws.send(
JSON.stringify({
type: "session.update",
session: {
type: "realtime",
instructions: "Be extra nice today!",
},
})
);
});
// Listen for and parse server events
ws.on("message", function incoming(message) {
console.log(JSON.parse(message.toString()));
});WebSocket インターフェースは、Realtime モデルとやり取りできるインターフェースの中で、おそらく最も低レベルなものです。ソケット接続を通じてやり取りする、Base64 でエンコードされた音声チャンクの送信と処理は、どちらも開発者自身で実装する必要があります。
WebSockets で音声を送受信する方法については、Realtime の会話ガイドを参照してください。