vLLM 推理引擎
使用 vLLM 加速自回归 LLM-based ASR 模型。新版引擎支持离线批量转写、SDK 级 chunk 流式推理,以及集成 VAD、热词和说话人标签的生产级 WebSocket 服务。
概述
音频 frontend、encoder、adaptor 和可选 CTC 时间戳解码器仍在 PyTorch 中运行;LLM 解码部分通过 prompt embedding 交给 vLLM,获得 PagedAttention、连续批处理和 tensor parallel 多卡并行能力。
适用模型
| 模型系列 | vLLM 支持 | 说明 |
| FunASRNano | 支持 | Audio encoder + adaptor + Qwen3-0.6B LLM。 |
| LLMASR / LLMASRNAR | 支持 | Whisper-style 音频编码器后接 Qwen、Vicuna 或 LLaMA 解码。 |
| GLMASR | 支持 | GLM-ASR-Nano 使用自回归 LLM 解码。 |
| QwenAudioWarp | 支持 | LLM-based audio generation path。 |
| Paraformer、SenseVoice、Conformer、Transformer | 不支持 | 这些模型不是 LLM 自回归解码路径,请使用标准 AutoModel。 |
三种入口
| 模式 | 入口 | 适用场景 |
| 离线 SDK 推理 | AutoModelVLLM 或 FunASRNanoVLLM | 大规模文件转写、吞吐优先的任务。 |
| 流式 SDK 推理 | FunASRNanoStreamingVLLM | Python 应用中需要 chunk 级增量文本。 |
| WebSocket 实时服务 | serve_realtime_ws.py | 生产实时客户端,支持 VAD 分句和说话人标签。 |
安装环境
pip install "funasr>=1.3.0"
pip install "vllm>=0.12.0"
pip install safetensors tiktoken websockets regex
# 如果使用源码树中的 AutoModelVLLM
cd /path/to/FunASR
pip install -e .
| 资源 | 最低要求 | 推荐 |
| GPU 显存 | 8 GB | 16 GB 以上,给 KV cache 留出更充足空间。 |
| CUDA | 11.8 | 12.x |
| GPU 数量 | 1 | 使用 tensor parallel 时建议 2 张或更多。 |
首次运行时,FunASR 会从 model.pt 提取 LLM 权重到 vLLM 兼容目录,例如 Qwen3-0.6B-vllm;后续启动会复用已准备好的权重。
离线 SDK 推理
推荐通用接口
from funasr.auto.auto_model_vllm import AutoModelVLLM
model = AutoModelVLLM(
model="FunAudioLLM/Fun-ASR-Nano-2512",
hub="ms", # 也可以使用 "hf"
tensor_parallel_size=2,
gpu_memory_utilization=0.8,
)
results = model.generate(
["audio1.wav", "audio2.wav"],
language="中文",
hotwords=["张三", "北京"],
)
for item in results:
print(f"[{item['key']}] {item['text']}")
Fun-ASR-Nano 直接接口
from funasr.models.fun_asr_nano.inference_vllm import FunASRNanoVLLM
engine = FunASRNanoVLLM.from_pretrained(
model="FunAudioLLM/Fun-ASR-Nano-2512",
tensor_parallel_size=4,
)
results = engine.generate(
inputs="wav.scp",
language="中文",
hotwords=["开放时间"],
max_new_tokens=512,
)
命令行
cd examples/industrial_data_pretraining/fun_asr_nano
# 单文件
python demo_vllm.py --input audio.wav --language 中文
# 批量 + 多卡 tensor parallel
python demo_vllm.py --input wav.scp --tensor-parallel-size 4 --batch-size 32
# 热词 + JSONL 输出
python demo_vllm.py --input audio.wav --hotwords 张三 北京 --output results.jsonl
流式 SDK 推理
FunASRNanoStreamingVLLM 将音频切成 720 ms chunk,对累积音频重编码,把各 chunk prompt 批量送入 vLLM,并返回 fixed/unfixed 文本区域。适合 Python 应用做实时字幕或增量展示。
from funasr.models.fun_asr_nano.inference_vllm_streaming import FunASRNanoStreamingVLLM
engine = FunASRNanoStreamingVLLM.from_pretrained(
model="FunAudioLLM/Fun-ASR-Nano-2512",
chunk_ms=720,
rollback_chars=8,
)
for result in engine.streaming_generate("audio.wav", language="中文"):
if result["is_final"]:
print(f"最终: {result['text']}")
else:
print(f"[{result['audio_duration_ms']:.0f} ms] 确认: {result['fixed_text']}")
| 行为 | 说明 |
| Stage 1 | 前 10 个 chunk 不使用 prev_text,用于找到稳定前缀。 |
| Stage 2 | 后续 chunk 使用稳定前缀作为 assistant context。 |
| Rollback | 最后 rollback_chars 个字符保持未确认,等待后续 chunk 修正。 |
| 短音频 | 前 1.5 到 3 秒可能为空或不稳定,这是模型特性。 |
WebSocket 实时服务
实时服务集成 streaming VAD、vLLM 分段解码、partial 预览、幻觉重复清理、热词、语种提示和说话人分离。
启动服务
cd examples/industrial_data_pretraining/fun_asr_nano
# 单卡
CUDA_VISIBLE_DEVICES=0 python serve_realtime_ws.py --port 10095 --language 中文
# 多卡 tensor parallel
CUDA_VISIBLE_DEVICES=0,1 python serve_realtime_ws.py \
--port 10095 \
--tensor-parallel-size 2 \
--language 中文
# 完整参数示例
python serve_realtime_ws.py \
--port 10095 \
--model FunAudioLLM/Fun-ASR-Nano-2512 \
--hub ms \
--device cuda:0 \
--decode-interval 0.48 \
--hotword-file 热词列表 \
--language 中文 \
--dtype bf16 \
--tensor-parallel-size 1 \
--gpu-memory-utilization 0.8 \
--max-model-len 2048
客户端
| 客户端 | 用法 |
| 浏览器 | 打开 client_mic.html,支持麦克风、文件上传、热词和说话人标签。 |
| Python CLI | python client_python.py --server ws://localhost:10095 --mic |
| 测试脚本 | python client_test.py --server ws://localhost:10095 --file audio.wav |
远程 GPU 服务器访问前先做端口转发:ssh -L 10095:localhost:10095 <server>。
协议
客户端 -> 服务端:
"START" 初始化会话
"HOTWORDS:词1,词2" 设置热词,可选
"LANGUAGE:中文" 设置语种,可选
[binary bytes] PCM16 16 kHz 单声道音频
"STOP" 结束并触发最终解码
服务端 -> 客户端:
{"event": "started"}
{"event": "hotwords_set", "hotwords": ["词1", "词2"]}
{"event": "language_set", "language": "中文"}
{"sentences": [...], "partial": "...", "is_final": false}
{"sentences": [...], "partial": "", "is_final": true}
{"event": "stopped"}
推理逻辑
| 路径 | 触发条件 | 输出 |
| 确认段解码 | 动态 VAD 检测到端点。 | 整段音频送入 vLLM 解码,结果锁定到 sentences。 |
| Partial 预览 | 达到 --decode-interval,默认 0.48 秒,且有足够新音频。 | 临时 partial 文本,后续可能覆盖。 |
| 最终处理 | 收到 STOP。 | flush 剩余 VAD 音频,强制结束当前语音段,执行最终说话人重聚类,返回 is_final: true。 |
动态 VAD
动态 VAD 会根据当前语音段累计时长调整静音阈值:短句等待更长静音,避免切碎;长句更快切分,避免 ASR 输入过长。
流式封装默认阈值
| 累计语音时长 | 静音阈值 | 效果 |
| ≤ 5 秒 | 2.0 秒 | 短句不被过早切断。 |
| 5-10 秒 | 1.5 秒 | 正常对话分句。 |
| 10-15 秒 | 1.0 秒 | 长句开始收紧。 |
| 15-30 秒 | 0.8 秒 | 更快切分。 |
| 30-45 秒 | 0.4 秒 | 防止 ASR 段过长。 |
| > 45 秒 | 0.1 秒 | 强制切分。 |
fsmn-vad 原生默认阈值
| 累计语音时长 | 静音阈值 |
| ≤ 5 秒 | 800 ms |
| 5-10 秒 | 600 ms |
| 10-20 秒 | 500 ms |
| 20-30 秒 | 400 ms |
| > 30 秒 | 300 ms |
# 默认 dynamic_silence=True
model.generate(input="audio.wav")
# 关闭动态静音阈值
model.generate(input="audio.wav", dynamic_silence=False)
# 自定义 schedule: (累计时长上限ms, 静音阈值ms)
model.generate(input="audio.wav", silence_schedule=[
(5000, 1000),
(15000, 500),
(float("inf"), 200),
])
from funasr import AutoModel
from funasr.models.fsmn_vad_streaming.dynamic_vad import DynamicStreamingVAD
vad_model = AutoModel(model="fsmn-vad", device="cuda:0")
vad = DynamicStreamingVAD(vad_model)
for chunk in audio_stream:
segments = vad.feed(chunk)
for start_ms, end_ms in segments:
print(f"Speech: {start_ms}-{end_ms} ms")
final_segments = vad.finalize()
API 参考
AutoModelVLLM
| 参数 | 默认值 | 说明 |
model | - | ModelScope/Hugging Face 模型 id 或本地模型目录。 |
hub | "ms" | "ms"、"modelscope"、"hf" 或 "huggingface"。 |
device | "cuda:0" | PyTorch 音频 encoder 和 adaptor 运行设备。 |
dtype | "bf16" | "bf16"、"fp16" 或 "fp32"。 |
tensor_parallel_size | 1 | vLLM tensor parallel 使用的 GPU 数量。 |
gpu_memory_utilization | 0.8 | vLLM KV cache 使用的 GPU 显存比例。 |
max_model_len | 4096 | vLLM 最大序列长度。 |
generate()
| 参数 | 默认值 | 说明 |
inputs | - | 音频路径、路径列表、numpy array、tensor、wav.scp 或 JSONL。 |
language | None | 语种提示,例如 "中文"、"English"、"日本語"。 |
hotwords | None | 热词列表,会写入 ASR prompt。 |
itn | True | 是否进行逆文本正则化。 |
max_new_tokens | 512 | 每条音频最大生成 token 数。 |
temperature | 0.0 | 默认贪心解码。 |
repetition_penalty | 1.0 | vLLM generation 的重复惩罚参数。 |
返回格式:[{"key": str, "text": str, "timestamps": [...]}]。当模型包含可选 CTC decoder 和 tokenizer 时,会返回时间戳。
FAQ
- Q: 首次启动为什么慢?
- vLLM 需要初始化 KV cache 和 CUDA graph,FunASR 也可能需要从
model.pt 提取 LLM 权重,首次启动通常约 60-90 秒。
- Q: CUDA OOM 怎么办?
- 降低
gpu_memory_utilization,降低 max_model_len,或增加 tensor_parallel_size。
- Q: Paraformer 能用 vLLM 吗?
- 不能。Paraformer 是非自回归模型,不使用 vLLM KV-cache 解码。
- Q: WebSocket 服务和
streaming_generate() 如何选择?
- 生产实时 ASR 推荐 WebSocket 服务,它基于 VAD 自然端点切分;SDK 集成或 chunk 级演示可使用
streaming_generate()。
- Q: 浏览器无法访问远程服务器麦克风?
- Chrome 要求 HTTPS 或 localhost。可先执行
ssh -L 10095:localhost:10095 <server>,再从本机 localhost 打开客户端。