Files
ai-video-fullstack/docs-developer/realtime-protocol.mdx
Xin Wang 86639692ba feat: implement OpenAI-compatible Realtime API with authentication and management features
- Added support for public Realtime API, including new routes for managing API keys and handling WebRTC connections.
- Introduced RealtimeApiKey model and associated CRUD operations for admin management of API keys.
- Implemented authentication mechanisms for API keys and client secrets.
- Enhanced environment configuration with new secrets for Realtime API.
- Created OpenAIRealtime session management and event processing for real-time interactions.
- Updated schemas and settings to accommodate new features and ensure compatibility with existing systems.
2026-08-11 10:05:55 +08:00

694 lines
25 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Realtime 兼容协议设计
description: 面向集成方的 OpenAI Realtime 兼容 WebRTC、WebSocket 与扩展事件协议。
icon: radio
---
# Realtime 兼容协议设计
> 当前实现版本为 `Interactive Media Realtime Extensions v1`。OpenAI 兼容接口与现有 `/api/webrtc/offer`、`/ws/voice`、`/ws/stream`、RTVI 消息并行提供RTVI 不是待删除的迁移接口。
## 设计目标
AI 视频助手以 OpenAI Realtime 协议作为对外接口,同时保留助手、知识库、工具、工作流、动态变量和视频输入等现有能力。
协议适配层只负责连接、事件和 Pipecat Frame 之间的转换,不引入第二套语音运行时:
```text
OpenAI Realtime 客户端
│ WebRTC / WebSocket
Realtime 协议适配层
│ Pipecat transport / frame
现有 run_pipeline()
├── Prompt / Workflow / External Agent
├── ASR / LLM / TTS / Realtime Model
├── Knowledge / System、HTTP、Client、MCP Tools
└── History / Handoff / End Call
```
协议分为两层:
| 层级 | 约束 | 面向对象 |
| --- | --- | --- |
| OpenAI Realtime Core | 保持标准事件名称、字段含义和生命周期,不改变标准事件语义 | 任意 OpenAI Realtime 兼容客户端 |
| `x.interactive_media.*` Extensions | 所有项目扩展均有命名空间和能力协商 | 了解 Interactive Media 扩展的客户端 |
普通兼容客户端不声明扩展能力时,服务端不会发送 `x.interactive_media.*` 事件。它仍可使用音频、文本、图片、转写、回复流、打断和函数调用,但不会获得工作流节点、动态变量、连续视频等项目专属状态。
## 接口概览
假设服务地址为 `https://api.example.com`
| 接口 | 用途 | 推荐客户端 |
| --- | --- | --- |
| `POST /v1/realtime/client_secrets` | 创建短期、绑定助手的临时令牌 | 浏览器或移动端 |
| `POST /v1/realtime/calls` | 用 SDP Offer 创建 WebRTC 会话并返回 SDP Answer | 浏览器、移动端 |
| `wss://api.example.com/v1/realtime?model=assistant:<id>` | 创建 WebSocket Realtime 会话 | 服务端、话务网关、自定义客户端 |
`model` 使用 `assistant:<id>`,其中 `id` 是当前数据库中的助手 ID例如
```text
assistant:asst_xxx
```
这里的 `model` 代表一个完整助手,而不是直接选择底层模型。助手绑定的系统提示词、模型凭证、知识库、服务端工具、工作流和运行模式仍由服务端控制。
### 鉴权
- 长期 API Key 仅供可信服务端使用,通过 `Authorization: Bearer <api-key>` 发送。
- 浏览器先由自己的业务后端创建临时令牌,再用临时令牌连接 Realtime 接口。
- 临时令牌绑定助手和会话配置,默认有效期为 60 秒;首版不保证单次使用,令牌只用于建连,建连后到期不会中止会话。
- 父长期 Key 被撤销或过期后,尚未使用的临时令牌立即失效。
- 管理后台 Cookie 不属于公开兼容协议,也不应作为第三方客户端的鉴权方式。
- 会话和事件不会返回底层模型密钥、助手系统提示词或工具密钥。
### 长期 API Key 管理
Realtime Key 存在数据库中,每个 Key 默认可访问当前及未来创建的全部助手。管理接口仅允许后台管理员 Cookie 或 Basic Auth 调用:
| 接口 | 行为 |
| --- | --- |
| `POST /api/realtime/api-keys` | 创建 Key完整 `sk-rt-...` 只在本次响应显示一次 |
| `GET /api/realtime/api-keys` | 查看名称、前缀、有效期、最后使用时间和状态,不返回密钥 |
| `DELETE /api/realtime/api-keys/{id}` | 逻辑撤销并保留审计记录 |
轮换方式为先创建新 Key再撤销旧 Key。服务端只保存使用部署级 pepper 计算的 HMAC-SHA256不保存可恢复的 Key 明文。
## 会话配置
首次建连配置以及后续 `session.update` 使用标准 Realtime session 结构:
```json
{
"type": "realtime",
"model": "assistant:asst_xxx",
"output_modalities": ["audio"],
"audio": {
"input": {
"format": {
"type": "audio/pcm",
"rate": 24000
},
"turn_detection": {
"type": "server_vad"
}
},
"output": {
"format": {
"type": "audio/pcm"
}
}
}
}
```
客户端可修改输出模态,以及输入端 Server VAD / Push-to-Talk 参数。服务端在 `session.updated` 中返回**实际生效**的配置。
以下内容始终以服务端助手配置为准:
- 助手身份和基础系统提示词;
- Pipeline 或 Realtime 运行模式;
- 底层模型资源及凭证;
- 知识库和服务端 System、HTTP、MCP 工具;
- 工作流图、结束条件和接管策略。
- voice、工具定义、系统提示词和每次回复的临时指令。
服务端不能静默接受一个实际未生效的字段。字段不支持、被锁定或没有权限时,应返回标准 `error` 事件。
## WebRTC
WebRTC 是浏览器和移动端实时音视频交互的首选通道。音频和可选视频走媒体轨道JSON 事件走 DataChannel。
### 建连方式一:统一接口
可信业务后端向 `/v1/realtime/calls` 发送 `multipart/form-data`
| 表单字段 | 类型 | 说明 |
| --- | --- | --- |
| `sdp` | string | 浏览器生成的 SDP Offer |
| `session` | JSON string | Realtime session 配置,必须选择一个已授权助手 |
请求使用长期 API Key 鉴权。成功响应为 `Content-Type: application/sdp` 的 SDP Answer。
```bash
curl -X POST "https://api.example.com/v1/realtime/calls" \
-H "Authorization: Bearer $REALTIME_API_KEY" \
-F 'sdp=<offer.sdp' \
-F 'session={"type":"realtime","model":"assistant:asst_xxx"}'
```
业务浏览器不应直接持有示例中的长期 API Key。它通常把 Offer 发给自己的业务后端,由后端完成上述调用。
### 建连方式二:临时令牌
1. 可信业务后端使用长期 API Key 调用 `/v1/realtime/client_secrets`。
2. 服务端返回绑定助手的短期令牌。
3. 浏览器创建 `RTCPeerConnection`、音频轨道和 `oai-events` DataChannel。
4. 浏览器以临时令牌调用 `/v1/realtime/calls`,请求体为原始 SDP Offer类型为 `application/sdp`。
5. 浏览器把响应中的 SDP Answer 设置为远端描述。
创建令牌的请求示例:
```json
{
"session": {
"type": "realtime",
"model": "assistant:asst_xxx",
"output_modalities": ["audio"]
}
}
```
浏览器侧的核心连接过程如下:
```js
const pc = new RTCPeerConnection({ iceServers });
const remoteAudio = new Audio();
remoteAudio.autoplay = true;
pc.ontrack = (event) => {
if (event.track.kind === "audio") {
remoteAudio.srcObject = event.streams[0];
}
};
const localStream = await navigator.mediaDevices.getUserMedia({ audio: true });
pc.addTrack(localStream.getAudioTracks()[0], localStream);
const events = pc.createDataChannel("oai-events");
events.onmessage = (event) => {
const serverEvent = JSON.parse(event.data);
console.log(serverEvent);
};
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
const answerSdp = await fetch("https://api.example.com/v1/realtime/calls", {
method: "POST",
headers: {
Authorization: `Bearer ${ephemeralToken}`,
"Content-Type": "application/sdp"
},
body: offer.sdp
}).then((response) => response.text());
await pc.setRemoteDescription({ type: "answer", sdp: answerSdp });
```
### WebRTC 数据分工
| 数据 | 通道 | 说明 |
| --- | --- | --- |
| 用户麦克风 | WebRTC audio track | 不再通过 JSON 重复发送音频块 |
| 助手语音 | WebRTC remote audio track | 浏览器直接播放,不依赖 `response.output_audio.delta` 拼接 |
| 标准和扩展事件 | `oai-events` DataChannel | 使用 UTF-8 JSON 文本,保持可靠、有序传输 |
| 用户摄像头 | 可选 WebRTC video track | 通过 `video_track` capability 协商,非 OpenAI 标准能力 |
摄像头轨道只在助手启用视觉、令牌允许且 SDP 协商成功时接收。普通 OpenAI 兼容客户端不添加视频轨道,仍可正常完成语音和文本会话。
切换摄像头、静音、音量控制和 `RTCPeerConnection.getStats()` 属于客户端 WebRTC 能力,不需要新增协议事件。切换设备时可使用 `RTCRtpSender.replaceTrack()`,无需重建会话。
## WebSocket
WebSocket 适合服务到服务、话务网关和希望自行处理音频缓冲的客户端。连接时在 query 中指定助手:
```text
wss://api.example.com/v1/realtime?model=assistant%3Aasst_xxx
```
可信服务端在握手请求中发送:
```http
Authorization: Bearer <api-key-or-ephemeral-token>
```
浏览器无法设置 `Authorization` 握手头时,可同时发送 `realtime` 与 `openai-insecure-api-key.<token>` WebSocket 子协议;服务端选择 `realtime`。这只适用于短期令牌,不应把长期 Key 放入浏览器。
连接成功后,客户端和服务端都只发送 UTF-8 JSON 文本帧。浏览器无法安全保存长期 API Key浏览器实时通话仍应优先使用 WebRTC。
### 文本输入
先创建用户消息,再请求生成回复:
```json
{
"event_id": "evt_text_001",
"type": "conversation.item.create",
"item": {
"type": "message",
"role": "user",
"content": [
{
"type": "input_text",
"text": "请介绍办理流程"
}
]
}
}
```
```json
{
"event_id": "evt_response_001",
"type": "response.create"
}
```
### 音频输入和输出
WebSocket 音频以 Base64 编码放入标准事件:
```json
{
"event_id": "evt_audio_001",
"type": "input_audio_buffer.append",
"audio": "<base64-pcm-bytes>"
}
```
- 开启 Server VAD 时,服务端自动产生 `speech_started`、`speech_stopped` 并触发回复。
- 关闭 VAD 时,客户端发送 `input_audio_buffer.commit`,然后发送 `response.create`。
- 服务端通过 `response.output_audio.delta` 返回 Base64 音频块,并以 `response.output_audio.done` 结束。
- 助手文本或音频转写分别通过 `response.output_text.*` 和 `response.output_audio_transcript.*` 返回。
WebSocket MVP 不传输连续原始视频。视觉输入使用标准 `input_image`;需要连续摄像头的客户端使用 WebRTC 视频轨道。
### 图片输入
图片和文本可放在同一个用户消息中:
```json
{
"type": "conversation.item.create",
"item": {
"type": "message",
"role": "user",
"content": [
{
"type": "input_text",
"text": "画面里有什么?"
},
{
"type": "input_image",
"image_url": "data:image/jpeg;base64,<base64-image>"
}
]
}
}
```
## Push-to-Talk
Push-to-Talk按住说话属于 OpenAI Realtime Core 兼容能力,不需要新增 `x.interactive_media.push_to_talk.*` 扩展事件。按钮是否按下、快捷键和麦克风 UI 状态由客户端本地管理,服务端只处理标准音频缓冲与回复事件。
### 关闭自动 VAD
客户端建立会话后先通过 `session.update` 关闭自动轮次检测:
```json
{
"event_id": "evt_ptt_config_001",
"type": "session.update",
"session": {
"type": "realtime",
"audio": {
"input": {
"turn_detection": null
}
}
}
}
```
关闭 VAD 后,服务端不会根据静音自动提交用户语音或创建回复。客户端必须在用户松开按钮时发送 `input_audio_buffer.commit`,然后发送 `response.create`。
### WebSocket 时序
WebSocket 的音频和控制事件在同一条有序连接上传输:
| 阶段 | 客户端行为 |
| --- | --- |
| 按下 | 开始在客户端录音;如果已有活动回复,发送 `response.cancel` |
| 打断播放 | 立即停止本地音频播放,并发送 `conversation.item.truncate`,用 `audio_end_ms` 删除用户未听到的内容 |
| 松开 | 通过一个或多个 `input_audio_buffer.append` 发送本次录音 |
| 提交 | 依次发送 `input_audio_buffer.commit` 和 `response.create` |
完整的松开事件序列如下:
```json
{
"event_id": "evt_ptt_audio_001",
"type": "input_audio_buffer.append",
"audio": "<base64-pcm-bytes>"
}
```
```json
{
"event_id": "evt_ptt_commit_001",
"type": "input_audio_buffer.commit"
}
```
```json
{
"event_id": "evt_ptt_response_001",
"type": "response.create"
}
```
如果用户在录音完成前取消本次输入,客户端应丢弃本地录音;已经发送到服务端的未提交音频则使用 `input_audio_buffer.clear` 清除。
### WebRTC 时序
WebRTC 的音频媒体轨道和 `oai-events` 控制事件属于不同通道,因此开始新一轮输入前必须显式清理旧缓冲:
| 阶段 | 客户端行为 |
| --- | --- |
| 按下 | 先发送 `input_audio_buffer.clear`,再开始或放开本地麦克风输入门控 |
| 打断生成 | 如果已有活动回复,发送 `response.cancel` |
| 打断播放 | 如果助手音频仍在播放,发送 `output_audio_buffer.clear`;服务端同时截断未播放的对话内容 |
| 松开 | 关闭本地麦克风输入门控,依次发送 `input_audio_buffer.commit` 和 `response.create` |
```js
function sendEvent(event) {
dataChannel.send(JSON.stringify(event));
}
function onPushDown() {
sendEvent({ type: "input_audio_buffer.clear" });
if (hasActiveResponse) {
sendEvent({ type: "response.cancel" });
}
if (isAssistantAudioPlaying) {
sendEvent({ type: "output_audio_buffer.clear" });
}
microphoneTrack.enabled = true;
}
function onPushUp() {
microphoneTrack.enabled = false;
sendEvent({ type: "input_audio_buffer.commit" });
sendEvent({ type: "response.create" });
}
```
客户端应处理指针移出、窗口失焦和权限撤销等情况,确保一次按下只产生一次提交。没有有效音频时不要发送 `input_audio_buffer.commit`。
### 保留 VAD、手动触发回复
如果只想由客户端决定何时生成回复,但仍希望服务端判断说话开始和结束,可以保留 VAD并关闭自动回复及自动打断
```json
{
"type": "session.update",
"session": {
"type": "realtime",
"audio": {
"input": {
"turn_detection": {
"type": "server_vad",
"interrupt_response": false,
"create_response": false
}
}
}
}
}
```
这种模式适合在提交前进行审核、输入校验或知识检索,不等同于严格的按住说话。
## 标准事件范围
首版兼容层至少支持以下 OpenAI Realtime 事件。
### 客户端发送
| 事件 | 用途 |
| --- | --- |
| `session.update` | 更新允许覆盖的会话配置 |
| `conversation.item.create` | 发送文本、整段音频、图片或函数结果 |
| `conversation.item.truncate` | 打断后删除未播放的助手内容 |
| `input_audio_buffer.append` | WebSocket 追加音频 |
| `input_audio_buffer.commit` | 无 VAD 时提交音频 |
| `input_audio_buffer.clear` | 清除未提交音频 |
| `output_audio_buffer.clear` | WebRTC/SIP 清除未播放音频并截断上下文 |
| `response.create` | 请求助手生成回复 |
| `response.cancel` | 取消正在生成的回复 |
### 服务端发送
| 事件组 | 事件 |
| --- | --- |
| 会话 | `session.created`、`session.updated`、`error` |
| 用户语音 | `input_audio_buffer.speech_started`、`input_audio_buffer.speech_stopped`、`input_audio_buffer.committed` |
| 用户转写 | `conversation.item.input_audio_transcription.completed` |
| 对话项 | `conversation.item.added`、`conversation.item.done` |
| 回复生命周期 | `response.created`、`response.output_item.added`、`response.output_item.done`、`response.done` |
| 文本 | `response.output_text.delta`、`response.output_text.done` |
| 音频 | `response.output_audio.delta`、`response.output_audio.done` |
| 音频转写 | `response.output_audio_transcript.delta`、`response.output_audio_transcript.done` |
| 函数调用 | `response.function_call_arguments.delta`、`response.function_call_arguments.done` |
WebRTC 中的助手音频本体走媒体轨道,因此客户端不应依赖 `response.output_audio.delta` 播放声音;音频生命周期和转写事件仍通过 `oai-events` 发送。
## 首版兼容矩阵
| 分类 | 状态 | 说明 |
| --- | --- | --- |
| WebRTC 音频、`oai-events` DataChannel | 支持 | 复用 SmallWebRTC 和现有 Pipeline |
| WebSocket 文本与 24 kHz PCM16 mono Base64 音频 | 支持 | 只接受 UTF-8 JSON 文本帧 |
| 文本轮次、Server VAD、PTT、取消和清空输出 | 支持 | `turn_detection: null` 时由 `commit` + `response.create` 结束轮次 |
| `input_image` Data URL | 条件支持 | 仅 Pipeline 模式且助手已授权视觉输入Realtime 模式明确报错 |
| Client Tool 标准 function call | 支持 | System、HTTP、MCP 工具仍在服务端内部执行 |
| `x.interactive_media.*` | 协商后支持 | 未协商的客户端不会收到扩展事件 |
| `model`、instructions、voice、工具和系统提示词 | 锁定 | 由助手配置决定,覆盖请求返回标准 `error` |
| WebSocket 其他采样率、压缩音频、连续视频 | 暂不支持 | WebRTC 音频由 SDP 协商;连续视频仅走 WebRTC |
| 公开助手别名、断线续传、并发回复、SIP | 暂不支持 | `model` 必须使用实际的 `assistant:asst_xxx` |
Qwen Realtime provider 只允许在 provider 会话首次配置前选择 PTT 或自动轮次检测建立连接后再次切换会返回明确错误。Pipeline 与支持动态更新的 Realtime provider 可在会话内切换。
## 工具调用
服务端 System、HTTP 和 MCP 工具在现有运行时内部执行。Client 工具使用标准函数调用协议,不再建立一套平行的 `client-tool-call` 公开协议。
服务端完成函数参数后发送标准函数调用事件;客户端执行函数并回传:
```json
{
"type": "conversation.item.create",
"item": {
"type": "function_call_output",
"call_id": "call_abc123",
"output": "{\"ok\":true,\"result\":{\"ticket_id\":\"T-1001\"}}"
}
}
```
随后发送 `response.create` 让助手基于工具结果继续当前轮次。`call_id` 在整个会话内唯一,客户端应原样返回。
## Interactive Media 扩展
### 能力协商
客户端收到 `session.created` 后主动声明所需扩展:
```json
{
"event_id": "evt_capabilities_001",
"type": "x.interactive_media.capabilities.update",
"capabilities": [
"workflow_events",
"dynamic_variables",
"handoff",
"video_track"
]
}
```
服务端返回实际接受的能力:
```json
{
"event_id": "evt_server_001",
"type": "x.interactive_media.capabilities.updated",
"capabilities": [
"workflow_events",
"dynamic_variables",
"handoff",
"video_track"
],
"rejected": []
}
```
服务端只发送已接受能力对应的扩展事件。扩展版本与 OpenAI Realtime Core 独立演进。
### 扩展事件目录
| 方向 | 事件 | 用途 |
| --- | --- | --- |
| Client → Server | `x.interactive_media.capabilities.update` | 请求扩展能力 |
| Server → Client | `x.interactive_media.capabilities.updated` | 返回允许的能力 |
| Client → Server | `x.interactive_media.session.variables.update` | 静默更新动态变量,不创建用户消息 |
| Server → Client | `x.interactive_media.session.variables.updated` | 确认变量更新状态 |
| Server → Client | `x.interactive_media.workflow.node_active` | 当前工作流节点变化 |
| Server → Client | `x.interactive_media.workflow.variables.updated` | 工作流变量变化 |
| Server → Client | `x.interactive_media.workflow.event` | 工作流业务事件 |
| Server → Client | `x.interactive_media.workflow.error` | 工作流执行失败 |
| Server → Client | `x.interactive_media.call.handoff_requested` | 助手请求人工接管 |
| Server → Client | `x.interactive_media.call.ended` | 会话结束及原因 |
动态变量更新示例:
```json
{
"event_id": "evt_variables_001",
"type": "x.interactive_media.session.variables.update",
"update_id": "update_001",
"variables": {
"user_name": "王先生",
"region": "上海"
}
}
```
```json
{
"event_id": "evt_server_002",
"type": "x.interactive_media.session.variables.updated",
"update_id": "update_001",
"status": "accepted"
}
```
工作流节点事件示例:
```json
{
"event_id": "evt_server_003",
"type": "x.interactive_media.workflow.node_active",
"nodeId": "collect_materials"
}
```
会话结束示例:
```json
{
"event_id": "evt_server_004",
"type": "x.interactive_media.call.ended",
"reason": "assistant_completed"
}
```
`x.interactive_media.call.ended` 发出后,服务端停止产生新回复并正常关闭媒体与信令通道。未启用扩展的客户端只观察到标准回复结束和连接关闭。
## 打断和回复并发
- 默认同一会话只允许一个活动回复。
- 用户开始说话且助手允许打断时,服务端取消当前生成并停止后续音频输出。
- 客户端可显式发送 `response.cancel`。
- WebSocket 客户端自行管理音频播放,因此停止播放后应发送 `conversation.item.truncate`,用 `audio_end_ms` 告诉服务端用户实际听到的位置。
- WebRTC/SIP 的输出音频由服务端缓冲VAD 打断时由服务端自动截断,显式 Push-to-Talk 打断使用 `output_audio_buffer.clear`。
- 助手配置禁止打断时,服务端继续当前回复,并对不允许的显式取消返回 `error`。
## 错误格式
协议错误使用标准 `error` 事件,不使用只存在于某个传输的自定义错误结构:
```json
{
"event_id": "evt_server_error_001",
"type": "error",
"error": {
"type": "invalid_request_error",
"code": "session_field_locked",
"message": "The assistant does not allow overriding instructions.",
"param": "session.instructions",
"event_id": "evt_update_001"
}
}
```
常见错误码包括:
| code | 含义 |
| --- | --- |
| `authentication_failed` | API Key 或临时令牌无效 |
| `assistant_not_found` | 助手不存在、未发布或不可访问 |
| `session_field_locked` | 客户端尝试覆盖服务端锁定配置 |
| `unsupported_event` | 当前协议版本不支持该事件 |
| `capability_not_enabled` | 未协商或无权使用某项扩展 |
| `response_in_progress` | 已有活动回复且当前请求不能并发执行 |
| `tool_result_timeout` | Client 工具未在规定时间内返回 |
| `internal_error` | 运行时出现不可恢复错误 |
错误只终止相关事件;只有鉴权失败、协议严重错误或运行时不可恢复时才关闭整个连接。
## 事件顺序和重连
- WebSocket 文本帧和 `oai-events` DataChannel 都按连接内的发送顺序处理。
- 客户端事件建议携带唯一 `event_id`;服务端错误通过 `error.event_id` 指回原事件。
- `session.created` 是连接建立后的第一个业务事件。
- 每个回复由 `response.created` 开始,以 `response.done` 结束Delta 必须携带所属的 response、item 和 content 索引。
- `Interactive Media Realtime Extensions v1` 不支持会话断线续传。重连会创建新会话;服务端历史记录仍按平台策略持久化。
- 客户端不得把收到的旧连接事件写入新连接。
## 现有能力映射
| 现有能力或旧事件 | 新协议 |
| --- | --- |
| WebRTC 麦克风与助手音频 | WebRTC audio track |
| 摄像头连续输入 | 可选 WebRTC video track + `video_track` capability |
| `user-input` 文本或图片 | `conversation.item.create` + `response.create` |
| `session-update` | `x.interactive_media.session.variables.update` |
| `transcript` 用户转写 | `conversation.item.input_audio_transcription.*` |
| `assistant-text-start/delta/end` | `response.output_text.*` 或 `response.output_audio_transcript.*` |
| `client-tool-call/result` | 标准 function call + `function_call_output` |
| `node-active` | `x.interactive_media.workflow.node_active` |
| `workflow-variables` | `x.interactive_media.workflow.variables.updated` |
| `workflow-event` | `x.interactive_media.workflow.event` |
| `call-ended` | `x.interactive_media.call.ended` |
| 知识库、服务端工具、工作流执行 | 继续在现有运行时内部完成 |
Pipeline 与 Realtime 两种助手运行模式对客户端使用同一套北向事件。协议适配层负责把不同内部模型事件归一化,客户端不需要根据运行模式切换协议。
原有 RTVI 入口和消息格式继续并行保留,当前前端不需要迁移;新兼容接口面向新的第三方集成。
## 传输选择
| 场景 | 推荐传输 | 原因 |
| --- | --- | --- |
| 浏览器实时语音、打断和摄像头 | WebRTC | 媒体自适应、播放简单、支持视频轨道 |
| 移动端实时语音 | WebRTC | 更适合不稳定网络和双向媒体 |
| 服务端 Agent 或批处理式实时流 | WebSocket | JSON 与音频缓冲可完全由调用方控制 |
| 电话网关 | WebSocket | 易于接入现有 PCM/PCMU 音频桥接 |
| 纯文本 Realtime | WebSocket | 无需建立媒体轨道 |
## 兼容性原则
1. 不修改标准事件的名称和含义。
2. 所有项目扩展使用 `x.interactive_media.*` 命名空间。
3. 扩展必须先协商再发送,普通客户端只看到标准事件。
4. 未支持字段明确报错,不静默伪装为成功。
5. OpenAI Realtime Core 和 Interactive Media 扩展分别进行版本管理。
6. 新协议只替换北向连接层,不改变现有 Pipecat 运行时和助手配置模型。
## 参考
- [OpenAI Realtime API with WebRTC](https://developers.openai.com/api/docs/guides/realtime-webrtc)
- [OpenAI Realtime API with WebSocket](https://developers.openai.com/api/docs/guides/realtime-websocket)
- [OpenAI Realtime conversations](https://developers.openai.com/api/docs/guides/realtime-conversations)
- [OpenAI Realtime Push-to-Talk](https://developers.openai.com/api/docs/guides/realtime-conversations#push-to-talk)