VibeVoice 两条推理路线:vLLM 插件还是 transformers
拿到 VibeVoice 的 ASR 模型之后,第一个要拍板的不是参数怎么调,而是从哪个入口进去。仓库里明摆着两条路:docs/vibevoice-vllm-asr.md 讲的 vLLM 插件部署,和 docs/vibevoice-asr.md 讲的 pip install -e . 之后直接跑 demo/ 下的脚本。这两条路不是「同一件事的两种包装」,它们在接入方式、依赖声明和暴露出来的能力上都不一样,选错了要回头重装环境。
下面只对照两边都白纸黑字写明的东西。运行速度、吞吐、显存占用、识别准确率这些,我们没有下载权重也没有跑过推理,一律不比。 仓库 docs/vibevoice-vllm-asr.md 里有一节 Performance Tips,那是仓库作者给的调参建议,不是我们的结论,本文只转述它调的是哪个参数、不转述效果。
接入方式:一边靠 entry point 注册,一边靠直接 import
vLLM 那条路的关键在 pyproject.toml 里的这一行声明:
[project.entry-points."vllm.general_plugins"]
vibevoice = "vllm_plugin:register_vibevoice"
装上 vibevoice 这个包之后,vLLM 会通过这个 entry point 自动调用 vllm_plugin/__init__.py 里的 register_vibevoice()。这个函数做的事情很直白,就是往几个注册表里塞东西:
AutoConfig.register("vibevoice", VibeVoiceConfig)
...
ModelRegistry.register_model("VibeVoice", VibeVoiceForCausalLM)
ModelRegistry.register_model("VibeVoiceForASRTraining", VibeVoiceForCausalLM)
它还把 VibeVoiceASRTextTokenizerFast 注册给 AutoTokenizer、把 Qwen2AudioProcessor 注册给 AutoProcessor。源码注释里专门标了一句,说这个 tokenizer 把 speech_start_id、speech_pad_id、speech_end_id 映射到与 PyTorch ASR 路径一致的特殊 token 上,并写明这一点即使在请求成功的情况下也会显著影响 ASR 质量——这是源码注释的自述,我们没有验证过。真正要记住的是:vLLM 这条路的正确性依赖于这次注册对齐,而注册是隐式发生的,ModelRegistry.register_model 里的架构名要和模型 config.json 的 architectures 对上(__init__.py 的注释里也这么写)。
transformers 那条路没有这层间接。demo/vibevoice_asr_inference_from_file.py 顶部直接 import 具体类:
from vibevoice.modular.modeling_vibevoice_asr import VibeVoiceASRForConditionalGeneration
from vibevoice.processor.vibevoice_asr_processor import VibeVoiceASRProcessor
加载时 processor 还带了一个容易被忽略的参数:
self.processor = VibeVoiceASRProcessor.from_pretrained(
model_path,
language_model_pretrained_name="Qwen/Qwen2.5-7B"
)
以上片段原样取自仓库文件。以上为按仓库代码中的接口语义组合的示例,未经实测,以仓库最新代码为准。
还有第三个入口容易和上面两个混淆:README 记载 2026-03-06 起 VibeVoice ASR 进入了 Transformers 发行版,对应模型卡是 microsoft/VibeVoice-ASR-HF,用的是上游 Transformers 里的 VibeVoiceAsrForConditionalGeneration(注意大小写与仓库内那个 VibeVoiceASRForConditionalGeneration 不同,抄错了会 import 失败),入口方法是 processor.apply_transcription_request(...)。这是第三条路,不是前两条的同义词。
依赖:这是本篇最会咬人的地方
仓库 pyproject.toml 声明 requires-python = ">=3.10",并把 transformers 钉在 "transformers>=4.51.3,<5.0.0";可选依赖 streamingtts 更进一步写成 transformers==4.51.3。而 VibeVoice-ASR-HF 的模型卡写明该模型自 Transformers v5.3.0 起可用,给的安装命令是 pip install "transformers>=5.3.0"。
把这两处放在一起看:仓库包声明的 transformers 上界与上游 Transformers 原生支持的下界互相排斥。 我们不推断哪边会先放宽,只提醒一句——如果你打算既用仓库里的 vibevoice 包、又用上游的原生类,按当前这两份声明就得分开两个虚拟环境。这是仓库当前的依赖声明,随版本可能变动。
vLLM 那条路的依赖是另一套。docs/vibevoice-vllm-asr.md 推荐直接用官方 vLLM 镜像起容器,文档里给的镜像标签是 vllm/vllm-openai:v0.14.1(这只是仓库文档里的示例值),入口是把仓库挂进 /app 再执行 python3 /app/vllm_plugin/scripts/start_server.py。这个启动脚本自己的 docstring 把流程列成五步:装系统依赖、装 VibeVoice 包、从 Hugging Face 下模型、生成 tokenizer 文件、起 vLLM 服务。对应代码里能看到 apt-get install -y ffmpeg libsndfile1、snapshot_download(model_id),以及调用 vllm_plugin.tools.generate_tokenizer_files 这个模块。
这里有一处仓库内部不一致值得记下来:start_server.py 的 install_vibevoice() 执行的是 pip install -e /app[vllm],而 pyproject.toml 的 [project.optional-dependencies] 里只定义了 streamingtts,没有名为 vllm 的 extra。两处摆在一起就是这样,我们不推断后果,只建议你装之前先自己看一眼当前版本的 pyproject.toml。
FFmpeg 是两条路都躲不掉的:vLLM 侧 vllm_plugin/inputs.py 用 load_audio_use_ffmpeg / load_audio_bytes_use_ffmpeg 解码;transformers 侧 vibevoice/processor/vibevoice_asr_processor.py 也先用 ffmpeg,失败才 warnings.warn 并回落到 soundfile。
Windows 侧要多想一步
docs/vibevoice-vllm-asr.md 给的全部是 Linux 风格的 docker 命令,挂载写成 -v $(pwd):/app,显卡透传写成 --gpus all。仓库里没有找到 Windows 原生(非容器)的部署说明。$(pwd) 是 POSIX shell 的展开语法,在 PowerShell 里不是同一回事——这属于通用 shell 常识,不是该项目的官方内容,照抄前请按自己的终端改写。
transformers 那条路对 Windows 读者反而更有依据可循。demo/vibevoice_asr_inference_from_file.py 的 --device 参数 choices 明确列出 cuda、cpu、mps、xpu、auto;--attn_implementation 取 auto 时,代码里写的是 CUDA 环境下尝试 import flash_attn,导入失败就打印一句提示并落到 sdpa,非 CUDA 设备直接用 sdpa;并且 mps、xpu、cpu 三种设备下 model_dtype 走 torch.float32,只有其余情况用 torch.bfloat16。这些是代码里写死的分支,不是我们对能不能跑的判断。
能力边界:文档与代码各自划在哪
长音频的硬闸门在 vLLM 这一侧是显式的。 vllm_plugin/inputs.py 里:
_MAX_AUDIO_DURATION = float(os.environ.get("VIBEVOICE_MAX_AUDIO_DURATION", "3660"))
超过这个秒数会直接 raise ValueError,报错文本里就提示你去调 VIBEVOICE_MAX_AUDIO_DURATION 或换短音频。这是仓库当前代码里的默认值,随版本可能变动;代码注释自述这个默认值对应模型的设计容量,并说明可以通过那个环境变量按自己的显卡情况调整。transformers 侧的 processor 则是另一种边界:__call__ 的 use_streaming 默认为 True,但代码里对时长小于 60 秒的音频会自动把它改回 False。
hotwords 的入口两边不在同一层。 transformers 侧是 processor 的参数:__call__ 与 _process_single_audio 都接受 context_info,拼出来的 user prompt 在 vibevoice/processor/vibevoice_asr_processor.py 里逐字是 This is a {audio_duration:.2f} seconds audio, with extra info: {context_info.strip()}——注意时长带两位小数的格式化、context_info 会先 strip(),你在别处自己拼这段文本时这两处得对齐。vLLM 侧没有这个参数,vllm_plugin/tests/test_api.py 是在客户端自己拼同样格式的 prompt 文本,再塞进 /v1/chat/completions 的消息里。这里还有一个坑:demo/vibevoice_asr_inference_from_file.py 调用 processor 时并没有传 context_info,也没有对应的命令行参数——想在 transformers 路线上用 hotwords,得走 demo/vibevoice_asr_gradio_demo.py、finetuning-asr/inference_lora.py,或者自己传参。
结构化输出的解析责任也不一样。 post_process_transcription 这个方法定义在 vibevoice/processor/vibevoice_asr_processor.py,全仓库调用它的只有三个 transformers 路线的脚本(两个 demo 加一个 LoRA 推理脚本)。vLLM 路线的测试脚本不调它:test_api.py 直接打印流式返回,test_api_auto_recover.py 的 docstring 写明它自己做重复循环检测、失败后按 temperature 递增重试并截断到最后一个完整段落边界。而 VibeVoice-ASR-HF 那条路把解析放进了 processor.decode 的 return_format 参数("parsed" / "transcription_only"),模型卡还自述解析失败时会原样返回生成结果。
多卡编排只在 vLLM 这一侧有。 start_server.py 的 --tp 与 --dp 分别是张量并行与数据并行,文档表格写明 --tp N 是把一个模型切到 N 张卡、--dp N 是起 N 个独立副本,并注明总 GPU 数 = dp × tp。--dp 大于 1 时脚本会 apt-get install nginx,用 least_conn 写一份反代配置把多个后端挂到同一个端口后面。transformers 那条路的 demo 脚本只有 --batch_size,仓库里没有找到对应的多副本编排说明。
微调只有一条路。 docs/vibevoice-asr.md 写明支持 LoRA 微调并指向 finetuning-asr/;vLLM 插件那份文档里我们没有找到与训练相关的说明,这一点不比。
决策路径
从你手里的活儿倒推,一般三步就够:
第一步,问你要不要给别人提供接口。如果目标是一个能被别的服务调用的 HTTP 端点,那就是 vLLM 这条路——它给的是 OpenAI 兼容的 /v1/chat/completions,音频以 data URL 形式放进 audio_url 内容块,服务端启动参数里还带了 --allowed-local-media-path。如果只是本机把几个文件转成带说话人和时间戳的结构化结果,走 transformers 那条路,直接跑 demo 脚本。
第二步,看你环境里的 transformers 版本被谁绑着。如果你的项目已经在上游 Transformers 5.x 上,那 VibeVoice-ASR-HF 那条原生路更省事;如果你要用仓库里的 finetuning-asr/,就得接受 <5.0.0 这个上界。这两个约束当前是互斥的,先看清楚再装。
第三步,看要不要多卡和 hotwords。多卡编排只有 vLLM 侧写明;hotwords 两边都有,但 transformers 侧要么自己传 context_info、要么用 gradio demo。
真正会咬到你的差异只有两个:依赖版本的互斥,和长音频上限的表现形式不同(vLLM 侧是一个可用环境变量改的显式 ValueError,transformers 侧则是 processor 里按时长切换 use_streaming 的隐式分支)。其余的差异,你在动手前把上面几个文件翻一遍就能自己确认。
本文依据 github.com/microsoft/VibeVoice 仓库与 Hugging Face 模型卡于 2026-08-18 的公开内容整理,
事实来自仓库内的文档与源码。我们没有下载权重、没有跑过推理、也没有做过训练,
因此不涉及显存占用、推理速度、识别准确率与音质的任何描述,也不与其它模型做比较或排名。
该项目持续更新,文中涉及的模块路径、配置字段与接口写法随版本变动,请以仓库最新内容为准。
仓库 README 的风险与限制一节写明:该模型仅供研究与开发用途, 未经进一步测试与开发不建议用于商业或真实场景,并特别提示了合成语音被用于伪造与虚假信息的风险。 使用合成语音时应遵守所在司法辖区的法律法规,并在分享 AI 生成内容时主动披露。
本文对照的是同一项目内的两种用法,依据均为上述仓库内容,不对两种用法做优劣排名, 选型结论只在仓库文档写明的能力边界内成立。
安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。