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

# 流式 ASR

> OpenAI Realtime 风格的流式语音识别协议参考

## 说明

基于 WebSocket 的流式语音转文字接口，协议风格对齐 OpenAI Realtime。**全部消息均为 JSON 文本帧**，PCM 音频以 Base64 放入 JSON 字段传输，适用于实时字幕、边录边转、直播转写等场景。

* **接入地址**：`wss://api.senseaudio.cn/ws/v1/realtime/transcriptions`
* **鉴权方式**：Bearer Token，格式 `Authorization: Bearer SENSEAUDIO_API_KEY`
* **消息载体**：全部为文本 JSON 帧（音频也以 Base64 放在 JSON 中，非二进制帧）
* **音频格式**：PCM signed 16-bit little-endian，单声道；采样率支持 **16000** / **24000**
* **模型参数**：默认 `senseaudio-asr-stream-1.5-260910`
* **计费单位**：按音频时长计费，**3.6 元 / 小时**，详见 [计费说明](/guides/account/billing)
* **离线转写**：文件批量转写请使用 [语音识别转写](/api-reference/endpoint/asr/transcriptions)

<Warning>
  - 本接口与 [`/ws/v1/audio/transcriptions`](/api-reference/endpoint/asr/websocket)（语音识别 WebSocket）**协议不兼容**：本接口全部使用 JSON 文本帧，音频必须 Base64 编码。
  - 声道数不可通过接口配置，服务端固定为单声道 `channel=1`。
  - 客户端需先发送 `session.update`，收到 `session.updated` 后再开始推送音频。
</Warning>

## 请求头 (Request Headers)

| 参数名               | 必填 | 说明                                      | 示例                  |
| :---------------- | :- | :-------------------------------------- | :------------------ |
| **Authorization** | 是  | 鉴权 Token。格式：Bearer SENSEAUDIO\_API\_KEY | Bearer sk-123456... |

***

## 通信流程

```
1. 客户端建立 WebSocket 连接
   ↓
2. 服务端返回 session.created（可选）
   ↓
3. 客户端发送 session.update（配置识别参数）
   ↓
4. 服务端返回 session.updated
   ↓
5. 客户端循环发送 input_audio_buffer.append（Base64 PCM）
   ↓
6. 服务端返回中间识别 / VAD / 转写事件（可多次）
   ↓
7. 客户端发送 sense_asr.session.finish
   ↓
8. 服务端返回最终识别事件 → sense_asr.session.finished
```

```mermaid theme={null}
sequenceDiagram
    participant C as 客户端
    participant S as 服务端

    Note over C,S: 1. 建立连接
    C->>S: WebSocket Connect (Authorization: Bearer sk-xxx)
    S-->>C: JSON: session.created（可选）

    Note over C,S: 2. 初始化会话
    C->>S: JSON: session.update
    S-->>C: JSON: session.updated

    Note over C,S: 3. 发送音频 (循环)
    loop Audio Streaming
        C->>S: JSON: input_audio_buffer.append (Base64 PCM)
        S-->>C: JSON: 中间识别 / VAD / ACK 事件
    end

    Note over C,S: 4. 结束识别
    C->>S: JSON: sense_asr.session.finish
    S-->>C: JSON: 最终识别事件 ...
    S-->>C: JSON: sense_asr.session.finished
```

***

## 客户端事件

### 1. session.update - 初始化会话

必须作为首个业务消息发送，用于配置音频格式、识别模型、VAD 与扩展能力。

**请求参数**

| 参数路径                                                             | 类型        | 必填 | 说明                                    |
| :--------------------------------------------------------------- | :-------- | :- | :------------------------------------ |
| **type**                                                         | string    | 是  | 固定值：`session.update`                  |
| **session.type**                                                 | string    | 否  | 只能为空或 `transcription`                 |
| **session.audio.input.format.type**                              | string    | 否  | 默认 `audio/pcm`；仅支持 `audio/pcm`、`pcm`  |
| **session.audio.input.format.rate**                              | int       | 否  | 默认 `16000`；仅支持 `16000` 或 `24000`      |
| **session.audio.input.transcription.model**                      | string    | 否  | 默认 `senseaudio-asr-stream-1.5-260910` |
| **session.audio.input.transcription.prompt**                     | string    | 否  | 识别提示词                                 |
| **session.audio.input.transcription.keywords**                   | string\[] | 否  | 热词数组                                  |
| **session.audio.input.transcription.languages**                  | string\[] | 否  | 语言代码数组，如 `["zh", "en"]`               |
| **session.audio.input.turn\_detection.type**                     | string    | 否  | 仅支持 `server_vad`；不传则不配置 VAD           |
| **session.audio.input.turn\_detection.threshold**                | number    | 否  | VAD 阈值                                |
| **session.audio.input.turn\_detection.prefix\_padding\_ms**      | int       | 否  | 语音开始前保留时长（毫秒）                         |
| **session.audio.input.turn\_detection.silence\_duration\_ms**    | int       | 否  | 判定语音结束的静音时长（毫秒）                       |
| **session.x\_sense\_asr.sentence\_timestamps.enabled**           | boolean   | 否  | 句子级时间戳，默认 `false`                     |
| **session.x\_sense\_asr.word\_timestamps.enabled**               | boolean   | 否  | 字级时间戳，默认 `false`                      |
| **session.x\_sense\_asr.speaker\_diarization.enabled**           | boolean   | 否  | 说话人区分                                 |
| **session.x\_sense\_asr.speaker\_diarization.preserve\_overlap** | boolean   | 否  | 是否保留重叠语音                              |
| **session.x\_sense\_asr.translation.enabled**                    | boolean   | 否  | 是否启用翻译                                |
| **session.x\_sense\_asr.translation.target\_language**           | string    | 否  | 翻译目标语言                                |
| **session.x\_sense\_asr.smart\_edit.enabled**                    | boolean   | 否  | 是否启用智能编辑                              |

**请求示例**

```json theme={null}
{
  "type": "session.update",
  "session": {
    "type": "transcription",
    "audio": {
      "input": {
        "format": { "type": "audio/pcm", "rate": 16000 },
        "transcription": {
          "model": "senseaudio-asr-stream-1.5-260910",
          "prompt": "",
          "keywords": ["SenseAudio", "实时转写"],
          "languages": ["zh", "en"]
        },
        "turn_detection": {
          "type": "server_vad",
          "threshold": 0.5,
          "prefix_padding_ms": 300,
          "silence_duration_ms": 500
        }
      }
    },
    "x_sense_asr": {
      "sentence_timestamps": { "enabled": true },
      "word_timestamps": { "enabled": false },
      "speaker_diarization": { "enabled": false, "preserve_overlap": false },
      "translation": { "enabled": false, "target_language": "en" },
      "smart_edit": { "enabled": false }
    }
  }
}
```

### 2. input\_audio\_buffer.append - 追加音频

每段音频一个 JSON 帧，`audio` 为 Base64 编码的 PCM 字节。建议每片约 100ms（16kHz 时约 3200 字节，24kHz 时约 4800 字节）。

```json theme={null}
{
  "type": "input_audio_buffer.append",
  "audio": "<Base64 编码的 PCM 字节>"
}
```

### 3. sense\_asr.session.finish - 结束识别

音频全部发送完成后发送：

```json theme={null}
{ "type": "sense_asr.session.finish" }
```

***

## 服务端事件

| 事件 type                                                     | 说明                                            |
| :---------------------------------------------------------- | :-------------------------------------------- |
| **session.created**                                         | 连接建立后服务端先下发初始 session（在 `session.updated` 之前） |
| **session.updated**                                         | 初始化成功，`session.type` 回显 `transcription`       |
| **sense\_asr.client\_ack**                                  | 对客户端事件的 ACK，含处理序号                             |
| **input\_audio\_buffer.speech\_started**                    | VAD 检测到语音开始                                   |
| **input\_audio\_buffer.speech\_stopped**                    | VAD 检测到语音结束                                   |
| **input\_audio\_buffer.committed**                          | 音频缓冲提交为一条 item                                |
| **conversation.item.created**                               | 对话项创建                                         |
| **conversation.item.input\_audio\_transcription.delta**     | 转写增量                                          |
| **conversation.item.input\_audio\_transcription.completed** | 转写最终稿，含 `transcript`、`usage` 等                |
| **sense\_asr.session.finished**                             | 本次会话结束标志                                      |

**session.updated 示例**

```json theme={null}
{
  "type": "session.updated",
  "session": { "type": "transcription" }
}
```

**sense\_asr.session.finished 示例**

```json theme={null}
{ "type": "sense_asr.session.finished" }
```

***

## 与语音识别 WebSocket 的区别

| 维度   | 语音识别 WebSocket `/ws/v1/audio/transcriptions`  | 本接口 `/ws/v1/realtime/transcriptions`                                        |
| :--- | :-------------------------------------------- | :-------------------------------------------------------------------------- |
| 音频上传 | **二进制帧**直接发 PCM                               | **文本 JSON 帧**，PCM 做 Base64 放 `audio` 字段                                     |
| 控制事件 | `task_start` / `task_finish` / `result_final` | `session.update` / `input_audio_buffer.append` / `sense_asr.session.finish` |
| 模型   | `senseaudio-asr-deepthink-1.5-260319`         | `senseaudio-asr-stream-1.5-260910`                                          |
| 采样率  | 仅 16000                                       | 16000 / 24000                                                               |
| 扩展能力 | `vad_setting` / `transcription_setting`       | `x_sense_asr`（时间戳、说话人区分、翻译、智能编辑）                                            |
| 计费   | 3.6 元 / 小时                                    | **3.6 元 / 小时**                                                              |

***

## 代码示例

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import asyncio
    import base64
    import json
    import os
    import websockets

    WS_URL = "wss://api.senseaudio.cn/ws/v1/realtime/transcriptions"
    API_KEY = os.environ["SENSEAUDIO_API_KEY"]
    AUDIO_FILE = "audio_16k.pcm"
    RATE = 16000

    async def main():
        headers = {"Authorization": f"Bearer {API_KEY}"}
        async with websockets.connect(WS_URL, additional_headers=headers) as ws:
            await ws.send(json.dumps({
                "type": "session.update",
                "session": {
                    "type": "transcription",
                    "audio": {
                        "input": {
                            "format": {"type": "audio/pcm", "rate": RATE},
                            "transcription": {
                                "model": "senseaudio-asr-stream-1.5-260910",
                                "languages": ["zh", "en"]
                            },
                            "turn_detection": {
                                "type": "server_vad",
                                "threshold": 0.5,
                                "prefix_padding_ms": 300,
                                "silence_duration_ms": 500
                            }
                        }
                    },
                    "x_sense_asr": {
                        "sentence_timestamps": {"enabled": True}
                    }
                }
            }))

            # 等待 session.updated
            while True:
                msg = json.loads(await ws.recv())
                if msg.get("type") == "session.updated":
                    break

            chunk = RATE * 2 // 10  # 约 100ms
            with open(AUDIO_FILE, "rb") as f:
                while True:
                    data = f.read(chunk)
                    if not data:
                        break
                    await ws.send(json.dumps({
                        "type": "input_audio_buffer.append",
                        "audio": base64.b64encode(data).decode()
                    }))

            await ws.send(json.dumps({"type": "sense_asr.session.finish"}))

            while True:
                msg = json.loads(await ws.recv())
                print(msg.get("type"), msg.get("transcript") or msg.get("delta") or "")
                if msg.get("type") == "sense_asr.session.finished":
                    break

    asyncio.run(main())
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    const WebSocket = require('ws');
    const fs = require('fs');

    const WS_URL = 'wss://api.senseaudio.cn/ws/v1/realtime/transcriptions';
    const API_KEY = process.env.SENSEAUDIO_API_KEY;
    const AUDIO_FILE = 'audio_16k.pcm';
    const RATE = 16000;

    const ws = new WebSocket(WS_URL, {
      headers: { Authorization: `Bearer ${API_KEY}` }
    });

    ws.on('open', () => {
      ws.send(JSON.stringify({
        type: 'session.update',
        session: {
          type: 'transcription',
          audio: {
            input: {
              format: { type: 'audio/pcm', rate: RATE },
              transcription: {
                model: 'senseaudio-asr-stream-1.5-260910',
                languages: ['zh', 'en']
              },
              turn_detection: {
                type: 'server_vad',
                threshold: 0.5,
                prefix_padding_ms: 300,
                silence_duration_ms: 500
              }
            }
          },
          x_sense_asr: {
            sentence_timestamps: { enabled: true }
          }
        }
      }));
    });

    let started = false;
    ws.on('message', (data) => {
      const msg = JSON.parse(data.toString());
      console.log('<', msg.type);

      if (msg.type === 'session.updated' && !started) {
        started = true;
        const chunkSize = Math.floor(RATE * 2 * 0.1);
        const buf = fs.readFileSync(AUDIO_FILE);
        for (let i = 0; i < buf.length; i += chunkSize) {
          const chunk = buf.subarray(i, i + chunkSize);
          ws.send(JSON.stringify({
            type: 'input_audio_buffer.append',
            audio: chunk.toString('base64')
          }));
        }
        ws.send(JSON.stringify({ type: 'sense_asr.session.finish' }));
      }

      if (msg.type === 'sense_asr.session.finished') {
        ws.close();
      }
    });
    ```
  </Tab>
</Tabs>

***

## 注意事项

1. **全部 JSON 文本帧**：控制消息与音频消息均使用文本帧；音频必须 Base64 编码，不要发送二进制帧。
2. **事件顺序**：`连接 → session.update → session.updated → append 音频 → finish → finished`。
3. **音频参数**：PCM s16le / 单声道；采样率须与 `format.rate` 一致（16000 或 24000）。
4. **实时节奏**：建议按约 100ms 一片发送，避免过快占满缓冲或过慢导致 VAD 误触发。
5. **计费**：按识别音频时长计费，不足 1 秒按 1 秒计时，单价 **3.6 元 / 小时**。

## 相关资源

<CardGroup cols={2}>
  <Card title="语音识别介绍" icon="book-open" href="/guides/asr/overview">
    模型对比、接口选型与接入步骤。
  </Card>

  <Card title="语音识别 WebSocket" icon="bolt" href="/api-reference/endpoint/asr/websocket">
    二进制帧协议的实时识别接口。
  </Card>

  <Card title="离线转写 API" icon="file-audio" href="/api-reference/endpoint/asr/transcriptions">
    基于 HTTP 的文件识别接口。
  </Card>

  <Card title="产品定价" icon="coins" href="/guides/account/billing">
    语音识别计费规则。
  </Card>
</CardGroup>
