OpenAI Realtime API 权威技术指南:从“Hello”到生产级语音代理

I. 引言:Realtime 架构及其“为何如此”
欢迎阅读这份关于 OpenAI Realtime API 的权威技术指南。本指南旨在深入剖析构建下一代实时语音和多模态交互所需的每一个核心概念。我们将从高层架构的设计理念一直深入到低级别的协议实现、高级会话管理和生产环境中的成本控制策略。
范式转变:超越“链式” AI
传统的语音助手架构是一种“链式”架构(Chained Architecture)¹。它涉及三个独立的顺序步骤:
- STT (Speech-to-Text): 将用户的语音转换为文本。
- LLM (Text-to-Text): 大语言模型处理该文本并生成一个文本响应。
- TTS (Text-to-Speech): 将该文本响应转换回语音。
这种方法虽然可预测且易于控制,但有其固有的局限性:它很慢,因为每一步都必须等待前一步完成;而且它会丢失信息。原始音频中包含的“言外之意”——如情感、语调、犹豫和背景噪音——在第一次STT转换中就被丢弃了。
Realtime API 引入了一种根本性的范式转变:端到端的语音到语音 (Speech-to-Speech, S2S) 架构 ¹。该 API 使用单一的、原生的多模态模型(如 gpt-realtime),该模型直接处理音频输入并直接生成音频输出 ¹。
S2S 的“为何”:捕捉意图与情感
S2S 的真正价值不仅仅在于其低延迟,更在于它能够“听到情感和意图”并“过滤噪音” ¹。
与扁平的文本字符串不同,原始音频流是一个丰富的信息载体。模型可以直接感知到微妙的线索,例如用户是“生气、沮丧还是高兴” ²。这种能力为构建能够进行真正共情和类人对话的代理打开了大门。
然而,这里存在一个关键的非对称性,开发人员必须理解:
- 输入(感知): 模型 原生 地理解传入音频中的情感和语调 ¹。
- 输出(表达): 模型的 传出 情感和语调主要通过 文本提示 来控制 ³。例如,您必须明确指示:
"instructions": "Speak in a cheerful and positive tone."(以欢快、积极的语气说话)。
社区中的开发人员曾询问如何从 AI 的响应中获取结构化的情感数据(例如,用于驱动头像动画),但答案是目前没有这样的 JSON 事件 ⁴。这意味着开发人员不能指望 AI“自动”镜像用户的情感。相反,他们必须设计一个有意的提示循环,例如:“# 规则:如果用户听起来很沮丧,请用冷静和体谅的语气回应。”
API 的核心:RealtimeSession
Realtime API 中的一切都围绕着一个核心概念:RealtimeSession ⁵。这与无状态的 REST API 调用根本不同。会话是一种有状态的交互 (stateful interaction),它会记住上下文、对话历史和连接配置 ⁵。正是这种状态性,使得诸如打断、动态提示更新和复杂的会话管理成为可能。
实现路径生态系统
本指南将带您了解使用此 API 的三个主要路径,从最高层的抽象到最底层的控制:
- SDK (Agents JS): 快速、抽象的路径,用于浏览器中的原型设计 ⁶。
- 协议 (WebRTC, WebSocket): 自定义客户端/服务器实现的低级别路径 ⁷。
- 电话 (SIP): 用于集成 VoIP 和电话网络的路径 ⁹。
II. 快速通道:使用 Agents JS SDK 的五分钟快速入门
对于希望在浏览器中快速构建语音代理原型的开发人员来说,Agents JS SDK (@openai/agents) 是最快的途径。它抽象了底层连接的复杂性 ⁶。
步骤 1:环境与安装
您可以使用 Vite 轻松创建一个新的 TypeScript 项目 ⁶。
# 创建一个 Vite 项目
npm create vite@latest my-realtime-app -- --template vanilla-ts
cd my-realtime-app
安装 Agents SDK 和所需的依赖
npm install @openai/agents zod@3
步骤 2:安全模型 - 临时客户端密钥 (Ephemeral Keys)
这是实现客户端应用最关键的安全概念。您永远不能将您的秘密 OpenAI API 密钥(sk_...)嵌入到您的 JavaScript 客户端代码中。
解决方案: 您必须设置一个您控制的后端服务器,该服务器的唯一工作是“铸造” (mint) 临时客户端密钥 (ephemeral client key) ⁶。这些密钥是短暂有效的,并被授权连接到 Realtime API。
您的后端服务器(使用您的标准 API 密钥)向 OpenAI API 发出一个 POST 请求:
# 在您的安全服务器上运行
export OPENAI_API_KEY="sk_...(您的标准密钥)"
curl -X POST https://api.openai.com/v1/realtime/client_secrets
-H "Authorization: Bearer $OPENAI_API_KEY"
-H "Content-Type: application/json"
-d ‘{ "session": { "type": "realtime", "model": "gpt-realtime" } }’
响应将包含一个以 ek_... 开头的 "value" 字符串 ⁶。这是您安全地发送到浏览器客户端以进行身份验证的密钥。
步骤 3:核心 SDK 概念
SDK 有两个主要类 ⁶:
RealtimeAgent: 这是代理的“大脑”。它定义了代理的身份、个性和系统指令。RealtimeSession: 这是代理的“身体”。它管理连接、麦克风、扬ક器以及对话的生命周期。
步骤 4:连接与通话
将所有部分组合在一起,客户端代码非常简洁 ⁶:
import { RealtimeAgent, RealtimeSession } from '@openai/agents/realtime';
// 1. 定义“大脑”
const agent = new RealtimeAgent({
name: ‘Assistant’,
instructions: ‘You are a helpful assistant.’,
});
// 2. 创建“身体”
const session = new RealtimeSession(agent, {
model: ‘gpt-realtime’,
});
// 3. 从您的后端获取临时密钥
// (在生产中,您会通过 fetch 从您的服务器获取这个)
const EPHEMERAL_KEY = ‘ek_…’; // (在此处粘贴您生成的密钥)
async function connectAndTalk() {
try {
// 4. 连接会话
// 这将自动请求麦克风权限并处理 WebRTC 连接
await session.connect({
apiKey: EPHEMERAL_KEY
});
console.log(‘You are connected!’);
// 现在您可以开始对着麦克风说话了
} catch (e) {
console.error(e);
}
}
connectAndTalk();
抽象的“黑匣子”
session.connect 这一行代码看似简单,实则蕴含深机。SDK 为您做出了一个关键的架构决策 ⁶:
- 在浏览器中运行时: 它会自动使用 WebRTC 进行连接,并配置您的麦克风和扬声器。
- 在后端(如 Node.js)运行时: SDK 将自动使用 WebSocket 进行连接。
这对于快速原型设计来说非常棒,但它是一个“黑匣子”。当您的应用程序因公司防火墙阻止 WebRTC 所需的 UDP 端口而失败时,您将陷入困境。要进行调试和构建生产级应用,您必须了解 SDK 正在为您隐藏的底层协议。
III. 深入剖析:核心连接协议
这是您的第一个重大架构决策。您的应用程序将如何与 OpenAI 通信?
A. WebRTC:浏览器与移动端的冠军
WebRTC (Web Real-Time Communication) 是构建实时浏览器应用的一组标准接口 ⁷。
-
适用场景: 推荐用于基于浏览器的语音到语音应用以及从客户端(Web 或移动设备)连接的任何场景。它为流式媒体提供了“更一致的性能” ⁷。
-
工作原理: WebRTC 原生处理音频轨道的流式传输。您只需管理两件事:
RTCPeerConnection: 核心连接对象。RTCDataChannel: 一个名为"oai-events"的数据通道,用于发送和接收 JSON 事件(如文本消息或控制命令)⁷。
-
实现流程: 您有两种方式来建立连接 ⁷:
- 临时令牌流程 (推荐):
- 浏览器从您的服务器获取一个
ek_...令牌。 - 浏览器创建一个 SDP (Session Description Protocol) Offer。
- 浏览器直接将此 SDP Offer POST 到
https://api.openai.com/v1/realtime/calls,并使用ek_...令牌进行Authorization头认证。
- 浏览器从您的服务器获取一个
- 统一接口流程:
- 浏览器将其 SDP Offer POST 到您自己的服务器。
- 您的服务器接收此 SDP,添加
sessionConfigJSON,并使用您的标准sk_...API 密钥将请求转发到 OpenAI 的.../calls端点。 - 优点: 客户端逻辑更简单。缺点: 这会将您的服务器置于会话初始化的“关键路径”上 ⁷。
- 临时令牌流程 (推荐):
-
关键客户端代码片段 ⁷:
// 1. 创建对等连接 const pc = new RTCPeerConnection();// 2. 处理入站音频 (来自 AI)
pc.ontrack = (event) => {
const audioEl = document.createElement(‘audio’);
audioEl.srcObject = event.streams;
audioEl.autoplay = true;
};// 3. 添加出站音频 (来自用户麦克风)
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
stream.getTracks().forEach(track => pc.addTrack(track, stream));// 4. 创建事件数据通道
const dc = pc.createDataChannel("oai-events");
dc.addEventListener("message", (event) => {
console.log(‘Server Event:’, JSON.parse(event.data));
});// 5. 启动 SDP 交换… (创建 offer, setLocalDescription, POST 到 OpenAI, setRemoteDescription)
B. WebSocket:服务器端的基石
WebSocket 是一种广泛支持的实时数据传输 API,是服务器到服务器 (server-to-server) 集成的绝佳选择 ⁸。
- 适用场景: 在您的后端系统和 OpenAI 之间建立持久连接。这是可用于交互的“最低级别接口” ⁸。
- 工作原理: 您必须管理一切。您不仅要通过 WebSocket 发送和接收 JSON 控制事件,还必须自己处理音频数据,这些数据必须是 Base64 编码的 ⁵。
- 连接 URL:
wss://api.openai.com/v1/realtime?model=gpt-realtime⁸。 - 身份验证: 使用您的标准 OpenAI API 密钥作为 Bearer 令牌 ⁸。
// Node.js 'ws' 模块示例 import WebSocket from 'ws';const ws = new WebSocket(
‘wss://api.openai.com/v1/realtime?model=gpt-realtime’,
{ headers: { ‘Authorization’:Bearer ${process.env.OPENAI_API_KEY}} }
);
- 音频数据流:
- 发送音频: 您将原始音频块 (chunk) 编码为 Base64 字符串,并将它们包装在一个
input_audio_buffer.appendJSON 事件中发送 ⁵。 - 接收音频: 您侦听
response.output_audio.delta事件。这些事件包含 Base64 编码的音频块,您需要自己负责将它们排队、解码、缓冲和播放 ⁵。
- 发送音频: 您将原始音频块 (chunk) 编码为 Base64 字符串,并将它们包装在一个
C. SIP:通往电话世界的大门
SIP (Session Initiation Protocol) 是一种用于通过互联网拨打电话的协议。当您想将电话号码连接到 AI 代理时,应使用此协议 ⁹。
- 适用场景: 呼叫中心、自动电话应答系统、或任何涉及传统电话 (VoIP) 的场景。
- 工作原理: 这是一个基于 Webhook 的“握手”流程。
- 配置: 您使用 SIP 中继提供商(如 Twilio)将您的电话号码指向 OpenAI 的 SIP 端点:
sip:$PROJECT_ID@sip.api.openai.com;transport=tls。您还需要在 OpenAI 项目设置中配置一个 Webhook ⁹。 - 来电: 当有电话打入时,OpenAI 会收到 SIP 流量,并向您的 Webhook URL 发送一个
realtime.call.incoming事件。此事件包含一个唯一的call_id⁹。 - 您的决策: 您的服务器必须通过调用 OpenAI 的 REST API 来响应此 Webhook ⁹:
- 接受通话:
POST /v1/realtime/calls/$CALL_ID/accept。您在此请求的正文中提供会话配置(例如,使用哪个模型、哪个声音)。 - 拒绝通话:
POST /v1/realtime/calls/$CALL_ID/reject。您可以选择提供一个 SIP 状态码(例如,486 表示“忙碌”)⁹。
- 接受通话:
- 配置: 您使用 SIP 中继提供商(如 Twilio)将您的电话号码指向 OpenAI 的 SIP 端点:
协议选择矩阵
为了帮助您做出这个关键的架构决策,这里有一个简单的矩阵:
| 协议 | 主要用例 | 连接端 | 音频处理 | 关键优势 |
|---|---|---|---|---|
| Agents JS SDK | 快速原型设计 | 客户端 (浏览器) | 自动 (WebRTC) | 最快上手,高度抽象 ⁶ |
| WebRTC | 生产级客户端 | 客户端 (浏览器/移动) | 自动 (原生) | 客户端媒体性能最佳 ⁷ |
| WebSocket | 生产级后端 | 服务器端 | 手动 (Base64) | 完全控制,S2S 集成 ⁸ |
| SIP | 电话系统 | 服务器端 (Webhook) | 自动 (VoIP) | 连接到电话号码 ⁹ |
生产架构的“黄金线索”:call_id
无论您选择哪种路径,都会发现一个共同的“黄金线索”:call_id。
- 在 SIP 中,
call_id在realtime.call.incomingWebhook 中提供 ⁹。 - 在 WebRTC 中,初始 SDP 响应包含一个
Location头,其中包含call_id¹²。
这个 call_id 是实现生产级架构的关键。它允许您实现所谓的**“旁路控制通道” (sideband control channel)**。
其思想是:您的最终用户通过 WebRTC(浏览器)或 SIP(电话)连接。同时,您的后端服务器可以使用这个 call_id 打开一个平行的 WebSocket 连接,连接到完全相同的会话:
wss://api.openai.com/v1/realtime?call_id={call_id} ¹²
这不是一个可选的高级功能;这是构建安全、可控的生产应用的标准方式。我们将在下一节探讨为什么。
IV. 掌控对话:状态、控制与打断
连接只是第一步。管理一场流畅、自然、可打断的对话才是真正的挑战。
A. 对话的剖析:事件驱动的状态
RealtimeSession 是一个有状态的对象。您通过发送客户端事件 (Client Events) 来更新其状态,并通过侦听服务器事件 (Server Events) 来对其变化做出反应 ⁵。
您需要侦听的核心服务器事件 ⁵:
session.created: 您的会话已准备就绪。session.updated: 确认您发送的配置更改(例如session.update)已生效。input_audio_buffer.speech_started: VAD 检测到用户开始讲话 ¹³。input_audio_buffer.speech_stopped: VAD 检测到用户停止讲话 ¹³。response.output_text.delta: AI 响应的实时文本流。response.done: AI 已完成其完整的文本响应。
您需要发送的核心客户端事件 ⁵:
session.update: 动态更改会话配置(例如,更新 VAD 设置或系统提示)。conversation.item.create: 手动向对话历史中添加一个项目(例如,用户输入的文本消息)。response.create: 请求 AI 根据当前对话历史生成一个响应。response.cancel: (极其重要) 立即取消当前正在生成的 AI 响应。
B. 打断的艺术 (Barge-In):语音代理的头号难题
在自然的对话中,人们会互相打断。如果您的 AI 代理在用户试图插话时喋喋不休,那么这种体验会非常糟糕。
问题: 默认的 VAD (Voice Activity Detection) 非常敏感。用户发出“嗯...”或“好的...”之类的附和声,或者仅仅是背景噪音,都可能被错误地识别为“打断”,从而破坏对话流程 ¹⁴。
解决方案第 1 部分:VAD 调优
您可以通过发送 session.update 事件来配置 VAD ¹⁵。您有两种模式可供选择 ¹⁵:
模式 1:server_vad (基于静音的 VAD)
- 机制: 基于音频中的静音周期来自动切分语音 ¹⁵。
- 配置参数:
threshold(0 到 1): 激活 VAD 所需的音量。在嘈杂环境中设置更高的值可能效果更好 ¹⁵。silence_duration_ms: 触发“语音停止”事件所需的静音时长(毫秒)。较短的值意味着转折更灵敏 ¹⁵。
模式 2:semantic_vad (语义 VAD - 推荐)
- 机制: 使用语义分类器来检测用户是否意图完成讲话。它理解“我想要一个,嗯...”(未完成)和“帮我订一张票”(已完成)之间的区别 ¹³。
- 优势: “不太可能在语音到语音对话中打断用户” ¹⁵。
- 配置参数:
eagerness("low", "medium", "high", "auto"): 控制模型打断的“急切程度”。low会让 VAD 更有耐心,允许用户进行更长的停顿 ¹⁵。
两种模式下的关键参数:
在您的 VAD 配置中,必须设置以下两个布尔值(仅在对话模式下)¹⁵:
"create_response": true: 当 VAD 检测到用户停止讲话时,自动触发 AI 响应。"interrupt_response": true: (最重要的设置) 允许在 AI 正在讲话时,用户的语音输入打断 AI 的响应。
解决方案第 2 部分:程序化处理(三步协同)
仅仅调优 VAD 是不够的。您必须在代码中主动处理打断事件流。
以下是实现无缝打断的三步协同模式:
- 配置: 在您的
session.update中,设置semantic_vad并确保"interrupt_response": true¹⁵。 - 侦听: 一旦 AI 开始讲话(例如,您正在接收
response.output_audio.delta事件),您的代码必须积极侦听input_audio_buffer.speech_started服务器事件 ¹³。 - 行动: 当您在 AI 讲话期间侦听到
input_audio_buffer.speech_started事件时,您的代码必须立即发送一个客户端事件:{"type": "response.cancel"}¹⁶。
这个 侦听 -> 取消 的循环是实现即时、自然打断体验的秘诀。它告诉 OpenAI:“停止你正在说的话,用户有更重要的事情要说。”
C. “旁路控制通道”:安全的服务器端控制
正如在第三节中提到的,“旁路” (sideband) 架构是生产环境的标准。
为何如此? 您的核心业务逻辑、工具使用 (function calling) 和私有 API 密钥绝不能放在客户端(浏览器或移动应用)上 ¹²。
如何实现?
- 客户端(WebRTC/SIP)建立连接并获得一个
call_id⁹。 - 您的后端服务器使用该
call_id打开一个平行的 WebSocket 连接到wss://api.openai.com/v1/realtime?call_id={call_id}¹²。
您的服务器现在可以做什么?
- 监控会话: 侦听所有服务器事件,记录日志,监控情绪 ¹²。
- 动态更新指令: 随时发送
session.update事件。- 示例:
ws.send(JSON.stringify({ type: "session.update", session: { instructions: "用户刚刚将商品 X 添加到了购物车。在对话的其余部分请称呼用户为‘VIP 客户’。" } }))¹²。
- 示例:
- 安全处理工具调用: 在服务器上安全地实现工具逻辑,远离客户端 ¹²。
- 主动挂断电话: 您的服务器可以决定何时结束通话,而不是依赖客户端。
POST https://api.openai.com/v1/realtime/calls/$CALL_ID/hangup⁹。
“不优雅”的会话结束
一个常见的问题是:客户端(例如浏览器)如何结束会话?
浏览 Realtime API 的文档,您会发现 .../hangup ⁹、.../reject ⁹ 等服务器端 REST API,但客户端事件列表中没有 session.end 或 session.close 这样的事件 ¹⁰。
正如社区所发现的,答案是:没有用于“优雅”关闭的 JSON 客户端事件 ¹⁹。
RealtimeSession 是一个绑定到网络连接的服务器端概念。要从客户端结束会话,您只需关闭网络连接即可:
- WebRTC: 调用
pc.close()和dc.close()¹⁹。 - WebSocket: 调用
ws.close()¹⁹。
V. 代理的“大脑”:为 gpt-realtime 编写提示
为 gpt-realtime 模型编写提示(Prompting)是一门艺术,它与为 GPT-4 编写文本提示有所不同,因为它必须实时响应并处理不完美的音频输入 ²⁰。
核心指导原则
- 坚持不懈地迭代: 微小的措辞变化(例如,“听不清” vs “无法理解”)可能会对行为产生重大影响 ²⁰。
- 要点 > 段落: 清晰、简短的要点列表比冗长的段落更有效 ²⁰。
- 使用示例引导: 模型会强烈地遵循您提供的示例短语。
- 使用大写字母强调: 对于关键规则,请使用全大写(ALL CAPS)使其突出。
- 将非文本规则文本化: 不要写
IF x > 3 THEN ESCALATE,而要写IF 超过三次失败,则转接人工²⁰。
生产就绪的提示结构
将您的系统提示组织成清晰、有标签的部分,以帮助模型保持一致性 ²⁰。
# 角色与目标
(你是一个...)
个性与语气
(你的语气应该是…)
上下文
(关于此用户或情况的背景信息…)
工具
(如何以及何时使用你的工具…)
指示 / 规则
(必须遵守的硬性规则。使用大写字母。)
对话流程
(定义对话的各个阶段,例如:问候 -> 发现 -> 诊断 -> 解决…)
安全与升级
(何时以及如何将对话转接给人工…)
关键提示片段示例
以下是一些必须包含在您的实时提示中的关键部分 ²⁰:
处理不清晰的音频:
明确告诉模型该说什么。
## 不清晰的音频
- 仅对清晰的音频或文本做出回应。
- 如果用户的音频不清晰(例如,输入模糊/背景噪音/静音/无法理解),请使用 {preferred_language} 的短语要求澄清。
- 澄清短语示例:
- “抱歉,我没有听清——您能再说一遍吗?”
- “背景有些噪音。请重复最后一部分。”
约束语言:
防止语言漂移。
## 语言
- 对话将**仅以英语**进行。
- 即使用户要求,也不要用任何其他语言回应。
- 如果用户说其他语言,请礼貌地解释支持仅限于英语。
定义对话流程:
将对话分解为多个阶段。
## 对话流程 — 问候
- 目标:设定基调并询问来电原因。
- 如何回应:
- 自我介绍为“ACME 支持”。
- 保持简短;邀请来电者说出他们的目标。
- 示例短语 (请变化使用):
- “感谢致电 ACME 支持—今天有什么可以帮您?”
- “您好,这里是 ACME 支持。您的服务出了什么问题?”
- 退出条件:当来电者陈述了初步目标或症状时。
定义升级路径 (至关重要):
这是您的安全网。
# 安全与升级
## 何时升级 (无需额外排查):
- 安全风险 (自我伤害、威胁、骚扰)
- 用户**明确要求**与人工交谈
- 严重不满 (例如,“极其沮丧”,重复投诉,使用亵渎性语言)
- 同一任务上 2 次工具调用失败
- 连续 3 次无匹配/无输入事件
在调用 escalate_to_human 工具时必须说的话:
- “感谢您的耐心——我正在将您转接给一位专家。”
- 然后立即调用工具:
escalate_to_human
VI. 高级主题与运维
A. 独立实时转录
Realtime API 不仅仅用于对话;它还可以作为一个高性能的纯转录服务 ²¹。
- 用例: 实时字幕、会议记录或任何不需要 AI 响应的场景。
- 会话配置: 您在创建会话时(通过 WebSocket 或 WebRTC)将
type设置为"transcription"²¹。 - 转录模型选择:
gpt-4o-transcribe/gpt-4o-mini-transcribe: 推荐模型。这些模型支持增量 delta 事件。这意味着您可以实时获取单词级别的转录,非常适合实时字幕 ²¹。whisper-1: 不推荐用于实时流。whisper-1在此 API 中的 delta 事件不提供增量结果;它只会在 VAD 检测到语音回合结束时,在delta和completed事件中发送完整的转录 ²¹。
- 性能: 官方文档没有提供延迟或准确性的具体基准 ²¹。然而,第三方基准测试表明
gpt-4o-transcribe在词错率 (Word Error Rate, WER) 方面达到了行业顶尖水平,优于 Whisper Large v2 和 Gemini ²²。 - 支持的语言:
- 配置中需要一个 ISO-639-1 语言代码(例如
en)²¹。 - 文档未提供完整的支持语言列表 ¹⁰。这是一个已知的文档空白。对于 Whisper 模型,最可靠的列表来源是其开源代码库 (
tokenizer.py) ²⁶。
- 配置中需要一个 ISO-639-1 语言代码(例如
B. Realtime 的经济学:管理成本
这可能是本指南中最重要的部分。如果您不了解 Realtime API 的计费方式,您的账单可能会失控。
核心计费模型: “成本在创建响应 (Response) 时产生” ²⁷。
这意味着您不是按分钟付费,而是按 AI 说话的回合付费。
计费详情:
- 响应成本: 每次 AI 生成响应时,您都需要支付该回合的输入令牌和输出令牌 ²⁷。
- 转录成本: 如果您启用了输入转录,这是一个单独的计费项,使用不同的模型(如
whisper-1)并按不同的费率卡计费 ²⁷。
隐藏的“二次方成本”陷阱:
文档中有一句关键的话:“为每个响应,整个对话都会被发送到模型” ²⁷。
这意味着:
- AI 的第 1 个响应(Turn 1)的输入 = (User 1)。
- AI 的第 2 个响应(Turn 2)的输入 = (User 1 + AI 1 + User 2)。
- AI 的第 10 个响应(Turn 10)的输入 = (User 1...AI 9 + User 10)。
结论: 您的输入令牌成本在对话的每一回合都会增加。一场长时间对话的成本不是线性的;它更接近于一条二次方曲线。第 50 回合的成本将远远高于第 1 回合。
强制性的成本控制策略 ²⁷:
由于上述原因,成本控制不是可选项,而是必须项。
- 使用 "Mini" 模型:
gpt-realtime-mini“便宜得多”,尽管在遵循指令和工具调用方面可能略逊一筹 ²⁷。 - 利用缓存: 如果一个响应的输入令牌与前一个响应匹配(即对话历史未变),提示缓存会自动应用,大幅减少输入令牌成本 ²⁷。
- 主动截断 (Truncation):
token_limits.post_instructions: 在会话配置中设置一个低于模型最大值的令牌窗口,以强制提前截断(丢弃最旧的消息)。retention_ratio: 设置一个小于 1.0 的保留率(例如 0.75)。这会在截断时丢弃比绝对必要更多的消息,从而为下一次截断留出“净空”,并减少缓存失效的频率 ²⁷。
- 手动删除/摘要: 您的旁路服务器可以发送
conversation.item.delete事件来删除旧消息,或发送conversation.item.create来用更短的摘要替换旧消息 ²⁷。
VII. 结论:综合您的 Realtime 策略
我们已经从高层抽象的 SDK 深入到了低级别的协议实现、复杂的对话控制和关键的成本管理。
要构建一个生产就绪的 Realtime 语音代理,请牢记以下 “生产黄金三角”:
- 检测 (Detection): 使用
semantic_vad并将interrupt_response设置为true。这是实现自然对话的基础 ¹⁵。 - 行动 (Action): 在您的代码中实现
input_audio_buffer.speech_started->response.cancel的事件循环。这是实现即时打断的关键 ¹³。 - 安全 (Security): 采用“旁路控制通道” (sideband) 架构。让客户端处理媒体流,让您的服务器使用
call_id通过 WebSocket 处理所有业务逻辑、工具调用和会话控制 ¹²。
最后,在编写任何生产代码之前,请先对您的成本结构进行建模 ²⁷。理解并主动管理因对话历史不断增长而带来的成本,是确保您的项目长期成功的决定性因素。第 50 回合的成本,与第 1 回合的成本截然不同。


