236 lines
5.8 KiB
Markdown
236 lines
5.8 KiB
Markdown
|
|
# 语音通话最新优化说明
|
|||
|
|
|
|||
|
|
## 📅 优化时间
|
|||
|
|
2026-02-28
|
|||
|
|
|
|||
|
|
## 🎯 优化目标
|
|||
|
|
解决 "idle timeout" 问题,确保语音通话流程完整运行(ASR → LLM → TTS)
|
|||
|
|
|
|||
|
|
## ✅ 已完成的优化
|
|||
|
|
|
|||
|
|
### 1. 服务器端优化
|
|||
|
|
|
|||
|
|
#### 增加超时时间
|
|||
|
|
- **文件**: `lover/.env`
|
|||
|
|
- **修改**: 添加 `VOICE_CALL_IDLE_TIMEOUT=120`
|
|||
|
|
- **说明**: 从默认的 60 秒增加到 120 秒,给 ASR + LLM + TTS 处理留出足够时间
|
|||
|
|
- **重要**: 修改后需要重启 FastAPI 服务器才能生效
|
|||
|
|
|
|||
|
|
### 2. 客户端优化
|
|||
|
|
|
|||
|
|
#### 使用官方推荐参数
|
|||
|
|
- **文件**: `xuniYou/pages/chat/phone.vue`
|
|||
|
|
- **修改**: `sendAudioInChunks()` 方法
|
|||
|
|
- **参数调整**:
|
|||
|
|
- 每片大小: 8192 bytes → **3200 bytes**(官方推荐)
|
|||
|
|
- 发送间隔: 50ms → **100ms**(官方推荐)
|
|||
|
|
|
|||
|
|
#### 为什么使用 3200 bytes?
|
|||
|
|
|
|||
|
|
根据官方文档和音频参数计算:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
PCM 音频参数:
|
|||
|
|
- 采样率: 16000 Hz
|
|||
|
|
- 位深度: 16 bit = 2 bytes
|
|||
|
|
- 声道数: 1(单声道)
|
|||
|
|
|
|||
|
|
每秒数据量 = 16000 × 2 × 1 = 32000 bytes
|
|||
|
|
|
|||
|
|
官方建议每包 100ms 音频:
|
|||
|
|
100ms 数据量 = 32000 × 0.1 = 3200 bytes
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
这就是官方示例代码中使用 `f.read(3200)` 的原因!
|
|||
|
|
|
|||
|
|
## 📊 优化效果对比
|
|||
|
|
|
|||
|
|
### 优化前
|
|||
|
|
```
|
|||
|
|
每片: 8KB
|
|||
|
|
间隔: 50ms
|
|||
|
|
速率: 8192 / 0.05 = 163840 bytes/s = 160 KB/s
|
|||
|
|
实际音频速率: 32000 bytes/s = 31.25 KB/s
|
|||
|
|
速率比: 5.12 倍(发送太快)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 优化后
|
|||
|
|
```
|
|||
|
|
每片: 3.2KB
|
|||
|
|
间隔: 100ms
|
|||
|
|
速率: 3200 / 0.1 = 32000 bytes/s = 31.25 KB/s
|
|||
|
|
实际音频速率: 32000 bytes/s = 31.25 KB/s
|
|||
|
|
速率比: 1 倍(完美匹配实时音频流)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🔧 如何测试
|
|||
|
|
|
|||
|
|
### 1. 重启服务器(必须)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 在服务器上
|
|||
|
|
cd /path/to/lover
|
|||
|
|
|
|||
|
|
# 停止旧进程
|
|||
|
|
pkill -f "uvicorn.*main:app"
|
|||
|
|
|
|||
|
|
# 启动新进程
|
|||
|
|
uvicorn main:app --host 0.0.0.0 --port 30101 --reload
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 重新编译客户端
|
|||
|
|
|
|||
|
|
在 HBuilderX 中:
|
|||
|
|
1. 选择运行 → 运行到手机或模拟器
|
|||
|
|
2. 或者制作自定义调试基座
|
|||
|
|
|
|||
|
|
### 3. 测试步骤
|
|||
|
|
|
|||
|
|
1. 打开 App,进入语音通话页面
|
|||
|
|
2. 按住"按住说话"按钮
|
|||
|
|
3. **清晰地说 3-5 秒的话**(重要!之前测试只有 1 秒太短)
|
|||
|
|
4. 松开按钮
|
|||
|
|
5. 观察日志和响应
|
|||
|
|
|
|||
|
|
### 4. 预期日志
|
|||
|
|
|
|||
|
|
#### 客户端日志
|
|||
|
|
```
|
|||
|
|
📦 开始分片发送(官方推荐参数)
|
|||
|
|
📊 总大小: 160000 bytes
|
|||
|
|
📊 每片大小: 3200 bytes
|
|||
|
|
📊 发送间隔: 100 ms
|
|||
|
|
📊 预计发送时间: 5000 ms
|
|||
|
|
📤 发送第 1 片,大小: 3200 bytes
|
|||
|
|
✅ 第 1 片发送成功
|
|||
|
|
...
|
|||
|
|
✅ 所有音频片段发送完成,共 50 片
|
|||
|
|
📊 实际发送时间: 5000 ms
|
|||
|
|
📤 发送结束标记 "end"
|
|||
|
|
✅ 结束标记发送成功,等待服务器处理...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 服务器日志
|
|||
|
|
```
|
|||
|
|
ASR connection opened
|
|||
|
|
ASR event end=False sentence=...
|
|||
|
|
ASR event end=True sentence=你好,我想问一个问题
|
|||
|
|
ASR complete
|
|||
|
|
Handle sentence: 你好,我想问一个问题
|
|||
|
|
[LLM 生成回复]
|
|||
|
|
[TTS 合成语音]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 客户端收到响应
|
|||
|
|
```
|
|||
|
|
📋 收到控制消息, type: reply_text
|
|||
|
|
📋 完整消息: {"type":"reply_text","text":"你好呀,有什么问题尽管问我~"}
|
|||
|
|
🎵 收到音频数据流, 当前缓存数量: 1
|
|||
|
|
🎵 收到音频数据流, 当前缓存数量: 2
|
|||
|
|
...
|
|||
|
|
📋 收到控制消息, type: reply_end
|
|||
|
|
[开始播放音频]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## ⚠️ 注意事项
|
|||
|
|
|
|||
|
|
### 1. 录音时长要求
|
|||
|
|
- **最少 2-3 秒**:太短的音频 ASR 可能无法识别
|
|||
|
|
- **建议 3-5 秒**:足够的语音内容,识别率更高
|
|||
|
|
- **最多 10 秒**:避免单次录音过长
|
|||
|
|
|
|||
|
|
### 2. 录音环境
|
|||
|
|
- 安静环境,减少背景噪音
|
|||
|
|
- 清晰发音,不要含糊不清
|
|||
|
|
- 正常语速,不要太快或太慢
|
|||
|
|
|
|||
|
|
### 3. 网络要求
|
|||
|
|
- 稳定的网络连接
|
|||
|
|
- WebSocket 保持连接
|
|||
|
|
- 如果网络不稳定,会自动重连
|
|||
|
|
|
|||
|
|
## 🐛 问题排查
|
|||
|
|
|
|||
|
|
### 如果还是出现 "idle timeout"
|
|||
|
|
|
|||
|
|
#### 1. 检查服务器是否重启
|
|||
|
|
```bash
|
|||
|
|
# 查看进程
|
|||
|
|
ps aux | grep uvicorn
|
|||
|
|
|
|||
|
|
# 查看日志
|
|||
|
|
tail -f /path/to/lover/logs/app.log
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 2. 检查 .env 配置是否生效
|
|||
|
|
```bash
|
|||
|
|
# 在服务器上
|
|||
|
|
cd /path/to/lover
|
|||
|
|
cat .env | grep VOICE_CALL_IDLE_TIMEOUT
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
应该显示:
|
|||
|
|
```
|
|||
|
|
VOICE_CALL_IDLE_TIMEOUT=120
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3. 检查录音时长
|
|||
|
|
查看客户端日志中的 "总大小":
|
|||
|
|
```
|
|||
|
|
📊 总大小: 160000 bytes
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
计算时长:
|
|||
|
|
```
|
|||
|
|
时长 = 总大小 / 32000
|
|||
|
|
160000 / 32000 = 5 秒 ✅ 足够
|
|||
|
|
|
|||
|
|
如果只有 9000 bytes:
|
|||
|
|
9000 / 32000 = 0.28 秒 ❌ 太短
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 4. 检查服务器日志
|
|||
|
|
如果服务器日志中没有 "ASR event",说明 ASR 没有收到数据或无法识别。
|
|||
|
|
|
|||
|
|
可能原因:
|
|||
|
|
- 音频格式不对(应该是 PCM 16kHz 单声道)
|
|||
|
|
- 音频太短
|
|||
|
|
- 音频质量太差(噪音太大)
|
|||
|
|
|
|||
|
|
## 📚 参考文档
|
|||
|
|
|
|||
|
|
- [Paraformer 实时语音识别 Python SDK](https://help.aliyun.com/zh/model-studio/paraformer-real-time-speech-recognition-python-sdk)
|
|||
|
|
- [实时语音识别](https://help.aliyun.com/zh/model-studio/real-time-speech-recognition)
|
|||
|
|
- `xuniYou/官方文档分析和正确实现.md` - 详细的技术分析
|
|||
|
|
- `xuniYou/问题定位总结.md` - 问题诊断过程
|
|||
|
|
|
|||
|
|
## 🎉 预期结果
|
|||
|
|
|
|||
|
|
优化后,语音通话应该能够:
|
|||
|
|
|
|||
|
|
1. ✅ 录音正常(PCM 格式,16kHz)
|
|||
|
|
2. ✅ 分片发送(3200 bytes/片,100ms 间隔)
|
|||
|
|
3. ✅ ASR 识别成功(收到识别结果)
|
|||
|
|
4. ✅ LLM 生成回复(收到文本回复)
|
|||
|
|
5. ✅ TTS 合成语音(收到音频数据)
|
|||
|
|
6. ✅ 播放音频(听到 AI 的声音)
|
|||
|
|
7. ✅ 不再出现 "idle timeout" 错误
|
|||
|
|
|
|||
|
|
## 💡 下一步优化建议
|
|||
|
|
|
|||
|
|
如果基本功能正常,可以考虑:
|
|||
|
|
|
|||
|
|
1. **实时流式录音**:使用 `onFrameRecorded` 实现真正的实时传输
|
|||
|
|
2. **降低延迟**:优化 LLM 和 TTS 的响应速度
|
|||
|
|
3. **打断功能**:允许用户在 AI 说话时打断
|
|||
|
|
4. **多轮对话**:优化对话历史管理
|
|||
|
|
5. **情感识别**:根据用户语气调整回复风格
|
|||
|
|
|
|||
|
|
## 📞 技术支持
|
|||
|
|
|
|||
|
|
如果遇到问题,请提供:
|
|||
|
|
1. 客户端完整日志(从按下按钮到收到响应)
|
|||
|
|
2. 服务器日志(ASR/LLM/TTS 相关)
|
|||
|
|
3. 录音时长和文件大小
|
|||
|
|
4. 网络状态和 WebSocket 连接状态
|