LogoAICore Docs

豆包实时语音接口

通过 WebSocket 调用豆包实时语音,包含连接鉴权、音频上传、流式回复和 Python 示例

豆包实时语音接口通过 WebSocket 收发 JSON 事件,可用于语音对话、语音识别结果接收及指定文本朗读。调用时使用 AiCore 平台的 API 令牌和 doubao-realtime 模型。

开始前,请在您使用的平台控制台创建 API 令牌,确认账号和令牌具有 doubao-realtime 的使用权限,并获取平台提供的接口地址。

连接与鉴权

WebSocketEndpoint
{REALTIME_BASE_URL}/v1/realtime?model=doubao-realtime

HTTP GET 升级为 WebSocket 后,使用 JSON 文本帧进行双向通信。

REALTIME_BASE_URL 表示您使用的 WebSocket 基础地址,请根据平台提供的接口域名填写:

  • 使用 wss:// 协议,地址格式为 wss://<接口域名>;如接口地址包含端口,也需保留。
  • 基础地址不包含 /v1、/realtime 或查询参数,完整路径由示例代码拼接。
  • 如果平台提供的是 HTTPS 接口地址,将协议 https:// 改为 wss://,并移除末尾已有的 /v1。

文中的 <接口域名> 是占位符,运行前需替换为实际接口域名。接口地址变化时,只需更新基础地址配置。

参数名位置必填描述
modelQuery是填写 doubao-realtime;账号所在分组和令牌需有该模型权限
AuthorizationHeader是Bearer YOUR_API_KEY,使用平台签发的 sk- 开头令牌

每条 WebSocket 文本消息对应一个完整 JSON 对象,通过顶层 type 区分事件。音频以 Base64 字符串放入 JSON,不直接发送二进制音频帧,也不使用 SSE。

本接口兼容部分 OpenAI Realtime WebSocket 事件。会话启动、文本输入和工具调用仍有豆包特有约定,具体见下方“兼容范围”。本文不提供 WebRTC 或临时令牌接口。

创建会话

连接后首先发送 session.update 创建会话,再等待 session.created。同一连接中再次发送 session.update 可更新配置,成功后返回 session.updated。

{
  "type": "session.update",
  "event_id": "evt_session_1",
  "session": {
    "audio": {
      "input": {
        "format": { "type": "audio/pcm", "rate": 24000 }
      },
      "output": {
        "format": { "type": "audio/pcm", "rate": 24000 },
        "voice": "zh_female_vv_jupiter_bigtts"
      }
    }
  },
  "extension": {
    "asr": {},
    "tts": { "audio_config": { "format": "pcm_s16le" } },
    "dialog": {}
  }
}

会话参数

参数名类型必填描述
typestring是使用 session.update
event_idstring否客户端事件标识,建议每条消息使用不同值以便排查问题
sessionobject是会话配置对象
session.instructionsstring否对话指令;实际支持范围以豆包模型为准
session.audio.input.formatobject否示例使用 audio/pcm、24000 Hz;上传音频应与此配置一致
session.audio.output.formatobject否示例请求 audio/pcm、24000 Hz 输出
session.audio.output.voicestring是豆包音色 ID;示例采用官方文档中的 zh_female_vv_jupiter_bigtts,不要直接填 OpenAI 的音色名
session.toolsarray否工具声明,使用 type、name、description、parameters 的函数定义结构
extensionobject否豆包扩展配置,包含 asr、tts、dialog,按官方文档填写

模型通过连接 URL 中的 model=doubao-realtime 指定,不需要在 session 中填写模型版本。示例通过 extension.tts.audio_config.format=pcm_s16le 明确请求 24000 Hz、单声道、16 位有符号小端 PCM 输出。pcm 可返回 Float32 音频,不能直接按 PCM16 播放。extension 也可以放在 session.extension 中,两种位置均支持。

会话创建响应示例:

{
  "type": "session.created",
  "event_id": "event_1",
  "session": { "id": "your-session-id" }
}

session.created 可能只包含 session.id,客户端应保存自己发送的音频配置,不要依赖服务端返回完整的 OpenAI session 对象。

发送音频与文本

音频输入

推荐客户端上传 24000 Hz、单声道、16 位有符号小端 PCM,按 20 ms 一包发送,每包原始数据为 960 字节,并将会话输入采样率设置为 24000 Hz。

{
  "type": "input_audio_buffer.append",
  "event_id": "evt_audio_1",
  "audio": "BASE64_ENCODED_PCM"
}

audio 只包含原始 PCM 数据的 Base64,不包含 WAV 文件头或 data:audio/...;base64, 前缀。MP3、WAV 文件不能直接整体放入该字段;浏览器 MediaRecorder 常见的 WebM/Opus 输出也不是此示例的输入格式。

发送录音文件时同样需要按照音频时长控制发送速度,20 ms 音频间隔约 20 ms 发送,不能一次性把整段录音高速发送给服务端。有效语音发送完毕后,可以强制判停:

{ "type": "input_audio_buffer.commit", "event_id": "evt_commit_1" }

input_audio_buffer.committed 只表示输入已提交,不代表回复完成。全静音数据不保证触发回复;收到 response.done 后再结束本轮处理。

麦克风暂停与恢复

停止上传音频时发送静音事件,恢复上传前发送取消静音事件。仅停止发送音频可能触发会话超时,WebSocket Ping 不能代替这些应用层事件。

{ "type": "input_audio_mute.commit" }
{ "type": "input_audio_unmute.commit" }

指定文本朗读

打招呼或朗读固定文本时,使用豆包扩展事件,将文本直接放在 speech_text_buffer.commit 的 text 字段:

{
  "type": "speech_text_buffer.commit",
  "event_id": "evt_greeting_1",
  "text": "你好,这是豆包实时语音接口测试。"
}

需要分段提交待合成文本时,可依次发送 speech_text_buffer.replacement.append(包含 text)和 speech_text_buffer.replacement.commit。这些事件用于指定朗读内容,不是向模型发起任意文本问答。

接收事件与结束会话

客户端收到的事件含义处理方式
session.created / session.updated会话创建或更新确认保存会话 ID,收到确认后继续发送
input_audio_buffer.committed输入音频已提交继续等待识别和回复事件
conversation.item.input_audio_transcription.delta / completed输入语音转录根据实际事件字段处理增量或最终文本
response.output_text.delta模型回复文本按顺序处理 delta,兼容保留的豆包字段
response.audio.started音频输出开始准备接收音频
response.audio.delta音频分片Base64 解码 delta,按到达顺序送入播放器或写入文件
response.audio.done音频输出结束完成音频处理,继续等待 response.done
response.done本轮交互结束完成本轮处理,可继续下一轮或关闭会话
response.canceled打断确认根据应用需要清理待播放音频
session.closed会话关闭确认随后关闭 WebSocket
error请求或服务端错误读取 error.type、error.code、error.message

本接口返回的音频事件使用 response.audio.* 命名,音频分片位于 response.audio.delta 的 delta 字段。使用示例配置时请求的是 24000 Hz、单声道、16 位有符号小端 PCM,数据不带文件容器头;请根据实际输出编码播放,不要把文件扩展名直接改为 .mp3。

打断回复可以发送 {"type":"response.cancel"},同时由客户端停止播放已经缓冲的音频。

结束时先等本轮 response.done,再发送:

{ "type": "session.close", "event_id": "evt_close_1" }

收到 session.closed 后再断开连接。response.audio.done 仅表示音频输出结束,请继续等待 response.done,完成本轮处理后再关闭会话。

Python 调用示例

下面的示例创建会话、朗读一段文字、保存原始音频,并在本轮交互结束后正常关闭会话。使用 Python 3.11 或更新版本:

python3 -m pip install "websockets>=14,<16"
export REALTIME_BASE_URL="wss://<接口域名>"
export XTY_API_KEY="sk-your-platform-api-key"

保存为 doubao_realtime.py,运行 python3 doubao_realtime.py:

import asyncio
import base64
import json
import os
from pathlib import Path

from websockets.asyncio.client import connect


async def receive_until(ws, expected_type, audio_file):
    # 限制整个阶段的等待时间,避免持续收到其他事件时无限等待。
    async with asyncio.timeout(60):
        while True:
            event = json.loads(await ws.recv())
            event_type = event.get("type")
            if event_type == "error":
                raise RuntimeError(json.dumps(event.get("error"), ensure_ascii=False))
            if event_type == "response.audio.delta":
                audio_file.write(base64.b64decode(event["delta"], validate=True))
            else:
                print("event:", event_type)
            if event_type == expected_type:
                return event


async def main():
    base_url = os.environ["REALTIME_BASE_URL"].rstrip("/")
    url = f"{base_url}/v1/realtime?model=doubao-realtime"
    headers = {"Authorization": f"Bearer {os.environ['XTY_API_KEY']}"}
    output = Path("doubao-output.pcm")

    async with connect(
        url, additional_headers=headers, open_timeout=15,
        close_timeout=5, max_size=32 * 1024 * 1024,
    ) as ws:
        with output.open("wb") as audio_file:
            await ws.send(json.dumps({
                "type": "session.update",
                "session": {
                    "audio": {
                        "input": {"format": {"type": "audio/pcm", "rate": 24000}},
                        "output": {
                            "format": {"type": "audio/pcm", "rate": 24000},
                            "voice": "zh_female_vv_jupiter_bigtts",
                        },
                    }
                },
                "extension": {
                    "asr": {},
                    "tts": {"audio_config": {"format": "pcm_s16le"}},
                    "dialog": {},
                },
            }))
            await receive_until(ws, "session.created", audio_file)
            # 本示例不上传麦克风音频,使用静音事件通知服务端。
            await ws.send(json.dumps({"type": "input_audio_mute.commit"}))
            await ws.send(json.dumps({
                "type": "speech_text_buffer.commit",
                "text": "你好,这是豆包实时语音接口测试。",
            }, ensure_ascii=False))
            await receive_until(ws, "response.done", audio_file)
            await ws.send(json.dumps({"type": "session.close"}))
            await receive_until(ws, "session.closed", audio_file)

    print("音频已保存:", output.resolve())


asyncio.run(main())

该示例验证固定文本的语音输出。接入麦克风时,需要按前文格式上传 input_audio_buffer.append,并在独立接收任务中持续处理下行事件,避免等录音全部上传后才开始接收音频。

兼容范围

能力或参数当前行为
WebSocket JSON 文本帧、Base64 音频支持
session.update首次用于创建会话,后续用于更新会话
input_audio_buffer.append / commit支持 PCM 音频上传与强制判停
response.create当前被忽略;豆包通过音频判停或指定文本事件触发回复
conversation.item.create 发起用户文本问答当前不支持普通 OpenAI 文本轮次,会返回错误;指定文本朗读使用 speech_text_buffer.commit
OpenAI SDK 的完整事件对象不保证完整兼容;部分事件保留豆包字段,建议按本文直接处理 JSON
工具调用下行 response.function_call_arguments.done 可能包含豆包 items 数组,需逐项读取 call_id、name 和 arguments,不能直接假设为 OpenAI 的单次调用结构
temperature、turn_detection、modalities 等当前接口不支持通过这些会话参数控制行为
音频格式本文覆盖 PCM 路径;其他编码和采样率不能直接套用此示例
WebRTC / 临时会话令牌当前接口未提供

常见问题

参考文档