跳到正文

FunASR Online Streaming ASR Server (WebSocket)

基于 FunASR C++ ONNX Runtime 构建的高性能实时语音识别服务,内置流式 FSMN-VAD 语音活动检测,支持 SenseVoiceSmall 极速伪流式(多语种 + 标点/ITN + 情绪识别)Paraformer 2-pass(Two-Pass 双流模式) 实时增量上屏与句尾高精度纠错、以及 Paraformer 1-pass 单流实时模式,完全契合 FreeSWITCH mod_audio_forkcallflow-esl


1. 核心架构与运行模式

服务支持通过配置文件中的 asr.activeProfile 自由切换不同的识别模式与模型组合:

模式 1:SenseVoiceSmall 极速伪流式 (mode: "pseudo_streaming",默认激活)

客户端 (WebSocket 16kHz PCM16 流)

    ├── 1. 独立轻量 FSMN-VAD 切句 (~0.1ms) ──> 判定语音起止与静音段

    ├── 2. 首字极速通道 (累积 150ms PCM) ────> 即刻触发第 1 次 partial 上屏

    ├── 3. 增量自适应步长 (200ms ~ 500ms) ───> SenseVoice 多实例池非阻塞推理 ──> 持续推送 partial 实时字幕

    └── 4. 句尾静音 / 流结束 ───────────────> SenseVoice 终态推理 + 富文本解析 ──> 推送 final 终态文本 + 语种/情绪/事件
  • 多语种与富文本原生支持:支持中、英、粤、日、韩等多语种混合识别(auto 自动检测),原生输出高质量标点、逆文本归一化(ITN)、情绪标签(开心、悲伤、愤怒等)与声音事件检测(笑声、掌声、BGM、哭声等);
  • 首字极速通道:发音达到 150ms 即刻触发首字上屏,自适应步长确保长时间语音推流性能平稳;
  • 多实例池并发:基于非阻塞推理实例池(instancePoolSize),避免 I/O Worker 线程阻塞,保证高并发 WebSocket 网络吞吐。

模式 2:Paraformer 2-Pass 双流模式 (mode: "2pass")

客户端 (WebSocket 16kHz PCM16 流)

    ├── 1. 每 600ms Chunk 到达 ──> Pass 1: Paraformer-Online (流式模型)
    │                                  └──> 毫秒级推送 partial 实时字幕

    └── 2. 句尾静音 / 流结束 ────> Pass 2: Paraformer-Offline (离线大模型) + CT-Transformer (标点模型)
                                       └──> 纠错并推送 final 高精度文本 + 标点恢复 + 热词偏置
  • Pass 1(在线流式):音频 chunk 到达即刻送入 Paraformer-Online 解码,毫秒级推送 type: "partial" 实时字幕;
  • Pass 2(离线重识别与标点):VAD 判定静音或推流结束时,自动调度 Paraformer 离线大模型与 CT-Transformer 标点模型重算整句,推送 type: "final" 终态文本与热词纠错。

模式 3:Paraformer 单流实时模式 (mode: "online")

  • 纯流式 CIF 毫秒级上屏,无句尾离线大模型二次重算,适合资源受限或超高并发单机场景。

2. 默认端口与协议

  • 默认监听0.0.0.0:10099
  • GET /health:HTTP 健康检查;
  • ws://<host>:10099/audio:WebSocket 实时音频推流协议。

3. WebSocket 协议交互规范

3.1 握手与连接建立

  • 请求路径:GET /audio HTTP/1.1
  • 握手协议:标准 RFC 6455 WebSocket 协议(101 Switching Protocols);
  • 子协议(可选):Sec-WebSocket-Protocol: audio.drachtio.org(若配置了 wsSubprotocol)。

3.2 客户端发送数据帧

1. 会话元数据帧(Text Frame, Opcode 0x1)(可选,建连后第一帧发送):

json
{
  "uuid": "call-session-uuid-12345",
  "direction": "inbound",
  "source": "mod_audio_fork",
  "hotwordProfile": "default",
  "hotwords": "阿里巴巴 通义千问 验证码",
  "language": "auto",
  "useItn": true,
  "enablePartial": true
}
  • uuid / channel_uuid:呼叫通道唯一 ID,后续下发的所有 transcription 事件都会携带该 ID;
  • hotwordProfile:指定激活的热词场景 ID(对应 hotword.json 中的 Profile);
  • hotwords:动态注入自定义热词字符串(空格分隔);
  • language:指定语种(autozhenyuejako);
  • useItn:是否启用逆文本归一化(数字、日期转换,默认 true);
  • enablePartial:是否推送中间 partial 字幕(默认 true)。

2. 音频数据帧(Binary Frame, Opcode 0x2):

  • 格式:16kHz, 16-bit, 单声道 PCM 原始音频;
  • 推荐分片:每包 20ms(640 字节,对接 FreeSWITCH RTP 默认切片)或 100ms~600ms 增量推流;
  • 结束标识:可发送一个长度为 0 的 Binary Frame 告知当前音频流输入完毕并触发句尾结算。

3. 断开连接帧(Close Frame, Opcode 0x8):

  • 服务端会在断开前确保 final 终态帧与 Close 握手全部下发给客户端后再关闭 Socket。

3.3 服务端下发事件 (type: "transcription")

A. 实时增量字幕 (Partial)

json
{
  "type": "transcription",
  "data": {
    "text": "重点呢想谈三个问题首先呢就是这一",
    "isFinal": false,
    "utteranceIndex": 1,
    "speechStarted": true,
    "speechActive": true,
    "speechSegmentDetected": true,
    "speechSegmentCompleted": false,
    "language": "zh",
    "emotion": "NEUTRAL",
    "event": "Speech",
    "serverSessionId": "ws-1",
    "channelUuid": "call-session-uuid-12345"
  }
}

B. 句尾纠错与标点终态 (Final)

json
{
  "type": "transcription",
  "data": {
    "text": "重点呢想谈三个问题。首先呢就是这一轮全球金融动荡的表现。",
    "isFinal": true,
    "utteranceIndex": 1,
    "speechStarted": true,
    "speechActive": false,
    "speechSegmentDetected": true,
    "speechSegmentCompleted": true,
    "finalReason": "segment-completed",
    "language": "zh",
    "emotion": "HAPPY",
    "event": "Speech",
    "serverSessionId": "ws-1",
    "channelUuid": "call-session-uuid-12345"
  }
}

4. HTTP 健康检查 (GET /health)

bash
curl http://127.0.0.1:10099/health

返回示例:

json
{
  "ok": true,
  "service": "fanasr-asr-online-server",
  "activeProfile": "sensevoice-pseudo-streaming",
  "activeModel": "sensevoice-pseudo-streaming",
  "description": "SenseVoiceSmall 极速伪流式(多语种检测 + 原生富文本标点/ITN + 情绪识别)",
  "mode": "pseudo_streaming",
  "activeSessions": 0,
  "maxSessions": 256,
  "wsPath": "/audio",
  "ioWorkers": 8
}

5. 配置文件说明 (config.json)

完整配置项定义、字段类型约束、默认值与高级场景调优请查阅专题文档: 📖 FunASR Online Streaming ASR Server 配置参考

json
{
  "server": {
    "host": "0.0.0.0",
    "port": 10099,
    "healthPath": "/health",
    "wsPath": "/audio",
    "wsSubprotocol": "audio.drachtio.org"
  },
  "concurrency": {
    "maxSessions": 256,
    "sessionPoolSize": 128,
    "ioWorkers": 8,
    "instancePoolSize": 6,
    "inferenceThreads": 2,
    "acceptBacklog": 256,
    "workerPollTimeoutMs": 20
  },
  "socket": {
    "tcpNoDelay": true,
    "maxFrameBytes": 1048576,
    "readBufferBytes": 65536,
    "writeBufferBytes": 65536,
    "recvBufferBytes": 65536,
    "sendBufferBytes": 65536
  },
  "logging": {
    "dir": "logs",
    "debugTextFrames": false,
    "debugAudioFrames": false,
    "debugRecognitionState": false
  },
  "recording": {
    "enabled": false,
    "dir": "recordings"
  },
  "asr": {
    "activeProfile": "paraformer-2pass",
    "sampleRate": 16000,
    "quantize": true,
    "idleFinalizeMs": 1000,
    "partialMinIntervalMs": 150,
    "puncDir": "funasr-ct-transformer-zh-onnx",
    "profiles": {
      "sensevoice-pseudo-streaming": {
        "description": "SenseVoiceSmall 极速伪流式(多语种检测 + 原生富文本标点/ITN + 情绪识别)",
        "mode": "pseudo_streaming",
        "modelDir": "funasr-sensevoicesmall-zh-cantonese-en-ja-ko-onnx",
        "language": "auto",
        "useItn": true,
        "firstPartialMinMs": 150
      },
      "paraformer-2pass": {
        "description": "Paraformer 2-Pass 双流模式(第1流实时 partial + 第2流离线大模型 final + CT-Transformer 标点纠错)",
        "mode": "2pass",
        "onlineModelDir": "funasr-paraformer-large-online-zh-onnx",
        "offlineModelDir": "funasr-paraformer-large-zh-onnx",
        "chunkSize": [5, 10, 5]
      },
      "paraformer-online": {
        "description": "Paraformer 单流实时模式(纯流式 CIF 毫秒级上屏,无句尾二次重算)",
        "mode": "online",
        "onlineModelDir": "funasr-paraformer-large-online-zh-onnx",
        "offlineModelDir": "funasr-paraformer-large-zh-onnx",
        "chunkSize": [5, 10, 5]
      }
    },
    "hotwordsFile": "hotword.json",
    "defaultHotwordProfile": "default"
  },
  "vad": {
    "modelDir": "funasr-fsmn-vad-zh-onnx",
    "quantize": true,
    "maxSpeechDuration": 20.0
  }
}

核心参数速查:

  • concurrency.maxSessions:系统最大流式并发会话数上限(超限返回 503 快速熔断,默认 256);
  • concurrency.ioWorkers:非阻塞 I/O 事件循环 Worker 线程数(默认 8);
  • concurrency.instancePoolSize:推理实例池容量 / 模型句柄池(默认 6),多路并发时由实例池轮转处理,避免互斥锁阻塞 I/O Worker;
  • concurrency.inferenceThreads:单个 ONNX Runtime 推理实例内部分配的计算线程数(默认 2);
  • asr.activeProfile:激活的场景 Profile 名称(如 "sensevoice-pseudo-streaming""paraformer-2pass""paraformer-online");
  • asr.idleFinalizeMs:静音自动终态结算超时(默认 1000ms);
  • asr.partialMinIntervalMs:流式中间字幕 (partial) 最小下发间隔毫秒数(默认 150ms);
  • vad.modelDir:全局 FSMN-VAD 模型目录路径(默认 "funasr-fsmn-vad-zh-onnx");
  • vad.quantize:是否使用 int8 量化 VAD 模型(默认 true);
  • vad.maxSpeechDuration:单句最大截断时长(秒,默认 20.0s,防止无静音超长说话占用内存)。
  • profiles.*.firstPartialMinMs:SenseVoice 伪流式专用的首字极速上屏门限(默认 150ms);
  • profiles.*.maxUtteranceMs:SenseVoice 伪流式专用的单句最长持续时长截断保护(默认 15000ms);
  • asr.hotwordsFile:独立热词配置文件路径(默认 hotword.json)。

6. 编译与运行

6.1 Windows (MSVC)

powershell
.\build.ps1

编译完成后运行:

powershell
cd target\win_x64
.\fanasr_asr_online_server.exe

6.2 Linux (Ninja / GCC)

bash
./build.sh
cd target/linux_x64
./fanasr_asr_online_server

7. 测试与批量识别

7.1 单音频实时推流联调

bash
bun run request-asr.js
# 或指定测试音频与热词
bun run request-asr.js path/to/sample.wav "阿里巴巴 通义千问"

7.2 目录批量音频极速识别

支持对指定目录下的 WAV 音频进行并发批量识别,实时追加写入 CSV 结果文件,并支持断点续传:

bash
# 默认扫描测试音频目录,4 路并发
bun run batch-recognize.js

# 自定义目录、输出路径与并发数
bun run batch-recognize.js --dir "path/to/recordings" --output "results.csv" --concurrency 8

文档与代码在同一仓库维护,现有 Markdown 是唯一内容源。