STT_INCOMPLETE_FIX.md 4.9 KB

STT 转写不完整问题修复

问题现象

用户说:"你好,你好,今天天气怎么样。" STT识别:"你好,你好,今天天气怎么。" ❌ 缺少最后的"样"

日志:

💬 STT识别(⏳中间): 你好,你好,今天天气怎么。  ← 停在这里
🔍 当前时刻距最后一次识别更新: 1598.9ms       ← 1.6秒没有新结果
⏰ 等待超时(300ms),再等待50ms...
🔄 未收到最终事件,再等待100ms...
⚠️  最终等待后仍无更新,使用现有结果        ← 过早放弃

✅ STT 是流式的

证据:

  1. 实时中间结果(stash_text)

    💬 STT识别(⏳中间): 你好
    💬 STT识别(⏳中间): 你好,你好
    💬 STT识别(⏳中间): 你好,你好,今天天气怎么
    
  2. 使用流式 API

    OmniRealtimeConversation(model='qwen3-asr-flash-realtime')
    
    1. 音频流式发送 ```

➡️ 发送至STT: 1536字节 ➡️ 发送至STT: 1792字节 ...


---

## 🐛 根本原因

### STT 识别流程

1. **中间结果**(Incremental):
   - 实时更新,频繁触发
   - 事件类型:`conversation.item.input_audio_transcription.text`
   - 触发时机:每识别到新的音素/词

2. **最终结果**(Final):
   - 只在 `commit()` 后触发**一次**
   - 事件类型:`conversation.item.input_audio_transcription.completed`
   - 触发时机:WebSocket 收到服务端最终转写

### 问题分析

**时间线:**

2.30秒 - 最后一次中间结果:"你好,你好,今天天气怎么。" 3.93秒 - 录音结束,调用 commit()

    ↓
 间隔 1.6秒 没有新的中间结果!
    ↓
 commit() 后需要 500-1000ms 最终结果才到达
    ↓

原等待时间:300ms + 50ms + 100ms = 450ms ← 太短!

    ↓
 超时,使用不完整的中间结果

**为什么最后 1.6秒 没有中间结果?**
- 用户说话变慢 / 停顿
- STT 引擎等待更多音频确认(连续说话 vs 单个词)
- 最后的"样"字留在了缓冲区,等待 commit() 后才在最终结果中返回

---

## 💡 解决方案

### 修改1:增加最终结果等待时间

**文件:** `websocket_handler.py` 行296-312

**修改前:**
```python
if not got_final_event:
    print(f"[{request_id}] 🔄 未收到最终事件,再等待100ms...")
    await asyncio.sleep(0.1)  # 只等100ms

修改后:

if not got_final_event:
    print(f"[{request_id}] 🔄 未收到最终事件,再等待500ms...")
    for i in range(5):  # 分5次检查,每次100ms
        await asyncio.sleep(0.1)
        # 检查是否有更新
        last_update_final = self.last_recognition_update_time.get(request_id, 0)
        if last_update_final > last_update_after:
            wait_time = (i + 1) * 100
            print(f"[{request_id}] ✅ 最终等待成功!(耗时: {wait_time}ms)")
            got_final_event = True
            break

好处:

  • 从 100ms → 500ms,给足时间让最终结果到达
  • 每 100ms 检查一次,一旦有结果立即返回(不浪费时间)
  • 最多等待 500ms,避免无限等待

修改2:增加 commit() 后缓冲时间

文件: websocket_handler.py 行667-669

修改前:

await asyncio.to_thread(conversation.commit)
await asyncio.sleep(0.05)  # 50ms
await asyncio.to_thread(conversation.close)

修改后:

await asyncio.to_thread(conversation.commit)
await asyncio.sleep(0.1)  # 100ms,让最终结果有时间到达
await asyncio.to_thread(conversation.close)

好处:

  • commit() 后多等 50ms,让 WebSocket 消息有更多时间到达
  • 避免过早关闭连接导致最终结果丢失

📊 预期效果

修改前:

总等待时间:300ms + 50ms + 100ms = 450ms
最终结果到达时间:500-1000ms
结果:超时,丢失最后的字

修改后:

总等待时间:300ms + 50ms + 500ms = 850ms
最终结果到达时间:500-1000ms
结果:✅ 成功等到最终结果

成功日志示例:

⏰ 等待超时(300ms),再等待50ms...
🔄 未收到最终事件,再等待500ms...
✅ 最终等待成功!识别结果已更新 (耗时: 200ms)
✓ STT识别完成: "你好,你好,今天天气怎么样。" (长度: 15字) ✅

🎯 总结

  1. STT 是流式的:实时返回中间结果
  2. 最终结果需要时间:commit() 后 500-1000ms 才到达
  3. 原等待时间太短:450ms 不够,导致丢失最后的字
  4. 修复方案:
    • 增加最终等待时间:100ms → 500ms
    • 增加 commit() 缓冲:50ms → 100ms
  5. 不影响性能:只在未收到最终事件时才延长等待

测试验证

重启后端,测试相同语音:

python main.py

观察日志:

  • ✅ 看到 💬 STT识别(✅最终): 包含完整文本
  • ✅ 看到 ✅ 最终等待成功!
  • ✅ 识别文本长度正确