从 pip install 到第一次 serve:默认装了些什么

2026-08-09

装 speech-to-speech 这件事,表面上只有一行 pip install speech-to-speech。麻烦的地方不在这一行能不能跑通,而在于它跑通之后,你手上究竟拼出了一条什么样的流水线——哪个模型在做转写、哪个后端在出声、语言模型的请求发到了谁家的地址上。这几个问题的答案全都藏在默认值里,而默认值和你脑子里想的多半不一样。

这篇就按官方 README 和 src/speech_to_speech/arguments_classes/ 下的参数定义,把「默认装了些什么」摊开讲一遍。

先交代边界:**我们只读了仓库文件,没有 pip install 过,没有启动过服务,也没有对着麦克风说过一句话。**下文所有毫秒数都是参数定义里的默认值,是流水线自己引入的可配置等待,不含 STT / LLM / TTS 的推理耗时,更不能相加当成端到端延迟。

一、先搞清楚这条流水线由什么组成

README 的定位是一条模块化的语音代理流水线:VAD → STT → LLM → TTS,对外通过一个兼容 OpenAI Realtime 的 WebSocket API 暴露。四个组件各跑在自己的线程里、用队列相连,每一级都有多个可互换后端,用 CLI 参数选。

仓库层面的量级:src/speech_to_speech/ 下 96 个 Python 文件,arguments_classes/ 里 18 个 dataclass 参数文件加起来 131 个 CLI 参数。GitHub API 在 2026-08-09 的快照是 11893 star、1463 fork、136 open issues,仓库标注 Apache-2.0。star 数只是个时间锚点,推不出任何关于稳定性或适用性的结论,这里列出来只是让你知道读到的是哪个时间点的仓库。

二、命令怎么写:三条路径,别混着走

路径 A:直接 pip 装(要求 Python 3.10+)

pip install speech-to-speech

但 Linux + CUDA 的用户先别急着敲这行。README 单独开了小节讲这个坑: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
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

每一行为什么在这:版本号后缀 +cu130 / +cu124 / +cpu 就是这批 wheel 区分 CUDA 运行时的方式,选错了等于没绕开原来的错配;-f 指向 wheelhouse 的索引地址,让 pip 从这里而不是 PyPI 取包;最后一行放在末尾,是为了让 speech-to-speech 的依赖解析看到已经装好的那个版本。

如果你不想折腾 GGML 那一路,README 给了另一个出口:传 --qwen3_tts_backend torch,改用之前那套 CUDA-graphs 实现。这个参数的默认值是 ggml

路径 B:从源码装

git clone https://github.com/huggingface/speech-to-speech.git
cd speech-to-speech
uv sync

README 说这会以可编辑模式安装,并提供 speech-to-speech 命令。要改 handler、要看某个默认值到底写在哪一行,走这条。

路径 C:Docker

装好 NVIDIA Container Toolkit 之后:

docker compose up

README 原文说 compose 文件会起一个跑 Gemma 4 的 llama.cpp 服务实时服务,暴露端口 80808765。仓库里另有 DockerfileDockerfile.arm64。这条路径的价值在于它顺手把「本地语言模型」那一环也拉起来了,不用你自己先支一个 OpenAI 兼容端点。

关于 Windows

需要说清楚的是:README 的依赖解析只在 pyproject.toml 里按 macOS / 非 macOS 做平台标记分岔,并没有单独的 Windows 章节。所以 Windows 上能参考的只有「非 macOS」这一支——也就是上面 CUDA wheel 那套顺序。至于 Windows 下的音频设备、驱动一类的具体差异,官方文档没写,我们也没跑过,不编。

以上命令均原样引自 README,未逐项验证过,以官方文档与 speech-to-speech serve -h 的实际输出为准。

三、默认装了些什么:三个默认值决定了一切

module_arguments.py 里三个选择器的默认值是这样的:

参数默认值含义
--sttparakeet-tdt默认转写后端
--llm_backendresponses-api默认语言模型走向
--ttsqwen3默认语音输出后端

对照 README 的支持矩阵:默认 STT 是 nvidia/parakeet-tdt-0.6b-v3(Apple Silicon 上走 MLX 变体),默认 TTS 是 Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice,非 macOS 用 GGML 后端、Apple Silicon 用 mlx-audio

有三件事值得单独拎出来说,因为它们跟项目给人的第一印象是反的。

第一,项目描述写着 “Build local voice agents with open-source models”,但默认的 LLM 那一环打的是 OpenAI。--llm_backend 默认 responses-api,而 responses_api_language_model_arguments.py--model_name 默认是 gpt-5.4-mini--responses_api_base_url 默认 None(help 原文注明 uses OpenAI)。想真正跑本地,你得显式把 base URL 指到自己的 vLLM 或 llama.cpp 上,或者干脆走 Docker compose 那条路。这不是项目在夸口,它说的是 LLM 那一环用的是 OpenAI 兼容协议,能指向托管服务商,也能指向你自己的机器——但默认指向的是前者。

第二,--model_name 这个名字在两个文件里都存在,默认值不一样。language_model_base_arguments.py 里它默认 Qwen/Qwen3-4B-Instruct-2507responses_api_language_model_arguments.py 里默认 gpt-5.4-mini。哪个生效取决于 --llm_backend 选了谁。看 help 输出时一定要连着后端一起看,不然会拿错默认值。

第三,默认 STT 的语言覆盖面。--parakeet_tdt_language 的 help 原文写的是 “Supports 25 European languages”。中文不在这个描述里。要做中文,README 的矩阵里给的是 Paraformer(paraformer extra,--paraformer_stt_model_name 默认 paraformer-zh)或者 Whisper 系(whisper_stt_arguments.py--language 可选 'zh')。这一条决定了很多人从第一步就得改配置,别等装完了才发现。

另外还有一个非常容易忽略的默认值:--num_pipelines 默认是 1。它的 help 说得很直白——池子里隔离的流水线实例个数,一个 uvicorn 监听 --port,把每个进来的客户端路由到下一个空闲流水线,最大并发 WebSocket 会话数就等于 num_pipelines,再多的连接会被拒绝。也就是说,默认配置下这就是一个单会话服务。

四、可选组件:只装你真要用的那一个

README 给的 extras 是这些:

pip install "speech-to-speech[kokoro]"          # 非 macOS 上的 Kokoro-82M TTS
pip install "speech-to-speech[pocket]"          # Pocket TTS
pip install "speech-to-speech[chattts]"         # ChatTTS
pip install "speech-to-speech[faster-whisper]"  # Faster Whisper STT
pip install "speech-to-speech[whisper-mlx]"     # macOS 上的 Lightning Whisper MLX STT
pip install "speech-to-speech[paraformer]"      # 通过 FunASR 的 Paraformer STT
pip install "speech-to-speech[mlx-lm]"          # macOS 上视觉模型的 mlx-vlm 支持
pip install "speech-to-speech[webrtc]"          # WebRTC 传输

**这里藏着第二个安装期的坑,而且它是纯依赖层面的死结。**README 原文说:DeepFilterNet(VAD 里可选的音频增强用)需要 numpy<2,Pocket TTS 需要 numpy>=2,两者冲突。结论是:只在不使用 Pocket TTS 的环境里手动安装 DeepFilterNet。对应到 CLI,就是 --audio_enhancement 这个开关,它的默认值是 False——默认不开,所以默认装的时候你不会撞上这个冲突,是你后来想加音频增强的时候才会撞上。

顺带一提:已弃用的实现(含 MeloTTS)都在 archive/ 目录里,不再接入 CLI,别照着老教程去找。

五、产出物长什么样

只说有依据的部分:

  • pip 装完之后,会提供一个名为 speech-to-speech 的命令;从源码走 uv sync 同样提供这个命令。
  • 服务端监听参数在 realtime_server_arguments.py--host 默认 127.0.0.1--port 默认 8765
  • Docker compose 那条路会额外起一个 llama.cpp 服务,端口 8080
  • 除了实时服务,README 还提到本地音频模式,对应 local_audio_arguments.py 里的一组参数(如 --local_audio_chunk_size 默认 1024、--local_audio_print_json 默认 False,后者用于打印本地音频客户端收到的原始 Realtime 事件)。

至于终端上会滚出哪几行日志、有没有进度条、模型下载到哪个缓存目录——我们没跑过,不写。

六、怎么验收:一条命令,两个陷阱

验收动作只有一条:

speech-to-speech serve -h

它会打印当前这套组合的默认值与专属参数。**关键在于「当前这套组合」这五个字。**README 明确说了:要看另一种组合的参数,把选择器放在 -h 前面,比如

speech-to-speech serve --stt mlx-audio-whisper -h

陷阱一:CLI 只为选中的后端构造配置,未激活后端的已知选项仍然被接受,但会带警告地忽略。JSON 配置同理,多余的非激活后端键会被忽略。翻译成人话——你把某个后端专属参数写错了位置,程序不会报错停下,只会警告一下然后照跑,跑出来的还是默认行为。所以人要检查的第一处,就是你传的每个后端专属参数,前缀是不是跟你选的那个后端对得上(比如你选了 --tts qwen3,就别指望 --kokoro_voice 生效)。

陷阱二:别把 -h 打印的毫秒值当成延迟指标。vad_arguments.py 里跟第一次启动最相关的几个默认值是:--thresh 0.6、--min_silence_ms 64、--min_speech_ms 384、--speech_pad_ms 500。它们是 VAD 切分语音边界用的门限和留白,不是任何形式的耗时测量。同一个文件里还有三个跟轮次重开有关的窗口——--speculative_reopen_ms 800、--unanswered_reopen_ms 7000、--smart_turn_max_wait_ms 2000,以及迟滞用的 --min_speech_continuation_ms 192、Smart Turn 的 --smart_turn_threshold 0.5 和 --smart_turn_incomplete_delay_ms 600。这些数值各自约束的是不同阶段的可配置等待,把它们加起来当成端到端耗时是彻底错误的读法,这些窗口本来就不同时生效,也完全不包含模型推理时间。第一次装完,你只需要知道这些值存在、能改;真要调它们是另一个话题。

如果想看得更细,可以把 --log_level 从默认的 info 调成 debug

七、什么情况不适用

这套默认组合有几个明确的边界,装之前就该判断清楚:

  1. 要做中文的,默认 STT 不适用。--parakeet_tdt_language 的 help 写的是 25 种欧洲语言。中文场景请从一开始就规划 paraformer extra 或 Whisper 系,别装完再返工。
  2. 要多人同时接入的,默认配置不适用。--num_pipelines 默认 1,超出的连接会被拒绝。要并发得先想清楚每条流水线各自持有一整套 VAD/STT/LM/TTS handler 和会话状态,这是资源问题不是配置问题。
  3. **要「完全离线」的,默认配置不适用。**默认 --llm_backend responses-api 打的是 OpenAI 兼容端点,--responses_api_base_url 默认 None。不改这一项,你的对话内容是出网的。
  4. **既要 Pocket TTS 又要 DeepFilterNet 音频增强的,装不到一个环境里。**numpy 版本硬冲突,只能二选一或者拆环境。
  5. 要把服务开到公网的,请先停一下。--host 的 help 原文写着:默认 127.0.0.1,显式传 0.0.0.0 才会把这个未认证的(unauthenticated) API 暴露到网络上。同样地,官方对 --enable_llm_proxy(默认 False)这条链路的声明是:服务端自身不做认证、不做限流,只应在受信网络内或放在网关后面启用。这里不给「怎么配就安全了」的结论——加反向代理、加防火墙、加鉴权层都属于通用运维做法,不是该项目官方文档的内容,也不构成安全方案建议。
  6. 密钥别写进命令行历史。--responses_api_api_key 默认 None,需要你自己传。写文档时一律用 <YOUR_API_KEY> 占位,实际使用建议通过 $OPENAI_API_KEY$HF_TOKEN 这样的 shell 变量引用(这是通用做法,不是该项目的官方要求)。

最后补一个不算坑但值得知道的事实:README 说这条流水线作为数千台 Reachy Mini 机器人的对话后端在生产环境运行。这是 README 的陈述,可以当作「有人在认真用它」的旁证,但 README 没有给出任何性能指标,所以它推不出「延迟低」或者「很稳」之类的结论,别顺着往下想。

延伸阅读


本文依据 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 原文为准,本文不构成法律意见;安全相关做法请结合自身环境评估,本文不构成安全方案建议。

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