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

# FunASR 流式语音识别

> 在 ModelHub 中使用自部署 FunASR 模型实时识别语音

# FunASR 流式语音识别

通过 WebSocket 接口 `/v1/stream_asr` 持续发送 PCM 音频，并实时接收中文识别结果。

## 可用模型

| 模型                     |
| ---------------------- |
| `aigc-cpp-funasr-sysj` |

## 音频要求

该模型部署的是中文 16 kHz Paraformer 在线识别模型。发送的音频必须满足以下要求：

| 项目  | 要求                              |
| --- | ------------------------------- |
| 编码  | PCM signed 16-bit little-endian |
| 采样率 | 16000 Hz                        |
| 声道  | 单声道                             |
| 容器  | 裸 PCM，不包含 WAV 文件头               |

转换示例：

```bash theme={null}
ffmpeg -i input.wav -f s16le -acodec pcm_s16le -ac 1 -ar 16000 input.pcm
```

## Python 示例

安装依赖：

```bash theme={null}
pip install websockets
```

```python theme={null}
import asyncio
import base64
import json
import os
import uuid

import websockets

API_KEY = os.environ["MODELHUB_API_KEY"]
WS_URL = "wss://model-api.skyengine.com.cn/v1/stream_asr?model=aigc-cpp-funasr-sysj"


async def recognize(path: str) -> None:
    headers = {"Authorization": f"Bearer {API_KEY}"}
    trace_id = f"segment_{uuid.uuid4().hex}"

    async with websockets.connect(WS_URL, additional_headers=headers) as ws:
        with open(path, "rb") as audio_file:
            while chunk := audio_file.read(3200):
                await ws.send(json.dumps({
                    "type": "input_audio_buffer.append",
                    "trace_id": trace_id,
                    "audio": base64.b64encode(chunk).decode(),
                }))
                await asyncio.sleep(0.1)

        # 发送静音，让服务端完成当前语音段
        await ws.send(json.dumps({
            "type": "input_audio_buffer.append",
            "trace_id": trace_id,
            "audio": base64.b64encode(bytes(32000)).decode(),
        }))

        completed = []
        while True:
            try:
                message = await asyncio.wait_for(ws.recv(), timeout=3)
            except asyncio.TimeoutError:
                break
            event = json.loads(message)
            if event.get("type") == "error":
                raise RuntimeError(event["error"]["message"])
            if event.get("type") == "conversation.item.input_audio_transcription.delta":
                print("实时结果：", event["transcript"])
            if event.get("type") == "conversation.item.input_audio_transcription.completed":
                completed.append(event["transcript"])

        print("最终结果：", "".join(completed))


asyncio.run(recognize("input.pcm"))
```

## 请求格式

ASR 使用 WebSocket 协议，因此建连请求没有 HTTP JSON Body。连接成功后，客户端通过 WebSocket 文本消息发送音频 JSON Body。

建连地址：

```text theme={null}
wss://model-api.skyengine.com.cn/v1/stream_asr?model=aigc-cpp-funasr-sysj
```

建连 Header：

```text theme={null}
Authorization: Bearer <API-KEY>
```

音频消息 Body：

```json theme={null}
{
  "type": "input_audio_buffer.append",
  "trace_id": "segment_01",
  "audio": "<BASE64_PCM_AUDIO>"
}
```

## 连接参数

| 参数              | 位置        | 必填 | 说明                          |
| --------------- | --------- | -- | --------------------------- |
| `model`         | URL Query | 是  | 固定为 `aigc-cpp-funasr-sysj`。 |
| `Authorization` | Header    | 是  | `Bearer <API-KEY>`。         |

WebSocket 建连成功后可以直接发送音频消息，无需先发送 `session.config`，也无需等待 `session.ready`。

## 输入事件

每个音频事件包含一段 Base64 编码的 PCM 字节：

```json theme={null}
{
  "type": "input_audio_buffer.append",
  "trace_id": "segment_01",
  "audio": "<BASE64_PCM_AUDIO>"
}
```

| 参数         | 类型     | 必填 | 说明                                                         |
| ---------- | ------ | -- | ---------------------------------------------------------- |
| `type`     | string | 是  | 固定为 `input_audio_buffer.append`。                           |
| `audio`    | string | 是  | Base64 编码的 PCM 音频字节。                                       |
| `trace_id` | string | 否  | 客户端为当前语音段生成的关联 ID；平台会在该语音段的识别事件中回传相同值。它不是平台请求链路的 Trace ID。 |

## 输出事件

| 事件                                                      | 说明                       |
| ------------------------------------------------------- | ------------------------ |
| `conversation.item.created`                             | 创建了一段新的语音识别内容。           |
| `input_audio_buffer.speech_started`                     | 检测到语音开始。                 |
| `conversation.item.input_audio_transcription.delta`     | 当前语音段的实时累计识别文本。          |
| `input_audio_buffer.speech_stopped`                     | 检测到语音结束。                 |
| `conversation.item.input_audio_transcription.completed` | 当前语音段的最终识别文本；一次连接可能返回多条。 |
| `error`                                                 | 请求或识别失败。                 |

最终结果事件示例：

```json theme={null}
{
  "event_id": "event_xxx",
  "type": "conversation.item.input_audio_transcription.completed",
  "item_id": "item_xxx",
  "content_index": 0,
  "transcript": "欢迎使用语音识别服务。",
  "audio_duration": 3
}
```
