用 vLLM 跑 VibeVoice-ASR:官方文档给的完整路径

2026-08-18

如果你已经用仓库 demo/ 里的脚本跑通过单文件转写,接下来大概率会碰到同一个问题:脚本是一次一个文件的形态,进程起来就为了转一段音频,转完退出。要把它变成一个能被别的服务调用、能并发接单的接口,靠 demo 脚本改是绕不开重复造轮子的。

VibeVoice 仓库为这件事单独准备了一条路径,文档在 docs/vibevoice-vllm-asr.md,代码在 vllm_plugin/ 目录下。这篇就沿着这两处,把安装、加载、请求形态和文档写明的限制过一遍。文中所有事实都来自 github.com/microsoft/VibeVoice 仓库内的文档与源码,我们没有下载权重也没有起过服务。

一、这条路径解决的是什么

docs/vibevoice-vllm-asr.md 开头写明的定位是:把 VibeVoice ASR 模型部署成 API 服务,对外暴露 OpenAI 兼容的 /v1/chat/completions 端点,支持流式返回。

它对使用者最实际的一点写在特性列表里:Plugin Architecture — No vLLM source code modification required,也就是不需要改 vLLM 源码。这句话在 pyproject.toml 里能找到对应的实现:

[project.entry-points."vllm.general_plugins"]
vibevoice = "vllm_plugin:register_vibevoice"

vLLM 通过 vllm.general_plugins 这个 entry point 组来发现插件,只要 vibevoice 这个包被 pip 装进环境,vLLM 启动时就会调用 vllm_plugin 包里的 register_vibevoice()vllm_plugin/__init__.py 的模块注释也是这么写的:这个函数由 vLLM 通过 entry point 机制自动调用,确保它在所有 vLLM 进程里都跑一遍。

理解这一点,后面很多现象才有落点——比如为什么排查清单里会有一条「验证 entry point」。

二、前置条件

这一段最容易被跳过,但它决定了你要不要换一台机器。

运行形态是容器。 文档给的安装方式只有一种,标注为 Recommended:用官方 vLLM Docker 镜像。命令里带 --gpus all,也就是需要一块(或多块)能被 Docker 直通的 NVIDIA GPU,宿主机要装好 NVIDIA Container Toolkit。仓库里我们没有找到不用容器、直接在宿主机装 vLLM 跑这个插件的官方步骤。

Python 版本。 pyproject.toml 写明 requires-python = ">=3.10"。不过既然走官方镜像,这一条更多是给想自己搭环境的人看的。

FFmpeg 是硬依赖,不是可选项。 音频解码整条链路都压在 FFmpeg 上:vllm_plugin/model.py 里定义了 _PatchedAudioMediaIO,直接继承并替换掉 vLLM 自己的 AudioMediaIOload_bytes / load_base64 / load_file 三个方法全部改走 vibevoice.processor.audio_utils 里的 load_audio_bytes_use_ffmpegload_audio_use_ffmpeg。所以文档 Troubleshooting 里「Audio decoding failed」那条的第一步是让你确认 ffmpeg -version。启动脚本会自己 apt-get install -y ffmpeg libsndfile1,前提是容器能连到 apt 源。

Windows 侧要注意的地方。 文档给的命令是 Linux/macOS shell 写法,里面有一处 -v $(pwd):/app$(pwd) 是 POSIX shell 语法,在 PowerShell 里不会被展开成当前目录,这一处你需要换成 PowerShell 自己的写法或写绝对路径;在 Git Bash 里则要注意路径会被转换成 Windows 形式。Windows 上要有 --gpus 支持,通常意味着走 Docker Desktop 的 WSL2 后端并配好 GPU 直通。仓库文档里没有针对 Windows 的专门说明,上面这段属于通用做法,不是该项目的官方内容。

三、启动脚本每一步在改什么

文档给的启动命令是把一个脚本塞进官方镜像跑:

docker run -d --gpus all --name vibevoice-vllm \
  --ipc=host \
  -p 8000:8000 \
  -e VIBEVOICE_FFMPEG_MAX_CONCURRENCY=64 \
  -e PYTORCH_ALLOC_CONF=expandable_segments:True \
  -v $(pwd):/app \
  -w /app \
  --entrypoint bash \
  vllm/vllm-openai:v0.14.1 \
  -c "python3 /app/vllm_plugin/scripts/start_server.py"

这是文档里原样给出的命令,其中的镜像 tag 与两个环境变量取值是仓库当前文档写的值,随版本可能变动。

真正干活的是 vllm_plugin/scripts/start_server.py。它的 main() 按顺序做五件事,每一件都在改环境里的某个具体东西:

  1. install_system_deps()——apt-get install -y ffmpeg libsndfile1,补上镜像里没有的音频依赖。--skip-deps 可以跳过。
  2. install_vibevoice()——把挂载进来的仓库目录以可编辑模式装进 Python 环境。这一步同时也是插件生效的那一步:完成安装后 entry point 才会注册到环境里。
  3. download_model(args.model)——调 huggingface_hub.snapshot_download,默认 model id 是 microsoft/VibeVoice-ASR。文档提醒模型会落到容器内的 Hugging Face 缓存目录,容器删了就得重下。
  4. generate_tokenizer(model_path)——调 vllm_plugin.tools.generate_tokenizer_files,把生成的文件写进模型目录。--skip-tokenizer 可以跳过。
  5. 起服务:data_parallel_size > 1 时走 start_dp_server(),否则走 start_vllm_server(),后者用 os.execvp 直接把当前进程替换成 vLLM。

第 4 步值得单独说。generate_tokenizer_files.py 的模块注释写明它从 Qwen2 base tokenizer 下载后打补丁(脚本里 DEFAULT_QWEN_MODEL 取的是 Qwen/Qwen2.5-7B,注释说明是因为扩展 token 在这一版里齐全;这是仓库当前代码里的默认值,随版本可能变动),加上 VibeVoice 用的音频 token(<|AUDIO|><|audio_bos|><|audio_eos|>)并改写 chat template。改写的关键在模板里那句判断:part['type'] == 'audio' or part['type'] == 'audio_url' 时输出 <|AUDIO|>。这就是「请求里塞一段音频」最终变成模型输入的那一步。

register_vibevoice() 注册的四样东西是:VibeVoiceConfig 注册进 AutoConfig(键名 vibevoice)、VibeVoiceASRTextTokenizerFast 注册进 AutoTokenizerQwen2AudioProcessor 注册进 AutoProcessor、以及把 VibeVoiceForCausalLMVibeVoiceVibeVoiceForASRTraining 两个架构名注册进 vLLM 的 ModelRegistry。代码注释特意强调架构名必须与 config.jsonarchitectures 列表对得上,并且 tokenizer 那一段带了一条大写的 IMPORTANT:它把 speech_start_idspeech_pad_idspeech_end_id 映射到三个 Qwen 扩展 token,注释自述这会显著影响 ASR 质量——即使请求本身返回成功。

四、请求长什么样

vllm_plugin/tests/test_api.py 是文档指定的测试脚本,也是请求形态最直接的出处。它拼出来的 payload 结构是这样:

payload = {
    "model": "vibevoice",
    "messages": [
        {"role": "system", "content": "..."},
        {"role": "user", "content": [
            {"type": "audio_url", "audio_url": {"url": data_url}},
            {"type": "text", "text": prompt_text}
        ]}
    ],
    "stream": True,
}

以上为按仓库代码中的接口语义组合的示例,未经实测,以仓库最新代码为准。

几处要专门指出来:model 字段的值是 vibevoice,对应启动命令里的 --served-model-name vibevoice,改了一边另一边也要改。data_urldata:{mime};base64,{audio_b64},也就是音频整个 base64 塞进请求体。脚本里的 max_tokenstemperaturetop_p 是仓库示例脚本中的取值,属于示例值,不是推荐配置。

hotwords 的实现方式尤其反直觉:它不是一个独立的 API 字段。脚本的文档字符串写得很清楚——hotwords 以 with extra info: {hotwords} 的形式拼进 prompt 文本里。同时 prompt 里还会带上音频时长(脚本用 ffprobe 读出来)和要求返回的字段名 Start timeEnd timeSpeaker IDContent。所以你如果自己写客户端,得照着这个 prompt 模板拼,而不是去找参数。

五、边界:文档与代码写明的限制

音频时长有上限,而且可以被环境变量改。 vllm_plugin/inputs.py 里定义了 _MAX_AUDIO_DURATION,取自环境变量 VIBEVOICE_MAX_AUDIO_DURATION,仓库当前代码里的默认值是 3660(秒),随版本可能变动。代码注释自述这个默认值对应模型的设计容量,并写明可以通过那个环境变量下调;校验发生在音频转成 tensor 之前,超过就直接抛 ValueError,代码里对这一步的注释是「提前拦住」而不是等到运行中才失败。

文件必须在挂载目录里。 _build_vllm_cmd() 拼给 vLLM 的参数里带了 --allowed-local-media-path /app,文档也单独提醒:音频或视频文件必须放在容器内挂载的 /app 目录下。

长音频可能进重复循环。 仓库为此专门提供了第二个测试脚本 test_api_auto_recover.py,其文档字符串把恢复策略写在明处:先贪心解码,检测到循环则用更高的 temperature 重试,有重试上限,并截断到最后一个完整片段边界。也就是说,这个问题在仓库层面是被承认存在的,处理方式是客户端侧的重试与截断,不是服务端自动兜住。

多卡的两个方向不要混。 文档的表格写明 --tp N 是把一个模型切到 N 张卡上,--dp N 是跑 N 份独立副本。start_dp_server() 的实现是:装 nginx,给每个副本分配 frontend_port + 100 + i 的内部端口,写一份 least_conn 的 upstream 配置,nginx worker 数默认取副本数的两倍。文档明确写了总卡数 = dp × tp,代码里也有对应的 assert。

两处源与源之间对不上的地方,说完就停,不做解释:一是 install_vibevoice() 执行的是 pip install -e /app[vllm],而 pyproject.toml[project.optional-dependencies] 里我们只找到 streamingtts 这一个 extra,没有 vllm;二是 vllm_plugin/model.py 的注释引用了 docs/max_num_batched_tokens_issue.md,但仓库 docs/ 目录下我们没有找到这个文件。

六、怎么验证配对了

按仓库文档给的顺序,有几个互相独立的检查点:

  • 服务起没起来docker logs -f vibevoice-vllm。DP 模式下脚本会逐个探测各后端的 /v1/models,就绪时打印对应端口,失败也会打印,日志里能直接看出是哪个副本没起来。
  • 插件加载没加载:文档 Troubleshooting 的「Plugin not loaded」给了两条命令,pip show vibevoice 确认包在,pip show -f vibevoice | grep entry 确认 entry point 在。这两条对应的正是第一节讲的注册机制。
  • 模型目录完整没完整:「Model not found」那条要求确认模型路径下有 config.json 与权重文件,并且 tokenizer 文件已生成——也就是第三节的第 4 步没被跳过。
  • 端到端docker exec -it vibevoice-vllm python3 vllm_plugin/tests/test_api.py /app/audio.wav,文档同时给了带 --hotwords 的形式。

最后一句提醒:这条链路在启动时会向容器内 apt-get installpip install,并会从 Hugging Face 拉取权重,属于在你的机器上执行外部程序与下载外部内容。上线前请自己确认网络出口与镜像来源符合你所在组织的要求。


本文依据 github.com/microsoft/VibeVoice 仓库与 Hugging Face 模型卡于 2026-08-18 的公开内容整理, 事实来自仓库内的文档与源码。我们没有下载权重、没有跑过推理、也没有做过训练, 因此不涉及显存占用、推理速度、识别准确率与音质的任何描述,也不与其它模型做比较或排名。 该项目持续更新,文中涉及的模块路径、配置字段与接口写法随版本变动,请以仓库最新内容为准。

仓库 README 的风险与限制一节写明:该模型仅供研究与开发用途, 未经进一步测试与开发不建议用于商业或真实场景,并特别提示了合成语音被用于伪造与虚假信息的风险。 使用合成语音时应遵守所在司法辖区的法律法规,并在分享 AI 生成内容时主动披露。

安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。