# 语音通话 "idle timeout" 问题 - 优化总结 ## 📅 日期 2026-02-28 ## 🎯 问题描述 用户在使用语音通话功能时,录音后总是收到 "idle timeout" 错误,无法完成完整的对话流程(ASR → LLM → TTS)。 ## 🔍 问题根因 ### 1. 客户端发送参数不符合官方推荐 - **问题**: 每片 8KB,间隔 50ms,发送速度是实际音频速度的 5 倍 - **影响**: 虽然在官方范围内(1KB-16KB),但不是最优参数 ### 2. 服务器超时时间不足 - **问题**: 默认 60 秒超时,但 ASR + LLM + TTS 可能需要更长时间 - **影响**: 在处理完成前就超时断开 ### 3. 测试音频太短 - **问题**: 之前测试只有 9KB(约 0.3 秒) - **影响**: ASR 无法识别太短的音频 ## ✅ 已实施的优化 ### 优化1: 调整客户端分片参数(符合官方推荐) **文件**: `xuniYou/pages/chat/phone.vue` **修改内容**: ```javascript // 优化前 const chunkSize = 8192 // 8KB const chunkDelay = 50 // 50ms // 优化后 const chunkSize = 3200 // 3.2KB(官方推荐) const chunkDelay = 100 // 100ms(官方推荐) ``` **理由**: - PCM 16kHz 单声道: 32000 bytes/秒 - 100ms 音频 = 32000 × 0.1 = 3200 bytes - 完美匹配实时音频流速度 ### 优化2: 增加服务器超时时间 **文件**: `lover/.env` **修改内容**: ```bash # 新增配置 VOICE_CALL_IDLE_TIMEOUT=120 ``` **理由**: - 从 60 秒增加到 120 秒 - 给 ASR + LLM + TTS 留出足够处理时间 - 避免在处理过程中超时 ## 📊 优化效果对比 ### 发送速度对比 | 参数 | 优化前 | 优化后 | |------|--------|--------| | 每片大小 | 8192 bytes | 3200 bytes | | 发送间隔 | 50ms | 100ms | | 发送速率 | 163840 bytes/s | 32000 bytes/s | | 音频速率 | 32000 bytes/s | 32000 bytes/s | | 速率比 | 5.12 倍 | 1 倍 ✅ | ### 时间线对比 **优化前**: ``` 0s - 用户说话 1 秒 1s - 一次性发送 32KB 1s - ASR 无法处理 60s - 超时断开 ❌ ``` **优化后**: ``` 0s - 用户说话 5 秒 5s - 开始分片发送(3200 bytes/片,100ms 间隔) 10s - 发送完成,ASR 识别 11s - LLM 生成回复 13s - TTS 合成语音 15s - 客户端播放音频 ✅ ``` ## 🔧 需要执行的操作 ### ⚠️ 重要:必须重启服务器 修改 `.env` 文件后,必须重启 FastAPI 服务器才能生效: ```bash # 1. 停止旧进程 pkill -f "uvicorn.*main:app" # 2. 启动新进程 cd /path/to/lover uvicorn main:app --host 0.0.0.0 --port 30101 --reload ``` ### 客户端重新编译 在 HBuilderX 中重新运行项目到手机或模拟器。 ## 📱 测试指南 ### 测试步骤 1. 打开 App,进入语音通话页面 2. 按住"按住说话"按钮 3. **清晰地说 3-5 秒的话**(重要!) 4. 松开按钮 5. 等待响应(应该在 20 秒内完成) ### 预期结果 - ✅ 看到"发送中..."提示 - ✅ 看到"识别中..."提示 - ✅ 收到文字回复 - ✅ 听到语音回复 - ✅ 没有 "idle timeout" 错误 ### 关键日志 **客户端应该看到**: ``` 📦 开始分片发送(官方推荐参数) 📊 总大小: 160000 bytes 📊 每片大小: 3200 bytes 📊 发送间隔: 100 ms ✅ 所有音频片段发送完成,共 50 片 ✅ 结束标记发送成功 📋 收到控制消息, type: reply_text 🎵 收到音频数据流 📋 收到控制消息, type: reply_end ``` **服务器应该看到**: ``` ASR connection opened ASR event end=True sentence=[识别的文字] Handle sentence: [识别的文字] ``` ## 📚 相关文档 已创建的文档: 1. `最新优化说明.md` - 详细的优化说明 2. `测试检查清单.md` - 测试步骤和检查项 3. `语音通话完整流程图.md` - 可视化流程图 4. `官方文档分析和正确实现.md` - 技术分析 5. `问题定位总结.md` - 问题诊断过程 ## 🎓 技术要点总结 ### 1. Paraformer-realtime-v2 的正确用法 - 必须流式发送音频数据 - 每包 1KB-16KB,建议 3200 bytes - 发送间隔建议 100ms - 发送完成后必须发送 "end" 标记 ### 2. PCM 音频参数 - 采样率: 16000 Hz - 位深度: 16 bit (2 bytes) - 声道数: 1(单声道) - 数据速率: 32000 bytes/秒 ### 3. 超时设置 - 空闲超时: 120 秒(从 60 秒增加) - 静默超时: 60 秒(无语音活动) - 给 ASR + LLM + TTS 留出足够时间 ### 4. 录音要求 - 最少 2-3 秒 - 建议 3-5 秒 - 清晰发音,避免噪音 ## 🐛 故障排查 ### 如果还是出现 "idle timeout" 1. **检查服务器是否重启** ```bash ps aux | grep uvicorn ``` 2. **检查配置是否生效** ```bash cat lover/.env | grep VOICE_CALL_IDLE_TIMEOUT ``` 3. **检查录音时长** - 客户端日志中的 "总大小" 应该 > 96000 bytes(3 秒) 4. **检查服务器日志** ```bash tail -f lover/logs/app.log ``` ### 常见问题 **Q: 为什么要用 3200 bytes?** A: 因为 PCM 16kHz 单声道每秒 32000 bytes,100ms 就是 3200 bytes,这是官方推荐的参数。 **Q: 为什么要延迟 100ms?** A: 模拟实时音频流的速度,让 ASR 能够边收边识别,而不是一次性收到大块数据。 **Q: 为什么要发送 "end" 标记?** A: 告诉服务器音频发送完毕,触发 ASR 的最终识别,否则 ASR 会一直等待更多数据。 **Q: 为什么要说 3-5 秒?** A: 太短的音频(< 1 秒)ASR 可能无法识别,3-5 秒是比较合适的长度。 ## 🎉 预期成果 优化完成后,语音通话应该能够: 1. ✅ 用户说话 3-5 秒 2. ✅ 客户端分片发送(3200 bytes/片,100ms 间隔) 3. ✅ 服务器 ASR 识别成功 4. ✅ LLM 生成回复 5. ✅ TTS 合成语音 6. ✅ 客户端播放音频 7. ✅ 整个流程在 20-30 秒内完成 8. ✅ 不再出现 "idle timeout" 错误 ## 💡 下一步建议 如果基本功能正常,可以考虑: 1. **实时流式录音**: 使用 `onFrameRecorded` 实现真正的实时传输 2. **降低延迟**: 优化 LLM 和 TTS 的响应速度 3. **打断功能**: 允许用户在 AI 说话时打断 4. **多轮对话**: 优化对话历史管理 5. **情感识别**: 根据用户语气调整回复风格 ## 📞 需要帮助? 如果测试失败,请提供: 1. 客户端完整日志 2. 服务器日志 3. 录音信息(大小、时长) 4. 错误信息 --- **优化完成时间**: 2026-02-28 **优化人员**: Kiro AI Assistant **状态**: ✅ 已完成,待测试