传输机制
请根据会话的运行位置,以及您对原始媒体或事件控制的需求程度选择传输机制。
| 场景 | 推荐的传输机制 | 原因 |
|---|---|---|
| 浏览器语音到语音应用 | OpenAIRealtimeWebRTC | 最简便的方式。SDK 会为您管理麦克风采集、播放和 WebRTC 连接。 |
| 使用服务器端 Realtime 控制的浏览器音频 | 应用自有的 WebRTC 音频与服务器端 RealtimeSession | 浏览器负责传输音频,同时将 Realtime 事件、工具和业务逻辑保留在应用服务器上。 |
| 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 音频转发和打断行为。 |
浏览器默认选择 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。
内置浏览器传输层会创建数据通道,因为浏览器端的 RealtimeSession 使用 Realtime 事件配置和控制会话。如果您的应用必须将这些事件和控制保留在应用服务器上,请使用下文的服务器端控制模式,而不要将浏览器端的对等连接自定义视为访问控制边界。
使用服务器端控制的浏览器音频
Section titled “使用服务器端控制的浏览器音频”某些应用需要在浏览器中采集麦克风音频并进行音频播放,同时将 Realtime 事件、工具和业务逻辑保留在应用服务器上。此架构结合了 Realtime API 的统一 WebRTC 接口和服务器端控制:
- 浏览器使用标准 WebRTC API 创建仅音频的对等连接,并将其 SDP 提议发送到应用服务器。浏览器不使用 SDK 的内置 WebRTC 传输层,也不创建 Realtime 数据通道。
- 应用服务器对用户进行身份验证、应用其 SDP 策略,并使用标准 API 密钥将提议和由服务器管理的会话配置发送到
/v1/realtime/calls。 - 应用服务器验证 SDP 应答,并从响应的
Location标头中读取通话 ID。 - 应用服务器使用 WebSocket 传输层创建服务器端
RealtimeSession、注册事件监听器、将通话 ID 传给session.connect(...),并等待初始session.updated事件。 - 应用服务器将 SDP 应答返回给浏览器。浏览器将该应答设置为远程描述,随后音频会直接在浏览器与 Realtime API 之间传输。
- 应用服务器处理 Realtime 事件和工具。如果浏览器需要状态更新,应用服务器应投射允许列表中的应用事件,而不是转发原始 Realtime 事件流。
完整应用请参阅 realtime-server-controlled 示例,其中包含仅音频的浏览器对等连接、服务器强制执行的 SDP 策略、带外 RealtimeSession、投射到浏览器的事件以及清理行为。
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”创建会话时传入 transport: 'websocket' 或 OpenAIRealtimeWebSocket 实例,即可使用 WebSocket 连接而非 WebRTC。这种方式非常适合服务器端用例、电话桥接和自定义音频管道。
在 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”如果希望将 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-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 上的语音智能体。
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() 发送原始事件。* 监听器会收到原始已解析 JSON 负载的结构化克隆副本,其中包括当前 SDK 模式尚不了解的字段。具名和派生事件监听器仍会收到经过验证和规范化的事件对象。这是执行 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);