WebSocket 实时语音 Web SDK (简称 WsChat SDK) 基于双向流式语音对话 WebSocket OpenAPI 封装 ,为开发者提供开箱即用的语音交互解决方案,详细的接口信息请参见双向流式语音对话。
扣子提供实时语音 Demo 和 TypeScript 格式的 实时语音 WebSocket 示例源码,帮助你快速体验实时语音的功能,并根据示例源码快速实现实时语音。
Demo 的主要功能包括:
配置参数。
单击右上角的 Settings,配置个人访问令牌和智能体 ID 等参数,具体如下表所示。
|
配置 |
说明 |
|---|---|
|
Base WS URL |
保持默认值 |
|
个人访问令牌 |
扣子 API & SDK 通过访问令牌进行 API & SDK 请求的鉴权。 说明
|
|
智能体 ID |
配置希望与其实时语音聊天的智能体 ID。 说明
|
|
音色 ID |
设置智能体使用的音色。扣子提供一系列系统音色,你可以在系统音色列表查看音色 ID。 |
|
工作流 ID |
如果你需要自定义传参,你可以在对话流的开始节点设置自定义参数,然后在本参数中指定相应的对话流 ID。 |
单击开始对话,向指定智能体发起实时语音通话。
说明
实时语音通话需要获取设备的麦克风权限,如果页面提示 [DEVICE_ACCESS_ERROR] Failed to get audio devices,表示浏览器禁用了麦克风设备。
运行以下命令安装 WsChat SDK 及其依赖项。
npm install @coze/api
在使用语音功能前,先检查并获取麦克风访问权限。调用WsToolsUtils.checkDevicePermission() 方法,检查浏览器是否已授予麦克风权限。
在 HTTPS 或 localhost 中执行该方法。首次调用时,浏览器会向用户显示权限请求对话框,如果用户拒绝麦克风权限,在应用中提供明确指引,告知用户如何在浏览器设置中启用权限。在 iOS Safari 浏览器中,必须由用户交互操作(如点击按钮)触发权限请求。
import { WsToolsUtils } from "@coze/api/ws-tools";
const result = await WsToolsUtils.checkDevicePermission();
if (!result.audio) {
throw new Error("需要麦克风访问权限");
}
调用 new WsChatClient 方法创建 WsChatClient 实例,配置 token 和 botId。
import { WsChatClient } from "@coze/api/ws-tools";
const client = new WsChatClient({
token: 'pat_Qm47PKJR5dvMOP53v6DyzwCbTtvEZHQc2TVINEveg9v1T3iSYlTdScJ8***',
botId: 'your bot id',
allowPersonalAccessTokenInBrowser: true, // 可选:允许在浏览器中使用个人访问令牌
});
参数说明:
说明
扣子 SDK 封装了多种鉴权方式,能够有效简化鉴权流程,你可以参考鉴权示例代码实现不同方式的 OAuth 认证,以获取和管理访问扣子 API 所需的令牌
bot 参数后的数字就是智能体ID。例如https://www.coze.cn/space/341****/bot/73428668*****,智能体ID 为73428668*****。在初始化客户端后,通过 client.on 方法注册各种事件监听器。详细的事件说明请参见双向流式对话上行事件。
import { WsChatEventNames } from "@coze/api/ws-tools";
// 监听所有事件
client.on(WsChatEventNames.ALL, (eventName, data) => {
console.log(eventName, data);
});
支持的事件类型如下,若想了解所有事件的详细情况,可以直接查看示例项目源码中的 types.ts。
// 事件监听示例
client.on(WsChatEventNames.ALL, (event) => {
console.log(`收到事件:`, event);
});
// 支持的事件类型
enum WsChatEventNames {
// 客户端基础事件
ALL = 'realtime.event', // 所有事件
CONNECTED = 'client.connected', // 客户端已连接
CONNECTING = 'client.connecting', // 客户端连接中
INTERRUPTED = 'client.interrupted', // 客户端已中断
DISCONNECTED = 'client.disconnected', // 客户端已断开
ERROR = 'client.error', // 客户端发生错误
// 音频控制事件
AUDIO_UNMUTED = 'client.audio.unmuted', // 音频已取消静音
AUDIO_MUTED = 'client.audio.muted', // 音频已静音
AUDIO_INPUT_DUMP = 'client.audio.input.dump', // 音频输入数据导出
// 设备变更事件
AUDIO_INPUT_DEVICE_CHANGED = 'client.input.device.changed', // 音频输入设备已改变
AUDIO_OUTPUT_DEVICE_CHANGED = 'client.output.device.changed', // 音频输出设备已改变
// 降噪控制事件
DENOISER_ENABLED = 'client.denoiser.enabled', // 降噪已启用
DENOISER_DISABLED = 'client.denoiser.disabled', // 降噪已禁用
// 服务端对话事件
CHAT_CREATED = 'server.chat.created', // 对话已创建
CHAT_UPDATED = 'server.chat.updated', // 对话已更新
// 会话状态事件
CONVERSATION_CHAT_CREATED = 'server.conversation.chat.created', // 会话对话已创建
CONVERSATION_CHAT_IN_PROGRESS = 'server.conversation.chat.in.progress', // 对话进行中
CONVERSATION_CHAT_COMPLETED = 'server.conversation.chat.completed', // 对话已完成
CONVERSATION_CHAT_FAILED = 'server.conversation.chat.failed', // 对话失败
CONVERSATION_CHAT_CANCELLED = 'server.conversation.chat.cancelled', // 对话已取消
CONVERSATION_CHAT_REQUIRES_ACTION = 'server.conversation.chat.requires_action', // 对话需要端插件响应
// 消息事件
CONVERSATION_MESSAGE_DELTA = 'server.conversation.message.delta', // 文本消息增量返回
CONVERSATION_MESSAGE_COMPLETED = 'server.conversation.message.completed', // 文本消息完成
// 音频事件
CONVERSATION_AUDIO_DELTA = 'server.conversation.audio.delta', // 语音消息增量返回
CONVERSATION_AUDIO_COMPLETED = 'server.conversation.audio.completed', // 语音回复完成
// 语音识别事件
CONVERSATION_AUDIO_TRANSCRIPT_UPDATE = 'server.conversation.audio_transcript.update', // 用户语音识别实时字幕更新
CONVERSATION_AUDIO_TRANSCRIPT_COMPLETED = 'server.conversation.audio_transcript.completed', // 用户语音识别完成
// 语音检测事件
INPUT_AUDIO_BUFFER_SPEECH_STARTED = 'server.input_audio_buffer.speech_started', // 检测到用户开始说话
INPUT_AUDIO_BUFFER_SPEECH_STOPPED = 'server.input_audio_buffer.speech_stopped', // 检测到用户停止说话
// 缓冲区事件
INPUT_AUDIO_BUFFER_COMPLETED = 'server.input_audio_buffer.completed', // 语音输入缓冲区提交完成
INPUT_AUDIO_BUFFER_CLEARED = 'server.input_audio_buffer.cleared', // 语音输入缓冲区已清除
// 其他事件
SERVER_ERROR = 'server.error', // 服务端错误
CONVERSATION_CLEARED = 'server.conversation.cleared', // 对话上下文已清除
DUMP_AUDIO = 'server.dump.audio' // 音频导出
}
在开始对话前,调用 client.connect 方法建立客户端和服务端之间的连接。
try {
await client.connect();
} catch (error) {
console.log('连接失败', error);
}
说明
audioMutedDefault 设置为 true,则会静音。在对话过程中支持静音或取消静音、打断对话、发送文本消息。
静音/取消静音
调用setAudioEnable() 方法静音或取消静音。该方法是一个异步方法,使用await 等待操作完成。在静音状态下,麦克风仍在工作,但不会发送音频数据。
// 静音与取消静音
await client.setAudioEnable(false); // 静音
await client.setAudioEnable(true); // 取消静音
打断对话
调用 interrupt() 方法打断当前对话,使智能体停止回复。
// 打断对话
client.interrupt();
发送文本消息
使用 sendTextMessage() 可以发送文本消息,而不是语音输入。
// 发送文本消息
client.sendTextMessage("你好,我想了解一下天气情况");
调用 disconnect() 方法关闭连接并释放资源。如果需要重新开始对话,重新调用 connect() 方法建立连接。
// 断开连接
await client.disconnect();
完整的示例代码如下。你也可以参考实时语音 WebSocket 示例源码获取更多示例代码。
import {
WsChatEventNames,
WsChatClient,
WsToolsUtils,
} from '@coze/api/ws-tools';
// 初始化 client & 注册事件
const initClient = async () => {
// 检查语音设备权限
const result = await WsToolsUtils.checkDevicePermission();
if (!result.audio) {
throw new Error("需要麦克风访问权限");
}
// 初始化 client
const client = new WsChatClient({
token: "pat_Qm47PKJR5dvMOP53v6DyzwCbTtvEZHQc2TVINEveg9v1T3iSYlTdScJ8***",
botId: "your bot id",
allowPersonalAccessTokenInBrowser: true, // 可选:允许在浏览器中使用个人访问令牌
});
// 注册事件,一般在初始化 client 后设置
client.on(WsChatEventNames.ALL, (eventName, data) => {
console.log(eventName, data);
});
return client;
};
const handleConnect = async () => {
try {
const client = await initClient();
// 连接
await client.connect();
} catch (error) {
console.log("连接失败", error);
}
};
在初始化客户端时,你还可以进行高级配置,以满足不同的开发需求,包括:开启调试模式、指定智能体的音色、指定音频输入设备、音频处理及录制、AI 降噪、音频录制等。完整的配置项请查看示例项目源码中的 types.ts。
interface WsChatClientOptions {
// 必填项
token: GetToken; // Personal Access Token (PAT) 或 OAuth2.0 token,或获取 token 的函数
botId: string; // 智能体 ID
// 选填项
debug?: boolean; // 是否启用调试模式
headers?: Headers | Record<string, unknown>; // 自定义请求头
allowPersonalAccessTokenInBrowser?: boolean; // 是否允许在浏览器环境中使用个人访问令牌
baseWsURL?: string; // WebSocket 基础 URL,默认为 wss://ws.coze.cn
websocketOptions?: WebsocketOptions; // WebSocket 配置选项
workflowId?: string; // 工作流 ID
voiceId?: string; // 音色 ID
deviceId?: string; // 音频输入设备 ID
// 音频相关配置
audioCaptureConfig?: {
noiseSuppression?: boolean; // 是否启用降噪
echoCancellation?: boolean; // 是否启用回声消除
autoGainControl?: boolean; // 是否启用自动增益控制
};
audioMutedDefault?: boolean; // 是否默认静音
// AI 降噪配置
aiDenoisingConfig?: {
mode?: AIDenoiserProcessorMode; // AI 降噪模式:NSNG(非平稳噪声抑制) 或 STATIONARY_NS(平稳噪声抑制)
level?: AIDenoiserProcessorLevel; // 降噪级别
assetsPath?: string; // AI 降噪 wasm 文件路径,默认为 '/external'
};
// 自定义音频流
mediaStreamTrack?: MediaStreamTrack;
// 音频录制配置(仅在 debug = true 时生效)
wavRecordConfig?: {
enableSourceRecord: boolean; // 是否启用源音频录制
enableDenoiseRecord: boolean; // 是否启用降噪后音频录制
};
}
在通话过程中,支持开启或关闭麦克风、设置输入音频设备。
开启或关闭麦克风
// 开启/关闭麦克风
await client.setAudioEnable(true); // 开启麦克风
await client.setAudioEnable(false); // 关闭麦克风
设置输入音频设备
你可以通过 WsToolsUtils.getAudioDevices 方法获取系统的音频输入和输出设备列表,返回的设备列表包含每个设备的 deviceId 和 label 信息。
import { WsToolsUtils } from "@coze/realtime-api";
const devices = await WsToolsUtils.getAudioDevices(); // 获取输入、输出设备列表
client.setAudioInputDevice(devices.audioInputs[0].deviceId); // 设置音频输入设备
说明
在调试模式下可以使用音频 dump 功能,以验证降噪效果。
在创建 WsChatClient 时,将 debug 设置为 true,开启调试模式,并配置音频录制。
const client = new WsChatClient({
// ... 其他配置
debug: true, // 必须设置为 true,否则录制配置不生效
wavRecordConfig: {
enableSourceRecord: true, // 启用原始音频录制
enableDenoiseRecord: true, // 启用降噪后的音频录制
}
});
监听音频 dump 事件来获取录制的音频数据。
// 监听音频输入 dump 事件
client.on(WsChatEventNames.AUDIO_INPUT_DUMP, (eventName, event) => {
// event.data 包含:
// - name: string 文件名
// - wav: Blob 音频数据(WAV格式)
const { name, wav } = event.data;
console.log(`收到音频dump,文件名: ${name}`);
// 可以将 wav 数据保存为文件
const url = URL.createObjectURL(wav);
const a = document.createElement('a');
a.href = url;
a.download = name;
a.click();
});
说明
dump 功能会产生两种音频文件:
enableSourceRecord = true 时才会生成原始音频文件。enableDenoiseRecord = true 时才会生成降噪后音频文件。说明
检查浏览器是否支持 AI 降噪。
某些浏览器或设备可能不支持全部降噪功能,当不确定设备支持情况时,可以使用 WsToolsUtils.checkDenoiserSupport() 方法检查。
const denoiserSupport = await WsToolsUtils.checkDenoiserSupport();
if (!denoiserSupport) {
console.warn("当前浏览器不支持 AI 降噪");
}
在初始化时配置 AI 降噪。
const client = new WsChatClient({
// 其它配置
aiDenoisingConfig: {
mode: 'NSNG', // AI 降噪模式,包括NSNG(非平稳噪声抑制) 或 STATIONARY_NS(平稳噪声抑制)
level: 'SOFT', // 降噪等级,包括SOFT(舒缓降噪), AGGRESSIVE(激进降噪。将降噪强度提高到激进降噪会增大损伤人声的概率。)
}
});
开启或关闭 AI 降噪,设置 AI 降噪的等级和模式。
// 设置降噪开关
client.setDenoiserEnabled(true);
// 设置降噪等级
client.setDenoiserLevel("SOFT"); // 可选值: SOFT(舒缓降噪), AGGRESSIVE(激进降噪。将降噪强度提高到激进降噪会增大损伤人声的概率。)
// 设置降噪模式
client.setDenoiserMode("NSNG"); // NSNG(非平稳噪声抑制) 或 STATIONARY_NS(平稳噪声抑制)
在初始化 WsChat SDK 时,你可以通过指定 voiceId 来自定义智能体在对话中使用的音色。以下是使用说明:
获取可用的音色 ID。
你可以通过查看音色列表 获取可用的音色列表。
import { CozeAPI, COZE_CN_BASE_URL } from '@coze/api';
const api = new CozeAPI({
token: 'your-access-token',
baseURL: COZE_CN_BASE_URL,
});
// 获取可用的音色列表
const voices = await api.audio.voices.list();
在初始化 WsChat 客户端时,指定智能体使用的音色。
每个音色都有不同的特征,如性别、语言、风格等,如果不指定 voiceId 或值为空,将使用默认的柔美女友音色,音色 ID 为 7426720361733046281。
import { WsChatClient } from "@coze/api/ws-tools";
const client = new WsChatClient({
// 其它配置
voiceId: "7426720361733046281",
});
信令事件的使用流程如下:
chat.created 事件,确认对话初始化完成。conversation.message.create)向智能体发送消息。conversation.message.delta)获取智能体的增量回复。conversation.chat.requires_action 后,执行插件操作并通过事件 conversation.chat.submit_tool_outputs 提交结果。根据需求选择 blocking 或 nonblocking 模式,控制插件执行是否阻塞对话。error 事件捕获和处理异常情况。使用示例如下:
import { WebsocketsEventType, RoleType } from '@coze/api';
// 使用示例
client.sendMessage({
"id": "1",
"event_type": WebsocketsEventType.CONVERSATION_MESSAGE_CREATE,
"data": {
"role": RoleType.User,
"content_type": "text",
"content": "你好"
}
});