实时音频处理 本节摘要:批处理流水线处理一个文件,实时流水线必须在下一个 20 毫秒到达之前处理好当前的 20 毫秒。每一个对话式 AI、广播工作室、电话机器人都生死系于这条延迟预算。你想让语音助手「感觉活着」——人类对话轮次切换延迟约 230 毫秒(静音到响应),超过 500 毫秒感觉像机器人,超过 1500 毫秒感觉坏了。2026 年一个完整「听 → 理解 → 响应 → 说」回路的预算约 400 毫秒,从麦克风缓冲(20 ms)到喇叭回放(20 ms),中间夹着流式 ASR、LLM 首 token、流式 TTS。Moshi(Kyutai, 2024)做到 200 毫秒全双工,GPT-4o-realtime 约 320 毫秒;而 2022 年的级联流水线还在 2500 毫秒。
本节摘要:批处理流水线处理一个文件,实时流水线必须在下一个 20 毫秒到达之前处理好当前的 20 毫秒。每一个对话式 AI、广播工作室、电话机器人都生死系于这条延迟预算。你想让语音助手「感觉活着」——人类对话轮次切换延迟约 230 毫秒(静音到响应),超过 500 毫秒感觉像机器人,超过 1500 毫秒感觉坏了。2026 年一个完整「听 → 理解 → 响应 → 说」回路的预算约 400 毫秒,从麦克风缓冲(20 ms)到喇叭回放(20 ms),中间夹着流式 ASR、LLM 首 token、流式 TTS。Moshi(Kyutai, 2024)做到 200 毫秒全双工,GPT-4o-realtime 约 320 毫秒;而 2022 年的级联流水线还在 2500 毫秒。10 倍提升来自三项技术:处处流式、带部分结果的异步流水线、可中断生成。本节将讲透环形缓冲、VAD 门控、流式 ASR、抢话(barge-in)中断、WebRTC Opus 传输、抖动缓冲,以及线程争用、重采样延迟、TTS 预热、回声消除等常见坑。读完本节,你能为任意实时语音场景设计逐级延迟预算,并避开「缓冲 500 毫秒求保险」「TTS 块太小」「无抖动缓冲」等陷阱。
阅读完本节,你应当能够:
人类对话轮次切换延迟约 230 毫秒(静音到响应)。超过 500 毫秒感觉像机器人,超过 1500 毫秒感觉坏了。2026 年一个完整「听 → 理解 → 响应 → 说」回路的预算:
| 阶段 | 预算 |
|---|---|
| 麦克风 → 缓冲 | 20 ms |
| VAD | 10 ms |
| ASR(流式) | 150 ms |
| LLM(首 token) | 100 ms |
| TTS(首块) | 100 ms |
| 渲染 → 喇叭 | 20 ms |
| 合计 | 约 400 ms |
Moshi(Kyutai, 2024)做到 200 毫秒全双工,GPT-4o-realtime(2024)约 320 毫秒。2022 年的级联流水线还在 2500 毫秒。10 倍提升来自三项技术:(1) 处处流式;(2) 带部分结果的异步流水线;(3) 可中断生成。
实时音频以固定大小的块流动。常见选择:20 毫秒(16 kHz 下 320 样本)。下游一切都必须跟上这个节奏。
固定大小的循环缓冲。生产者线程写新帧,消费者线程读。避免在热路径上分配内存。大小 ≈ 最大延迟 × 采样率;一个 2 秒 16 kHz 环 = 32 000 样本。
没人说话时门控下游工作。Silero VAD 4.0(2024)在 CPU 上每 30 毫秒帧 <1 毫秒。webrtcvad 是较老的替代。
音频到达时即吐部分转录的模型。Parakeet-CTC-0.6B 流式模式(NeMo, 2024)在 320 毫秒延迟下做到 2~5% WER。Whisper-Streaming(Macháček et al., 2023)把 Whisper 分块成近流式,延迟约 2 秒。
当用户在助手说话时插话,你必须 (a) 检测抢话,(b) 停止 TTS,(c) 丢弃剩余 LLM 输出——全部在 100 毫秒内完成,否则用户感觉助手是聋子。
20 毫秒帧,48 kHz,自适应码率 8~128 kbps。浏览器与移动端的标准。LiveKit、Daily.co、Pion 是 2026 年搭语音应用的技术栈。
网络包乱序/迟到。抖动缓冲负责重排与平滑;太小 → 听感断续,太大 → 延迟。典型 60~80 毫秒。
soxr_hq)。💡 延迟预算里每一毫秒都是真金白银。人类对「单向延迟 < 150 毫秒」感知为即时,「150~300 毫秒」感知为流畅,「> 300 毫秒」开始改变对话节奏(人会不自觉地拉长停顿、抢话)。所以实时语音工程的本质,是把每一级的尾延迟(latency)而非吞吐量(throughput)优化到极致——一个每秒能处理 1000 路但每路要等 2 秒的系统,在对话场景里毫无用处。
import collections class RingBuffer: def __init__(self, capacity): self.buf = collections.deque(maxlen=capacity) def write(self, frame): self.buf.extend(frame) def read(self, n): return [self.buf.popleft() for _ in range(min(n, len(self.buf)))] def level(self): return len(self.buf)
容量决定最大缓冲延迟。16 kHz 下 32 000 样本 = 2 秒。
def simple_energy_vad(frame, threshold=0.01): return sum(x * x for x in frame) / len(frame) > threshold ** 2
生产环境换成 Silero VAD:
import torch vad, _ = torch.hub.load("snakers4/silero-vad", "silero_vad") is_speech = vad(torch.tensor(frame), 16000).item() > 0.5
# 经 NeMo 的 Parakeet-CTC-0.6B 流式 from nemo.collections.asr.models import EncDecCTCModelBPE asr = EncDecCTCModelBPE.from_pretrained("nvidia/parakeet-ctc-0.6b") # chunk_ms=320 ms, look_ahead_ms=80 ms for chunk in audio_stream(): partial_text = asr.transcribe_streaming(chunk) print(partial_text, end="\r")
class Dialog: def __init__(self): self.tts_task = None def on_user_speech(self, frame): if self.tts_task and not self.tts_task.done(): self.tts_task.cancel() # 抢话 # 然后喂给流式 ASR def on_final_user_utterance(self, text): self.tts_task = asyncio.create_task(self.reply(text)) async def reply(self, text): async for tts_chunk in llm_then_tts(text): speaker.write(tts_chunk)
hinges on async I/O 与可取消的 TTS 流。WebRTC 在音频轨道上 peerconnection.stop() 是经典做法。
设计要点:抢话中断的难点不是「停 TTS」,而是「丢 LLM 余量」——LLM 通常已在生成后续 token,如果不及时取消,它会把已经被用户打断的话继续说完再喂给 TTS,造成延迟债务。所以
tts_task.cancel()必须连带取消上游的 LLM 流式生成,这是一条贯穿整条异步链路的取消传播。
2026 年技术栈:
| 层 | 选择 |
|---|---|
| 传输 | LiveKit(WebRTC)或 Pion(Go) |
| VAD | Silero VAD 4.0 |
| 流式 ASR | Parakeet-CTC-0.6B 或 Whisper-Streaming |
| LLM 首 token | Groq、Cerebras、vLLM-streaming |
| 流式 TTS | Kokoro 或 ElevenLabs Turbo v2.5 |
| 回声消除 | WebRTC AEC3 |
| 端到端原生 | OpenAI Realtime API 或 Moshi |
⚠️ 五类坑:① 缓冲 500 毫秒求保险——缓冲就是你的延迟下限,要缩小它;② 不固定线程——音频回调跑在比 UI 还低的优先级线程上,负载一来就出爆音;③ TTS 块太小——小于 200 毫秒的块会让声码器瑕疵可闻,320 毫秒是甜点;④ 无抖动缓冲——真实网络有抖动,不平滑就会爆音;⑤ 单次错误处理——音频流水线必须抗崩,一次异常就杀死整个会话。
本节产出技能文档(原课程 outputs/skill-realtime-designer.md),为实时音频流水线设计逐级具体延迟预算。模板:
| 阶段 | 预算 | 实现 |
|---|---|---|
| 麦克风 → 缓冲 | 20 ms | sounddevice 回调,环形缓冲 |
| VAD | 10 ms | Silero VAD 4.0 |
| 流式 ASR | 150 ms | Parakeet-CTC-0.6B chunk=320ms |
| LLM 首 token | 100 ms | Groq / vLLM 流式 |
| 流式 TTS 首 chunk | 100 ms | Kokoro,chunk=320ms |
| 渲染 → 喇叭 | 20 ms | sounddevice 回调 |
| 抢话中断 | <100 ms | async cancel 链路 |
| 抖动缓冲 | 60~80 ms | WebRTC 自带 |
| 回声消除 | — | WebRTC AEC3 |
基础:运行 code/main.py。它模拟环形缓冲 + 能量 VAD,对一段假的 10 秒流打印逐级延迟。
进阶:用 sounddevice 搭一个直通回路,以 20 毫秒帧处理麦克风,每帧打印 VAD 状态。
挑战:用 aiortc 搭全双工回声测试:浏览器 → WebRTC → Python → WebRTC → 浏览器。用 1 kHz 脉冲测玻璃到玻璃延迟。
下一节是本章的毕业项目——把 VAD、流式 ASR、LLM、流式 TTS、抢话中断全部接成一条端到端的语音助手流水线,目标端到端延迟 < 1 秒。