> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coze.cn/llms.txt
> Use this file to discover all available pages before exploring further.

WebSocket 实时语音 Web SDK （简称 WsChat SDK） 基于双向流式语音对话 WebSocket OpenAPI 封装 ，为开发者提供开箱即用的语音交互解决方案，详细的接口信息请参见[双向流式语音对话](/developer_guides/streaming_chat_api)。
## 体验 Demo {#4c8e9e31}
扣子提供[实时语音 Demo](https://www.coze.cn/open-platform/realtime/websocket) 和 TypeScript 格式的 [实时语音 WebSocket 示例源码](https://github.com/coze-dev/coze-js/tree/main/examples/realtime-websocket)，帮助你快速体验实时语音的功能，并根据示例源码快速实现实时语音。
### Demo 功能简介 {#a835d597}
Demo 的主要功能包括：

* 实时语音识别：将用户语音即时转换为文本。
* 配置管理：支持设置降噪模式、音频配置。
* 设备管理：支持设置不同的音频输入设备。
* 交互控制：支持打断对话、静音、发送文本、发送事件等。
* 会话管理：支持建立和断开语音连接、设置静音等。

![Image=800x423](https://p9-arcosite.byteimg.com/tos-cn-i-goo7wpa0wc/1df953239d60401f9545e71515b4912f~tplv-goo7wpa0wc-image.image)
### 使用 Demo {#20446dcc}

1. 配置参数。
   单击右上角的 **Settings**，配置个人访问令牌和智能体 ID 等参数，具体如下表所示。
   <!-- @cols-width: 141,638 -->
   | | | \
   |**配置** |**说明** |
   |---|---|
   | | | \
   |Base WS URL |保持默认值 `wss://ws.coze.cn`。 |
   | | | \
   |个人访问令牌 |扣子 API & SDK 通过访问令牌进行 API & SDK 请求的鉴权。 |\
   | |个人访问令牌的获取方式可参考[添加个人访问令牌](/developer_guides/pat)。 |\
   | |:::tip 说明 |\
   | |* 应为令牌授予 chat、createVoice 和 listVoice 的权限。 |\
   | |* 令牌授予的访问工作空间中包含了待与其通话的智能体，否则会提示鉴权失败。 |\
   | |::: |
   | | | \
   |智能体 ID |配置希望与其实时语音聊天的智能体 ID。 |\
   | |进入智能体的开发页面，开发页面 URL 中 `bot` 参数后的数字就是智能体ID。例如`https://www.coze.cn/space/341****/bot/73428668*****`，智能体ID 为`73428668*****`。 |\
   | |:::tip 说明 |\
   | |* 确保个人访问令牌开通了此智能体所在空间的权限。 |\
   | |* 确保该智能体已发布为 API 服务。 |\
   | |::: |
   | | | \
   |音色 ID |设置智能体使用的音色。扣子提供一系列系统音色，你可以在[系统音色列表](/dev_how_to_guides/sys_voice)查看音色 ID。 |
   | | | \
   |工作流 ID |如果你需要自定义传参，你可以在对话流的开始节点设置自定义参数，然后在本参数中指定相应的对话流 ID。 |\
   | |获取对话流 ID 的方法：通过进入 Workflow 编排页面，在页面 URL 中，`workflow` 参数后的数字就是对话流 ID。例如 `https://www.coze.com/work_flow?space_id=42463***&workflow_id=73505836754923***`，对话流 ID 为 `73505836754923***`。 |

2. 单击**开始对话**，向指定智能体发起实时语音通话。
   :::tip 说明
   实时语音通话需要获取设备的麦克风权限，如果页面提示 `[DEVICE_ACCESS_ERROR] Failed to get audio devices`，表示浏览器禁用了麦克风设备。
   :::

## 实现流程 {#3836269d}
### 步骤一： 安装依赖 {#7d3c17b1}
运行以下命令安装 WsChat SDK 及其依赖项。
```Shell
npm install @coze/api
```

### 步骤二：检查设备权限 {#c546d249}
在使用语音功能前，先检查并获取麦克风访问权限。调用`WsToolsUtils.checkDevicePermission() `方法，检查浏览器是否已授予麦克风权限。
在 HTTPS 或 localhost 中执行该方法。首次调用时，浏览器会向用户显示权限请求对话框，如果用户拒绝麦克风权限，在应用中提供明确指引，告知用户如何在浏览器设置中启用权限。在 iOS Safari 浏览器中，必须由用户交互操作（如点击按钮）触发权限请求。
```TypeScript
import { WsToolsUtils } from "@coze/api/ws-tools";

const result = await WsToolsUtils.checkDevicePermission();
if (!result.audio) {
   throw new Error("需要麦克风访问权限");
}
```

### 步骤三：初始化客户端 {#8c00df18}
调用 `new WsChatClient` 方法创建 WsChatClient 实例，配置 token 和 botId。
```JavaScript
import { WsChatClient } from "@coze/api/ws-tools";

const client = new WsChatClient({
    token: 'pat_Qm47PKJR5dvMOP53v6DyzwCbTtvEZHQc2TVINEveg9v1T3iSYlTdScJ8***',
    botId: 'your bot id',
    allowPersonalAccessTokenInBrowser: true,  // 可选：允许在浏览器中使用个人访问令牌
});
```

参数说明：

* **token**：访问密钥，用于身份认证与鉴权。体验或调试场景可以生成短期的个人访问令牌（PAT），以快速完成 WsChat SDK 的整体流程。个人访问令牌的获取方法请参见[添加个人访问令牌](/developer_guides/pat)。在线上环境中，应使用服务访问令牌（SAT）或 OAuth 鉴权方案，详细说明请参见[鉴权方式概述](/developer_guides/authentication)。
   :::tip 说明
   扣子 SDK 封装了多种鉴权方式，能够有效简化鉴权流程，你可以参考[鉴权示例代码](https://github.com/coze-dev/coze-py/tree/main/examples)实现不同方式的 OAuth 认证，以获取和管理访问扣子 API 所需的令牌
   :::
* **botId**：配置希望与其实时语音聊天的智能体 ID。进入智能体的开发页面，开发页面 URL 中 `bot` 参数后的数字就是智能体ID。例如`https://www.coze.cn/space/341****/bot/73428668*****`，智能体ID 为`73428668*****`。

### 步骤四：监听事件 {#9bc2a47e}
在初始化客户端后，通过 `client.on` 方法注册各种事件监听器。详细的事件说明请参见[双向流式对话上行事件](/developer_guides/streaming_chat_event)。
```JavaScript
import { WsChatEventNames } from "@coze/api/ws-tools";

// 监听所有事件
client.on(WsChatEventNames.ALL, (eventName, data) => {
    console.log(eventName, data);
});
```

支持的事件类型如下，若想了解所有事件的详细情况，可以直接查看[示例项目源码中的 types.ts](https://github.com/coze-dev/coze-js/blob/main/packages/coze-js/src/ws-tools/types.ts)。
```TypeScript
// 事件监听示例
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'            // 音频导出
}
```

### 步骤五：建立连接 {#26273050}
在开始对话前，调用 `client.connect` 方法建立客户端和服务端之间的连接。
```TypeScript

try {
  await client.connect();
} catch (error) {
  console.log('连接失败', error);
}
```

:::tip 说明
* 连接过程可能因网络问题、权限问题或配置错误而失败，确保妥善处理异常。
* 连接成功后，麦克风会自动开始录音。若 `audioMutedDefault` 设置为 `true`，则会静音。
:::
### 步骤六：对话控制 {#689ba728}
在对话过程中支持静音或取消静音、打断对话、发送文本消息。

* **静音/取消静音**
   调用`setAudioEnable()`  方法静音或取消静音。该方法是一个异步方法，使用`await` 等待操作完成。在静音状态下，麦克风仍在工作，但不会发送音频数据。
   ```JavaScript
   // 静音与取消静音
   await client.setAudioEnable(false); // 静音
   await client.setAudioEnable(true);  // 取消静音
   ```

* **打断对话**
   调用 `interrupt()` 方法打断当前对话，使智能体停止回复。
   ```JavaScript
   // 打断对话
   client.interrupt();
   ```

* **发送文本消息**
   使用 `sendTextMessage()` 可以发送文本消息，而不是语音输入。
   ```JavaScript
   // 发送文本消息
   client.sendTextMessage("你好，我想了解一下天气情况");
   ```


### 步骤七：断开连接 {#5c32f9a5}
调用 `disconnect()` 方法关闭连接并释放资源。如果需要重新开始对话，重新调用 `connect()` 方法建立连接。
```JavaScript
// 断开连接
await client.disconnect();
```

### 完整示例代码 {#297cb520}
完整的示例代码如下。你也可以参考[实时语音 WebSocket 示例源码](https://github.com/coze-dev/coze-js/tree/main/examples/realtime-websocket)获取更多示例代码。
```TypeScript
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);
  }
};
```

## 进阶功能 {#325a3bbc}
### 客户端配置 {#ef818aed}
在初始化客户端时，你还可以进行高级配置，以满足不同的开发需求，包括：开启调试模式、指定智能体的音色、指定音频输入设备、音频处理及录制、AI 降噪、音频录制等。完整的配置项请查看[示例项目源码中的 types.ts](https://github.com/coze-dev/coze-js/blob/main/packages/coze-js/src/ws-tools/types.ts)。
```TypeScript
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; // 是否启用降噪后音频录制
  };
}
```

### 音视频控制 {#226a7b39}
在通话过程中，支持开启或关闭麦克风、设置输入音频设备。

* 开启或关闭麦克风
   ```TypeScript
   // 开启/关闭麦克风
   await client.setAudioEnable(true);  // 开启麦克风
   await client.setAudioEnable(false); // 关闭麦克风
   ```

* 设置输入音频设备
   你可以通过` WsToolsUtils.getAudioDevices` 方法获取系统的音频输入和输出设备列表，返回的设备列表包含每个设备的 deviceId 和 label 信息。
   ```TypeScript
   import { WsToolsUtils } from "@coze/realtime-api";
   
   const devices = await WsToolsUtils.getAudioDevices();  // 获取输入、输出设备列表
   client.setAudioInputDevice(devices.audioInputs[0].deviceId); // 设置音频输入设备
   ```

   :::tip 说明
   * 获取设备列表需要用户已授予麦克风权限。
   * 在用户插入或移除设备后，应重新获取设备列表。
   * 切换设备是异步操作，需要使用 await 等待完成。
   * 如果指定的设备不可用，用户操作时将抛出异常。
   :::

### 开启或关闭调试模式 {#5b7712b3}
在调试模式下可以使用音频 dump 功能，以验证降噪效果。

1. 在创建 WsChatClient 时，将 `debug` 设置为 `true`，开启调试模式，并配置音频录制。
   ```Java
   const client = new WsChatClient({
     // ... 其他配置
     debug: true, // 必须设置为 true，否则录制配置不生效
     wavRecordConfig: {
       enableSourceRecord: true,    // 启用原始音频录制
       enableDenoiseRecord: true,   // 启用降噪后的音频录制
     }
   });
   ```

2. 监听音频 dump 事件来获取录制的音频数据。
   ```TypeScript
   // 监听音频输入 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();
   });
   ```

   :::tip 说明
   dump 功能会产生两种音频文件：
   
   * 原始音频文件：包含用户的原始输入音频。只有当 `enableSourceRecord = true` 时才会生成原始音频文件。
   * 降噪后音频文件：包含经过降噪处理后的音频。只有当 `enableDenoiseRecord = true` 时才会生成降噪后音频文件。
   :::

### AI 降噪 {#81043cc9}
:::tip 说明
* 降噪处理可能增加 CPU 使用率，在低性能设备上可能影响体验。
* 不同的降噪等级会影响音频质量和降噪效果。
:::

1. 检查浏览器是否支持 AI 降噪。
   某些浏览器或设备可能不支持全部降噪功能，当不确定设备支持情况时，可以使用 `WsToolsUtils.checkDenoiserSupport() `方法检查。
   ```TypeScript
   const denoiserSupport = await WsToolsUtils.checkDenoiserSupport();
   if (!denoiserSupport) {
     console.warn("当前浏览器不支持 AI 降噪");
   }
   ```

2. 在初始化时配置 AI 降噪。
   ```TypeScript
   const client = new WsChatClient({
     // 其它配置
     aiDenoisingConfig: {
       mode: 'NSNG',           // AI 降噪模式，包括NSNG(非平稳噪声抑制) 或 STATIONARY_NS(平稳噪声抑制)
       level: 'SOFT',        //  降噪等级，包括SOFT（舒缓降噪）, AGGRESSIVE（激进降噪。将降噪强度提高到激进降噪会增大损伤人声的概率。）
     }
   });
   ```

3. 开启或关闭 AI 降噪，设置 AI 降噪的等级和模式。
   ```TypeScript
   // 设置降噪开关
   client.setDenoiserEnabled(true);
   
   // 设置降噪等级
   client.setDenoiserLevel("SOFT"); // 可选值: SOFT（舒缓降噪）, AGGRESSIVE（激进降噪。将降噪强度提高到激进降噪会增大损伤人声的概率。）
   
   // 设置降噪模式
   client.setDenoiserMode("NSNG"); // NSNG(非平稳噪声抑制) 或 STATIONARY_NS(平稳噪声抑制)
   ```


### 配置音色 {#2583373e}
在初始化 WsChat SDK 时，你可以通过指定 `voiceId` 来自定义智能体在对话中使用的音色。以下是使用说明：

1. 获取可用的音色 ID。
   你可以通过[查看音色列表](/developer_guides/list_voices) 获取可用的音色列表。
   ```TypeScript
   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();
   ```

2. 在初始化 WsChat 客户端时，指定智能体使用的音色。
   每个音色都有不同的特征，如性别、语言、风格等，如果不指定 `voiceId` 或值为空，将使用默认的`柔美女友`音色，音色 ID 为 7426720361733046281。
   ```TypeScript
   import { WsChatClient } from "@coze/api/ws-tools";
    
   const client = new WsChatClient({
     // 其它配置
     voiceId: "7426720361733046281",
   });
   ```


## WebSocket 事件 {#d6cba9e7}
### 基本概念 {#1e0333bb}

* 上行事件：设备端向服务端发送的请求，如提交插件执行结果、更新对话配置等。
* 下行事件：服务端向设备端发送的通知，如端插件请求、对话状态变化等。
* 会话与对话：会话是用户与智能体的一次完整交互，对话是会话中的一次具体调用。
* 事件 ID：每个事件应有唯一 ID，便于问题定位。

### 事件的使用流程 {#3efd14fe}
信令事件的使用流程如下：

1. 初始化。通过监听 `chat.created` 事件，确认对话初始化完成。
2. 发送请求。使用上行事件（如 `conversation.message.create`）向智能体发送消息。
3. 处理响应。监听下行事件（如 `conversation.message.delta`）获取智能体的增量回复。
4. 插件交互。在收到事件 `conversation.chat.requires_action` 后，执行插件操作并通过事件 `conversation.chat.submit_tool_outputs` 提交结果。根据需求选择 blocking 或 nonblocking 模式，控制插件执行是否阻塞对话。
5. 错误处理。通过监听 `error` 事件捕获和处理异常情况。

使用示例如下：
```TypeScript
import { WebsocketsEventType, RoleType } from '@coze/api';
// 使用示例
client.sendMessage({
  "id": "1",
  "event_type": WebsocketsEventType.CONVERSATION_MESSAGE_CREATE,
  "data": {
    "role": RoleType.User,
    "content_type": "text",
    "content": "你好"
  }
});
```

###  {#f17ff44b}
