语音智能体流水线深度拆解:VAD→STT→LLM→TTS 四级管线如何用开源模型复刻 OpenAI Realtime
引言:语音,正在成为 AI 的默认交互层
2026 年的今天,如果你还在用键盘跟 AI 对话,你大概已经落后了半个时代。
过去一年里,语音交互发生了两件看似矛盾的事:一方面,以 OpenAI Realtime API 为代表的云端实时语音方案把「对话式 AI」的体验拉到了电影级别——延迟低于 800ms,支持随时插话打断,语气、停顿、情绪全部在线;另一方面,越来越多的开发者开始把语音能力从云端搬回本地,用一堆开源模型拼出一条完全自托管的语音流水线。
为什么?三个字:成本、隐私、可控。
云端 Realtime API 按分钟计费,一个每天工作 8 小时的语音客服机器人,一个月下来账单能让你怀疑人生;语音数据本身就是敏感数据——医疗问诊、金融咨询、会议纪要,没有几家公司敢把原始语音流直接扔给第三方;更别提那些需要离线运行、断网可用的场景:车载、机器人、工厂车间、手术室。
huggingface/speech-to-speech 这个项目,就是在这样的背景下冲上了 GitHub Trending。它的定位非常清晰:用开源模型构建本地语音智能体。核心是一条 VAD→STT→LLM→TTS 的四级流水线,通过一个 OpenAI Realtime 兼容的 WebSocket API 对外暴露。已经跑在数千台 Reachy Mini 机器人上做生产级对话后端。
这篇文章,我带你把这套系统的每一层拆开:从四级流水线的核心概念,到模块化架构的线程模型,再到真实的代码实战(包括最难搞的打断处理),最后是一份延迟优化的实操清单。读完你不仅能跑起来一个本地语音助手,还能理解语音智能体工程化的全部关键决策点。
一、背景:为什么语音智能体需要一条「流水线」
1.1 从聊天机器人到语音智能体的范式跃迁
先想一个问题:ChatGPT 网页版和 Siri 有什么区别?
表面上看,前者是文字,后者是语音。但工程上的差异是天壤之别的。文字对话是「请求-响应」模型:你打一句话,模型回一段文字,完事。语音对话是实时流式模型:用户的嘴在动,系统就得开始听;用户话没说完,系统就得判断他是不是要停;模型开始生成回答,系统就得边生成边合成语音边播出去;用户听到一半突然插话,系统还得立刻闭嘴。
这中间任何一环做得不够好,体验就是灾难级的:要么「你等 3 秒它才开口」,要么「你说话它听不见」,要么「它自己跟自己吵起来了」。
所以语音智能体从来不是「语音识别 + 聊天机器人 + 语音合成」三个模块的简单拼接,而是一条流水线——每个阶段独立处理、独立调度、独立优化,阶段之间用队列和协议衔接,任何一个环节都可以单独替换升级。
1.2 为什么是 OpenAI Realtime 兼容
这里有一个非常重要的工程决策:speech-to-speech 对外暴露的不是什么自定义协议,而是 OpenAI Realtime 兼容的 WebSocket API。
这个决策的精妙之处在于生态复用。OpenAI Realtime API 已经有一堆成熟的客户端 SDK、前端组件、测试工具。你做兼容层,意味着:
- 前端可以直接用
openai-realtime-client这类库,不用重写 - 从 OpenAI 云端切到自托管,客户端代码零改动
- 大量现成的测试工具和示例可以直接跑
协议兼容是最被低估的护城河。协议一旦统一,底层实现随便换——今天用云端 GPT,明天换本地 Gemma,后天换 DeepSeek,客户端无感。这跟当年 PostgreSQL 兼容 MySQL 协议、Docker 兼容 OCI 规范是同一个逻辑:锁定协议,而不是锁定实现。
1.3 四级流水线的延迟预算
先建立一个全局认知:语音对话的「实时感」有硬指标。业界公认的经验值:
- < 300ms:感觉像本地应用,几乎无感知
- 300-800ms:正常对话,人会觉得「它反应挺快」
- 800ms-2s:明显卡顿,用户开始不耐烦
- > 2s:用户会以为系统坏了,开始重复说话
人类对话的「轮流」间隔大约是 200-500ms。你要在 800ms 内完成「听清 → 听懂 → 想好 → 说出来」四件事,每一环的预算大概是:
VAD 判定语音结束 ~100ms
STT 转写 ~150-300ms(流式,边听边出字)
LLM 首 token ~100-300ms(流式,不等完整回答)
TTS 首包音频 ~100-200ms(流式合成,不等完整音频)
─────────────────────────────
合计 ~450-900ms
看出来了吗?每一级都必须流式。如果哪个环节是「等全部完成再交给下一级」,总延迟直接翻倍甚至翻三倍。这条流水线的全部设计,都围绕「怎么让每一级尽早把部分结果吐给下一级」展开。
二、核心概念:四级流水线的每一级
2.1 VAD:语音活动检测
VAD(Voice Activity Detection)是流水线的入口,负责回答一个问题:「人开始说话了吗?说完了吗?」
它是最容易被忽视、却最影响体验的组件。VAD 判断得太晚,开头几个字就被吞了;判断得太早,停顿一下就被当成「话说完了」;噪声环境下误触发,系统就会对着空气回答。
speech-to-speech 默认使用 Silero VAD v5。这是目前开源 VAD 的事实标准:
- 基于 ONNX Runtime,纯 CPU 就能跑,单帧推理 < 1ms
- 输入 16kHz 单声道音频,按 512 样本(32ms)为一帧处理
- 输出每个语音帧的「语音概率」0~1
- 内部有状态(h 和 c 两个 LSTM 隐状态),支持流式调用
Silero 的核心优势是流式友好:它不像 WebrtcVAD 那样只看当前帧,而是带记忆地判断——前面一直在说话、中间小停顿,它知道这是句内停顿而不是句尾。这一点对打断检测至关重要。
# Silero VAD 流式使用示意
import onnxruntime as ort
import numpy as np
session = ort.InferenceSession("silero_vad_v5.onnx")
h = np.zeros((2, 1, 64), dtype=np.float32) # LSTM 隐状态
c = np.zeros((2, 1, 64), dtype=np.float32)
def process_frame(audio_16k: np.ndarray, sr: int = 16000) -> float:
global h, c
# 每帧 512 样本 = 32ms
assert len(audio_16k) == 512
audio = audio_16k.astype(np.float32) / 32768.0
out, h, c = session.run(
["output", "stateH", "stateC"],
{"input": audio[None, None, :], "stateH": h, "stateC": c, "sr": np.array(sr, dtype=np.int64)},
)
return out[0][0] # 语音概率
生产使用时的判定策略是带迟滞的:开始说话阈值 0.5,结束说话阈值 0.3,加上最短语音时长(比如 250ms)和最长静音容忍(比如 800ms)。低于开始阈值不启动;一旦启动,要连续低于结束阈值超过静音容忍时长,才判定「话说完了」。这就是经典的「进入/退出双阈值 + 回滞」状态机,能极大减少误判。
2.2 STT:语音转文本
STT 把音频流变成文本流。speech-to-speech 支持三个后端:
| 后端 | 特点 | 适用场景 |
|---|---|---|
| Parakeet TDT(默认) | NVIDIA 出品,流式,支持部分转录 | 低延迟首选 |
| Whisper / Faster-Whisper | 精度高,多语言强 | 追求准确率 |
| Paraformer | 阿里开源,中文友好 | 中文场景 |
关键概念是部分转录(partial transcription)。传统 STT 是「整句识别」:你说完一句话,它给你完整文本。流式 STT 是「边说边出字」:你刚说出「今」,它先给你 partial: "今",你继续说,它更新为 partial: "今天天",等检测到句子结束,它给出 final: "今天的天气怎么样"。
// 流式 STT 的事件流(示意)
{"type": "transcript.partial", "text": "今天"}
{"type": "transcript.partial", "text": "今天的天气"}
{"type": "transcript.final", "text": "今天的天气怎么样"}
这个「部分转录」机制是整个低延迟架构的基石之一:LLM 可以在用户说完之前就开始「预热」——虽然不能真的提前生成回答,但可以提前把上下文、系统提示词准备好,把首 token 延迟再压一压。
2.3 LLM:语言模型
LLM 是大脑。speech-to-speech 的 LLM 插槽说话的是 OpenAI 兼容协议,所以它可以是:
- 任何云端 OpenAI 兼容 API(GPT、DeepSeek、Qwen、Kimi……)
- 本地 llama.cpp / llama-server(
-hf ggml-org/gemma-4-E4B-it-GGUF) - vLLM 自托管
- HuggingFace Inference Providers
这里有个很有意思的细节:它支持流式文本 + 工具调用。这意味着你的语音助手不只是聊天,还能调用函数——查天气、订闹钟、操作智能家居。工具调用能力是语音智能体从「玩具」走向「生产力工具」的分水岭。
2.4 TTS:文本转语音
TTS 把文本变回语音。默认 Qwen3-TTS(GGML 格式,CPU/CUDA/macOS 都能跑),也支持 Kokoro-82M、PocketTTS。
语音智能体对 TTS 的要求和「配音」完全不同:
- 首包延迟要低:不是等整段合成完,而是合成出前几百毫秒音频就立刻推给播放器
- 支持流式合成:边生成边播,才能做到「模型还没说完,声音已经开始」
- 支持打断:用户插话时,TTS 要能立刻停止、丢弃未合成的部分
- 自然度:停顿、语气词、情绪——Qwen3-TTS 这类新一代模型在这些方面已经相当能打
值得一提的时事背景:就在 2026 年 7 月 20 日,阿里发布了 Qwen-Audio-3.0-TTS,Flash 版本首包延迟压到 300ms 级别,登顶 Artificial Analysis 榜单。语音合成正在从「能说话」走向「会表演」,这给本地语音智能体的体验上限又抬高了一截。
三、架构分析:模块化与线程模型
3.1 设计哲学:一切皆可替换
speech-to-speech 的核心设计原则一句话:每个组件都是可插拔的插槽,组件之间只通过标准接口通信。
┌─────────────────────────────────────────────────────┐
│ speech-to-speech server │
│ │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ VAD │→ │ STT │→ │ LLM │→ │ TTS │ │
│ │Silero│ │Para- │ │OpenAI│ │Qwen3-│ │
│ │ v5 │ │keet │ │兼容 │ │ TTS │ │
│ └──────┘ └──────┘ └──────┘ └──────┘ │
│ ↑队列 ↑队列 ↑队列 ↑队列 │
│ │
│ ┌───────────────────────────────────────────────┐ │
│ │ WebSocket 服务端 (ws://host:8765/v1/realtime)│ │
│ │ OpenAI Realtime 兼容协议 │ │
│ └───────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
这种「协议解耦 + 组件插槽」的架构带来三个直接好处:
- 演进自由:明天 NVIDIA 出了更强的 STT,换个实现就行,不动其他任何代码
- 混合部署:VAD 本地、STT 本地、LLM 云端、TTS 本地——每个组件可以独立选择本地或云端,按预算和隐私要求灵活组合
- 测试友好:每个组件可以单独 mock,流水线端到端测试和单组件单元测试可以分开写
3.2 线程模型:每级一线程,队列做缓冲
语音流水线最常见的实现错误是「同步串行」:一个线程里 听完→识别→生成→合成,整条链路一个慢组件拖垮全部。speech-to-speech 的做法是每个阶段一个独立线程,阶段之间用线程安全队列连接:
- 采集线程:从麦克风读音频帧(16kHz 16bit 单声道)→ 推入 VAD 队列
- VAD/STT 线程:消费音频帧,跑 VAD 判定语音边界,把有效语音段送 STT → 产出文本事件推入 LLM 队列
- LLM 线程:消费文本,流式生成回答 → 文本块推入 TTS 队列
- TTS 线程:消费文本块,流式合成音频 → 音频帧推入播放队列
- 播放线程:把音频帧送到扬声器
为什么用队列而不是直接调用?因为每一级的处理速度不同。STT 可能比采集慢(需要攒够上下文),LLM 可能比 STT 慢(首 token 延迟),TTS 可能比 LLM 慢(音频合成)。队列天然提供了背压(backpressure)和削峰填谷的能力:慢的一级不会阻塞快的一级,快的一级不会丢数据。
Python 里具体实现时,线程安全队列用 queue.Queue,每个 worker 是 threading.Thread 的守护线程,用 sentinel 对象做优雅退出信号:
import queue
import threading
class PipelineStage(threading.Thread):
def __init__(self, name, in_q, out_q=None):
super().__init__(daemon=True, name=name)
self.in_q = in_q
self.out_q = out_q
self._stop = object() # sentinel
def run(self):
while True:
item = self.in_q.get()
if item is self._stop:
break
result = self.process(item)
if result is not None and self.out_q:
self.out_q.put(result)
def process(self, item):
raise NotImplementedError
def shutdown(self):
self.in_q.put(self._stop)
3.3 OpenAI Realtime 协议:事件驱动的双向通信
整个服务端对外是一个 WebSocket 端点。协议的核心是事件(event)——客户端和服务端之间通过 JSON 事件双向通信,而不是传统的「请求-响应」。
关键事件类型:
| 方向 | 事件 | 作用 |
|---|---|---|
| C→S | session.update | 配置会话(模型、指令、语音) |
| C→S | input_audio_buffer.append | 上传音频块(base64 PCM16) |
| C→S | input_audio_buffer.commit | 提交音频缓冲区(触发 VAD/STT) |
| C→S | response.cancel | 打断当前响应 |
| S→C | conversation.item.input_audio_transcription.completed | STT 最终结果 |
| S→C | response.audio_transcript.delta | LLM 文本增量 |
| S→C | response.audio.delta | TTS 音频增量 |
| S→C | response.done | 响应结束 |
客户端的核心循环大概是:
import asyncio, base64, json
import websockets
async def client_loop(ws):
# 1. 配置会话
await ws.send(json.dumps({
"type": "session.update",
"session": {
"instructions": "你是一个友好的语音助手,回答尽量简短自然。",
"voice": "alloy",
"modalities": ["text", "audio"],
},
}))
# 2. 流式发送音频 + 处理事件
async for raw in mic_stream():
await ws.send(json.dumps({
"type": "input_audio_buffer.append",
"audio": base64.b64encode(raw).decode(),
}))
# 3. 处理服务端事件(伪代码)
async for msg in ws:
evt = json.loads(msg)
if evt["type"] == "response.audio.delta":
play_audio(base64.b64decode(evt["delta"]))
elif evt["type"] == "response.audio_transcript.delta":
print(evt["delta"], end="", flush=True)
这套协议的妙处:音频和文本可以同时双向流动。用户说话的音频在上行,模型回答的音频在下行,两边互不阻塞。这正是「能打断」的协议基础——response.cancel 事件一发出,服务端立刻停止 TTS 合成和下行音频。
四、代码实战:从零搭建本地语音智能体
4.1 快速起步
# 安装(Python 3.10+)
pip install speech-to-speech
# 方式 A:LLM 用云端 API
export OPENAI_API_KEY="sk-..."
speech-to-speech
# 默认启动 ws://localhost:8765/v1/realtime
# 默认组件:Silero VAD + Parakeet TDT(STT) + OpenAI兼容LLM + Qwen3-TTS
# 方式 B:完全本地(用 llama.cpp 跑 LLM)
llama-server -hf ggml-org/gemma-4-E4B-it-GGUF -np 2 -c 65536 -fa on --swa-full
# 然后配置 LLM 指向本地 llama-server 的 OpenAI 兼容端点
跑起来之后,用项目自带的客户端验证:
python scripts/listen_and_play_realtime.py --host 127.0.0.1 --port 8765
对着麦克风说话,扬声器里应该能听到模型用中文回答。到这一步,你已经有了一个「可以说话的本地 AI」。
4.2 自定义组件:把插槽换成你的实现
真正生产使用,几乎必然要替换组件。比如中文场景想换 Paraformer,或者想换一个特定的 TTS。speech-to-speech 的组件接口设计得很干净——每个插槽是一个基类,你只需要实现它的核心方法。
以自定义 STT 为例:
# 伪代码示意:实现一个 STT 组件
class MySTT(STTBase):
def __init__(self, model_path: str):
# 加载模型,建立上下文
self.model = load_my_stt(model_path)
def transcribe(self, audio: bytes, partial: bool = True):
"""输入音频帧,返回 (is_final, text)"""
text = self.model.decode(audio)
# partial=True 时返回部分转录,False 时输出最终结果
return text
def reset(self):
"""一轮对话结束,清空内部状态"""
self.model.reset_state()
组件注册后,服务启动时按配置加载对应实现。由于所有组件都走统一接口,你甚至可以写一个「组合组件」——白天用云端 Whisper 大模型,晚上切本地小模型,同一个插槽,按配置切换。
4.3 最难的部分:打断(Barge-in)处理
打断是语音智能体工程里公认的「地狱难度」。场景:模型正在回答,用户突然说「等等,我不是这个意思」。系统必须做到:
- 立刻停止 TTS 播放——用户已经听到的声音收不回,但没播的必须停
- 清空 TTS 队列——已合成未播放的音频块全部丢弃
- LLM 上下文要保留——打断的话是新的用户输入,但之前的对话历史不能丢
- 时序竞态——「用户新说的话」和「模型正在生成的话」同时存在,必须让新的覆盖旧的
实现上,打断检测的关键在于 VAD 的灵敏度切换:TTS 播放期间,VAD 的触发阈值要降低(因为扬声器在响,麦克风拾到的环境噪声更大,但用户插话必须立刻被识别)。这就是所谓的「echo cancellation + barge-in」联合处理。
# 打断处理的核心逻辑(伪代码)
class ConversationManager:
def __init__(self):
self.is_speaking = False # 模型是否正在说话
self.tts_queue = queue.Queue()
def on_vad_speech_start(self):
# 用户开始说话
if self.is_speaking:
# 模型正在回答 → 打断!
self.tts_queue.clear() # 1. 清空待播音频
self.tts.stop() # 2. 停止当前播放
self.llm.cancel_generation() # 3. 取消 LLM 生成
# 注意:对话历史保留,把打断内容作为新输入
def on_tts_audio(self, chunk):
if self.is_interrupted:
return # 打断后,后续音频块直接丢弃
self.tts_queue.put(chunk)
这里有一个容易被忽略的细节:打断后 TTS 引擎的状态复位。Qwen3-TTS 这类流式合成器内部有状态(音素上下文、韵律缓冲),打断后必须调用 reset,否则下一次合成会「带着上一句的尾巴」。这个 bug 的典型症状是:打断一次之后,模型回答的开头总是奇怪地拖长或吞字。
4.4 会话状态管理
语音智能体是有状态的:用户说「帮我定个明早 8 点的闹钟」,下一句「再帮我加一个 9 点的」,系统必须记得「明早」这个上下文。所以流水线里需要维护:
- 对话历史:STT 结果 + LLM 回答的文本记录
- 工具调用状态:如果 LLM 调用了函数,函数执行结果要回到 LLM
- 用户配置文件:语音偏好、时区、称呼
speech-to-speech 通过 OpenAI Realtime 协议的 conversation 机制管理这些状态,每次 session.update 或工具调用都会更新会话上下文。对于长会话,还要考虑上下文窗口管理:超过窗口长度时,把最早的对话做摘要压缩(这正是 MCP 协议无状态化重构浪潮下,语音智能体依然保持有状态会话的一个典型反例——语音对话的连续性太重要,无状态化在这里代价过高)。
五、性能优化:把延迟从 2 秒压到 800 毫秒
5.1 延迟预算的工程拆解
前面说过总预算约 800ms。实际工程里,每个环节都有可榨的水分:
VAD 环节
- Silero v5 单帧推理 < 1ms,主要开销在音频预处理。注意统一采样率:麦克风如果输出 48kHz,要先降采样到 16kHz,这个转换本身有延迟(滤波器群延迟),用高质量的短滤波器
- 判定「话说完了」的静音容忍时长:800ms 太保守,300-500ms 更合适,但要配合噪声底估计,避免误判
STT 环节
- 流式识别用「增量解码」而非「重新解码」:每来一帧新音频,只处理增量部分。有些 STT 实现偷懒,每次把整段音频重新过一遍,延迟直接随语音长度线性增长——这是最常见的性能陷阱
- Parakeet TDT 支持 CUDA 推理,GPU 上单句延迟可压到 100ms 内
- 中文场景 Paraformer 的流式模式延迟表现也很优秀
LLM 环节
- 用流式生成 + 投机解码(speculative decoding):小模型草稿 + 大模型验证,首 token 延迟和吞吐都能提升
- 系统提示词不要写太长——语音助手的 system prompt 每多 100 token,首 token 延迟大约多 5-15ms(视硬件)
- 本地推理用
-np 2(2 个并行序列)允许 prefetch,--swa-full这类优化要按模型规格开 - 考虑「语义缓存」:高频问题(「现在几点」「你是谁」)命中缓存直接返回,跳过完整生成
TTS 环节
- 首包延迟优化:TTS 合成器按「句块」而非「整段」输入。LLM 每吐出一个完整句子,立刻送 TTS。这就是「LLM 流式 + TTS 流式」的衔接点——文本分句器(sentence splitter)的质量直接决定首包延迟
- Qwen3-TTS 的 GGML 版本在 CPU 上就能跑,但 GPU 上首包延迟更低;Flash 类模型(如 Qwen-Audio-3.0-TTS Flash,300ms 级首包)是实时对话的最优解
- 音频格式统一用 24kHz 16bit PCM,避免转码开销
5.2 一个真实的优化案例
假设当前端到端延迟是 1.8s,逐级排查:
1.8s = VAD 判定延迟 500ms(静音容忍设太大)
+ STT 全量重解码 600ms(每次重跑整段)
+ LLM 首 token 400ms
+ TTS 等整段合成完才播 300ms
优化动作:
- 静音容忍 800ms → 400ms,配合噪声门限(-30dBFS 以下直接不算语音)
- STT 改增量解码 → 150ms
- LLM 换投机解码 + 精简 system prompt → 200ms
- TTS 改句块流式 + 首包即播 → 100ms
优化后:400 + 150 + 200 + 100 = 850ms,进入「正常对话」区间。如果 VAD 判定再激进一点(语音结束概率连续 2 帧低于阈值即判定结束),能压到 700ms 左右——但要冒一点「把句内停顿当句尾」的风险,需要根据实际场景调。
5.3 资源占用与并发
本地部署的另一大挑战是资源。一条完整流水线的典型占用:
| 组件 | CPU 占用 | 内存 | 显存 |
|---|---|---|---|
| Silero VAD | ~1% | <100MB | 0 |
| Parakeet TDT | 1-2 核 | ~1GB | 可选 1-2GB |
| LLM (4B 量化) | 多核 | ~4GB | 4-6GB |
| Qwen3-TTS | 1-2 核 | ~1GB | 可选 2-4GB |
所以「全本地」方案一台 16GB 内存 + 8GB 显存的机器就能跑,或者纯 CPU 也能跑(只是 LLM 慢一些)。混合部署(LLM 上云、其他本地)则能把资源需求压到 2GB 内存以内——这在嵌入式设备、树莓派级硬件上是关键决策。
并发方面,一套流水线通常服务一个会话(一个用户)。多用户场景需要多实例或线程池化,注意 VAD 和 TTS 是有状态的,不能跨会话共享实例——这是语音智能体水平扩展与普通 Web 服务最大的区别。
六、生产化:从 Demo 到真正可用
6.1 可靠性与容错
Demo 能跑和产品可用之间,隔着大量工程细节:
- 音频质量监控:采集端掉帧、爆音、静音,都要有指标。PCM 流里插入一个
audio monitor组件,统计 RMS 能量、削波率(clipping ratio)、连续静音时长 - 超时熔断:LLM 挂了怎么办?STT 卡死怎么办?每一级都要有超时和降级策略——LLM 超时直接返回「我没听清,请再说一遍」,STT 超时跳过当前音频块
- 优雅退出:WebSocket 断开时,清空所有队列、复位所有有状态组件(VAD 隐状态、TTS 状态),防止下一个会话「继承」上一个会话的脏状态
- 日志与追踪:给每个音频块打 trace id,端到端追踪「这段音频从进来到回答出去」的每一级耗时。没有这个,延迟优化就是瞎猜
6.2 安全与隐私
本地部署的核心卖点就是隐私,但隐私需要工程保证:
- 音频流默认不留存,必须留存的做脱敏(去除身份信息)
- WebSocket 上 TLS 是必须的(
wss://),否则原始语音在网络上是明文 - 组件鉴权:服务端口不要裸奔,加 token 校验
- 工具调用权限最小化:语音助手能调用的函数要白名单化——一个能语音触发「删库」的助手,不是助手,是事故
6.3 评测:怎么知道你的流水线够好
语音智能体的评测维度:
- 延迟分位数:p50/p90/p95 端到端延迟。p95 比 p50 重要——偶发卡顿毁掉的是整个会话的信任
- 打断成功率:人为打断 100 次,成功停止 TTS 多少次、上下文是否保留正确
- WER(词错误率):STT 的准确率,特别是打断后的残留音频、噪声场景
- 用户感知分(MOS):TTS 自然度。注意「自然」不等于「慢」——实时对话场景,稍微快一点的语速反而更自然
- 长会话稳定性:跑 1 小时连续对话,内存不涨、延迟不劣化、上下文不串
建议把这套评测做成 CI 流程的一部分,每次换模型、调参数都跑一遍基线,用数据说话而不是凭感觉。
七、总结与展望
7.1 本文要点回顾
- 语音智能体是实时流式流水线(VAD→STT→LLM→TTS),不是模块拼接;每级必须流式,否则延迟失控
- 协议兼容是最大的护城河:OpenAI Realtime 兼容让自托管方案直接复用整个生态的客户端和工具
- 模块化 + 队列线程模型是架构核心:每级独立线程、队列解耦、组件可替换,支持本地/云端混合部署
- 打断(barge-in)是最难的工程点:VAD 灵敏度切换 + TTS 状态复位 + 上下文保留,三者缺一不可
- 延迟优化要逐级拆预算:VAD 判定阈值、STT 增量解码、LLM 投机解码、TTS 句块流式,每一级都能省下几百毫秒
- 生产化 = 可靠性 + 安全 + 评测体系,Demo 到产品之间隔着大量细节
7.2 趋势判断
语音智能体的下一阶段,我认为有四个方向值得关注:
端到端语音模型:VAD→STT→LLM→TTS 的四级流水线,本质上是一种工程妥协——每一级都有信息损失(音频→文本丢语气,文本→音频丢口音)。端到端模型(音频进、音频出,中间不分级)正在快速发展,未来可能会颠覆这条流水线。但短期内,模块化流水线在可替换性、可调试性、资源灵活性上的优势依然明显——就像微服务之于单体应用,各有各的适用场景。
语音智能体 + 工具调用:当语音助手能真正操作软件、控制设备、执行任务时,它就从「聊天玩具」变成了「数字员工」。speech-to-speech 对工具调用的支持,正是这个方向的基础设施。
多模态融合:语音 + 视觉 + 触觉。机器人场景(Reachy Mini 就是例子)里,语音只是交互通道之一,未来的智能体要同时处理「看」和「听」。
Agent 基础设施的标准化:随着 MCP 协议无状态化、Agent 调度层(如 Google Agent Substrate)的兴起,语音智能体会成为更大 Agent 生态的一个「感官接口」。流水线本身可能会被抽象成 Agent 框架里的一个标准「工具」。
最后说句实在话:这套流水线的门槛,已经从「大厂专属」降到了「一个周末能跑通」。语音交互作为 AI 的默认交互层,正在从云端垄断走向百花齐放。工具已经齐了,剩下的就看你想用它造什么了。