serve、talk、local:三个命令分别在什么场景

2026-08-09

第一次翻 speech-to-speech 的 README,最容易产生的误解是:servetalklocal 是同一件事的三种运行方式,随便挑一个都能说上话。真按这个理解去配,多半会在第二天遇到「为什么局域网里另一台机器连不上」「为什么两个人同时连就有一个被踢」这类问题。

这三个命令切的其实是两条正交的线:谁来托管那条 VAD → STT → LLM → TTS 的流水线,以及谁来把麦克风的音频推进去、把合成音频播出来。想清楚这两件事归谁,选命令就是三秒钟的事。

一、README 原表:三个命令各自是什么

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

照这张表反推:serve 只有流水线,没有嘴也没有耳朵;talk 只有嘴和耳朵,没有流水线;local 是把两边塞进同一个进程,中间用环回连起来。

所以「我只是想试试效果」应该用 local,「我要写一个前端/一台设备去连它」应该用 serve,「服务已经跑在另一台机器上、我想拿本机麦克风验证它活着」才轮到 talk。Quickstart 给的两条命令正是最后这种拆法:

pip install speech-to-speech
export OPENAI_API_KEY=...
speech-to-speech serve
speech-to-speech talk --url ws://127.0.0.1:8765/v1/realtime

默认服务地址是 ws://localhost:8765/v1/realtime--port 默认 8765

二、判断依据一:绑定地址不一样,这是安全边界

这是三个命令最实质的差别,也是最该记住的一条。

  • serve 默认绑定 127.0.0.1。要暴露到网络,必须显式--host 0.0.0.0。参数 help 原文的措辞是「显式传 0.0.0.0 以把这个未认证的 API 暴露到网络上」。
  • local 永远绑定环回,并把自带客户端连到 ws://127.0.0.1:<port>/v1/realtime。它的 --port help 写的就是「本地 Realtime 服务与音频客户端的环回端口」。

判断依据由此非常清楚:只要你的需求里出现「另一台机器」「手机」「同事」「设备」,local 就直接出局,它没有把服务对外开的选项。反过来,如果你只想在这台机器上验证一下链路是否通,用 local 比自己开两个终端更省事,也顺带避免了误把服务开到网络上。

至于 --host 0.0.0.0,README 在 LLM 代理那一节把话说得很直白:服务端自己不做任何认证,也不做任何限流;只在受信网络上启用,或者把它部署在一个由你自己掌管访问控制的网关后面。这句话对 --enable_llm_proxy 成立,对把 Realtime 端口开到 0.0.0.0 同样是该带上的前提。README 举的例子是让计算副本充当网关,只对「用 HF token 创建了会话的客户端」开放这些路径,并按该 token 校验与限流。本文不给「这样配就安全了」的结论——加不加反向代理、怎么做鉴权,属于你自己环境里的运维决策。

三、判断依据二:参数加在哪个命令上才有用

初学者常犯的另一个错,是把回合检测参数加到 talk 上。

按 Realtime Engine 的数据流,客户端只负责把 base64 PCM 用 input_audio_buffer.append 发上来,服务端的 RealtimeService 解码、重采样到 16 kHz、切成 512 采样点的块,再交给 VAD;VAD 在 text_output_queue 上发 speech_started / speech_stopped。也就是说,语音边界与轮次判定全部发生在托管流水线的那一侧

结论是:--thresh(默认 0.6)、--min_silence_ms(默认 64)、--min_speech_ms(默认 384)这些 VAD 参数,属于 servelocal,不属于 talk。同理,--stt--llm_backend--tts 这类组件选择器也是给起流水线的那个命令用的。talk 需要的只是一个 --url

顺带一个查文档的技巧:想看某个组合的默认值与专属参数,用 speech-to-speech serve -h;要看另一种组合,把选择器放在 -h 前面,例如 speech-to-speech serve --stt mlx-audio-whisper -h。这比翻 README 快得多。

四、判断依据三:默认配置里 LLM 是走云端的

这一点和选命令直接相关,但很多人到部署那天才发现。README 给出了「默认等价的完整命令」,其中和后端选择相关的几行是:

speech-to-speech serve \
    --thresh 0.6 \
    --stt parakeet-tdt \
    --llm_backend responses-api \
    --tts qwen3 \
    --model_name gpt-5.4-mini \
    --chat_size 30 \
    --responses_api_stream \
    --enable_live_transcription

项目的一句话描述是 “Build local voice agents with open-source models”,但默认配置里真正跑在本地的只有 STT 和 TTS,LLM 那一环默认打的是 OpenAI 的 Responses API,模型是 gpt-5.4-mini。所以 Quickstart 第二条才是 export OPENAI_API_KEY=...

这个默认值会改变你的命令选择:如果你的目标就是「断网也能用」,那么无论 serve 还是 local,都得先把 LLM 换掉。README 给的最省事做法是把 LLM 放进独立的 llama.cpp 进程,然后让 speech-to-speech 指过去:

# 终端 1:llama.cpp 提供 Gemma 4
llama-server -hf ggml-org/gemma-4-E4B-it-GGUF -np 2 -c 65536 -fa on --swa-full
# 终端 2:speech-to-speech 指向这个本地服务
speech-to-speech serve \
    --stt parakeet-tdt \
    --llm_backend responses-api \
    --tts qwen3 \
    --model_name "ggml-org/gemma-4-E4B-it-GGUF" \
    --responses_api_base_url "http://127.0.0.1:8080/v1" \
    --responses_api_api_key "" \
    --responses_api_stream \
    --enable_live_transcription

想在托管这台机器上直接说话,把上面的 serve 换成 local 即可;如果是 Apple Silicon 或 CUDA/CPU,也可以用进程内后端 --llm_backend mlx-lm / --llm_backend transformers,省掉独立进程。仓库还提供了 docker compose up,compose 文件会同时起一个跑 Gemma 4 的 llama.cpp 服务和实时服务,暴露 80808765 两个端口——8080 是 llama.cpp,8765 是 Realtime,别记反。

另外提醒一句面向中文场景的读者:默认 STT 是 Parakeet TDT,README 的多语言表里写的是25 种欧洲语言。要做中文语音代理,无论走哪个命令都得换 STT(Whisper 系或 Paraformer)。

五、把它压成一条选择路径

  • 只想在本机验证链路通不通local。它永远绑环回,不会误开到网络上;macOS 上还能直接加 --mac-optimal-settings
  • 要写前端 / 做设备 / 给同事连serve。默认只绑 127.0.0.1,要对外必须显式 --host 0.0.0.0,而这一步意味着你要自己处理认证与限流。
  • 服务已经在别处跑着,只想用本机麦克风验证一下talk --url <完整 realtime url>
  • 要给多人用 → 一定是 serve,并且第一个要改的参数是 --num_pipelines。它默认为 1,help 原文说得很清楚:一个 uvicorn server 监听 --port,把每个进来的客户端路由到下一个空闲的 pipeline,每个 pipeline 有自己的 VAD/STT/LM/TTS handler 与会话状态,最大并发 WebSocket 会话数等于 num_pipelines,再多的连接会被拒绝。默认值 1 意味着开箱只能承载一个会话——这是我认为整份参数表里最容易在演示当天翻车的一个默认值。

六、两个容易踩空的细节

其一,--mode 已经废弃。 README 说它很快将停止工作。迁移窗口内 --mode realtime 等价于 serve--mode local 等价于 local,两者都会打印警告;其余所有 mode 取值已被移除,会带着「改用新命令」的提示直接退出。网上大量旧教程用的正是 --mode,跑不通不是你的环境问题,是命令行结构换了。

其二,--mac-optimal-settings 该挂在哪个命令上。 它做四件事:对支持的模型组件使用 MPS 默认值、STT 设为 Parakeet TDT、LLM 后端设为 MLX LM、TTS 设为 Qwen3-TTS 并默认取 6bit 的 MLX 变体。关键是优先级——预设只提供默认值,显式的 --device、组件级设备参数(如 --qwen3_tts_device),以及 --stt--llm_backend--model_name--tts 全部优先于预设。README 还给了一条明确指引:想暴露服务而不启动麦克风客户端时,把它用在 serve 而不是 local。很多人默认它只配 local,于是在做 macOS 上的服务端时白白丢掉了这套默认值。

最后补一条排错方向:如果你写了某个后端的专属参数却发现毫无效果,先确认那个后端是不是真被选中了。README 明说 CLI 只为选中的后端构造配置,未激活后端的已知选项仍然被接受,但会带警告地忽略。程序不会报错停下,只会警告后照跑——这类「配了没生效」的问题,答案往往就在启动日志的警告里。

延伸阅读


本文依据 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?报名体系课或加入会员,照着学、照着用。