リアルタイムトランスポート
セッションを実行する場所と、元のメディアやイベントをどの程度制御する必要があるかに応じて、トランスポートを選択します。
| シナリオ | 推奨トランスポート | 理由 |
|---|---|---|
| ブラウザーの音声対音声アプリ | OpenAIRealtimeWebRTC | 最も手軽な方法です。SDK がマイク入力、再生、WebRTC 接続を管理します。 |
| React Native モバイルアプリ | ネイティブ WebRTC を基盤とする、アプリ所有の RealtimeTransportLayer | SDK は React Native のパッケージ条件を提供し、アプリがネイティブ WebRTC、権限、オーディオルーティング、ライフサイクルを管理します。 |
| サーバー側の音声ループまたはカスタムオーディオパイプライン | OpenAIRealtimeWebSocket | オーディオのキャプチャと再生をすでに制御しており、イベントへ直接アクセスしたい場合に適しています。 |
| SIP またはテレフォニーブリッジ | OpenAIRealtimeSIP | callId を使って、RealtimeSession を既存の SIP 経由で開始された Realtime 通話に接続します。 |
| Cloudflare Workers / workerd | Cloudflare 拡張トランスポート | workerd では、グローバルな WebSocket コンストラクターを使用して外向きの WebSocket 接続を開けません。 |
| Twilio でのプロバイダー固有の電話フロー | Twilio 拡張トランスポート | Twilio のオーディオ転送と割り込み動作を処理します。 |
デフォルトのトランスポートレイヤー
Section titled “デフォルトのトランスポートレイヤー”ブラウザーでのデフォルトの選択肢:WebRTC
Section titled “ブラウザーでのデフォルトの選択肢:WebRTC”デフォルトのブラウザートランスポートは WebRTC を使用します。マイクからオーディオがキャプチャされ、自動的に再生されるため、クイックスタートでは、一時トークンと session.connect(...) だけで接続できます。
この方法では、session.connect() は初期セッション設定が session.updated によって確認応答されるまで待ってから完了するよう試みます。そのため、オーディオのストリーミングが始まる前に instructions と tools が適用されます。確認応答が届かない場合に備えて、タイムアウトによるフォールバックも用意されています。
独自のメディアストリームまたはオーディオ要素を使用するには、セッションの作成時に OpenAIRealtimeWebRTC インスタンスを指定します。
import { RealtimeAgent, RealtimeSession, OpenAIRealtimeWebRTC,} from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'Greeter', instructions: 'Greet the user with cheer and answer questions.',});
async function main() { // Keep a handle on the stream you pass in. The transport does not stop a caller-supplied // stream on close(), so your application owns it and is responsible for ending it. const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, });
const transport = new OpenAIRealtimeWebRTC({ mediaStream, audioElement: document.createElement('audio'), });
const customSession = new RealtimeSession(agent, { transport });
// Later, once the application is actually done with the microphone, release it. Leaving this // out keeps the microphone indicator on even after the session closes. // mediaStream.getTracks().forEach((track) => track.stop());}OpenAIRealtimeWebRTC に渡した mediaStream の所有権は、引き続きアプリケーションにあります。トランスポートを閉じても、そのストリームのトラックは停止しないため、レベルメーター、レコーダー、再接続に引き続き使用できます。アプリケーションでそのストリームの使用が実際に完了した時点で、トラックに対して stop() を呼び出してください。トランスポートがデフォルトのマイクを自ら開いた場合は、close() によって SDK が所有するトラックも停止します。
より低レベルのカスタマイズ向けに、OpenAIRealtimeWebRTC は changePeerConnection も受け付けます。これにより、オファーが生成される前に、新しく作成された RTCPeerConnection を確認または置き換えられます。
React Native でアプリが所有するトランスポート
Section titled “React Native でアプリが所有するトランスポート”@openai/agents-core と @openai/agents-realtime は React Native のパッケージ条件を公開しているため、Metro は Node.js の組み込み機能を使わずに、移植可能な SDK シムを解決できます。組み込みの OpenAIRealtimeWebRTC は、ブラウザーの WebRTC グローバルと DOM オーディオ要素に依存するため、引き続きブラウザー向けトランスポートです。React Native では、ネイティブ WebRTC 実装をインストールしてアプリで管理し、カスタム RealtimeTransportLayer を介して RealtimeSession に接続します。
examples/realtime-react-native の Expo 開発ビルドのコード例では、アプリが所有する react-native-webrtc トランスポート、マイク権限、オーディオルーティング、サーバー側の一時トークンエンドポイントを紹介しています。react-native-webrtc にはネイティブコードが含まれるため、Expo Go はサポートされていません。通常の OpenAI API キーはサーバーに保持し、有効期間の短い一時クライアントトークンだけをモバイルアプリに渡してください。
サーバーでのデフォルトの選択肢:WebSocket
Section titled “サーバーでのデフォルトの選択肢:WebSocket”WebRTC の代わりに WebSocket 接続を使用するには、セッションの作成時に transport: 'websocket' または OpenAIRealtimeWebSocket のインスタンスを渡します。これは、サーバー側のユースケース、テレフォニーブリッジ、カスタムオーディオパイプラインに適しています。
WebSocket では、ソケットが開き、初期設定が送信されると session.connect() が完了します。対応する session.updated イベントは少し遅れて届く場合があるため、connect() の完了時点ですでに更新が送り返されているとは想定しないでください。
import { RealtimeAgent, RealtimeSession } from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'Greeter', instructions: 'Greet the user with cheer and answer questions.',});
const myRecordedArrayBuffer = new ArrayBuffer(0);
const wsSession = new RealtimeSession(agent, { transport: 'websocket', model: 'gpt-realtime-2.1',});await wsSession.connect({ apiKey: process.env.OPENAI_API_KEY! });
wsSession.on('audio', (event) => { // event.data is a chunk of PCM16 audio});
wsSession.sendAudio(myRecordedArrayBuffer);任意の録音・再生ライブラリを使用して、元の PCM16 オーディオバイトを処理します。
高度な連携向けに、OpenAIRealtimeWebSocket は createWebSocket() を受け付けるため、独自のソケット実装を指定できます。また、カスタムコネクターがソケットを接続済み状態へ遷移させる場合は、skipOpenEventListeners を使用できます。@openai/agents-extensions の Cloudflare トランスポートは、これらのフックを基盤に構築されています。
通話プロバイダーとテレフォニーブリッジ向けの SIP
Section titled “通話プロバイダーとテレフォニーブリッジ向けの SIP”既存の SIP 経由で開始された Realtime 通話に RealtimeSession を接続する場合は、OpenAIRealtimeSIP を使用します。これは SIP 対応の軽量なトランスポートです。オーディオは SIP 通話自体によって処理され、callId を使って SDK セッションを接続します。
OpenAIRealtimeSIP.buildInitialConfig()で初期セッション設定を生成し、着信通話を受け入れます。これにより、SIP 招待と、その後の SDK セッションが同じデフォルト設定から開始されます。OpenAIRealtimeSIPトランスポートを使用するRealtimeSessionを接続し、プロバイダーの Webhook から発行されたcallIdで接続します。- プロバイダー固有のメディア転送やイベントブリッジが必要な場合は、Twilio 拡張機能などの連携トランスポートを使用します。
import OpenAI from 'openai';import { OpenAIRealtimeSIP, RealtimeAgent, RealtimeSession, type RealtimeSessionOptions,} from '@openai/agents/realtime';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY!, webhookSecret: process.env.OPENAI_WEBHOOK_SECRET!,});
const agent = new RealtimeAgent({ name: 'Receptionist', instructions: 'Welcome the caller, answer scheduling questions, and hand off if the caller requests a human.',});
const sessionOptions: Partial<RealtimeSessionOptions> = { model: 'gpt-realtime-2.1', config: { audio: { input: { turnDetection: { type: 'semantic_vad', interruptResponse: true }, }, }, },};
export async function acceptIncomingCall(callId: string): Promise<void> { const initialConfig = await OpenAIRealtimeSIP.buildInitialConfig( agent, sessionOptions, ); await openai.realtime.calls.accept(callId, initialConfig);}
export async function attachRealtimeSession( callId: string,): Promise<RealtimeSession> { const session = new RealtimeSession(agent, { transport: new OpenAIRealtimeSIP(), ...sessionOptions, });
session.on('history_added', (item) => { console.log('Realtime update:', item.type); });
await session.connect({ apiKey: process.env.OPENAI_API_KEY!, callId, });
return session;}Cloudflare Workers と workerd
Section titled “Cloudflare Workers と workerd”Cloudflare Workers やその他の workerd ランタイムでは、グローバルな WebSocket コンストラクターを使用して外向きの WebSocket 接続を開けません。拡張機能パッケージの Cloudflare トランスポートを使用してください。このトランスポートは、内部で fetch() ベースのアップグレードを実行します。
import { CloudflareRealtimeTransportLayer } from '@openai/agents-extensions';import { RealtimeAgent, RealtimeSession } from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'My Agent',});
// Create a transport that connects to OpenAI Realtime via Cloudflare/workerd's fetch-based upgrade.const cfTransport = new CloudflareRealtimeTransportLayer({ url: 'wss://api.openai.com/v1/realtime?model=gpt-realtime-2.1',});
const session = new RealtimeSession(agent, { // Set your own transport. transport: cfTransport,});完全なセットアップについては、Cloudflare 上の Realtime Agentを参照してください。
Twilio の電話通話
Section titled “Twilio の電話通話”元の WebSocket または @openai/agents-extensions の専用 Twilio トランスポートを使用して、RealtimeSession を Twilio に接続できます。SDK に Twilio Media Streams の割り込みタイミングとオーディオ転送を処理させる場合は、専用トランスポートがより適切なデフォルトです。
完全なセットアップについては、Twilio 上の Realtime Agentを参照してください。
独自トランスポートの導入
Section titled “独自トランスポートの導入”別の音声対音声 API を使用する場合や、独自のカスタムトランスポートメカニズムがある場合は、RealtimeTransportLayer インターフェースを実装し、RealtimeTransportEventTypes イベントを自分で発行できます。
必要に応じた元の Realtime イベントへのアクセス
Section titled “必要に応じた元の Realtime イベントへのアクセス”基盤となる Realtime API により直接的にアクセスするには、2 つの選択肢があります。
オプション 1 - トランスポートレイヤーへのアクセス
Section titled “オプション 1 - トランスポートレイヤーへのアクセス”RealtimeSession のすべての機能を引き続き利用しながら、session.transport を介してトランスポートレイヤーにアクセスできます。
トランスポートレイヤーは、受信したすべてのイベントを * イベントとして発行し、sendEvent() を使用して元のイベントを送信できます。* リスナーは、現在の SDK スキーマが認識していないフィールドも含め、解析された元の JSON ペイロードの構造化クローンを受け取ります。名前付きイベントリスナーと派生イベントリスナーは、引き続き検証・正規化されたイベントオブジェクトを受け取ります。これは、session.update、response.create、response.cancel などの低レベル操作を行うためのエスケープハッチです。
import { RealtimeAgent, RealtimeSession } from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'Greeter', instructions: 'Greet the user with cheer and answer questions.',});
const session = new RealtimeSession(agent, { model: 'gpt-realtime-2.1',});
session.transport.on('*', (event) => { // Event received from the underlying Realtime transport});
// Send any valid client event, for example, to trigger a new responsesession.transport.sendEvent({ type: 'response.create', // ...});オプション 2 - トランスポートレイヤーのみの使用
Section titled “オプション 2 - トランスポートレイヤーのみの使用”ツールの自動実行、ガードレール、ローカル履歴管理が不要な場合は、接続と割り込みだけを管理する「軽量な」クライアントとしてトランスポートレイヤーを使用することもできます。
import { OpenAIRealtimeWebRTC } from '@openai/agents/realtime';
const client = new OpenAIRealtimeWebRTC();const audioBuffer = new ArrayBuffer(0);
await client.connect({ apiKey: '<api key>', model: 'gpt-realtime-2.1', initialSessionConfig: { instructions: 'Speak like a pirate', outputModalities: ['audio'], audio: { input: { format: 'pcm16', }, output: { format: 'pcm16', voice: 'ash', }, }, },});
// Listen for audio when you manage playback yourselfclient.on('audio', (newAudio) => {});
client.sendAudio(audioBuffer);