开源项目

speech-to-speech

speech-to-speech

模块化的语音 agent 管道,将 VAD、STT、LLM、TTS 四个组件串联成低延迟的完全本地可部署系统,对外暴露 OpenAI Realtime 兼容的 WebSocket API。亮点是每个组件均可替换,LLM 槽支持 OpenAI 协议的任何 provider 或本地 vLLM/llama.cpp 服务器,已用于数千台 Reachy Mini 机器人生产环境,且工具链与 Hugging Face 生态无缝衔接。

README

 

Speech To Speech: Build voice agents with open-source models(用开源模型构建语音智能体)

PyPI Python License

一个低延迟、完全模块化的语音智能体流水线:VAD(语音活动检测) -> STT(语音转文字) -> LLM(大语言模型) -> TTS(文字转语音),通过兼容 OpenAI Realtime 的 WebSocket API 对外暴露。每个组件均可替换。LLM 槽位使用 OpenAI 兼容协议,因此你可以将其指向托管提供商、HF Inference Providers,或指向自己硬件上运行的 vLLM 或 llama.cpp 服务器,实现完全本地、完全开源的技术栈。

该流水线已在生产环境中作为数千台 Reachy Mini 机器人的对话后端运行。

将 OpenAI Realtime 客户端端点从托管 OpenAI 切换到自托管的 speech-to-speech 服务器

快速开始

pip install speech-to-speech
export OPENAI_API_KEY=...
speech-to-speech

这将启动一个 OpenAI Realtime 兼容服务器,位于 ws://localhost:8765/v1/realtime,使用 Parakeet TDT 进行本地 STT,使用 OpenAI 兼容的 LLM,以及 Qwen3-TTS 进行本地语音输出。

从源码签出后,可以在第二个终端与其对话:

python scripts/listen_and_play_realtime.py --host 127.0.0.1 --port 8765

更愿意将 LLM 保留在自己的机器上?使用 llama.cpp 提供 Gemma 4 服务:

llama-server -hf ggml-org/gemma-4-E4B-it-GGUF -np 2 -c 65536 -fa on --swa-full

然后将 OpenAI 兼容的 LLM 后端指向它:

speech-to-speech \
    --model_name "ggml-org/gemma-4-E4B-it-GGUF" \
    --responses_api_base_url "http://127.0.0.1:8080/v1" \
    --responses_api_api_key ""

任何兼容 OpenAI Realtime 的客户端均可连接。参见 Realtime API 了解协议,参见 LLM backends 了解提供商和本地服务器选项。

目录

工作原理

流水线由四个组件级联组成,每个组件在自己的线程中运行,并通过队列连接:

  1. 语音活动检测 (VAD):Silero VAD v5 检测语音边界和话轮转换。
  2. 语音转文字 (STT):转写用户的话轮,可选的实时部分转写。
  3. 语言模型 (LLM):生成响应,流式输出文本和工具调用。
  4. 文字转语音 (TTS):合成音频并流式传回客户端。

每个阶段都有多个可互换的后端,通过 CLI 标志选择。代码设计易于修改,重点支持通过 Transformers 和 Hugging Face Hub 提供的模型。

安装

需要 Python 3.10+。

pip install speech-to-speech

默认安装覆盖标准实时路径:

  • STT 使用 Parakeet TDT
  • 语言模型使用 OpenAI 兼容 API
  • 语音输出使用 Qwen3-TTS,在非 macOS 平台上默认使用 GGML 后端,在 Apple Silicon 上使用 mlx-audio
  • 本地音频和实时服务器模式

macOS 和非 macOS 的依赖关系通过 pyproject.toml 中的平台标记自动解析。

Qwen3-TTS 的 CUDA 说明

在 Linux 上,Qwen3-TTS GGML 后端来自 faster-qwen3-tts[ggml]。其在 PyPI 上的默认 qwentts-cpp-python wheel 针对 CUDA 12.8。如果你的机器没有该 wheel 期望的 CUDA 12 运行时,请在安装 speech-to-speech 之前从 Hugging Face wheelhouse 安装匹配的 wheel:

# 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-only fallback(仅 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

要使用之前的 CUDA-graphs 实现而不是 GGML,请传递 --qwen3_tts_backend torch。

可选后端

可选后端通过 pip 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[facebook-mms]"    # MMS TTS
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 支持

已弃用的实现(包括 MeloTTS)位于 archive/,不再连接到 CLI。

关于 DeepFilterNet: DeepFilterNet 用于 VAD 中的可选音频增强,需要 numpy<2,与需要 numpy>=2 的 Pocket TTS 冲突。请仅在未使用 Pocket TTS 的环境中手动安装。

从源码安装

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

这会将包安装为可编辑模式,并使 speech-to-speech CLI 可用。

支持的组件

组件 后端 平台 安装方式
VAD Silero VAD v5 所有 内置
STT Parakeet TDT(默认) 通过 nano-parakeet 支持 CUDA/CPU,通过 MLX 支持 Apple Silicon 内置
STT 通过 Transformers 的 Whisper CUDA/CPU 内置
STT Faster Whisper CUDA/CPU faster-whisper
STT Lightning Whisper MLX Apple Silicon whisper-mlx
STT MLX Audio Whisper Apple Silicon macOS 上内置
STT Paraformer CUDA/CPU paraformer
LLM OpenAI 兼容 API(responses-api、chat-completions) 托管提供商或自托管服务器 内置
LLM Transformers CUDA/CPU 内置
LLM mlx-lm Apple Silicon macOS 上内置
TTS Qwen3-TTS(默认) Linux 上 GGML/CUDA,macOS 上 mlx-audio 内置
TTS Kokoro-82M CUDA/CPU、Apple Silicon 非 macOS 上 kokoro;macOS 上内置
TTS Pocket TTS CPU/CUDA pocket
TTS ChatTTS CUDA/CPU chattts
TTS MMS TTS CUDA/CPU facebook-mms

使用 --stt、--llm_backend 和 --tts 选择实现。运行 speech-to-speech -h 查看具体数值和后端特定标志。

运行模式

模式 传输方式 使用场景
realtime(默认) WebSocket,OpenAI Realtime 协议,路径 /v1/realtime 你正在针对标准语音 API 构建应用或设备。
local 你机器的麦克风和扬声器 你想直接与该流水线对话,无需客户端。
websocket 通过 WebSocket 传输原始 PCM 你想要一个不使用 Realtime 协议的最小化自定义客户端。
socket 通过 TCP 传输原始 PCM 模型运行在远程服务器上,使用简单的麦克风/播放客户端。

Realtime 服务器

export OPENAI_API_KEY=...
speech-to-speech

等价于:

speech-to-speech \
    --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 \
    --mode realtime

默认模型是通过 OpenAI Responses API 的 gpt-5.4-mini。使用 --model_name 覆盖,并使用 --responses_api_base_url 指向另一个 OpenAI 兼容提供商或服务器。

本地 Mac

speech-to-speech --local_mac_optimal_settings

可选地指定 LLM:

speech-to-speech \
    --local_mac_optimal_settings \
    --model_name mlx-community/Qwen3-4B-Instruct-2507-bf16

该设置:

  • 添加 --device mps 以对所有模型使用 MPS。
  • 为 STT 设置 Parakeet TDT。
  • 将 MLX LM 设置为 LLM 后端。
  • 为 TTS 设置 Qwen3-TTS,默认使用 mlx-audio 及 6bit MLX 变体。
  • 设置 --mode local。

--tts pocket 和 --tts kokoro 在 macOS 上同样有效。

要在本地比较 MLX 量化变体:

python scripts/benchmark_tts.py \
    --handlers qwen3 \
    --iterations 3 \
    --qwen3_mlx_quantizations bf16 4bit 6bit 8bit

WebSocket

  1. 以 WebSocket 模式运行流水线:

    speech-to-speech --mode websocket --ws_host 0.0.0.0 --ws_port 8765
    
  2. 客户端连接到 ws://<server-ip>:8765。发送原始音频字节(16 kHz、int16、单声道 PCM),接收生成的音频字节。

TCP Socket

TCP socket 模式特意保持最小化。它流式传输原始 PCM 音频,但不提供完整的 Realtime API 功能集,包括中断处理、实时转录事件或工具调用事件。

  1. 在服务器上运行流水线:

    speech-to-speech --mode socket --recv_host 0.0.0.0 --send_host 0.0.0.0
    
  2. 在本地运行客户端以处理麦克风输入和播放:

    python scripts/listen_and_play.py --host <IP address of your server>
    

Docker

安装 NVIDIA Container Toolkit,然后:

docker compose up

Compose 文件启动一个带有 Gemma 4 的 llama.cpp 服务器,启动 TCP socket 服务器,并暴露端口 8080、12345 和 12346。

Realtime API

Realtime 模式通过 WebSocket 使用 OpenAI Realtime 协议流式传输音频,支持实时转录和低延迟话轮转换。服务器暴露 /v1/realtime,任何兼容 OpenAI Realtime 的客户端均可连接:

from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:8765/v1",
    websocket_base_url="ws://localhost:8765/v1",
    api_key="not-needed",
)

with client.realtime.connect(model="local") as conn:
    conn.send(
        {
            "type": "session.update",
            "session": {
                "type": "realtime",
                "instructions": "You are a helpful assistant.",
                "audio": {
                    "input": {
                        "turn_detection": {
                            "type": "server_vad",
                            "interrupt_response": True,
                        }
                    }
                },
            },
        }
    )

    for event in conn:
        print(event.type)

服务器实现了核心 Realtime 事件集:入站事件包括 input_audio_buffer.append、session.update、conversation.item.create、response.create 和 response.cancel;出站事件包括语音开始/停止、流式转录、音频增量、工具调用和 response.done。完整的事件参考、架构和设计细节位于 Realtime Engine README。

LLM 后端

LLM 是流水线中计算量最大、延迟最高的组件。对大模型进行一次前向传播可能会主导端到端的响应时间,因此根据你的硬件和延迟预算选择正确的后端非常重要。该流水线支持:

  • 本地推理:CUDA/CPU 上的 transformers 和 Apple Silicon 上的 mlx-lm。
  • 自托管服务器:responses-api 和 chat-completions 可以指向本地 vLLM 或 llama.cpp 服务器。
  • 提供商 API:相同的后端适用于 OpenAI、HF Inference Providers、OpenRouter 和其他 OpenAI 兼容的提供商。

提供两个 API 后端,共享相同的 --responses_api_* 连接标志:

  • --llm_backend responses-api(默认)指向 /v1/responses。
  • --llm_backend chat-completions 指向 /v1/chat/completions。

下面的示例将本地 STT 的 Parakeet TDT 和本地 TTS 的 Qwen3-TTS 与不同的 LLM 后端配对。

Responses API 后端

适用于任何实现 OpenAI Responses API 的提供商或服务器。将 --responses_api_base_url 指向端点,并相应设置 --model_name:

提供商 / 服务器 --responses_api_base_url --responses_api_api_key
OpenAI 省略,使用 OpenAI 默认值 $OPENAI_API_KEY
HF Inference Providers https://router.huggingface.co/v1 $HF_TOKEN
OpenRouter https://openrouter.ai/api/v1 $OPENROUTER_API_KEY
vLLM http://localhost:8000/v1 省略或任意字符串
llama.cpp http://127.0.0.1:8080/v1 空字符串
# OpenAI
speech-to-speech \
    --mode local \
    --stt parakeet-tdt \
    --llm_backend responses-api \
    --tts qwen3 \
    --qwen3_tts_mlx_quantization 6bit \
    --model_name "gpt-4o-mini" \
    --responses_api_api_key "$OPENAI_API_KEY" \
    --responses_api_stream \
    --enable_live_transcription
# HF Inference Providers:通过 Together 的 Qwen3.5-9B
speech-to-speech \
    --mode local \
    --stt parakeet-tdt \
    --llm_backend responses-api \
    --tts qwen3 \
    --qwen3_tts_mlx_quantization 6bit \
    --model_name "Qwen/Qwen3.5-9B:together" \
    --responses_api_base_url "https://router.huggingface.co/v1" \
    --responses_api_api_key "$HF_TOKEN" \
    --responses_api_stream \
    --enable_live_transcription
# HF Inference Providers:通过 Groq 的 GPT-oss-20B
speech-to-speech \
    --stt parakeet-tdt \
    --llm_backend responses-api \
    --tts qwen3 \
    --qwen3_tts_mlx_quantization 6bit \
    --model_name "openai/gpt-oss-20b:groq" \
    --responses_api_base_url "https://router.huggingface.co/v1" \
    --responses_api_api_key "$HF_TOKEN" \
    --responses_api_stream \
    --enable_live_transcription

Chat Completions 后端

配置与 responses-api 相同,重用相同的 --responses_api_* 连接标志,但连接到 /v1/chat/completions 而非 /v1/responses。在以下情况下推荐使用:

  • 提供商在 Responses 路径上忽略 chat_template_kwargs.enable_thinking,并且需要 reasoning_effort 旋钮来抑制推理链,或
  • 服务器的 Responses 流式工具调用路径不可靠,而其 Chat Completions 流式工具调用健壮。这对于某些 vLLM 构建很有用;参见 #312。

添加 --responses_api_reasoning_effort none 以在聊天模板标志无效的提供商上禁用推理:

# 提供 Qwen 模型并支持工具调用的 vLLM
speech-to-speech \
    --mode realtime \
    --stt parakeet-tdt \
    --llm_backend chat-completions \
    --tts qwen3 \
    --model_name "Qwen/Qwen3-4B-Instruct-2507" \
    --responses_api_base_url "http://localhost:8000/v1" \
    --responses_api_stream
# 通过 HF router 在 Cerebras 上运行 Gemma 4 31B,禁用推理以实现低语音延迟
speech-to-speech \
    --mode realtime \
    --stt parakeet-tdt \
    --llm_backend chat-completions \
    --tts qwen3 \
    --model_name "google/gemma-4-31B-it:cerebras" \
    --responses_api_base_url "https://router.huggingface.co/v1" \
    --responses_api_api_key "$HF_TOKEN" \
    --responses_api_reasoning_effort none \
    --responses_api_stream

完全本地

在单独的 llama.cpp 进程中运行 LLM,以获得阻力最小的完全本地设置,如 Reachy Mini local conversation guide 所示:

# 终端 1:提供 Gemma 4 服务的 llama.cpp
llama-server -hf ggml-org/gemma-4-E4B-it-GGUF -np 2 -c 65536 -fa on --swa-full
# 终端 2:使用该本地 LLM 服务器的 speech-to-speech
speech-to-speech \
    --mode realtime \
    --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

当你希望直接通过运行服务器的机器进行对话时,可以使用 --mode local 代替 --mode realtime。进程内本地后端仍然可用:Apple Silicon 上使用 --llm_backend mlx-lm,或 CUDA/CPU 上使用 --llm_backend transformers。

多语言支持

语言覆盖范围取决于你选择的 STT 和 TTS 后端,而非流水线本身:

组件 后端 语言
STT Parakeet TDT(默认) 25 种欧洲语言
STT Whisper / Whisper MLX / Faster Whisper 广泛的多语言覆盖,取决于所选 Whisper 检查点
STT Paraformer 取决于所选 FunASR 检查点;默认以中文为主
TTS Qwen3-TTS(默认) 多语言,默认 --qwen3_tts_language auto
TTS Kokoro 多种语言/语音映射,取决于后端可用性
TTS ChatTTS 英语和中文
TTS MMS TTS 通过 MMS 检查点实现广泛的多语言覆盖

确保你配对的 STT、LLM 和 TTS 都覆盖你的目标语言。两种使用模式:

  • 单一语言:将 --language 设置为目标语言代码。默认为 en。
  • 语言切换:设置 --language auto。STT 检测每个语音提示的语言,并将其转发给 LLM。可选地添加 --enable_lang_prompt,以附加一条“请用...回复我的消息”的指令。默认为 False;大型 LLM 通常从上下文中推断语言,但显式指令可以帮助较小的模型。

自动语言检测:

speech-to-speech \
    --stt parakeet-tdt \
    --language auto \
    --llm_backend mlx-lm \
    --model_name "mlx-community/Qwen3-4B-Instruct-2507-bf16"

单一非英语语言(本例为中文):

speech-to-speech \
    --stt whisper-mlx \
    --stt_model_name large-v3 \
    --language zh \
    --llm_backend mlx-lm \
    --model_name mlx-community/Qwen3-4B-Instruct-2507-bf16

两个命令也可在 --local_mac_optimal_settings 基础上使用;显式的 --stt 标志会覆盖其设置的默认值。

Pocket TTS

Kyutai Labs 的 Pocket TTS 提供带有语音克隆的流式 TTS:

speech-to-speech \
    --tts pocket \
    --pocket_tts_voice jean \
    --pocket_tts_device cpu

可用的语音预设:alba、marius、javert、jean、fantine、cosette、eponine、azelma。自定义语音文件和 Hugging Face 路径也可用。

CLI 参考

所有 CLI 参数的参考信息位于 arguments classes 和 speech-to-speech -h 中。

模块级参数

参见 ModuleArguments。它可以设置:

  • 公共的 --device,如果每个部分应在同一设备上运行
  • --mode:realtime(默认)、local、socket 或 websocket
  • STT 实现(--stt)
  • LLM 后端(--llm_backend:transformers、mlx-lm、responses-api 或 chat-completions)
  • TTS 实现(--tts)
  • 日志级别
  • 实时流水线池大小(--num_pipelines)

VAD 参数

参见 VADHandlerArguments。值得注意的选项:

  • --thresh:触发语音活动检测的阈值。
  • --min_speech_ms:被认定为语音的检测到语音活动的最小持续时间。
  • --min_speech_continuation_ms:对于在重新打开窗口内继续一个可重新打开的软结束、未提交话轮的语音,维持磁滞阈值。默认且推荐的配对为 --min_speech_ms 384 --min_speech_continuation_ms 192。
  • --min_silence_ms:用于分割语音的静音间隔最小长度。默认值 64 ms。
  • --short_segment_merge_ms:可选合并窗口,用于缝合每个都短于 --min_speech_ms 的相邻 VAD 片段。
  • --unanswered_reopen_ms:一个限制上限,用于控制一个尚未收到任何助手输出的软结束推测性话轮保持可重新打开状态的最长时间。

STT、LLM 和 TTS 参数

每个 STT、LLM 和 TTS 实现都暴露了 model_name、torch_dtype 和 device。STT 和 TTS 参数使用处理程序前缀,例如 --stt_model_name 或 --qwen3_tts_device。LLM 模型选择和聊天设置通过不带前缀的标志在所有后端间共享,例如 --model_name 和 --chat_size;后端特定标志对于 responses-api 和 chat-completions 后端使用 responses_api_ 前缀,对于本地后端使用 llm_ 前缀。

例如:

# 本地 transformers/mlx-lm 后端
--model_name google/gemma-2b-it

# OpenAI 兼容后端
--llm_backend responses-api --model_name deepseek-chat --responses_api_base_url https://api.deepseek.com

生成参数

其他生成参数可以使用处理程序前缀加上 _gen_ 设置,例如 --stt_gen_max_new_tokens 128 或 --llm_gen_temperature 0.7。尚未暴露的参数可以添加到相关的参数类中。

贡献

欢迎提交 Issue 和 PR。一个很好的起点是 open issues。对于较大改动,请先开 issue 讨论方法。

本地开发:

uv sync
pytest
ruff check

星标历史

Star History Chart

引用

如果你使用了该流水线,请同时引用你运行的组件模型。默认组件如下:

Silero VAD

@misc{SileroVAD,
  author = {Silero Team},
  title = {Silero VAD: pre-trained enterprise-grade Voice Activity Detector (VAD), Number Detector and Language Classifier},
  year = {2021},
  publisher = {GitHub},
  journal = {GitHub repository},
  howpublished = {\url{https://github.com/snakers4/silero-vad}},
  email = {hello@silero.ai}
}

Parakeet TDT

@misc{parakeet-tdt,
  author = {NVIDIA},
  title = {Parakeet TDT 0.6B v3},
  publisher = {Hugging Face},
  howpublished = {\url{https://huggingface.co/nvidia/parakeet-tdt-0.6b-v3}}
}

Qwen3-TTS

@misc{qwen3-tts,
  author = {Qwen Team},
  title = {Qwen3-TTS},
  publisher = {Hugging Face},
  howpublished = {\url{https://huggingface.co/Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice}}
}

可选后端(如 Kokoro、Pocket TTS、ChatTTS、Whisper 变体、Paraformer 和 MMS)的引用位于各自的 component READMEs 中。

开源项目huggingface2026-07-28原文

相关内容