> ## 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.

语音识别 SDK（简称 WsTranscription SDK）是基于双向流式语音识别 WebSocket OpenAPI 封装的软件开发工具包。它为开发者提供了一套高效且完整的语音交互解决方案，可快速集成至各类应用程序，实现高质量的语音识别功能。扣子提供 Demo 和示例项目源码，助力开发者迅速掌握并应用。更多接口详情请参考[双向流式语音识别](/developer_guides/asr_api)。

## 体验 Demo {#a39bfc89}

扣子提供[语音识别 Demo](https://www.coze.cn/open-platform/realtime/websocket#/transcription) 和 TypeScript 格式的[语音识别示例源码](https://github.com/coze-dev/coze-js/tree/main/examples/realtime-websocket) ，帮助你快速体验语音识别的功能，并根据示例源码快速实现语音识别。

### Demo 功能简介 {#81372938}

Demo 的主要功能包括：

* 实时语音转录
* AI 智能降噪
* 选择音频输入设备
* 录音控制，包括开始录音、暂停和恢复录音、停止录音
* 错误处理

![Image=700x508](https://p9-arcosite.byteimg.com/tos-cn-i-goo7wpa0wc/1e43573b47064926a7e082a88d945adc~tplv-goo7wpa0wc-image.image)

### 使用 Demo {#5ca9c269}

1. 配置参数。
   单击右上角的 **Settings**，配置个人访问令牌，具体如下表所示。
   <!-- @cols-width: 141,638 -->
   |**配置** |**说明** |
   |---|---|
   |Base WS URL |保持默认值 `wss://ws.coze.cn`。 |
   |个人访问令牌 |扣子 API & SDK 通过访问令牌进行 API & SDK 请求的鉴权。 |\
   | | |\
   | |个人访问令牌的获取方式可参考[添加个人访问令牌](/developer_guides/pat)。 |\
   | | |\
   | |:::tip 说明 |\
   | |应为令牌授予 `createTranscription` 的权限。 |\
   | |::: |\
   | | |\
   | | |
2. 单击**开始录音**，在识别结果中会将浏览器采集的音频进行语音识别并转为文字。

## 完整示例代码 {#66589aa2}

以下是基于 React 和 Antd 框架开发的语音识别示例代码，包含用户界面、状态管理和错误处理等功能，帮助你快速了解语音识别的实现流程。

使用前需配置个人访问令牌（ACCESS_TOKEN），并安装`@coze/api`、`antd`、`react` 等依赖，建议开发时开启调试模式。

该示例代码中包含如下功能：

* 基础功能：实时语音转录、录音控制（开始/暂停/恢复/停止）、状态显示和错误处理、AI降噪检测。
* 界面组件：前置条件检查面板、录音控制面板、实时转录结果显示、使用说明。

```TypeScript
import React, { useEffect, useRef, useState } from 'react';
import {
  Button,
  Layout,
  Space,
  Typography,
  message,
  Row,
  Col,
  Card,
} from 'antd';
import {
  WsToolsUtils,
  WsTranscriptionClient,
  AIDenoiserProcessorMode,
  AIDenoiserProcessorLevel,
} from '@coze/api/ws-tools';
import {
  type CommonErrorEvent,
  type TranscriptionsMessageUpdateEvent,
  WebsocketsEventType,
} from '@coze/api';
import {
  AudioOutlined,
  PauseOutlined,
  PlayCircleOutlined,
} from '@ant-design/icons';

const { Title, Paragraph, Text } = Typography;

// 个人访问令牌
const ACCESS_TOKEN = 'pat_Qm47PKJR5dvMOP53v6DyzwCbTtvEZHQc2TVINEveg9v1T3iSYlTdScJ8***';

const TranscriptionDemo: React.FC = () => {
  const clientRef = useRef<WsTranscriptionClient>();
  const [isRecording, setIsRecording] = useState(false);
  const [isPaused, setIsPaused] = useState(false);
  const [transcriptionText, setTranscriptionText] = useState('');
  const [disabled, setDisabled] = useState(false);
  const [hasPermission, setHasPermission] = useState<boolean | null>(null);
  const [hasToken, setHasToken] = useState<boolean>(false);
  const [status, setStatus] = useState<string>('未开始');
  const [denoiserSupported, setDenoiserSupported] = useState<boolean>(false);

  // 检查权限和令牌
  useEffect(() => {
    const checkRequirements = async () => {
      // 检查麦克风权限
      const permission = await WsToolsUtils.checkDevicePermission();
      setHasPermission(permission.audio);

      // 检查是否配置了PAT令牌
      const hasConfiguredToken = !!ACCESS_TOKEN;
      setHasToken(hasConfiguredToken);
      
      // 检查是否支持AI降噪
      const isDenoiserSupported = WsToolsUtils.checkDenoiserSupport();
      setDenoiserSupported(isDenoiserSupported);
    };

    checkRequirements();
  }, []);

  // 初始化客户端
  const initClient = async () => {
    if (!hasPermission) {
      throw new Error('麦克风权限未授予');
    }

    const client = new WsTranscriptionClient({
      token: ACCESS_TOKEN,
      allowPersonalAccessTokenInBrowser: true,
      debug: true,
      // AI降噪配置 - 仅当浏览器支持并且选择使用时开启
      aiDenoisingConfig: denoiserSupported
        ? {
            mode: AIDenoiserProcessorMode.NSNG, // AI降噪模式
            level: AIDenoiserProcessorLevel.SOFT, // 舒缓降噪
            assetsPath:
              'https://lf3-static.bytednsdoc.com/obj/eden-cn/613eh7lpqvhpeuloz/websocket',
          }
        : undefined,
      // 音频捕获配置
      audioCaptureConfig: {
        echoCancellation: true,
        noiseSuppression: !denoiserSupported, // 如果支持AI降噪，则禁用浏览器内置降噪
        autoGainControl: true,
      },
    });

    // 如果使用AI降噪但浏览器不支持，则提示用户
    if (!denoiserSupported) {
      message.info('当前浏览器不支持AI降噪，将使用浏览器内置降噪');
    }

    // 监听转录结果更新
    client.on(
      WebsocketsEventType.TRANSCRIPTIONS_MESSAGE_UPDATE,
      (event: unknown) => {
        setTranscriptionText(
          (event as TranscriptionsMessageUpdateEvent).data.content,
        );
      },
    );

    // 监听错误事件
    client.on(WebsocketsEventType.ERROR, (error: unknown) => {
      console.error(error);
      message.error((error as CommonErrorEvent).data.msg);
    });

    // 监听所有事件（用于调试）
    client.on(WebsocketsEventType.ALL, (event: unknown) => {
      console.log('收到事件', event);
    });

    clientRef.current = client;
  };

  // 开始/停止录音
  const handleStartAndStop = async () => {
    try {
      setDisabled(true); // 操作过程中禁用按钮，防止重复点击

      if (!clientRef.current) {
        await initClient();
      }

      if (!isRecording) {
        // 开始语音识别
        await clientRef.current?.start();
        setTranscriptionText('');
        setIsRecording(true);
        setStatus('录音中');
      } else {
        // 停止语音识别
        await clientRef.current?.stop();
        setIsRecording(false);
        setIsPaused(false);
        setStatus('已停止');
      }
    } catch (error) {
      message.error(`操作失败：${error}`);
      console.error(error);
      setIsRecording(false);
      setStatus('发生错误');
    } finally {
      setDisabled(false);
    }
  };

  // 暂停/恢复录音
  const handlePauseAndResume = () => {
    try {
      if (clientRef.current?.getStatus() === 'paused') {
        clientRef.current?.resume();
        setIsPaused(false);
        setStatus('录音中');
      } else {
        clientRef.current?.pause();
        setIsPaused(true);
        setStatus('已暂停');
      }
    } catch (error) {
      message.error(`暂停/恢复失败: ${error}`);
    }
  };

  return (
    <Layout style={{ height: '100%' }}>
      <Layout.Content style={{ background: '#fff', padding: '20px' }}>
        <Title level={2}>语音识别 (ASR) 演示</Title>

        {/* 前置条件检查 */}
        <Card title="前置条件检查" style={{ marginBottom: '20px' }}>
          <Row gutter={[0, 16]}>
            <Col span={24}>
              <Space>
                <Text strong>麦克风权限：</Text>
                {hasPermission === null ? (
                  <Text type="warning">正在检查...</Text>
                ) : hasPermission ? (
                  <Text type="success">已授权</Text>
                ) : (
                  <Text type="danger">未授权 - 请允许浏览器使用麦克风</Text>
                )}
              </Space>
            </Col>
            <Col span={24}>
              <Space>
                <Text strong>个人访问令牌 (PAT)：</Text>
                {hasToken ? (
                  <Text type="success">已配置</Text>
                ) : (
                  <Text type="danger">未配置 - 请在右上角 Settings 中设置</Text>
                )}
              </Space>
            </Col>
            <Col span={24}>
              <Space>
                <Text strong>AI降噪支持：</Text>
                {denoiserSupported ? (
                  <Text type="success">支持</Text>
                ) : (
                  <Text type="warning">不支持 - 将使用浏览器内置降噪</Text>
                )}
              </Space>
            </Col>
          </Row>
        </Card>

        {/* 录音控制 */}
        <Card title="录音控制" style={{ marginBottom: '20px' }}>
          <Row justify="start" style={{ marginBottom: '16px' }}>
            <Col>
              <Text>
                当前状态: <Text strong>{status}</Text>
              </Text>
            </Col>
          </Row>
          <Row gutter={16}>
            <Col>
              <Button
                type="primary"
                icon={<AudioOutlined />}
                disabled={disabled || !hasPermission || !hasToken}
                onClick={handleStartAndStop}
                danger={isRecording}
                size="large"
              >
                {isRecording ? '停止录音' : '开始录音'}
              </Button>
            </Col>
            {isRecording && (
              <Col>
                <Button
                  icon={isPaused ? <PlayCircleOutlined /> : <PauseOutlined />}
                  onClick={handlePauseAndResume}
                  size="large"
                >
                  {isPaused ? '恢复录音' : '暂停录音'}
                </Button>
              </Col>
            )}
          </Row>
        </Card>

        {/* 识别结果 */}
        <Card title="识别结果" bodyStyle={{ minHeight: '120px' }}>
          <Paragraph>
            {transcriptionText || <Text type="secondary">等待识别结果...</Text>}
          </Paragraph>
        </Card>

        {/* 使用说明 */}
        <Card title="使用说明" style={{ marginTop: '20px' }}>
          <Paragraph>
            <ol>
              <li>确保授予浏览器麦克风访问权限</li>
              <li>在代码中配置个人访问令牌 (ACCESS_TOKEN)</li>
              <li>点击"开始录音"按钮开始语音识别</li>
              <li>说话时，实时识别结果将显示在"识别结果"区域</li>
              <li>可以使用"暂停录音"按钮暂时停止录音（保持上下文）</li>
              <li>完成后点击"停止录音"按钮结束会话</li>
            </ol>
          </Paragraph>
        </Card>
      </Layout.Content>
    </Layout>
  );
};

export default TranscriptionDemo;
```

## 实现流程 {#34f72982}

你可以通过 WsTranscriptionClient 类来实现实时语音识别功能，支持开始、暂停、恢复和停止等基本操作。

### 步骤一：安装依赖 {#28d6c446}

运行以下命令安装 WsTranscription SDK 及其依赖项。

```Shell
npm install @coze/api
```

### 步骤二：导入模块 {#60be9d1c}

在项目中导入 WsTranscription SDK 提供的模块，包括工具类、客户端类以及事件类型等。

```TypeScript
import {
  WsToolsUtils,
  WsTranscriptionClient,
  AIDenoiserProcessorMode,
  AIDenoiserProcessorLevel,
} from '@coze/api/ws-tools';
import {
  CommonErrorEvent,
  TranscriptionsMessageUpdateEvent,
  WebsocketsEventType,
} from '@coze/api';
```

### 步骤三：检查设备权限 {#b7418a8a}

在使用语音识别功能前，先检查并获取麦克风访问权限。调用`WsToolsUtils.checkDevicePermission() `方法，检查浏览器是否已授予麦克风权限。

在 HTTPS 或 localhost 中执行该方法。首次调用时，浏览器会向用户显示权限请求对话框，如果用户拒绝麦克风权限，在应用中提供明确指引，告知用户如何在浏览器设置中启用权限。在 iOS Safari 浏览器中，必须由用户交互操作（如点击按钮）触发权限请求。

```TypeScript
import { WsToolsUtils } from "@coze/api/ws-tools";

const checkPermission = async () => {
  const permission = await WsToolsUtils.checkDevicePermission();
  if (!permission.audio) {
    throw new Error("麦克风权限未授予");
  }
};
```

### 步骤四：初始化客户端 {#2443d50c}

调用 `new WsTranscriptionClient` 方法创建 WsTranscriptionClient 实例，配置 `token` 等参数以连接到服务。

```JavaScript
import {
  AIDenoiserProcessorLevel,
  AIDenoiserProcessorMode,
  WsTranscriptionClient,
} from '@coze/api/ws-tools';

const initClient = async () => {
  // 确保麦克风权限已授予
  checkPermission();

  // 创建新的转录客户端
  const client = new WsTranscriptionClient({
    token: 'pat_Qm47PKJR5dvMOP53v6DyzwCbTtvEZHQc2TVINEveg9v1T3iSYlTdScJ8***', // 替换为您的个人访问令牌
    allowPersonalAccessTokenInBrowser: true,

    // 可选：AI降噪配置
    aiDenoisingConfig: {
      mode: AIDenoiserProcessorMode.NSNG, // 'NSNG'(AI降噪)或'STATIONARY_NS'(稳态噪声抑制)
      level: AIDenoiserProcessorLevel.SOFT, // 'SOFT'(推荐)或'AGGRESSIVE'(激进)
    },

    // 可选：调试模式
    debug: true,
  });

  return client;
};
```

参数说明：

* **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 所需的令牌
   :::
* **allowPersonalAccessTokenInBrowser**：在浏览器环境中使用个人访问令牌时，必须设置为 true。
* **debug**：启用调试日志，便于开发和测试阶段的问题排查。

### 步骤五：注册事件监听 {#4a34f919}

在初始化后注册事件监听器，以便处理语音识别过程中的各种事件。详细的事件说明请参见[双向流式语音识别事件](/developer_guides/asr_event)。

```TypeScript
import {
  TranscriptionsMessageUpdateEvent,
  WebsocketsEventType,
} from '@coze/api';
import { WsTranscriptionClient } from '@coze/api/ws-tools';

const setupListeners = (client: WsTranscriptionClient) => {
  // 监听识别结果
  client.on(
    WebsocketsEventType.TRANSCRIPTIONS_MESSAGE_UPDATE,
    (event: unknown) => {
      const transcriptionText = (event as TranscriptionsMessageUpdateEvent).data
        .content;
      console.log('识别结果:', transcriptionText);
      // 更新您的UI
    },
  );

  // 监听错误
  client.on(WebsocketsEventType.ERROR, (error: unknown) => {
    console.error('错误:', error);
    // 适当处理错误
  });

  // 可选：监听所有事件进行调试
  client.on(WebsocketsEventType.ALL, (event: unknown) => {
    console.debug('收到事件:', event);
  });
};
```

### 步骤六：语音识别 {#6d30720a}

你可以管理语音识别的状态，包括开始、暂停、恢复和停止录音。

* **开始录音**：调用 `start` 接口开始录音和语音识别，调用后会立即开始采集音频并进行语音识别。
   :::tip 说明
   * 确保在调用 `start` 方法之前完成所有事件监听的注册。
   * 建议在开始录音前重新获取设备列表，具体请参见[获取音频设备列表](/dev_how_to_guides/install_wstranscription_sdk#d5c18cae)。
   :::
* **停止录音**：调用 `stop` 接口停止录音和语音识别，调用后会结束当前会话。停止录音后如需重新开始录音，需要重新调用 start 方法。
* **暂停和恢复录音**：调用 `pause` 或 `resume` 接口暂停或恢复录音。暂停期间会保持会话连接状态，但不会产生新的转录结果。

```TypeScript
import { WsTranscriptionClient } from '@coze/api/ws-tools';

// 开始识别
const startRecording = async (client: WsTranscriptionClient) => {
  try {
    await client.start();
    console.log('开始识别');
    // 更新您的UI以显示录音状态
  } catch (error) {
    console.error('开始识别失败:', error);
  }
};

// 停止录音和转录
const stopRecording = (client: WsTranscriptionClient) => {
  try {
    client.stop();
    console.log('停止识别');
    // 更新您的UI以显示停止状态
  } catch (error) {
    console.error('停止识别失败:', error);
  }
};

// 暂停录音(保持上下文)
const pauseRecording = (client: WsTranscriptionClient) => {
  try {
    client.pause();
    console.log('暂停识别');
    // 更新您的UI以显示暂停状态
  } catch (error) {
    console.error('暂停识别失败:', error);
  }
};

// 恢复已暂停的录音
const resumeRecording = (client: WsTranscriptionClient) => {
  try {
    client.resume();
    console.log('恢复识别');
    // 更新您的UI以显示识别状态
  } catch (error) {
    console.error('恢复识别失败:', error);
  }
};
```

### 步骤七：断开连接 {#c76a1370}

在组件卸载或不再需要语音识别功能时，调用 `destory` 方法断开连接，清理资源，以避免潜在的内存泄漏或其他资源占用问题。

```TypeScript
// 使用完客户端后
client.destroy();
```

## 进阶功能 {#61b796e1}

### 客户端配置 {#5f0f3fce}

在初始化客户端时，你还可以进行高级配置，以满足不同的开发需求，包括配置音频采集、AI降噪、调试模式等。

```TypeScript
interface WsTranscriptionClientOptions {
  // 必填项
  token: GetToken; // Personal Access Token (PAT) 或 OAuth2.0 token，或获取 token 的函数
  
  // 选填项
  debug?: boolean; // 是否启用调试模式
  headers?: Headers | Record<string, unknown>; // 自定义请求头
  allowPersonalAccessTokenInBrowser?: boolean; // 是否允许在浏览器环境中使用个人访问令牌
  baseWsURL?: string; // WebSocket 基础 URL，默认为 wss://ws.coze.cn
  websocketOptions?: WebsocketOptions; // WebSocket 配置选项
  
  deviceId?: string; // 音频输入设备 ID

  // 音频相关配置
  audioCaptureConfig?: {
    noiseSuppression?: boolean; // 是否启用降噪
    echoCancellation?: boolean; // 是否启用回声消除
    autoGainControl?: 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; // 是否启用降噪后音频录制
  };
}
```

### 获取音频设备列表 {#d5c18cae}

你可以通过` WsToolsUtils.getAudioDevices` 方法获取系统的音频输入和输出设备列表，返回的设备列表包含每个设备的 deviceId 和 label 信息。

```TypeScript
// 获取音频设备列表
const devices = await WsToolsUtils.getAudioDevices()
const audioInputs = devices.audioInputs
const deviceId = audioInputs[0].deviceId
```

:::tip 说明
* 在调用 `getAudioDevices` 之前，确保已获得用户的麦克风权限。
* 设备列表可能会动态变化，建议在开始录音前重新获取设备列表。
:::

### AI 降噪 {#99d19301}

配置 AI 降噪的步骤如下：

1. 检查浏览器是否支持 AI 降噪。
   某些浏览器或设备可能不支持全部降噪功能，当不确定设备支持情况时，可以使用 `WsToolsUtils.checkDenoiserSupport() `方法检查。
2. 开启或关闭降噪，设置降噪的等级和模式。
   ```TypeScript
   import { AIDenoiserProcessorLevel, AIDenoiserProcessorMode, WsToolsUtils } from "@coze/api/ws-tools";
   
   //检查浏览器是否支持 AI 降噪
   const isDenoiserSupported = WsToolsUtils.checkDenoiserSupport();
   console.log("支持AI降噪:", isDenoiserSupported);
   
   // 启用/禁用降噪器
   client.setDenoiserEnabled(true);
   
   // 更改降噪模式(NSNG或STATIONARY_NS)
   client.setDenoiserMode(AIDenoiserProcessorMode.NSNG);
   
   // 更改降噪级别(SOFT或AGGRESSIVE)
   client.setDenoiserLevel(AIDenoiserProcessorLevel.SOFT);
   ```   


### 检查客户端状态 {#1c95daa7}

通过监听 `getStatus`中的状态回调，你可以管理语音识别客户端的运行状态。根据不同的状态，你可以执行相应的业务逻辑。建议在状态发生变化时，及时更新用户界面，以提供更直观的交互体验。

```TypeScript
const status = client.getStatus(); // 'recording', 'paused'或'ended'
console.log("当前状态:", status);
```

客户端的运行状态包括：

* recording：录音中
* paused：已暂停
* ended：已结束
