Ai_GirlFriend/xuniYou/语音通话完整流程图.md

374 lines
16 KiB
Markdown
Raw Normal View History

2026-02-28 18:04:34 +08:00
# 语音通话完整流程图
## 📊 整体架构
```
┌─────────────┐ WebSocket ┌─────────────┐
│ │ ◄─────────────────────────► │ │
│ 客户端 │ │ 服务器端 │
│ (uni-app) │ │ (FastAPI) │
│ │ │ │
└─────────────┘ └─────────────┘
│ │
│ │
▼ ▼
┌─────────────┐ ┌─────────────────┐
│ 录音管理器 │ │ ASR (阿里云) │
│ recorderMgr │ │ Paraformer │
└─────────────┘ └─────────────────┘
┌─────────────────┐
│ LLM (通义千问) │
│ Qwen-flash │
└─────────────────┘
┌─────────────────┐
│ TTS (阿里云) │
│ CosyVoice-v2 │
└─────────────────┘
```
## 🔄 详细流程
### 阶段1连接建立
```
客户端 服务器
│ │
├─ 1. 打开语音通话页面 │
│ │
├─ 2. 建立 WebSocket 连接 ──────────────►│
│ ws://192.168.1.141:30101/voice/call │
│ │
│ ├─ 3. 验证用户身份
│ │
│ ├─ 4. 创建会话 (VoiceCallSession)
│ │
│ ├─ 5. 启动 ASR (paraformer-realtime-v2)
│ │
│ ├─ 6. 启动后台任务
│ │ - LLM 处理循环
│ │ - TTS 处理循环
│ │ - 空闲检测
│ │ - 静默检测
│ │
│ ◄──────────────────────────────────── ├─ 7. 发送 ready 信号
│ {"type":"ready"} │
│ │
├─ 8. 显示"已连接" │
│ │
```
### 阶段2用户说话优化后的流程
```
客户端 服务器
│ │
├─ 1. 用户按住"按住说话"按钮 │
│ isTalking = true │
│ │
├─ 2. 启动录音 │
│ format: 'pcm' │
│ sampleRate: 16000 │
│ numberOfChannels: 1 │
│ │
├─ 3. 用户说话 3-5 秒 │
│ "你好,今天天气怎么样?" │
│ │
├─ 4. 用户松开按钮 │
│ isTalking = false │
│ │
├─ 5. 停止录音 │
│ recorderManager.stop() │
│ │
├─ 6. 读取录音文件 │
│ tempFilePath → ArrayBuffer │
│ 大小: 160000 bytes (5秒) │
│ │
├─ 7. 开始分片发送 ─────────────────────►│
│ 【官方推荐参数】 │
│ 每片: 3200 bytes │
│ 间隔: 100ms │
│ │
├─ 片段 1 (3200 bytes) ─────────────────►├─ 接收片段 1
│ ├─ feed_audio(data)
│ ├─ recognition.send_audio_frame(data)
│ │
├─ 延迟 100ms │
│ │
├─ 片段 2 (3200 bytes) ─────────────────►├─ 接收片段 2
│ ├─ feed_audio(data)
│ ├─ recognition.send_audio_frame(data)
│ │
├─ 延迟 100ms │
│ │
├─ ... │
│ │
├─ 片段 50 (3200 bytes) ────────────────►├─ 接收片段 50
│ ├─ feed_audio(data)
│ ├─ recognition.send_audio_frame(data)
│ │
├─ 延迟 100ms │
│ │
├─ 发送 "end" 标记 ─────────────────────►├─ 接收 "end"
│ ├─ finalize_asr()
│ ├─ recognition.stop()
│ │
│ ├─ ASR 完成识别
│ │ 识别结果: "你好,今天天气怎么样?"
│ │
```
### 阶段3ASR 识别
```
服务器端流程
├─ 1. ASR 接收音频片段
│ - 片段 1: 3200 bytes
│ - 片段 2: 3200 bytes
│ - ...
│ - 片段 50: 3200 bytes
├─ 2. 实时识别(边收边识别)
│ - 部分结果: "你好"
│ - 部分结果: "你好,今天"
│ - 部分结果: "你好,今天天气"
├─ 3. 收到 "end" 标记
│ - 调用 recognition.stop()
├─ 4. 返回最终结果
│ - is_sentence_end: true
│ - text: "你好,今天天气怎么样?"
├─ 5. 触发回调
│ - WSRecognitionCallback.on_event()
│ - session.handle_sentence(text)
├─ 6. 放入 LLM 队列
│ - asr_to_llm.put(text)
```
### 阶段4LLM 生成回复
```
服务器端流程 客户端
│ │
├─ 1. 从队列获取识别文本 │
│ text = "你好,今天天气怎么样?" │
│ │
├─ 2. 构建对话历史 │
│ history = [ │
│ {role: "system", content: "..."},│
│ {role: "user", content: text} │
│ ] │
│ │
├─ 3. 调用 LLM (Qwen-flash) │
│ stream = chat_completion_stream() │
│ │
├─ 4. 流式接收 LLM 输出 │
│ chunk 1: "你" │
│ chunk 2: "好" │
│ chunk 3: "呀" │
│ chunk 4: "" ◄─ 遇到标点 │
│ │
├─ 5. 发送文本给客户端 ─────────────────►├─ 收到 reply_text
│ {"type":"reply_text", │ 显示文字
│ "text":"你好呀,今天..."} │
│ │
├─ 6. 放入 TTS 队列 │
│ llm_to_tts.put("你好呀,") │
│ │
├─ 7. 继续接收 LLM 输出 │
│ chunk 5: "今" │
│ chunk 6: "天" │
│ ... │
│ │
├─ 8. LLM 完成 │
│ llm_to_tts.put(END_OF_TTS) │
│ │
```
### 阶段5TTS 合成语音
```
服务器端流程 客户端
│ │
├─ 1. 从队列获取文本片段 │
│ text = "你好呀," │
│ │
├─ 2. 清理文本 │
│ - 去除 Markdown 符号 │
│ - 去除动作描述 │
│ - 去除波浪线 │
│ │
├─ 3. 调用 TTS (CosyVoice-v2) │
│ model: "cosyvoice-v2" │
│ voice: "longxiaochun_v2" │
│ format: "mp3" │
│ │
├─ 4. 合成音频 │
│ audio_bytes = synthesize(text) │
│ │
├─ 5. 发送音频数据 ─────────────────────►├─ 收到音频流
│ websocket.send_bytes(audio_bytes) │ audioData.push(data)
│ │
├─ 6. 继续处理下一个片段 │
│ text = "今天天气不错~" │
│ │
├─ 7. 合成并发送 ───────────────────────►├─ 收到音频流
│ │ audioData.push(data)
│ │
├─ 8. 所有片段完成 │
│ │
├─ 9. 发送结束信号 ─────────────────────►├─ 收到 reply_end
│ {"type":"reply_end"} │
│ │
│ ├─ 合并音频数据
│ │ mergeAudioData()
│ │
│ ├─ 播放音频
│ │ audioContext.play()
│ │
│ ├─ 用户听到 AI 的声音
│ │ "你好呀,今天天气不错~"
│ │
```
## ⏱️ 时间线分析
### 优化前(会超时)
```
时间轴:
0s ─ 用户按住按钮
1s ─ 用户说话
2s ─ 用户松开按钮
2s ─ 一次性发送 260KB ❌
3s ─ 服务器收到大块数据
3s ─ ASR 无法处理 ❌
...
62s ─ 空闲超时 ❌
62s ─ 连接关闭
```
### 优化后(正常工作)
```
时间轴:
0s ─ 用户按住按钮
5s ─ 用户松开按钮(说了 5 秒)
5s ─ 开始分片发送
5.1s ─ 发送片段 1 (3200 bytes)
5.2s ─ 发送片段 2 (3200 bytes)
...
10s ─ 发送片段 50 (3200 bytes)
10s ─ 发送 "end" 标记
10s ─ ASR 完成识别 ✅
11s ─ LLM 开始生成
13s ─ LLM 完成TTS 开始
15s ─ TTS 完成,发送音频
16s ─ 客户端播放音频 ✅
20s ─ 播放完成 ✅
总耗时: 20 秒
超时时间: 120 秒
状态: 正常 ✅
```
## 🔑 关键优化点
### 1. 分片大小
```
优化前: 8192 bytes (8KB)
优化后: 3200 bytes (3.2KB) ✅
原因: 匹配官方推荐,符合 100ms 音频时长
```
### 2. 发送间隔
```
优化前: 50ms
优化后: 100ms ✅
原因: 匹配官方推荐,模拟实时音频流
```
### 3. 超时时间
```
优化前: 60 秒
优化后: 120 秒 ✅
原因: 给 ASR + LLM + TTS 留出足够处理时间
```
### 4. 结束标记
```
优化前: 无
优化后: 发送 "end" 标记 ✅
原因: 告诉服务器音频发送完毕,触发最终识别
```
## 📈 性能指标
### 延迟分析
```
┌─────────────────┬──────────┬─────────┐
│ 环节 │ 耗时 │ 占比 │
├─────────────────┼──────────┼─────────┤
│ 用户说话 │ 5s │ 25% │
│ 分片发送 │ 5s │ 25% │
│ ASR 识别 │ 1s │ 5% │
│ LLM 生成 │ 3s │ 15% │
│ TTS 合成 │ 2s │ 10% │
│ 音频传输 │ 1s │ 5% │
│ 音频播放 │ 3s │ 15% │
├─────────────────┼──────────┼─────────┤
│ 总计 │ 20s │ 100% │
└─────────────────┴──────────┴─────────┘
```
### 网络流量
```
上行(客户端 → 服务器):
- 音频数据: 160KB (5秒录音)
- 控制消息: < 1KB
- 总计: ~160KB
下行(服务器 → 客户端):
- 音频数据: ~50KB (MP3 格式)
- 控制消息: < 1KB
- 总计: ~50KB
总流量: ~210KB / 次对话
```
## 🎯 成功标准
一次成功的语音通话应该满足:
1. ✅ 录音时长 ≥ 3 秒
2. ✅ 分片发送完成50 片左右)
3. ✅ ASR 识别成功(收到识别文本)
4. ✅ LLM 生成成功(收到回复文本)
5. ✅ TTS 合成成功(收到音频数据)
6. ✅ 音频播放成功(听到声音)
7. ✅ 总耗时 < 30
8. ✅ 无 "idle timeout" 错误
## 🚀 下一步优化方向
### 短期优化
1. 实现真正的实时流式录音(使用 `onFrameRecorded`
2. 优化 LLM 响应速度(使用更快的模型)
3. 实现打断功能(用户可以打断 AI
### 长期优化
1. 多轮对话优化(更好的上下文管理)
2. 情感识别(根据语气调整回复)
3. 个性化语音(用户自定义音色)
4. 降噪处理(提高识别准确率)