223 lines
5.0 KiB
Markdown
223 lines
5.0 KiB
Markdown
# "idle timeout" 问题分析
|
||
|
||
## 问题来源
|
||
|
||
错误来自 **FastAPI 服务器**(`192.168.1.141:30101`)的 `lover/routers/voice_call.py` 文件。
|
||
|
||
## 代码分析
|
||
|
||
### 1. idle timeout 的触发位置
|
||
|
||
在 `VoiceCallSession` 类中有两个超时检测:
|
||
|
||
#### 1.1 空闲超时(idle timeout)
|
||
|
||
```python
|
||
async def _idle_watchdog(self):
|
||
timeout = settings.VOICE_CALL_IDLE_TIMEOUT or 0
|
||
if timeout <= 0:
|
||
return
|
||
try:
|
||
while True:
|
||
await asyncio.sleep(5)
|
||
if time.time() - self.last_activity > timeout:
|
||
await self.send_signal({"type": "error", "msg": "idle timeout"})
|
||
await self.close()
|
||
break
|
||
except asyncio.CancelledError:
|
||
return
|
||
```
|
||
|
||
**触发条件**:
|
||
- 每 5 秒检查一次
|
||
- 如果 `time.time() - self.last_activity > timeout`
|
||
- 就发送 "idle timeout" 错误并关闭连接
|
||
|
||
#### 1.2 静默超时(silence timeout)
|
||
|
||
```python
|
||
async def _silence_watchdog(self):
|
||
"""长时间静默时关闭会话,ASR 常驻不再因短静音 stop。"""
|
||
try:
|
||
while True:
|
||
await asyncio.sleep(1.0)
|
||
if time.time() - self.last_voice_activity > 60:
|
||
logger.info("Long silence, closing session")
|
||
await self.send_signal({"type": "error", "msg": "idle timeout"})
|
||
await self.close()
|
||
break
|
||
except asyncio.CancelledError:
|
||
return
|
||
```
|
||
|
||
**触发条件**:
|
||
- 每 1 秒检查一次
|
||
- 如果 60 秒内没有语音活动
|
||
- 就发送 "idle timeout" 错误并关闭连接
|
||
|
||
### 2. last_activity 的更新
|
||
|
||
`last_activity` 在以下情况更新:
|
||
|
||
```python
|
||
def _touch(self):
|
||
self.last_activity = time.time()
|
||
```
|
||
|
||
调用 `_touch()` 的地方:
|
||
1. `feed_audio()` - 接收音频数据时
|
||
2. `send_signal()` - 发送信号时
|
||
3. `_synthesize_stream()` - 发送 TTS 音频时
|
||
|
||
### 3. 问题分析
|
||
|
||
从你的日志看:
|
||
```
|
||
✅ 录音文件发送成功
|
||
❌ WebSocket 关闭, code: 1000
|
||
{"type":"error","msg":"idle timeout"}
|
||
```
|
||
|
||
**可能的原因**:
|
||
|
||
#### 原因1:服务器配置的超时时间太短
|
||
|
||
检查 `lover/config.py` 或 `.env` 文件中的配置:
|
||
```python
|
||
VOICE_CALL_IDLE_TIMEOUT = ? # 这个值可能太小
|
||
```
|
||
|
||
#### 原因2:服务器处理音频时间过长
|
||
|
||
服务器接收音频后需要:
|
||
1. ASR 识别(~1-2秒)
|
||
2. LLM 生成回复(~2-5秒)
|
||
3. TTS 合成语音(~1-2秒)
|
||
|
||
如果 `VOICE_CALL_IDLE_TIMEOUT` 设置为 5 秒,而处理需要 6 秒,就会超时。
|
||
|
||
#### 原因3:客户端发送的是完整文件,不是流式数据
|
||
|
||
当前客户端实现:
|
||
- 录音完成后一次性发送整个 MP3 文件
|
||
- 服务器期望的是 PCM 流式数据
|
||
|
||
服务器代码中:
|
||
```python
|
||
self.recognition = Recognition(
|
||
model="paraformer-realtime-v2",
|
||
format="pcm", # 期望 PCM 格式
|
||
sample_rate=16000,
|
||
...
|
||
)
|
||
```
|
||
|
||
但客户端发送的是:
|
||
```javascript
|
||
format: 'mp3' // 发送的是 MP3
|
||
```
|
||
|
||
**这是主要问题!**
|
||
|
||
## 解决方案
|
||
|
||
### 方案1:修改服务器超时配置(临时方案)
|
||
|
||
在 `lover/.env` 或 `lover/config.py` 中增加超时时间:
|
||
|
||
```python
|
||
VOICE_CALL_IDLE_TIMEOUT = 30 # 从默认值增加到 30 秒
|
||
```
|
||
|
||
### 方案2:客户端改用 PCM 格式(推荐)
|
||
|
||
修改客户端录音配置:
|
||
|
||
```javascript
|
||
const recorderOptions = {
|
||
duration: 600000,
|
||
sampleRate: 16000,
|
||
numberOfChannels: 1,
|
||
format: 'pcm', // 改为 PCM
|
||
audioSource: 'auto'
|
||
}
|
||
```
|
||
|
||
**但是**:PCM 文件很大,一次性发送会很慢。
|
||
|
||
### 方案3:修改服务器支持 MP3 格式(最佳方案)
|
||
|
||
修改 `lover/routers/voice_call.py`:
|
||
|
||
```python
|
||
async def feed_audio(self, data: bytes):
|
||
# 检测音频格式
|
||
if self._is_mp3(data):
|
||
# 转换 MP3 到 PCM
|
||
pcm_data = self._convert_mp3_to_pcm(data)
|
||
data = pcm_data
|
||
|
||
# 原有逻辑
|
||
if self.recognition:
|
||
self.recognition.send_audio_frame(data)
|
||
```
|
||
|
||
### 方案4:使用流式录音(理想方案)
|
||
|
||
客户端使用实时音频帧:
|
||
```javascript
|
||
format: 'pcm',
|
||
frameSize: 5, // 启用 onFrameRecorded
|
||
```
|
||
|
||
但这需要 App 端支持 `onFrameRecorded`。
|
||
|
||
## 当前最快的解决方案
|
||
|
||
### 步骤1:增加服务器超时时间
|
||
|
||
编辑 `lover/.env` 文件:
|
||
|
||
```bash
|
||
# 在文件中添加或修改
|
||
VOICE_CALL_IDLE_TIMEOUT=30
|
||
```
|
||
|
||
### 步骤2:重启 FastAPI 服务器
|
||
|
||
```bash
|
||
# 在服务器上
|
||
cd /path/to/lover
|
||
# 停止旧进程
|
||
pkill -f "uvicorn.*main:app"
|
||
# 启动新进程
|
||
uvicorn main:app --host 0.0.0.0 --port 30101 --reload
|
||
```
|
||
|
||
### 步骤3:测试
|
||
|
||
重新测试语音通话,看是否还有 "idle timeout" 错误。
|
||
|
||
## 长期优化建议
|
||
|
||
1. **服务器端支持 MP3 输入**
|
||
- 添加音频格式检测
|
||
- 自动转换 MP3 到 PCM
|
||
|
||
2. **优化处理流程**
|
||
- 使用更快的 ASR 模型
|
||
- 优化 LLM 调用
|
||
- 使用流式 TTS
|
||
|
||
3. **客户端使用流式录音**
|
||
- 实现 PCM 实时传输
|
||
- 降低延迟
|
||
|
||
## 配置文件位置
|
||
|
||
需要检查的文件:
|
||
- `lover/.env` - 环境变量配置
|
||
- `lover/config.py` - 配置类定义
|
||
|
||
查找 `VOICE_CALL_IDLE_TIMEOUT` 的当前值。
|