跳到正文

FunASR ASR Server 架构设计与 2-Pass 流水线 ​

本文档详细介绍 onnx-platform/funasr-asr-server 的系统整体架构、单进程双端口设计与核心 2-Pass 语音识别流水线。


1. 系统设计背景与演进 ​

在旧版语音平台中,语音识别通常依赖封装好的 C++ 动态库(如 fanasr-runtime.dll / .so),存在以下维护痛点:

  1. 跨平台构建脆弱:不同操作系统与 GCC/MSVC 编译链对 C++ 动态库 ABI 极度敏感,容易产生符号冲突或崩溃;
  2. 多进程内存膨胀:流式服务与离线服务拆分为两个独立二进制进程,每个进程都要单独加载一遍数百 MB 至数 GB 的声学模型,内存占用翻倍;
  3. 黑盒排障困难:底层 C++ 崩溃难以生成可追踪的 Rust/Go 级堆栈日志。

纯 Rust 统一重构方案 ​

onnx-platform/funasr-asr-server 采用 纯 Rust 自研算法前端与调度器,将所有外部 C++ 库彻底剥离,仅保留官方标准的 onnxruntime 动态库。 在单一进程中,通过 Axum 与 Tokio 同时绑定双端口网络监听,统一管理共享声学模型实例。


2. 单进程双端口并发架构 ​

系统在单一可执行程序中同时启动两个独立的异步 TCP 监听服务,架构图如下:

text
       +-------------------------------+       +-------------------------------+
       | FreeSWITCH / WS 流式推流客户端 |       | HTTP 客户端 / Whisper API 调用 |
       +---------------+---------------+       +---------------+---------------+
                       |                                       |
                       | WS ws://host:10099/audio              | HTTP POST http://host:10094/asr
                       v                                       v
+-----------------------------------------------------------------------------------------------+
| funasr-asr-server (单一操作系统进程)                                                          |
|                                                                                               |
|  [Listener 1: 10099 端口]                              [Listener 2: 10094 端口]               |
|  server::ws_streaming                                  server::http_offline                   |
|  - GET /health                                         - GET /health                          |
|  - GET /audio (WebSocket 升级)                         - POST /asr, POST /recognize           |
|  - 子协议: audio.drachtio.org                          - POST /v1/audio/transcriptions        |
|  - 每连接分配独立 TwoPassSession 会话上下文            - Multipart 解析 + FFmpeg 转码辅助     |
|                      \                                       /                                |
|                       \                                     /                                 |
|                        v                                   v                                  |
|               +-----------------------------------------------------+                         |
|               |  FunAsrEngine (全局单例, Arc<FunAsrEngine> 共享)    |                         |
|               |  - Tokenizer: BPE 词表编解码                        |                         |
|               |  - FSMN-VAD: 语音活动检测                           |                         |
|               |  - Paraformer-Online: 流式实时编码与预测            |                         |
|               |  - Paraformer-Large: 离线全局双向 Attention 大模型  |                         |
|               |  - CT-Transformer: 智能标点符号预测                 |                         |
|               +-----------------------------------------------------+                         |
+-----------------------------------------------------------------------------------------------+

核心收益 ​

  1. 模型内存零浪费:大模型权重只在堆内存驻留一份,Arc<FunAsrEngine> 提供跨线程、跨协议的并发只读共享;
  2. 连接隔离与并发控制:流式连接数受 concurrency.maxSessions 严格保护,离线请求受独立的请求计数器追踪;
  3. 优雅停机(Graceful Shutdown):通过 Tokio watch::channel 广播停机信号,捕获 SIGINT / Ctrl+C 后,Axum 平滑等待进行中的识别完成再安全退出。

3. 2-Pass 双流实时识别流水线 ​

2-Pass(双遍流式流水线)是当前工业级语音识别兼顾超低延迟与极高准确率的核心算法范式:

  • Pass 1(流式实时流):每收到一小包音频(约 20ms~600ms),立即通过轻量流式模型计算,在用户说话过程中以毫秒级延迟吐出字(Partial 增量文字),用户能即时看到字幕跳出;
  • Pass 2(整句离线精算流):当用户说完一整句话、停顿(静音)触发 VAD 切句时,系统将这一整句话的完整音频输入到参数量更大的全局双向 Attention 离线模型,重新做全局解码,并结合标点模型加上逗号、句号(Final 终态文字),纠正 Pass 1 产生的同音字或语法偏差。

详细处理时序图 (ASCII) ​

text
[音频输入: PCM16 16kHz]
          |
          v
[TwoPassSession::push_pcm16] (src/engine/session.rs)
          |
          |-- 1. 音频格式转换: [u8] -> i16 -> f32 浮点采样 [-32768.0, 32768.0]
          |
          +---------------------------------------------------------------------+
          | [步骤 1: FSMN-VAD 语音活动检测]                                     |
          | - 提取 80 维 Fbank 特征                                             |
          | - 结合 4 层历史 Memory Cache 评估静音/人声概率                       |
          | - 状态机输出边沿:                                                   |
          |     * speech_started: 标记进入语音段,冲刷 1s Pre-roll 缓冲          |
          |     * speech_completed: 标记本句结束,进入 Pass 2 结算               |
          +---------------------------------------------------------------------+
          |
          |-- 处于说话中 (speech_active == true)
          |
          +---------------------------------------------------------------------+
          | [步骤 2: Pass 1 流式推理 - Paraformer-Online + CIF]                 |
          | - 将音频推入滑动窗口 (约 600ms 一步)                                |
          | - 提取 LFR (560维) 特征并做 CMVN 均值归一化                          |
          | - Encoder 提取声学向量 -> Predictor 预测声学权重 alpha              |
          | - CIF (连续积分点火器):                                             |
          |     累加 sum(alpha) >= 1.0 时 -> 触发点火 (Fire!)                   |
          |     发射声学 Embedding 向量到 Decoder                               |
          | - Decoder 实时出字 -> 下发 partial 文本帧 (isFinal: false)           |
          +---------------------------------------------------------------------+
          |
          |-- 检测到断句 (speech_completed == true 或收到客户端结束信号)
          |
          +---------------------------------------------------------------------+
          | [步骤 3: Pass 2 整句离线精算 - Paraformer-Large + CT-Transformer]   |
          | - 取出整句缓存的原始音频 PCM (包含字头与字尾)                       |
          | - 送入 Paraformer-Large 离线大模型:                                 |
          |     全局双向自注意力机制 (Global Self-Attention) 全局精算             |
          |     贪心解码 (Greedy Search) 产出高质量无标点文本                   |
          | - 送入 CT-Transformer 标点模型:                                     |
          |     结合 jieba 分词与拼音嵌入,自动回填逗号、句号、问号             |
          | - 下发 final 文本帧 (isFinal: true) 替换此前的 partial 结果         |
          | - 重置单句缓存与 CIF 积分器,准备接收下一句话                        |
          +---------------------------------------------------------------------+

4. 模块职责与目录结构 ​

text
onnx-platform/funasr-asr-server/
├── src/
│   ├── main.rs                  # 主入口:初始化配置、绑定双 Listener、优雅停机
│   ├── config.rs                # 强类型配置反序列化与多 Profile 管理
│   ├── logging.rs               # tracing 结构化日志子系统
│   ├── engine/                  # 纯 Rust 推理引擎核心
│   │   ├── mod.rs               # ONNX Runtime 动态库自动定位与 Engine 单例组装
│   │   ├── session.rs           # TwoPassSession 会话协调器与推流调度
│   │   ├── frontend.rs          # Kaldi 兼容 Fbank (FFT/Mel/LFR/CMVN)
│   │   ├── tokenizer.rs         # BPE 词表编解码
│   │   ├── vad.rs               # FSMN-VAD 状态机与 Memory Cache
│   │   ├── online.rs            # Paraformer-Online 双模型与 CIF 积分点火器
│   │   ├── paraformer.rs        # Paraformer-Large 离线全局模型
│   │   ├── punc.rs              # CT-Transformer 标点恢复与 jieba 切词
│   │   └── sensevoice.rs        # SenseVoice 多语种标签解析
│   └── server/                  # 双端口网络协议层
│       ├── mod.rs
│       ├── ws_streaming.rs      # 10099 端口 WebSocket 流式推流实现
│       ├── http_offline.rs      # 10094 端口 HTTP 离线与 Whisper 接口实现
│       ├── protocol.rs          # 协议帧序列化与 DTO 契约
│       └── ffmpeg.rs            # 外部 FFmpeg 子进程音频转码防线

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