OpenAI 推出的 Realtime API 标志着语音交互技术的一次重大突破。它允许开发者构建低延迟、高效率的多模态对话体验,支持文本和音频输入输出,为语音助手、在线教育、游戏等场景带来了新的可能性。
1.会话区域
2.语音设置
3.功能与模型配置
- message: 表示一条消息,包含文本或音频的输入。
- function_call: 表示模型希望调用某个工具或函数。
- function_call_output: 表示函数调用的返回结果。
1.引入模块
2.floatTo16BitPCM 函数
3.base64EncodeAudio 函数
4.解码音频文件并获取通道数据
5.发送音频数据
- event_id:可选的客户端生成的 ID,用于标识事件。
- type:事件类型,必须为 "conversation.item.create"。
- previous_item_id:新项目插入后,前一个项目的 ID。
- item:要添加的项目,包含:
- id:项目唯一 ID。
- type:项目类型("message"、"function_call"、"function_call_output")。
- status:项目状态("completed"、"in_progress"、"incomplete")。
- role:消息发送者的角色("user"、"assistant"、"system")。
- content:消息内容。
- call_id、name、arguments、output:用于函数调用相关的详细信息。
我们把关注点放到代码实现上, 如下图所示,这里是Python Server端的代码,__init__.py。 它主要完成了下面三个方面的任务,
- WebSocket 连接:connect()负责管理与 OpenAI API 的 WebSocket 连接,发送和接收数据。
- 工具执行:VoiceToolExecutor 负责工具调用的异步执行,确保并发操作的安全性。
- 实时 API 代理:OpenAIVoiceReactAgent 管理与 OpenAI 实时 API 的交互,执行工具并根据输入流式传输响应。
1.connect()(函数)
2.VoiceToolExecutor (类)
- tools_by_name: 工具名称与 BaseTool 对象的字典。
- _trigger_future: 管理函数调用触发的 asyncio.Future 对象。
- _lock: 用于安全处理并发操作的asyncio.Lock。
- _trigger_func():等待 future 对象返回工具调用数据。
- add_tool_call(tool_call):添加工具调用,确保不会被其他并发调用覆盖。
- _create_tool_call_task(tool_call):创建并运行处理工具调用的任务,使用工具的 `ainvoke()` 方法解析 JSON 参数并处理错误。
- output_iterator():持续返回任务结果的主循环,管理并发任务并处理错误。
3.OpenAIVoiceReactAgent (类)
- model: 使用的 OpenAI 模型。
- api_key: 以 SecretStr 格式安全存储的 API 密钥。
- instructions: 可选的模型指令。
- tools: 可用的工具列表。
- url: OpenAI API 的 WebSocket URL。
- aconnect(input_stream, send_output_chunk):连接 OpenAI API 并管理实时的输入输出通讯。它发送工具信息和指令,监听响应,处理工具输出,并流式传输对话内容,使用 amerge 合并多个输入输出流。该方法还处理特定的响应类型并触发必要的工具调用。
- audio-playback-worklet.js:实现了一个 AudioPlaybackWorklet,负责将接收到的 PCM 数据解码并播放。它包含了handleMessage 方法,将传入的音频数据存入缓冲区;process 方法负责将缓冲区的数据输出到扬声器,按每次的缓冲量来处理数据。
- audio-processor-worklet.js:实现了 PCMAudioProcessor,将麦克风捕获的 Float32 音频数据转换为 Int16 格式,然后通过 postMessage 发送到主线程,供后续处理。
- Index.html:通过WebSocket("ws://localhost:3000/ws")与服务器建立连接后,即可实现音频的实时传输和处理。为此,我们创建了一个Player类来初始化音频上下文,并利用AudioWorkletNode(引用audio-playback-worklet.js)播放服务器传来的音频数据。同时,设计Recorder类,用于获取用户麦克风输入,通过audio-processor-worklet.js提供的方法处理音频数据,将其分片编码为base64格式,然后通过WebSocket发送到服务器。在接收到服务器返回的音频流后,客户端会对其进行解码,并传递给播放器,从而实现音频的播放功能。整个流程形成了一个闭环,确保了音频从录入到播放的顺畅进行。