DeepFilterNet 要 numpy 小于 2,Pocket TTS 要大于等于 2
依赖冲突里最讨厌的一类,是两个组件各自都没写错,但它们对同一个基础库提了相反的要求。speech-to-speech(github.com/huggingface/speech-to-speech,仓库标注 Apache-2.0,2026-08-09 快照 star 11893)的 README 就单独开了一小节交代这么一条:DeepFilterNet 需要 numpy<2,Pocket TTS 需要 numpy>=2,两者冲突。
这条约束不在任何 CLI 参数的 help 文本里,也不会在你选后端的时候被提示。它写在安装章节,而安装章节恰恰是大多数人 pip install 完就跳过的地方。下面按排查的实际顺序走一遍。
一、现象长什么样
先说清楚我们的位置:本文只读了官方仓库的 README、pyproject.toml 和 src/ 目录,没有 pip install 过,没有启动过服务,所以不会给你任何具体的报错文案或日志行——那些必须以你自己终端里的实际输出为准。
能确定的是这类冲突的形状:你的环境里同时出现了 DeepFilterNet 和 Pocket TTS 这两条线,而 numpy 只能是一个大版本。于是三种走向:pip 在解析阶段就拒绝装;或者装是装上了,但后装的那一方把 numpy 换到了另一个大版本,先装的那一方在 import 阶段崩掉;再或者你用了 --no-deps、用了不同时刻分别安装的顺序,环境表面完好,只有真正走到那条代码路径时才炸。
第三种最费时间。因为 speech-to-speech 是 VAD → STT → LLM → TTS 四段级联,每个组件跑在自己的线程里、用队列相连,出问题的组件不一定是你正盯着的那一段。
二、怎么确认是这个问题
三个动作,从便宜到贵,按顺序做。
动作一:看你到底有没有把这两条线都拉进来。 默认安装根本碰不到这个冲突。README 写得很清楚,pip install speech-to-speech 的默认覆盖是 Parakeet TDT 做 STT、OpenAI 兼容 API 做语言模型、Qwen3-TTS 做语音输出(非 macOS 平台默认 GGML 后端,Apple Silicon 上用 mlx-audio)。Pocket TTS 是 extra,得显式装:
pip install "speech-to-speech[pocket]"
DeepFilterNet 更是——README 的措辞是手动安装,它不在任何 extra 里,不会被自动拉进来。所以如果这两条命令你一条都没执行过,那这篇文章讲的问题跟你无关,直接跳到第五节。
动作二:查环境里的 numpy 到底是哪一侧。
python -c "import numpy; print(numpy.__version__)"
pip show numpy
只看首位数字。是 1.x 就说明环境被拉到了 DeepFilterNet 那一侧,是 2.x 就说明被拉到了 Pocket TTS 那一侧。这一步的价值在于把「我装了什么」和「实际生效的是什么」分开——安装顺序不同,结果会不同,而 pip 不一定会为此拦你。
动作三:确认你的启动命令里到底激活了哪些东西。 这一步很多人漏掉,因为它反直觉。
--audio_enhancement 是布尔参数,默认值是 False,它的 help 原文说的是通过降噪、均衡、回声消除一类手段改善音质。也就是说,你把 DeepFilterNet 装进环境了,但只要没在命令行里显式打开这个开关,那条路径就不会被走到。同理,--tts 的默认值是 "qwen3",你装了 pocket extra 但没写 --tts pocket,Pocket TTS 也不会被实例化。
这里有个必须知道的行为,README 原文明确说了:CLI 只为选中的后端构造配置,未激活后端的已知选项仍然被接受,但会带警告地忽略;JSON 配置同理,多余的非激活后端键会被忽略。翻译成排查语言就是——你写了 --pocket_tts_device 或 --pocket_tts_voice,而 --tts 并不是 pocket,程序不会报错停下,只会警告一句然后照跑。所以「我明明配了 Pocket TTS 的参数却没生效」这个现象,第一嫌疑人不是 numpy,是你压根没激活它。
想看某个组合到底吃哪些参数、默认值是多少,README 给的办法是把选择器放在 -h 前面:
speech-to-speech serve -h
speech-to-speech serve --tts pocket -h
三、文档语义给出的处置
README 的原话很短:只在不使用 Pocket TTS 的环境里手动安装 DeepFilterNet。
这句话把选择权还给了你,但也意味着官方没有提供「同一个环境里让两者共存」的方案。我们没有读到任何这样的说明,也不会替它编一个。据此能落地的只有三条路:
路线 A:要降噪,不要 Pocket TTS。 这个环境里手动装 DeepFilterNet,让 numpy 停在 1.x,然后 --audio_enhancement 才有意义。TTS 用默认的 Qwen3-TTS(Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice),或者按支持矩阵换成 Kokoro-82M(hexgrad/Kokoro-82M,非 macOS 需要 kokoro extra)、ChatTTS(chattts extra)、内置的 MMS TTS——就是别碰 pocket。
路线 B:要 Pocket TTS,放弃 DeepFilterNet。 装 speech-to-speech[pocket],让 numpy 停在 2.x,--audio_enhancement 保持默认的 False,别去手动装 DeepFilterNet。Pocket TTS 自己那组参数默认值是 --pocket_tts_device 为 "cpu"、--pocket_tts_voice 为 "jean"、--pocket_tts_sample_rate 为 16000、--pocket_tts_blocksize 为 512、--pocket_tts_max_tokens 为 50。
路线 C:两个都要,就拆成两个环境。 两套 venv(或者两个容器),各装各的,各自跑一个服务实例。要提醒一句:--port 默认是 8765,两个实例同机跑必须错开端口;另外 --num_pipelines 默认只有 1,它的 help 说得很直白——一个 uvicorn 服务监听 --port,把每个进来的客户端路由到下一个空闲流水线,每条流水线有自己的 VAD/STT/LM/TTS handler 和会话状态,最大并发 WebSocket 会话数等于 num_pipelines,超出的连接会被拒绝。别把「连不上」误判成依赖问题。
顺带提醒一件跟拆环境常常绑在一起的事:--host 的默认值是 127.0.0.1,它的 help 原文说的是要显式传 0.0.0.0 才会把这个 unauthenticated(不认证)的 API 暴露到网络上。拆成两套环境之后,很多人为了让另一台机器也能连过来就顺手改了 host——这条链路不认证也不限流,只应在受信网络内或网关之后启用,不要因为「反正只是临时调试」就把它挂到公网上。要不要加反向代理、要不要加鉴权,是你自己环境里的运维决策,不在官方文档的范围内。
以上为按官方参数语义组合的示例,未逐项实测,以官方文档与 --help 的实际输出为准。
四、处置后怎么验证
改完别急着开麦,按这个顺序自查:
- 再查一次 numpy 大版本,确认它落在你选的那一侧。这是唯一能一眼看穿的客观信号。
- 确认对侧组件确实不在环境里。路线 A 的环境里不该有 pocket 那条线,路线 B 的环境里不该有手动装的 DeepFilterNet。留着不用也可能在下次
pip install时被解析器重新拽起来。 - 用
speech-to-speech serve --tts pocket -h这类带选择器的 help 核对参数归属,确认你写的每个--pocket_tts_*参数确实属于当前激活的后端,而不是那类「被警告后忽略」的多余键。 - 回看启动日志里的警告行。既然官方明说未激活后端的选项会带警告忽略,那警告就是你的免费体检报告,别把它滚过去。
- 确认 Python 版本满足要求,README 写的是 Python 3.10+。装依赖出怪事的时候,解释器版本永远值得再看一眼。
五、什么情况说明不是这个原因
这一节才是排查文章相对「报错大全」的价值所在。以下几类现象长得像,但根子不在 numpy:
你从没装过 pocket extra,也没手动装过 DeepFilterNet。 默认安装路径压根不会同时引入这两条线,那你的问题在别处。
问题出在 Qwen3-TTS 的 GGML 后端上。 README 单独写了另一个坑:Linux 上 Qwen3-TTS 的 GGML 后端来自 faster-qwen3-tts[ggml],它在 PyPI 上的默认 qwentts-cpp-python wheel 面向 CUDA 12.8。机器上没有该 wheel 期望的 CUDA 12 运行时,就得先从 Hugging Face wheelhouse 装匹配的 wheel 再装 speech-to-speech,比如 CUDA 13.x 装 qwentts-cpp-python==0.3.1+cu130、CUDA 12.4 装 0.3.1+cu124、纯 CPU 兜底装 0.3.1+cpu。想回到之前那套 CUDA-graphs 实现,传 --qwen3_tts_backend torch(该参数默认是 "ggml")。这是 CUDA 版本错配,不是 numpy 互斥,两者的处置完全不同。
参数写了但没生效。 前面说过的那条行为——未激活后端的已知选项会被警告后忽略。这类现象的特征是「服务正常起来了、也能跑,只是行为不是我配的那样」,跟依赖冲突那种硬失败不是一回事。
你走的是 Docker 路径。 README 说装好 NVIDIA Container Toolkit 后 docker compose up,compose 文件会启动一个跑 Gemma 4 的 llama.cpp 服务和实时服务,暴露端口 8080 与 8765。镜像里的依赖是固化的,你本机 venv 里的 numpy 版本影响不到它。这时候要查的是镜像、compose 配置和端口占用。
你照着某篇旧教程装 MeloTTS 之类的东西。 README 明确说已弃用的实现(含 MeloTTS)放在 archive/ 里,不再接入 CLI。装了也接不上,这不是版本冲突,是那条路已经被拆掉了。
症状与音频质量有关而不是与启动有关。 这已经超出我们能判断的范围了——我们没有运行过这套流水线,任何关于音质、识别效果的判断都得你自己在真实环境里做。
最后留一句:--audio_enhancement 默认关闭这件事本身就是个信号。一个默认不开的可选增强,值不值得你为它把整个环境的 numpy 锁在 1.x、并因此永久排除掉 Pocket TTS 这条线,这是个取舍题,没有标准答案。先想清楚你这套服务到底要什么,再决定装哪一侧——比装完再拆干净得多。
延伸阅读
本文依据 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 的实际输出为准。
许可条款请以官方 LICENSE 原文为准,本文不构成法律意见。
安全相关做法请结合自身环境评估,本文不构成安全方案建议。