ASR、TTS 与实时语音对话架构
前言
随着 GPT-4o 和 OpenAI Realtime API 的发布,语音交互进入了“原生多模态”时代。然而,构建一个低延迟、高自然度的语音对话系统仍然面临诸多挑战。本文将详细介绍语音交互系统的核心组件与实战架构。
语音对话系统架构
传统的语音对话系统通常采用 ASR -> LLM -> TTS 的级联架构。
┌─────────────────────────────────────────────────────────────────┐
│ 语音对话流水线 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. [VAD] → 检测用户是否开始/停止说话 │
│ │
│ 2. [ASR] → 语音转文字 (Whisper / FunASR) │
│ │
│ 3. [LLM] → 理解意图并生成文本回复 │
│ │
│ 4. [TTS] → 文字转语音 (OpenAI TTS / Fish Speech) │
│ │
│ 5. [播放] → 音频流式输出 │
│ │
└─────────────────────────────────────────────────────────────────┘
语音识别 (ASR)
常用工具对比
| 工具 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Whisper (OpenAI) | 准确度极高,支持多语言 | 速度较慢(非流式) | 离线转录 |
| Groq Whisper | 极速(<100ms) | 云端 API | 实时对话 |
| FunASR (阿里) | 中文优化好,支持流式 | 部署较重 | 国内生产环境 |
| Faster-Whisper | 本地推理优化 | 需要 GPU | 本地部署 |
使用 Faster-Whisper
from faster_whisper import WhisperModel
model_size = "base"
model = WhisperModel(model_size, device="cuda", compute_type="float16")
def transcribe(audio_path: str):
segments, info = model.transcribe(audio_path, beam_size=5)
text = "".join([segment.text for segment in segments])
return text
语音合成 (TTS)
常用工具对比
| 工具 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| OpenAI TTS | 极其自然,简单易用 | 无法克隆声音 | 通用助手 |
| Fish Speech | 开源,支持中英日,效果惊人 | 部署复杂 | 高质量克隆 |
| GPT-SoVITS | 强大的声音克隆能力 | 训练门槛高 | 个性化角色 |
| Edge-TTS | 免费,速度快 | 机械感稍强 | 简单播报 |
使用 OpenAI TTS 流式输出
from openai import OpenAI
client = OpenAI()
def text_to_speech_stream(text: str):
response = client.audio.speech.create(
model="tts-1",
voice="alloy",
input=text,
)
# 流式写入文件或播放器
response.stream_to_file("output.mp3")
---
#### 核心挑战:低延迟与打断处理
在语音对话中,**延迟 (Latency)** 是用户体验的杀手。如果回复延迟超过 1 秒,对话就会变得尴尬。
#### 1. VAD (静音检测) 与打断逻辑
系统必须知道用户什么时候开始说话,什么时候说完。
- **Silero VAD**:目前最流行的轻量级 VAD 模型,可以在 CPU 上实时运行。
- **打断逻辑**:当用户在 AI 说话时突然插话,系统必须立即停止 TTS 播放,并清空当前的生成队列。
```python
# 打断逻辑伪代码
def on_user_speech_start():
if tts_player.is_playing():
tts_player.stop()
llm_chain.cancel_current_generation()
print("用户插话,已停止播放")
2. 流式级联 (Streaming Cascade)
不要等 LLM 生成完所有文字再传给 TTS。
- 策略:LLM 每生成一个完整的句子(遇到句号、问号),就立即发给 TTS 进行合成。这样用户在听第一句时,系统正在合成第二句。
进阶:原生多模态 (OpenAI Realtime API)
传统的 ASR-LLM-TTS 架构存在“信息丢失”:ASR 丢失了用户的语气、情感;TTS 无法根据上下文调整语调。 OpenAI Realtime API 解决了这个问题:它直接在音频流上进行推理。
核心优势
- 极低延迟:通过 WebSocket 实现全双工通信。
- 情感感知:模型能听出用户的焦虑、兴奋或讽刺。
- 原生打断:服务器端自动处理语音打断。
# Realtime API (WebSocket) 概念示例
import websockets
async def voice_chat():
url = "wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview"
async with websockets.connect(url, extra_headers=headers) as ws:
# 发送音频流
await ws.send(audio_chunk)
# 接收音频流并直接播放
async for message in ws:
if message.type == "response.audio.delta":
play_audio(message.delta)
声音克隆与个性化 (TTS)
Fish Speech 和 GPT-SoVITS 让普通人也能拥有高质量的 AI 替身。
- 1-shot 克隆:只需要 5-10 秒的样本音频,就能模仿一个人的音色。
- 跨语言合成:用你的声音说出流利的德语或日语。
总结:构建语音助手的建议
- VAD 是灵魂:一个好的 VAD 能让对话感觉像真人在交流。
- 延迟优化:优先选择支持流式的 ASR 和 TTS。
- 关注情感:在 Prompt 中加入语气指导(如:“请用温柔、耐心的语气回答”)。
- 端侧处理:如果可能,将 VAD 和 ASR 放在客户端运行,减少网络往返。
语音交互是 AI 的终极形态。随着技术的进步,我们正在告别冰冷的文字框,迎来一个可以随时随地“聊天”的 AI 时代。
---
#### 实时性优化:低延迟架构设计
在语音对话中,延迟超过 1 秒就会让用户感到不自然。我们需要在每个环节进行极致优化。
#### 1. 语音活动检测 (VAD) 与打断处理
使用 `Silero VAD` 可以精准识别用户是否说话结束,并支持“打断”逻辑:如果用户在 AI 播放音频时开始说话,立即停止播放。
```python
import torch
import numpy as np
# 加载 Silero VAD 模型
model, utils = torch.hub.load(repo_or_dir='snakers4/silero-vad', model='silero_vad')
(get_speech_timestamps, save_audio, read_audio, VADIterator, collect_chunks) = utils
def process_audio_stream(audio_chunk, is_ai_speaking):
"""
处理实时音频流
audio_chunk: 采样率为 16000 的音频片段
is_ai_speaking: 当前 AI 是否正在播放声音
"""
# 检测当前片段是否包含人声
speech_prob = model(torch.from_numpy(audio_chunk), 16000).item()
if speech_prob > 0.5:
if is_ai_speaking:
# 触发打断逻辑
stop_ai_playback()
clear_audio_buffer()
print("检测到用户插话,停止 AI 播放")
return True # 正在说话
return False
2. 流式级联:Sentence-level Streaming
不要等待 LLM 生成全文。利用标点符号进行“断句流式输出”,将文本片段立即送入 TTS。
async def streaming_pipeline(user_audio):
# 1. ASR (使用 Faster-Whisper 流式)
text_stream = await asr.transcribe_stream(user_audio)
# 2. LLM (流式生成)
full_response = ""
async for chunk in llm.astream(text_stream):
full_response += chunk
# 遇到句号、问号等结束符,立即合成语音
if chunk in ["。", "!", "?", ".", "!", "?"]:
audio_segment = await tts.generate(full_response)
await audio_player.play(audio_segment)
full_response = "" # 清空缓冲区,准备下一句
进阶:Fish Speech 零样本声音克隆
Fish Speech 支持仅凭一段 10 秒的参考音频实现极高相似度的声音克隆。
from fish_speech import FishSpeechClient
client = FishSpeechClient(api_key="your_key")
async def clone_voice(text: str, reference_audio_path: str):
"""
使用参考音频克隆声音并合成新文本
"""
audio_content = await client.tts(
text=text,
ref_audio=reference_audio_path,
format="mp3"
)
with open("cloned_voice.mp3", "wb") as f:
f.write(audio_content)
OpenAI Realtime API
OpenAI 推出的原生多模态接口,跳过了 ASR/TTS 级联,直接进行语音到语音的交互。
import asyncio
import websockets
import json
async def realtime_chat():
url = "wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2024-10-01"
headers = {
"Authorization": f"Bearer {os.getenv('OPENAI_API_KEY')}",
"OpenAI-Beta": "realtime=v1"
}
async with websockets.connect(url, extra_headers=headers) as ws:
# 发送配置
await ws.send(json.dumps({
"type": "session.update",
"session": {
"modalities": ["text", "audio"],
"voice": "alloy"
}
}))
# 发送音频数据 (Base64)
# await ws.send(json.dumps({"type": "input_audio_buffer.append", "audio": "..."}))
完整实战架构:WebRTC 语音助手
对于 Web 应用,推荐使用 WebRTC 传输音频流。
-
前端:使用
MediaRecorder采集音频,通过 WebRTC 传给后端。 -
后端:
- 接收音频流。
- 使用
VAD截断。 - 异步调用
ASR。 -
LLM生成回复。 -
TTS生成音频切片。 - 通过 WebRTC 回传音频流。
常见问题与对策
- 打断处理 (Interruption):当用户在 AI 说话时突然插话,系统需要立即停止当前的 TTS 输出并清空缓冲区。
-
背景噪音:使用
DeepFilterNet或RNNoise进行前端降噪。 - 方言支持:Whisper 对方言支持较好,但特定行业术语可能需要微调 ASR 模型。
总结
语音交互是 LLM 应用的高级形态。虽然级联架构(ASR+LLM+TTS)目前仍是主流且灵活,但原生多模态(如 Realtime API)代表了未来的方向。开发者应根据业务对延迟、成本和个性化(声音克隆)的要求选择合适的技术栈。
参考资源
版权声明: 如无特别声明,本文版权归 sshipanoo 所有,转载请注明本文链接。
(采用 CC BY-NC-SA 4.0 许可协议进行授权)
本文标题:《 LLM应用开发——语音交互系统实战 》
本文链接:http://localhost:3015/ai/%E8%AF%AD%E9%9F%B3%E4%BA%A4%E4%BA%92%E7%B3%BB%E7%BB%9F.html
本文最后一次更新为 天前,文章中的某些内容可能已过时!