老教程里的 --mode 为什么跑不通了

2026-08-09

搜索引擎里关于 speech-to-speech 的中文教程,有相当一部分停留在 --mode 那个年代。你复制一条命令过来,敲下回车,出来的东西跟教程截图对不上——这篇就专门处理这一类问题。

先说清楚本文的依据边界:以下内容全部来自官方仓库的 README 与 src/speech_to_speech/arguments_classes/ 下的参数定义,核对日 2026-08-09。我们没有 pip install 过它,没有启动过服务,也没有对着麦克风说过一句话。所以下面不会出现任何延迟数字、音质评价或者「装完界面上会显示什么」的描述。

一、现象长什么样

照抄老教程时,--mode 的失败方式不止一种,这也是它容易被误判的原因:

第一种:命令跑起来了,但多了一行警告。 README 写得很明确,迁移窗口内 --mode realtime 等价于 serve--mode local 等价于 local,两者都会打印警告。很多人看到服务起来了就跳过了那行警告,直到某次升级后彻底跑不动才回头找原因。

第二种:直接退出,带一句「改用新命令」的提示。 除了上面那两个取值,其余所有 mode 取值都已经被移除了。老教程里如果写的是别的 mode 名字,现在的行为就是带提示退出,不会继续往下走。

第三种:你想查 --mode 到底还支持什么,却在参数定义里翻不到它。 我们把 src/speech_to_speech/arguments_classes/ 下的参数整份导出数过,131 个 CLI 参数里没有 --mode 这一项。这一种最让人困惑——命令还能被接受,但常规参数定义那一层已经查无此项。

README 对这件事的定性是:--mode 已废弃,且很快将停止工作。也就是说,第一种「只是警告」的状态是有保质期的,不能当成长期方案。

二、怎么确认是这个问题

别靠猜,下面三个动作都能在一分钟内做完。

动作一:查帮助输出里还有没有 --mode

speech-to-speech serve -h

这里有个 README 单独强调过的用法细节:CLI 只为选中的后端构造配置,所以你想看另一种组合的参数,得把选择器放在 -h 前面,例如:

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

动作二:核对参数定义那一层。 承接上面第三种现象——src/speech_to_speech/arguments_classes/ 下是 18 个 dataclass 参数文件,131 个参数分散在里面(VAD 19 个、Qwen3-TTS 23 个、模块级 12 个是大头),--mode 不在其中。这个结果本身就是一个判据:一个连常规参数定义里都不再出现的开关,你不该把长期脚本压在它上面。

动作三:看启动的第一屏有没有警告。 如果你的命令里带着 --mode realtime--mode local 且服务确实起来了,那行警告就是本次失败(或未来失败)的唯一预告。想把日志看得更细,可以用 --log_level debug(默认是 info)。

对照下面这张表就能立刻定性:

老教程里写的当前行为你该改成
--mode realtime等价于 serve,打印警告speech-to-speech serve
--mode local等价于 local,打印警告speech-to-speech local
其它任何 mode 取值已移除,带提示直接退出按下一节的三命令模型重写

三、按文档语义怎么处置

新的命令模型是三个动词,README 原文给的分工是这样的:

命令行为什么时候用
serve以 OpenAI Realtime WebSocket 与 WebRTC 运行流水线服务你在对着这个 API 做应用或设备
talk --url <完整 realtime url>运行自带的麦克风/扬声器客户端你想连上一个已有的 Realtime 服务说话
local在同一进程内通过环回把 servetalk 组合起来你想一条命令同时起服务并对它说话

老教程里的 --mode realtime 对应的是「只起服务」,改写成 serve--mode local 对应的是「起服务顺便自己说话」,改写成 local。如果你原来是起了服务再另开一个终端连上去,那么现在的写法是:

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

默认服务地址是 ws://localhost:8765/v1/realtime,端口默认 8765

改写命令的时候,真正的坑往往不在 --mode 这个词本身,而在它后面跟着的一串老参数上。这里有一条 README 明说的行为必须记住:未激活后端的已知选项仍然被接受,只是会带警告地被忽略,JSON 配置里多余的非激活后端键同理。换句话说,你从老教程里抄来一串属于别的后端的参数,程序不会报错停下,只会警告一声继续跑——你以为配置生效了,实际上它一个字都没读。排查这类「命令跑通了但行为不对」的问题时,这一条是首要嫌疑。

四、处置后怎么验证

第一步,确认默认配置是不是你要的。 README 给出了一段等价展开,export OPENAI_API_KEY=... 之后直接 speech-to-speech serve,等价于:

speech-to-speech serve \
    --thresh 0.6 \
    --stt parakeet-tdt \
    --llm_backend responses-api \
    --tts qwen3 \
    --qwen3_tts_model_name Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice \
    --qwen3_tts_speaker Aiden \
    --qwen3_tts_language auto \
    --qwen3_tts_backend ggml \
    --qwen3_tts_non_streaming_mode True \
    --qwen3_tts_mlx_quantization 6bit \
    --model_name gpt-5.4-mini \
    --chat_size 30 \
    --responses_api_stream \
    --enable_live_transcription

这段值得逐行读一遍,因为它藏着一个很多人换完命令才发现的事实:默认模型是通过 OpenAI Responses API 的 gpt-5.4-mini。项目自我介绍是「用开源模型搭建本地语音代理」,但默认配置里在本地跑的只有 STT 和 TTS 这两环,LLM 那一环默认打的是云端。想全本地就得自己换后端,README 给的最省事做法是把 LLM 放到独立的 llama.cpp 进程,再用 --responses_api_base_url 指过去(http://127.0.0.1:8080/v1)。顺带记一下端口分工:docker compose 里 llama.cpp 是 8080,实时服务是 8765,这两个别写反。

第二步,用 talk 连一次确认服务确实在监听。 用上面那条 talk --url 的命令连本机 8765,这是最直接的连通性验证。

第三步,检查绑定地址有没有被老教程带偏。 serve 默认绑定 127.0.0.1,要暴露到网络必须显式--host 0.0.0.0local 则永远绑定环回,并把自带客户端连到 ws://127.0.0.1:<port>/v1/realtime。如果你抄来的老命令里带着 --host 0.0.0.0,或者顺手开了 --enable_llm_proxy,务必先看清官方自己的声明:服务端不做任何认证,也不做任何限流,只应在受信网络上启用,或者部署在一个你自己掌管访问控制的网关后面。这句话不能弱化,也不存在「照着某个配置写就安全了」的说法;nginx、防火墙、反向代理一类做法属于通用运维手段,不是该项目文档的内容,要不要用、怎么用得结合你自己的环境评估。

以上命令均按官方参数语义组合,我们未逐项实测,以官方文档与 --help 的实际输出为准。

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

排查文章最容易漏掉的就是这一节。下面几种失败长得很像,但换成 serve 是治不好的:

你的命令里根本没有 --mode 那就别在这条线上耗了。失败提示里没有出现 mode 相关字样时,直接去看下面几类。

失败发生在安装/导入阶段,服务还没起来。 这类问题通常是依赖矩阵而不是命令语法。README 单列了两个坑:Linux 上 Qwen3-TTS 的 GGML 后端来自 faster-qwen3-tts[ggml],其 PyPI 默认 wheel 面向 CUDA 12.8,机器上没有对应运行时就得先从 Hugging Face wheelhouse 装匹配版本再装本体;另一个是 DeepFilterNet 需要 numpy<2、Pocket TTS 需要 numpy>=2,两者互斥,只能在不用 Pocket TTS 的环境里手动装 DeepFilterNet。

第二个客户端连不上、被拒绝。--num_pipelines,它默认是 1。按参数说明,一个 uvicorn 服务监听 --port,把每个进来的客户端路由到下一个空闲流水线,每条流水线有自己的 VAD/STT/LM/TTS handler 与会话状态;最大并发 WebSocket 会话数等于 num_pipelines,再多的连接会被拒绝。这是并发上限,跟命令怎么写没关系。

断网环境下起不来。 检查是不是只加了 HF_HUB_OFFLINE=1 却没有把 LLM 的 base URL 覆盖到本地——默认的 responses-api 后端会去调远程服务,离线自然就断了。README 的建议是断网前先在联网状态下用完全相同的配置启动一次,让 STT、LLM、TTS、Silero VAD、NLTK 和 Smart Turn 需要的资源都缓存好。

老教程用的组件本身已经被下架了。 已弃用的实现(含 MeloTTS)被放进了 archive/,不再接入 CLI。这种情况下无论 --mode 还是 serve 都救不回来,只能按当前的后端支持矩阵重新选型。

你抄的参数属于没被激活的后端。 回到第三节那条:它会被带警告地忽略而不是报错。命令能跑、行为不对、日志里有警告——这三件事同时出现时,先怀疑参数落空,而不是继续改命令动词。

最后补一句关于版本的态度。这个仓库在 2026-08-09 的快照里有 11893 个 star,最后一次推送就在前一天——更新很勤。star 数说明不了稳定性或者适不适合你,但推送频率至少提示一件事:任何一篇带具体参数的教程(包括这一篇)都有保质期,动手前先跑一次 -h 永远比相信文章便宜。

延伸阅读


本文依据 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 的实际输出为准。

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

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