传输机制
请根据会话运行位置,以及您对原始媒体或事件控制的需求程度来选择传输层。
| 场景 | 推荐传输层 | 原因 |
|---|---|---|
| 浏览器端语音到语音应用 | OpenAIRealtimeWebRTC | 阻力最低的路径。SDK 会为您管理麦克风采集、音频播放和 WebRTC 连接。 |
| 服务端语音循环或自定义音频管线 | OpenAIRealtimeWebSocket | 当您已自行控制音频采集/播放并希望直接访问事件时,这种方式非常适合。 |
| SIP 或电话桥接 | OpenAIRealtimeSIP | 通过 callId 将 RealtimeSession 绑定到现有的 SIP 发起 Realtime 通话。 |
| Cloudflare Workers / workerd | Cloudflare 扩展传输层 | workerd 无法通过全局 WebSocket 构造函数发起出站 WebSocket 连接。 |
| Twilio 上的特定供应商电话流程 | Twilio 扩展传输层 | 可为您处理 Twilio 音频转发与中断行为。 |
WebRTC 是浏览器端默认选择
Section titled “WebRTC 是浏览器端默认选择”默认的浏览器传输层使用 WebRTC。音频会自动从麦克风采集并自动播放,这也是为什么快速开始只需临时 token 和 session.connect(...) 即可连接。
在此路径下,session.connect() 会尽量等待初始会话配置通过 session.updated 确认后再 resolve,因此您的 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() { const transport = new OpenAIRealtimeWebRTC({ mediaStream: await navigator.mediaDevices.getUserMedia({ audio: true }), audioElement: document.createElement('audio'), });
const customSession = new RealtimeSession(agent, { transport });}对于更底层的自定义,OpenAIRealtimeWebRTC 还支持 changePeerConnection,让您可在生成 offer 前检查或替换新创建的 RTCPeerConnection。
WebSocket 是服务端默认选择
Section titled “WebSocket 是服务端默认选择”创建会话时传入 transport: 'websocket' 或 OpenAIRealtimeWebSocket 实例,即可使用 WebSocket 连接而非 WebRTC。这非常适合服务端场景、电话桥接和自定义音频管线。
在 WebSocket 路径下,session.connect() 会在 socket 打开且初始配置已发送后 resolve。对应的 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',});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(),便于您提供自定义 socket 实现;还支持 skipOpenEventListeners,用于自定义连接器负责将 socket 切换到已连接状态的场景。@openai/agents-extensions 中的 Cloudflare 传输层就是基于这些钩子构建的。
SIP 适用于呼叫服务商与电话桥接
Section titled “SIP 适用于呼叫服务商与电话桥接”当您希望 RealtimeSession 绑定到现有 SIP 发起的 Realtime 通话时,请使用 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', 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',});
const session = new RealtimeSession(agent, { // Set your own transport. transport: cfTransport,});完整配置请参阅 Cloudflare 上的实时智能体。
Twilio 电话通话
Section titled “Twilio 电话通话”您可以使用原始 WebSocket,或 @openai/agents-extensions 中专用的 Twilio 传输层,将 RealtimeSession 连接到 Twilio。若希望 SDK 为 Twilio Media Streams 处理打断时机与音频转发,专用传输层是更好的默认选择。
完整配置请参阅 Twilio 上的实时智能体。
自定义传输层
Section titled “自定义传输层”如果您想使用不同的语音到语音 API,或已有自定义传输机制,可以实现 RealtimeTransportLayer 接口,并自行发出 RealtimeTransportEventTypes 事件。
按需访问原始 Realtime 事件
Section titled “按需访问原始 Realtime 事件”如果您希望更直接地访问底层 Realtime API,有两种方式。
方式 1 - 访问传输层
Section titled “方式 1 - 访问传输层”如果您仍希望利用 RealtimeSession 的全部能力,可通过 session.transport 访问传输层。
传输层会将其收到的所有事件都通过 * 事件发出,您也可使用 sendEvent() 发送原始事件。这是进行底层操作(如 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',});
session.transport.on('*', (event) => { // JSON parsed version of the event received on the connection});
// Send any valid event as JSON. For example triggering 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', initialSessionConfig: { instructions: 'Speak like a pirate', outputModalities: ['audio'], audio: { input: { format: 'pcm16', }, output: { format: 'pcm16', voice: 'ash', }, }, },});
// optionally for WebSocketsclient.on('audio', (newAudio) => {});
client.sendAudio(audioBuffer);