ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

FunASR 中 Qwen3-ASR 流式 WebSocket 服务实战:VAD 职责辨析、vLLM 版本锁定与部署踩坑指南

2026/9/13 19:32:17 拓冰建站 浏览量
FunASR 中 Qwen3-ASR 流式 WebSocket 服务实战:VAD 职责辨析、vLLM 版本锁定与部署踩坑指南 FunASR 中 Qwen3-ASR 流式 WebSocket 服务实战VAD 职责辨析、vLLM 版本锁定与部署踩坑指南【免费下载链接】FunASROpen-source speech recognition toolkit for training, inference, streaming ASR, VAD, punctuation, speaker diarization pipelines, and OpenAI-compatible/MCP serving.项目地址: https://gitcode.com/GitHub_Trending/fun/FunASR导读本文围绕 FunASR 仓库 examples/industrial_data_pretraining/qwen3_asr 下的serve_qwen3_asr_ws.py流式 WebSocket 服务及其配套说明文档展开系统讲解 Qwen3-ASR 增量式流式 API 在使用中的核心难点VAD 在 ASR 中的两种完全不同职责、vLLM 版本为何必须锁定 0.14、模型权重的离线下载链路以及 tokenizer 与启动日志中几类看起来吓人、实际无害的警告。读完本文你将能正确部署 Qwen3-ASR 流式 WebSocket 服务、规避版本与下载陷阱并理解服务端自动断句在开源流式 API 中应由哪一层实现。1. 服务定位把 Qwen3-ASR 原生流式 API 包装成 WebSocket 服务serve_qwen3_asr_ws.py的核心作用是把官方example_qwen3_asr_vllm_streaming.py的原生流式 APIinit_streaming_state/streaming_transcribe/finish_streaming_transcribe包装成一个 WebSocket 服务协议与 Fun-ASR-Nano 的serve_realtime_ws.py保持一致因此可以直接复用同一个bench_streaming_ws.py压测脚本进行横向对比。从 源码 可以看到其完整协议客户端连接ws://host:port客户端发送文本START服务端回{event: started}客户端持续发送二进制 int16 PCM 块16 kHz 单声道服务端随转写增长发送{partial: 当前文本}客户端发送文本STOP服务端回{is_final: true, sentences: [{text: 最终文本}]}随后发送{event: stopped}。架构上它刻意对齐serve_realtime_ws.py单 asyncio 事件循环streaming_transcribe/finish_streaming_transcribe为同步调用、阻塞整个循环——这样压测出的并发特性才能与 Fun-ASR-Nano 那条路径同口径可比。生产扩容同样依赖多进程 CUDA MPS nginx方案详见 vllm_guide.md §6.7 中关于单进程先压满再复制的扩容建议。启动方式源码头注释明确给出pip install -U qwen-asr[vllm]0.0.6 transformers4.57.6 websockets numpy python serve_qwen3_asr_ws.py --port 10095 --gpu-memory-utilization 0.8其中--chunk-size-sec为可选参数用于控制流式块大小默认 2.0。几个关键实现细节值得注意全局只加载一次模型asr None在模块级初始化所有连接共用同一模型实例每个连接各自持有独立的 streaming statehandle_client中每次收到START才调用init_streaming_state创建新 state。int16 PCM 转 float32int16_pcm_to_float32将 bench 发来的 int16 小端 PCM 除以 32768 转为 Qwen3-ASR 的streaming_transcribe期望的[-1, 1)浮点波形。partial 去重只在state.text与上一次发送的文本不同时才下发{partial: ...}避免刷屏且不影响 bench 的首词延迟统计口径。max_new_tokens 固定 32流式用小值与官方 example 一致。websocket 日志降噪logging.getLogger(websockets).setLevel(logging.WARNING)压测时不再刷 connection open/closed INFO 日志。配套的契约测试 tests/test_qwen3_asr_ws_example.py 还验证了两件事说明文档不得残留以下是我的个人理解这类审阅占位语handle_client必须捕获通用Exception并调用logging.exception记录防止连接异常静默吞掉。2. VAD 的两种用途切段 VAD 与端点 VAD切勿混为一谈VAD语音活动检测在 ASR 中其实有两种完全不同的用途容易混为一谈这是理解 Qwen3-ASR 流式 API 设计的第一道门槛。2.1 A 类切段用的 VAD给非流式 encoder 喂分段像 Fun-ASR-Nano 这类模型的 encoder 是非流式的一次必须看到完整一段音频因此必须靠 VAD 把连续音频切成一句句每句整体编码、整体解码。这种 VAD 对这类模型是技术必需的——不切段就无法编码。2.2 B 类端点/轮次检测用的 VAD判断这一轮说完了没检测说话人停顿例如静音 800ms判定一句话/一轮结束从而触发锁定文本 / 发 is_final / 该回应了。这是产品行为层面的需求与 encoder 是否流式无关。2.3 Qwen3-ASR 开源流式 API 需要哪一种对本服务使用的qwen-asr[vllm]的init_streaming_state/streaming_transcribe而言不需要 A 类切段VAD。它是增量式流式——每次只吃新增的一小段音频状态向前滚动、连续转写不存在先切句再解码。哪些字定了、哪些会变由unfixed_chunk_num/unfixed_token_num两个参数表达尾部 N 个 chunk/token 算未定、可能被后续音频修正其余视为已确认。这相当于内置的 partial/锁定机制取代了 A 类 VAD 的切段职责。所以你在开源流式 API 的代码里搜不到vad——因为它这一层根本不切段。仍然需要 B 类端点VAD只是开源 API 自己不带。自动判断用户停顿 这一轮结束这件事streaming_transcribe本身不管。商用服务是在 ASR之外包一层server_vad来做即turn_detection。本服务目前用客户端显式发STOP来代替端点判断bench 里音频放完即发 STOP。若要在真实场景自动断句/断轮需要自己在本服务之外接一个 VAD / 端点检测角色等同商用的 server_vad而不是去 Qwen3-ASR 内部找——它的开源流式 API 不含这一层。一句话总结Qwen3-ASR 增量流式省掉了切段 VADA但**端点/轮次 VADB这个职责依然存在**——商用版用server_vad实现、本服务用手动STOP代替。两者并不矛盾。2.4 官方佐证商用 Qwen-ASR-Realtime 的VAD 模式 / Manual 模式阿里云百炼的实时语音识别Qwen-ASR-Realtime官方文档明确把断句由谁做分成两种模式本质就是session.turn_detection开还是关VAD 模式默认turn_detection配置为 server_vad服务端自动检测语音起点/终点来断句客户端只管持续发音频流服务端在检测到一句话结束时自动返回最终结果。流程中服务端会发input_audio_buffer.speech_started/speech_stopped等事件——这正是 B 类端点 VAD由服务端那一层server_vad实现不是ASR 内核在切段。商用配置示例turn_detection: { type: server_vad, threshold: 0.2, silence_duration_ms: 800 }Manual 模式turn_detection设为 null由客户端控制断句——发完一整句音频后客户端发input_audio_buffer.commit通知服务端边界。适用于客户端能明确判断语句边界的场景如按住说话、聊天发语音。对应关系本服务serve_qwen3_asr_ws.py用客户端显式发STOP标记一轮结束等价于商用的Manual 模式turn_detectionnull客户端控制边界。若要做成服务端自动断句就是去实现商用 VAD 模式的那一层端点检测server_vad加在本服务的增量转写之外而不是在 Qwen3-ASR 转写内核里找。2.5chunk-size-sec控制流式块大小--chunk-size-sec控制流式块大小默认 2.0最终传入init_streaming_state的chunk_size_sec参数。值越小出字越快/越勤但并发开销越大。配套说明给出了实测结论在 L20 上29 秒音频 48 路并发时1.0全部失败、2.0全部通过。因此并发压力大的场景不宜盲目调小该值需要在出字时延与并发容量之间取平衡。3. 必须用 vllm 0.14不要用 0.19rope_scaling / thinker_config 警告本服务需要 vllm 加速即安装qwen-asr[vllm]它锁定vllm0.14.0同目录下的 transcribe_vllm_offline_notes.md 也强调建议使用独立环境因为该依赖被固定。若换成更新的 vllm 版本如 0.19.x启动会出现Unrecognized keys in rope_scaling for rope_typedefault: {mrope_section, mrope_interleaved, interleaved} thinker_config is None. Initializing thinker model with default values3.1 根因vllm 把rope_type从mrope改写为defaultvllm 在 0.14 → 0.19 之间注意 transformers 两版都是 4.57.6可排除其影响config 解析里的patch_rope_scaling_dict会把rope_type从mrope改写成default——它把 mrope 当作 legacy、假设由 vllm 内部消化mrope_section等字段elif rope_scaling[rope_type] mrope: assert mrope_section in rope_scaling rope_scaling[rope_type] default # ← 改写但 Qwen3-ASR 自带的Qwen3ASRThinkerTextRotaryEmbedding期望从rope_scaling里读到mrope才走多模态 RoPE 分支self.rope_type config.rope_scaling.get(rope_type, default)被 vllm 改写成default后它走了普通 RoPEmrope_section/mrope_interleaved/interleaved这几个键没人认领 → 打印 Unrecognized keys 警告且音频/文本的多模态位置编码退化。thinker_config is None那条同源0.19 的加载路径没有正确解析 Qwen3-ASR 的 thinker 子配置回退到默认参数。3.2 影响与抉择在 0.19 上服务能起、也能出字两条是 WARNING/INFO 而非 ERROR抽查几条转写也看着正常但位置编码退化对长音频/复杂内容可能有害且未做 CER 量化对比无法判定等价。保守起见固定使用qwen-asr[vllm]自带的vllm0.14.0。3.3 vllm 加速必须用 Qwen3ASRModel而非 AutoModelVLLMfunasr 的AutoModelVLLM不能加速 Qwen3-ASR必须使用from qwen_asr import Qwen3ASRModel这一条解答了仓库 issue #3026 中提出的问题。离线示例 transcribe_vllm_offline.py 同样直接使用Qwen3ASRModel.LLM(...)作为后端不经过AutoModelVLLM——后者服务于 FunASR 自有模型当前不支持 Qwen3-ASR。3.4 关于模型下载仅设 VLLM_USE_MODELSCOPE 不够官方推荐做法如果运行环境不能在线下载、或无法访问 Hugging Face先手动把权重下载到本地目录再把本地路径传给--model# ModelScope国内推荐 pip install -U modelscope modelscope download --model Qwen/Qwen3-ASR-1.7B --local_dir ./Qwen3-ASR-1.7B # 启动时指定本地模型目录 python serve_qwen3_asr_ws.py --model ./Qwen3-ASR-1.7B ...注意仅设置VLLM_USE_MODELSCOPETrue并安装modelscope只能接管一部分下载流程。vLLM 会从 ModelScope 拉取 config、tokenizer、merges、vocab、model.safetensors.index.json等文件但真正的权重model.safetensors仍可能回到huggingface.co拉取。这是 Qwen-ASR 当前下载链路的一个已知问题实测日志形态如下Downloading Model from https://www.modelscope.cn to directory: /home/vllm/.cache/modelscope/hub/models/Qwen/Qwen3-ASR-1.7B 2026-06-28 10:15:56,301 - modelscope - INFO - Got 10 files, start to download ... ... INFO 06-28 10:15:59 [model.py:530] Resolved architecture: Qwen3ASRForConditionalGeneration (MaxRetryError(HTTPSConnectionPool(hosthuggingface.co, port443): Max retries exceeded with url: /Qwen/Qwen3-ASR-1.7B/resolve/main/model.safetensors ...因此最稳妥的做法仍是提前用modelscope download将完整权重落盘到本地目录再以本地路径启动而不是依赖环境变量接管全部下载。4. tokenizer 的fix_mistral_regex警告无害可忽略启动时可能出现The tokenizer you are loading from .../Qwen3-ASR-1.7B with an incorrect regex pattern ... This will lead to incorrect tokenization. You should set the fix_mistral_regexTrue flag when loading this tokenizer to fix this issue.原因Qwen3-ASR 的 tokenizer 沿用了一类带已知 regex 问题的分词器实现底层库检测到该 regex 模式后给出提醒建议加fix_mistral_regexTrue修正切分。本服务通过 qwen-asr 的高层 API 加载模型、并不直接构造 tokenizer没有暴露这个开关所以这条提醒按原样打印。影响实测对中文 ASR 转写结果无可见影响抽查多条转写正常未做 CER 量化。该 regex 修正主要影响某些特殊 token 的边界切分对语音转写路径未观察到差异。属于提醒级噪音可忽略。若要彻底消除需在更底层自行加载 tokenizer 时传fix_mistral_regexTrue但 qwen-asr 高层 API 当前不直接支持且无实测必要。5. 顺带另外两条启动日志均无害Error retrieving safetensors: Repo id must be in the form ...把本地模型路径当成 HF 仓库 id 去查线上元数据失败后重试 2 次、回退本地加载不影响功能。可设环境变量HF_HUB_OFFLINE1消除。Downcasting torch.float32 to torch.bfloat16权重以 fp32 存、按 bf16 加载正常省显存/提速。bf16 与 fp32 指数位同宽精度几乎无损。这是 INFO 不是错误。6. 部署与压测实践从单进程到多进程扩容理解了上述踩坑点之后部署路径就清晰了第一步搭建独立环境并锁定版本参考 transcribe_vllm_offline_notes.md 的做法python -m venv .venv-qwen3-vllm source .venv-qwen3-vllm/bin/activate pip install -U qwen-asr[vllm]0.0.6 transformers4.57.6 websockets numpy第二步启动服务并验证协议python examples/industrial_data_pretraining/qwen3_asr/serve_qwen3_asr_ws.py \ --port 10095 --gpu-memory-utilization 0.8第三步压测。服务协议与 Fun-ASR-Nano 的serve_realtime_ws.py一致可复用 realtime_ws_benchmark.md 中描述的压测方法与指标口径如first_update_ms、final_after_stop_ms、client_response_lag_ms等输入须为 16 kHz 单声道 PCM16 WAV。注意单循环同步转写的架构下chunk-size-sec调小会显著放大并发开销前述 L20 实测29 秒音频 48 路并发1.0 全失败、2.0 全通过压测时应如实记录该参数。第四步扩容。按照 vllm_guide.md §6.7 的实践一个进程是第一个扩容单元先用内置批处理路径压满单进程达到 GPU、CPU 或尾延迟上限再增加副本单卡上多个进程共享同一 GPU 时用 CUDA MPS如多个进程都指向CUDA_VISIBLE_DEVICES0或按卡拆分进程对外用 nginx 做负载均衡。每个额外进程都会重复占用模型显存并可能削弱批处理机会因此支持 N 路连接没有普适数字必须以自己的真实流量形态实测为准。7. 总结三类坑的本质与处理策略现象本质处理方式开源流式 API 里找不到 VAD增量式流式自带 partial/锁定机制省掉了切段 VAD端点 VAD 属于产品层职责自动断句需在本服务之外接 server_vad 等效层当前用手动STOP代替vllm 0.19 出现 rope_scaling / thinker_config 警告vllm 把mrope改写成default多模态位置编码退化固定vllm0.14.0qwen-asr[vllm]自带VLLM_USE_MODELSCOPETrue仍连 huggingface.co下载链路只接管部分文件权重文件仍回退线上用modelscope download提前落盘--model指向本地目录fix_mistral_regex与两条启动日志提醒级/INFO 噪音可忽略HF_HUB_OFFLINE1可消除其中一条核心结论Qwen3-ASR 的增量流式 API 把切段职责交给了内置的unfixed_chunk_num/unfixed_token_num机制把端点检测留给了上层使用它的 WebSocket 服务时版本锁定vllm 0.14与权重本地化是保证转写质量与离线可部署性的两个关键前提。【免费下载链接】FunASROpen-source speech recognition toolkit for training, inference, streaming ASR, VAD, punctuation, speaker diarization pipelines, and OpenAI-compatible/MCP serving.项目地址: https://gitcode.com/GitHub_Trending/fun/FunASR创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考