Qwen3-TTS 装不上:那个 wheel 默认盯的是 CUDA 12.8

2026-08-09

speech-to-speech 的 README 在 Installation 一节里专门开了个小标题,讲两个安装期的坑。一个开源项目愿意把「装不上」写进正门口的文档,通常意味着这事儿踩的人不少。第一个坑就是本文的主角:默认那条 TTS 路径,依赖的预编译 wheel 是盯着 CUDA 12.8 编的。

先说清楚本文的边界:我们只读了仓库文件与 README,没有 pip install 过它,没有启动过服务,也没有对着麦克风说过一句话。所以下面不会出现任何报错原文、日志行、界面提示——那些我们没有依据,编一个出来就是害人。本文给的是判定动作文档语义给出的处置,报错长什么样以你自己终端里的输出为准。(顺带一句:该仓库 2026-08-09 的快照 star 是 11893,这个数字只说明关注度,跟你这台机器装不装得上没有任何关系。)

一、现象长什么样

典型链路是这样的:你按 Quickstart 敲了 pip install speech-to-speech,然后 speech-to-speech serve,结果卡在 TTS 这一环——要么装的时候就在 qwentts-cpp-python 这个包上失败,要么装完了、服务起不来或起来后合成那一段不工作。

要理解为什么偏偏是 TTS,得先知道默认配置到底选了谁。README 给出的等价完整命令里,跟 TTS 有关的是这几行:

speech-to-speech serve \
    --tts qwen3 \
    --qwen3_tts_model_name Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice \
    --qwen3_tts_backend ggml

也就是说,默认 TTS 是 Qwen3-TTS,非 macOS 平台上默认后端是 ggml。而 README 明说:在 Linux 上,Qwen3-TTS 的 GGML 后端来自 faster-qwen3-tts[ggml],它在 PyPI 上的默认 qwentts-cpp-python wheel 面向 CUDA 12.8。如果你机器上没有该 wheel 期望的 CUDA 12 运行时,这条路就走不通。

这个坑之所以隐蔽,是因为它藏在两层默认值后面:你没写 --tts,所以你不知道自己选了 Qwen3-TTS;你没写 --qwen3_tts_backend,所以你不知道自己选了 GGML。链条上没有任何一步是你主动做的决定。

二、怎么确认是这个问题

三个可执行的判定动作,从便宜到麻烦:

动作一:确认你确实站在这条默认路径上。speech-to-speech serve -h,看 --tts--qwen3_tts_backend 当前的默认取值。这里有个 README 特意讲过的用法:CLI 只为选中的后端构造配置,所以想看另一种组合的参数,得把选择器放在 -h 前面,例如 speech-to-speech serve --stt mlx-audio-whisper -h。Qwen3-TTS 本身是默认 TTS,所以它那 23 个参数在裸 serve -h 里就能看到;但你要横向对照别家(比如 --tts pocket)的专属参数,就得先把选择器写上去让它被选中,否则那一堆参数压根不会出现在帮助里。

动作二:确认你装的是哪个 wheel。 用 pip 查一下 qwentts-cpp-python 的已装版本,看版本号后面带不带本地标签。README 给出的匹配 wheel 版本长这样:0.3.1+cu1300.3.1+cu1240.3.1+cpu。如果你装到的是 PyPI 上不带本地标签的那个默认 wheel,它就是面向 CUDA 12.8 的那一份。这一步几乎是一锤定音的:版本号自己会说话。

动作三:确认本机 CUDA 运行时的版本。 查 CUDA 运行时版本属于通用运维动作,不是该项目文档的内容,各平台方法不同,按你自己的习惯来。判定标准很简单:机器上有没有那个 wheel 期望的 CUDA 12 运行时。

如果三条对上了——默认 TTS + 默认 GGML 后端 + 不带本地标签的 wheel + 手上不是 CUDA 12 运行时,那基本就是它。

三、文档语义给出的处置

README 给了四类出路,选哪一条取决于你到底想要什么。

处置 A:先装匹配的 wheel,再装 speech-to-speech。 这是官方给的正解,命令原样如下:

# CUDA 13.x
pip install "qwentts-cpp-python==0.3.1+cu130" \
  -f https://huggingface.co/datasets/andito/qwentts-cpp-python-wheels/tree/main/whl/cu130

# CUDA 12.4
pip install "qwentts-cpp-python==0.3.1+cu124" \
  -f https://huggingface.co/datasets/andito/qwentts-cpp-python-wheels/tree/main/whl/cu124

# 仅 CPU 兜底
pip install "qwentts-cpp-python==0.3.1+cpu" \
  -f https://huggingface.co/datasets/andito/qwentts-cpp-python-wheels/tree/main/whl/cpu
pip install speech-to-speech

顺序是文档写死的:先 wheel,后主包。别反过来。至于反过来会发生什么,我们没有依据下结论,就别去试探了。三行 CUDA 版本号(+cu130 / +cu124 / +cpu)请原样抄,不要自己「换算」成别的写法。

处置 B:换掉 GGML 后端。 README 写得很直白:想改用之前那套 CUDA-graphs 实现,传 --qwen3_tts_backend torch。这个参数的取值只有 ggmltorch 两个。

处置 C:换掉 Qwen3-TTS。 TTS 是可替换的一级,仓库支持矩阵里还有几家:Kokoro-82M(非 macOS 需要 kokoro extra,macOS 内置)、Pocket TTS(pocket extra,--pocket_tts_device 默认就是 cpu)、ChatTTS(chattts extra)、MMS TTS(内置)。装法就是 pip extras:

pip install "speech-to-speech[kokoro]"
pip install "speech-to-speech[pocket]"

处置 D:交给容器。 README 说装好 NVIDIA Container Toolkit 后直接:

docker compose up

compose 文件会启动一个跑 Gemma 4 的 llama.cpp 服务和实时服务,暴露端口 80808765。这条路的价值在于把 CUDA 依赖的匹配问题挪进镜像里,不用在宿主机上一层层对版本。

还有一点得说明白:--qwen3_tts_device(默认 cuda,可取 cuda / cpu / mps / auto)和你装的是哪个 wheel,是两个层面的开关,别混着理解。装 +cpu 的 wheel 和把 device 设成 cpu 不是同一件事,两者的组合行为我们没有依据,以官方文档为准。

四、处置后怎么验证

第一步,回到动作二。 再查一次 qwentts-cpp-python 的版本号,确认它现在带着你想要的本地标签(+cu130 / +cu124 / +cpu)。这是最直接的证据。

第二步,用 -h 确认参数落位。 再跑一次 speech-to-speech serve -h,看 --qwen3_tts_backend 的取值是不是你要的那个。这里有一条极其容易吃亏的行为,README 单独讲过:CLI 只为选中的后端构造配置,未激活后端的已知选项仍然被接受,但会带警告地忽略;JSON 配置同理,多余的非激活后端键也会被忽略。翻译成人话就是——你把后端专属参数写错了,程序不会报错停下,只会警告一句然后照跑。所以「我明明加了参数」从来不等于「参数生效了」,得去 -h 和警告输出里找证据。

第三步,把链路走一遍。 服务默认地址是 ws://localhost:8765/v1/realtime,仓库自带客户端可以连上去:

speech-to-speech talk --url ws://127.0.0.1:8765/v1/realtime

验证的目标是链路能不能建立、TTS 这一环还报不报错,不是评判音质——我们没有听过一秒钟输出,也不建议你把主观听感当成安装成功的判据。

顺带一条安全提醒serve 默认绑定 127.0.0.1,验证阶段千万别顺手加 --host 0.0.0.0。该参数的官方帮助原文说得很清楚,传 0.0.0.0 是把未鉴权的 API 暴露到网络上;服务端自身不认证、不限流,只应在受信网络内或放在网关之后启用。这不是「配好就安全了」的事,请结合自身环境评估。

五、什么情况说明不是这个原因

这一节比前面几节更值钱。以下任一条成立,你就该去查别的地方,别在 wheel 上耗着:

你在 Apple Silicon 上。 macOS 走的是 mlx-audio--qwen3_tts_backend 的帮助文本明说:在 Apple Silicon 上自动选择 mlx-audio,该选项被忽略。GGML wheel 压根不参与,这个坑与你无关。你那边的默认是 --qwen3_tts_mlx_quantization 6bit,是另一套参数。

你已经换过 TTS 或后端。 只要传了 --tts kokoro / --tts pocket / --tts chattts,或者 --qwen3_tts_backend torch,GGML 那条 wheel 依赖就不在路径上。此时症状另有其因。

症状其实是 numpy 版本互斥。 这是 README 讲的第二个坑,和第一个完全不同源:DeepFilterNet(VAD 里可选的音频增强用)需要 numpy<2,而 Pocket TTS 需要 numpy>=2,两者冲突,只能在不使用 Pocket TTS 的环境里手动装 DeepFilterNet。对应的开关是 --audio_enhancement默认 False。看到 numpy 相关的依赖打架,先往这条上想。

症状出在 LLM 那一环。 默认 --llm_backend responses-api--model_name gpt-5.4-mini,也就是说——这个自称 “Build local voice agents” 的项目,开箱默认的 LLM 是走云端的,要 $OPENAI_API_KEY。这一环跟你本机 CUDA 一点关系都没有。想全本地得自己换后端或换 base URL。

症状是「中文识别不出来」。 默认 STT 是 Parakeet TDT,README 的多语言表里写着它覆盖 25 种欧洲语言。中文场景必须换 STT(Whisper 系或 Paraformer),这是选型问题,不是安装问题。

症状是「只能一个人连」。 --num_pipelines 默认是 1,默认只承载一个并发会话。多用户部署第一个要动的就是它。

你照着老教程敲 --mode 这个参数已废弃且很快将停止工作:--mode realtime 等价于 serve--mode local 等价于 local,两者都会打印警告,其余取值已被移除、会带提示直接退出。网上大量旧文还停在 --mode 的写法上。

最后一条留给 Windows 读者:README 把这条 wheel 陷阱明确限定在 Linux 上讲。Windows 侧会怎样,我们手上没有任何依据,不替你下结论——真要在 Windows 上折腾,容器那条路(处置 D)至少把版本匹配的责任交给了镜像。

延伸阅读


本文依据 speech-to-speech 官方仓库(github.com/huggingface/speech-to-speech)的 README、 src/speech_to_speech/arguments_classes/ 下的参数定义与 Realtime Engine 架构文档整理,核对日 2026-08-09。 本文内容为仓库源码与文档口径,我们没有安装、部署或调用过该服务,文中毫秒值均为参数默认值而非实测延迟。 参数与默认值随版本变动,请以 speech-to-speech serve -h 的实际输出为准。 仓库标注为 Apache-2.0,各模型权重另有各自许可,我们一份都没读过,一律以官方 LICENSE 与模型页面原文为准,本文不构成法律意见。 安全相关做法请结合自身环境评估,本文不构成安全方案建议。

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