Ai_GirlFriend/xuniYou/语音通话完整流程图.md
2026-02-28 18:04:34 +08:00

374 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 语音通话完整流程图
## 📊 整体架构
```
┌─────────────┐ 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. 降噪处理提高识别准确率