使用する API を選択すると、接続手順とセッションイベントを確認できます。
サーバーから GPT-Live への接続
サーバー間連携を含め、GPT-Live の音声接続には WebRTC を推奨します。
WebSocket も利用できます。メインの WebSocket 接続で音声と JSON イベントを双方向に送受信します。プロジェクトの API キーは信頼できるサーバーで保管してください。
このガイドでは、GPT-Live に音声をストリーミングする方法を説明します。既存のセッションを監視または制御する方法については、サーバー側の制御を参照してください。推論とツール実行を担うバックエンドを Responses API に接続する方法については、Responses の WebSocket モードを参照してください。
認証とセッションの開始
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 では、openai と ws を npm install openai ws でインストールし、JavaScript の例を client.mjs として保存します。macOS または Linux で Python を使う場合は、openai[realtime] をインストールし、Python の例を client.py として保存します。サーバーの環境変数に OPENAI_API_KEY を設定してください。これらの例には、Live に対応したバージョンの SDK が必要です。サンプルは標準入力から 24 kHz のモノラル PCM16 の生音声データを読み取り、返された音声を同じ形式で標準出力に書き込みます。入力ストリームをアプリケーションの録音処理に、出力ストリームを再生処理に接続してください。音声ストリームにログや文字起こしイベントが混入しないよう、これらは標準エラー出力に書き出されます。
Go では、Go 1.26 以降を使用します。例を main.go として保存し、go mod init live-demo でモジュールを初期化してから、github.com/openai/openai-go/v3@v3.85.1 を go get でインストールします。Java では、Java 17 以降を使用し、Maven の依存関係に com.openai:openai-java:4.95.0 を追加して、LiveConnectionWebsocketExample クラスを使用します。Ruby 3.3 以降では、gem install openai -v 0.110.0 で SDK を、gem install async-websocket で WebSocket 接続に必要な gem をインストールし、例を client.rb として保存します。
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、go run .、ruby client.rb、または Java アプリケーションを実行します。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 でエンコードしてメッセージに含めます。
- 音声の送信:
session.input_audio.appendを送信する際に、audioに Base64 でエンコードした生のバイト列を指定します。音声の追加に対する確認応答はありません。 - 音声の受信:
deltaを各session.output_audio.deltaイベントから取り出してデコードし、設定した形式で順番に再生できるよう音声をキューに追加します。 - 文字起こしの受信:
deltaに含まれるテキストをsession.input_transcript.deltaとsession.output_transcript.deltaから取得し、それぞれ対応する文字起こしに追記します。 - バックエンドイベントの受信: Responses に処理を委任する場合は、
eventを各response.eventエンベロープから取り出して処理します。 - エラー処理:
errorイベントを受け取ったら、コマンドの拒否やセッションエラーに対処します。error.client_event_idがある場合は、その値で対象のコマンドを特定します。
再生状況はアプリケーションの音声キューで追跡します。GPT-Live のメインの WebSocket 接続で送られる出力音声にはタイミングを示すフィールドがなく、音声出力の完了を示すイベントもありません。字幕のタイミングを合わせるには文字起こしのタイムスタンプを、委任した処理の進行状況を追跡するにはバックエンドイベントを使用してください。
音声が連続して流れる間、GPT-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 の方が安定した接続を確保できます。
ws モジュール(Node.js)
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",
},
});
let connected = false;
ws.on("message", function incoming(message) {
const event = JSON.parse(message.toString());
if (event.type === "error") throw new Error(event.error.message);
if (event.type === "session.created") {
connected = true;
console.log(`Connected to the Realtime API: ${event.session.id}`);
ws.close(1000);
}
});
ws.on("close", () => {
if (!connected)
throw new Error("Connection closed before the session was created");
});# 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",
]
session_created = False
def on_message(ws, message):
global session_created
data = json.loads(message)
if data["type"] == "session.created":
session_created = True
print("Connected to the Realtime API:", data["session"]["id"])
ws.close()
elif data["type"] == "error":
ws.close()
ws = websocket.WebSocketApp(
url,
header=headers,
on_message=on_message,
)
ws.run_forever()
if not session_created:
raise RuntimeError("Connection closed before the Realtime session was created.")必要な 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|
created = connection.find do |event|
raise event.error.message if event.is_a?(OpenAI::Realtime::RealtimeErrorEvent)
next unless event.is_a?(OpenAI::Realtime::SessionCreatedEvent)
puts("Connected to the Realtime API: #{event.session[:id]}")
true
end
raise "Connection closed before the session was created" unless created
endJava 17 以降と com.openai:openai-java:4.75.1 を使用してください。
import com.openai.client.okhttp.OkHttpClient;
import com.openai.core.ClientOptions;
import com.openai.helpers.RealtimeConnection;
import com.openai.helpers.RealtimeWebSocketOptions;
import com.openai.models.realtime.*;
var http = OkHttpClient.builder().build();
var options =
ClientOptions.builder()
.fromEnv()
.httpClient(http)
.putHeader("OpenAI-Safety-Identifier", "hashed-user-id")
.build();
try (http;
var connection =
RealtimeConnection.connect(
options,
RealtimeWebSocketOptions.builder().model("gpt-realtime-2.1").build())) {
while (true) {
var event = connection.receive();
if (event.error().isPresent())
throw new IllegalStateException(event.error().orElseThrow().toString());
if (event.sessionCreated().isPresent()) {
System.out.println("Connected to server.");
System.out.println(event);
break;
}
}
}Go 1.26 以降を使用し、SDK は
go get github.com/openai/openai-go/v3@v3.85.1 でインストールしてください。ソケット通信は SDK が管理します。イベントの処理、音声の録音と再生、再接続後のセッション状態の復元はアプリケーション側で行います。
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
client := openai.NewClient()
conn, err := client.Realtime.Connect(ctx, realtime.RealtimeConnectionOptions{}, option.WithQuery("model", "gpt-realtime-2.1"), option.WithHeader("OpenAI-Safety-Identifier", "hashed-user-id"))
if err != nil {
return err
}
defer conn.Close()SDK は
dotnet add package OpenAI –version 2.14.0 でインストールしてください。Realtime API は SDK の実験的な型を使用するため、この例には必要な警告ディレクティブを含めています。
using OpenAI.Realtime;
#pragma warning disable OPENAI002
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
RealtimeClient client = new(key);
RealtimeSessionClientOptions options = new();
options.Headers["OpenAI-Safety-Identifier"] = "hashed-user-id";
using CancellationTokenSource timeout = new(TimeSpan.FromSeconds(30));
using RealtimeSessionClient session = await client.StartConversationSessionAsync("gpt-realtime-2.1", options, cancellationToken: timeout.Token);
await foreach (RealtimeServerUpdate update in session.ReceiveUpdatesAsync(timeout.Token))
{
if (update is RealtimeServerUpdateError error)
throw new InvalidOperationException(error.Error.Message);
if (update is RealtimeServerUpdateSessionCreated)
{
Console.WriteLine("Connected to server.");
Console.WriteLine(System.ClientModel.Primitives.ModelReaderWriter.Write(update));
return;
}
}
throw new InvalidOperationException("Connection closed before session creation.");/*
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,
]
);
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 でエンコードした音声チャンクをソケット接続で送信する処理も、受信したチャンクの処理も、アプリケーション側で行います。
WebSocket 経由で音声を送受信する方法については、Realtime の会話ガイドを参照してください。