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

16 KiB
Raw Permalink Blame History

语音通话完整流程图

📊 整体架构

┌─────────────┐         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. 降噪处理(提高识别准确率)