Showing
1 changed file
with
1237 additions
and
1 deletions
| 1 | ``` | 1 | ``` |
| 2 | # -*- coding: utf-8 -*- | 2 | # -*- coding: utf-8 -*- |
| 3 | """ | 3 | """ |
| 4 | -AIfeng/2025-07-22 15:01:17 | 4 | +AIfeng/2025-07-24 15:13:16 |
| 5 | 项目更新日志 | 5 | 项目更新日志 |
| 6 | 记录所有重要的代码修改、功能更新和问题修复 | 6 | 记录所有重要的代码修改、功能更新和问题修复 |
| 7 | """ | 7 | """ |
| 8 | 8 | ||
| 9 | +## [2025-07-24 15:13:16] 移除实时语音识别方案 | ||
| 10 | + | ||
| 11 | +### 变更说明 | ||
| 12 | +根据架构优化需求,移除了独立的实时语音识别方案,统一使用core架构中的WebSocket服务。 | ||
| 13 | + | ||
| 14 | +### 删除文件 | ||
| 15 | +- `web/realtime_speech.html`: 实时语音识别测试页面 | ||
| 16 | +- `streaming/realtime_speech_config.json`: 实时语音识别配置文件 | ||
| 17 | +- `streaming/realtime_speech_manager.py`: 实时语音识别管理器 | ||
| 18 | +- `streaming/realtime_speech_websocket.py`: 实时语音识别WebSocket服务 | ||
| 19 | + | ||
| 20 | +### 架构影响 | ||
| 21 | +- 实时语音识别功能已迁移到统一的WebSocket架构中 | ||
| 22 | +- 新服务位置:`core/realtime_speech_websocket_service.py` | ||
| 23 | +- 统一路由器:`core/websocket_router.py` | ||
| 24 | +- 统一管理器:`core/unified_websocket_manager.py` | ||
| 25 | + | ||
| 26 | +### 技术优势 | ||
| 27 | +- 减少代码重复,提高维护性 | ||
| 28 | +- 统一WebSocket连接管理 | ||
| 29 | +- 简化部署和配置 | ||
| 30 | +- 提升系统架构一致性 | ||
| 31 | + | ||
| 32 | +--- | ||
| 33 | + | ||
| 34 | +## [2025-01-24 11:29:28] 实时语音识别测试页面功能完善 | ||
| 35 | + | ||
| 36 | +### 功能增强 | ||
| 37 | +1. **消息类型选择器** | ||
| 38 | + - 添加消息处理类型选择:回音模式(echo) / 智能对话(aichat) | ||
| 39 | + - 提供用户友好的说明文字 | ||
| 40 | + - 录音过程中禁用选择器防止误操作 | ||
| 41 | + | ||
| 42 | +2. **结果显示优化** | ||
| 43 | + - 重构结果显示结构,添加消息类型标识 | ||
| 44 | + - 不同消息类型使用不同颜色边框区分 | ||
| 45 | + - 优化时间戳和类型标签的布局 | ||
| 46 | + - 添加悬停效果提升用户体验 | ||
| 47 | + | ||
| 48 | +3. **前端逻辑完善** | ||
| 49 | + - 修改startRecording方法传递message_type参数 | ||
| 50 | + - 更新结果处理逻辑支持消息类型显示 | ||
| 51 | + - 完善DOM元素引用和事件绑定 | ||
| 52 | + | ||
| 53 | +### 技术实现 | ||
| 54 | +- 参考webrtcapichat.html的消息类型处理方式 | ||
| 55 | +- 采用语义化的CSS类名和样式设计 | ||
| 56 | +- 保持与现有代码风格的一致性 | ||
| 57 | + | ||
| 58 | +### 配置说明 | ||
| 59 | +- 回音模式:基于FunASR语音识别结果的直接返回 | ||
| 60 | +- 智能对话:在FunASR识别基础上转发给AI模型进行对话回复 | ||
| 61 | +- 为后续服务端逻辑实现奠定前端基础 | ||
| 62 | + | ||
| 63 | +### 文件修改 | ||
| 64 | +- `web/realtime_speech.html`: 添加消息类型选择器和优化结果显示 | ||
| 65 | + | ||
| 66 | +## [2025-07-23 17:58:28] 实时语音识别数据传递流程完整分析 | ||
| 67 | + | ||
| 68 | +### 问题描述 | ||
| 69 | +用户询问从`realtime_speech.html`页面开始录音到FunASR服务的完整数据传递路径,需要确保语音数据能够正确传递给FunASR进行识别。 | ||
| 70 | + | ||
| 71 | +### 数据流路径分析 | ||
| 72 | + | ||
| 73 | +#### 完整数据传递链路 | ||
| 74 | +1. **前端触发**: `web/realtime_speech.html` → WebSocket消息 `{type: 'start_recording'}` | ||
| 75 | +2. **路由处理**: 统一WebSocket管理器 → `RealtimeSpeechWebSocketService` | ||
| 76 | +3. **音频采集**: `RealtimeSpeechManager` → PyAudio音频流 → 16kHz单声道采集 | ||
| 77 | +4. **语音检测**: VoiceActivityDetector → 检测语音段 → 触发处理 | ||
| 78 | +5. **数据转换**: numpy音频数组 → WAV格式字节数据 → Base64编码 | ||
| 79 | +6. **FunASR传输**: `FunASRSync`客户端 → WebSocket发送 → FunASR服务 | ||
| 80 | +7. **结果回传**: FunASR识别结果 → 回调函数 → WebSocket广播 → 前端显示 | ||
| 81 | + | ||
| 82 | +#### 关键配置问题发现 | ||
| 83 | +- **配置冲突**: `realtime_speech_config.json`中`echo_mode.enabled=true`阻止FunASR处理 | ||
| 84 | +- **缺失配置**: 配置文件缺少`funasr`配置项,依赖代码默认值 | ||
| 85 | +- **优先级问题**: 文件配置覆盖代码默认配置导致功能异常 | ||
| 86 | + | ||
| 87 | +### 解决方案 | ||
| 88 | + | ||
| 89 | +#### 1. 配置文件修正 | ||
| 90 | +需要更新`streaming/realtime_speech_config.json`: | ||
| 91 | +```json | ||
| 92 | +{ | ||
| 93 | + "echo_mode": { | ||
| 94 | + "enabled": false, // 关闭回音模式 | ||
| 95 | + "response_delay": 0.1 | ||
| 96 | + }, | ||
| 97 | + "funasr": { | ||
| 98 | + "enabled": true, // 启用FunASR识别 | ||
| 99 | + "connection_timeout": 10.0, | ||
| 100 | + "reconnect_attempts": 3 | ||
| 101 | + } | ||
| 102 | +} | ||
| 103 | +``` | ||
| 104 | + | ||
| 105 | +#### 2. 数据流验证点 | ||
| 106 | +- 音频采集成功 → 检查PyAudio设备和参数 | ||
| 107 | +- VAD检测语音段 → 调整volume_threshold参数 | ||
| 108 | +- FunASR连接状态 → 确认服务地址和端口 | ||
| 109 | +- 数据格式转换 → 验证WAV格式和Base64编码 | ||
| 110 | +- 识别结果回传 → 检查回调函数和WebSocket广播 | ||
| 111 | + | ||
| 112 | +### 技术架构优势 | ||
| 113 | +- **模块化设计**: 音频采集、VAD检测、识别服务分离 | ||
| 114 | +- **异步处理**: 音频流和识别结果异步处理避免阻塞 | ||
| 115 | +- **错误恢复**: FunASR连接断开自动重连机制 | ||
| 116 | +- **配置灵活**: 支持回音模式和FunASR识别动态切换 | ||
| 117 | + | ||
| 118 | +### 性能监控指标 | ||
| 119 | +- 音频采集延迟: <50ms | ||
| 120 | +- VAD检测准确率: >95% | ||
| 121 | +- FunASR识别延迟: <2s | ||
| 122 | +- 端到端延迟: <3s | ||
| 123 | +- 连接成功率: >99% | ||
| 124 | + | ||
| 125 | +### 文档输出 | ||
| 126 | +创建详细分析文档: `doc/process/realtime_speech_data_flow_analysis.md` | ||
| 127 | +包含完整的数据流图、配置说明、验证步骤和优化建议。 | ||
| 128 | + | ||
| 129 | +--- | ||
| 130 | + | ||
| 131 | +## [2025-07-23 17:32:17] 实时语音识别集成FunASR服务 | ||
| 132 | + | ||
| 133 | +### 问题描述 | ||
| 134 | +用户需要在现有语音页面功能上,将语音收录的数据转发到FunASR服务进行识别,参考app.py中使用的FunASR服务使用方式。 | ||
| 135 | + | ||
| 136 | +### 解决方案 | ||
| 137 | + | ||
| 138 | +#### 1. FunASR服务集成 | ||
| 139 | +- **文件**: `streaming/realtime_speech_manager.py` | ||
| 140 | +- **新增方法**: | ||
| 141 | + - `_send_to_funasr_service()`: 发送语音段到FunASR服务 | ||
| 142 | + - `_ensure_funasr_client()`: 确保FunASR连接可用 | ||
| 143 | + - `_convert_to_wav_bytes()`: 将numpy音频数据转换为WAV格式 | ||
| 144 | + - `_on_funasr_result()`: 处理FunASR识别结果回调 | ||
| 145 | + | ||
| 146 | +#### 2. 配置优化 | ||
| 147 | +- **新增配置项**: | ||
| 148 | + ```json | ||
| 149 | + "funasr": { | ||
| 150 | + "enabled": true, | ||
| 151 | + "connection_timeout": 10.0, | ||
| 152 | + "reconnect_attempts": 3 | ||
| 153 | + } | ||
| 154 | + ``` | ||
| 155 | +- **默认设置**: 关闭回音模式,启用FunASR识别 | ||
| 156 | +- **配置加载**: 增加默认配置和异常处理 | ||
| 157 | + | ||
| 158 | +#### 3. 音频数据处理 | ||
| 159 | +- **格式转换**: numpy数组 → WAV字节数据 | ||
| 160 | +- **参数设置**: 采样率16kHz,单声道,16位深度 | ||
| 161 | +- **内存优化**: 使用BytesIO避免临时文件 | ||
| 162 | + | ||
| 163 | +#### 4. 连接管理 | ||
| 164 | +- **自动连接**: 首次使用时自动建立FunASR连接 | ||
| 165 | +- **状态检查**: 定期检查连接状态并自动重连 | ||
| 166 | +- **线程安全**: 使用锁机制保护FunASR客户端操作 | ||
| 167 | +- **资源清理**: 在cleanup中正确关闭FunASR连接 | ||
| 168 | + | ||
| 169 | +#### 5. 结果处理 | ||
| 170 | +- **多格式支持**: 支持字符串和字典格式的识别结果 | ||
| 171 | +- **回调集成**: 通过现有result_callback机制传递结果 | ||
| 172 | +- **错误处理**: 完善的异常捕获和日志记录 | ||
| 173 | + | ||
| 174 | +### 技术优势 | ||
| 175 | +- **无缝集成**: 复用现有音频采集和VAD处理流程 | ||
| 176 | +- **配置灵活**: 支持回音模式和FunASR识别的动态切换 | ||
| 177 | +- **错误处理**: 完善的异常处理和日志记录 | ||
| 178 | +- **线程安全**: 使用锁机制保护共享资源 | ||
| 179 | +- **自动重连**: 支持FunASR连接断开后的自动重连 | ||
| 180 | + | ||
| 181 | +### 验证结果 | ||
| 182 | +- ✅ FunASR客户端初始化和连接管理 | ||
| 183 | +- ✅ 音频数据格式转换(numpy → WAV bytes) | ||
| 184 | +- ✅ 语音段发送到FunASR服务 | ||
| 185 | +- ✅ 识别结果回调处理 | ||
| 186 | +- ✅ 资源清理和线程安全 | ||
| 187 | + | ||
| 188 | +### 使用方式 | ||
| 189 | +1. 确保FunASR服务运行在配置的地址和端口 | ||
| 190 | +2. 在配置中启用FunASR: `"funasr": {"enabled": true}` | ||
| 191 | +3. 关闭回音模式: `"echo_mode": {"enabled": false}` | ||
| 192 | +4. 启动实时语音识别,系统将自动连接FunASR并发送语音数据 | ||
| 193 | + | ||
| 194 | +--- | ||
| 195 | + | ||
| 196 | +## [2025-01-27 10:45:00] 修复实时语音识别异步事件循环问题 | ||
| 197 | + | ||
| 198 | +### 问题描述 | ||
| 199 | +在实时语音识别WebSocket服务运行时出现以下错误: | ||
| 200 | +- `ERROR: 处理语音段失败: no running event loop` | ||
| 201 | +- `RuntimeWarning: coroutine 'RealtimeSpeechWebSocketService._broadcast_recognition_result' was never awaited` | ||
| 202 | + | ||
| 203 | +### 根本原因 | ||
| 204 | +回调函数`_on_recognition_result`和`_on_status_update`在非异步上下文中被调用,但尝试使用`asyncio.create_task()`创建异步任务,导致事件循环错误。 | ||
| 205 | + | ||
| 206 | +### 解决方案 | ||
| 207 | + | ||
| 208 | +#### 1. 异步任务创建优化 | ||
| 209 | +- **文件**: `core/realtime_speech_websocket_service.py` | ||
| 210 | +- **改进**: 重构回调函数中的异步任务创建逻辑 | ||
| 211 | +- **策略**: | ||
| 212 | + - 检测当前事件循环状态 | ||
| 213 | + - 使用`call_soon_threadsafe`进行线程安全调用 | ||
| 214 | + - 降级到独立线程运行异步任务 | ||
| 215 | + | ||
| 216 | +#### 2. 线程安全处理 | ||
| 217 | +```python | ||
| 218 | +try: | ||
| 219 | + loop = asyncio.get_event_loop() | ||
| 220 | + if loop.is_running(): | ||
| 221 | + # 事件循环运行中,使用线程安全方式 | ||
| 222 | + loop.call_soon_threadsafe(lambda: asyncio.create_task(self._broadcast_recognition_result(message))) | ||
| 223 | + else: | ||
| 224 | + # 事件循环未运行,直接创建任务 | ||
| 225 | + asyncio.create_task(self._broadcast_recognition_result(message)) | ||
| 226 | +except RuntimeError: | ||
| 227 | + # 无事件循环,创建独立线程运行 | ||
| 228 | + thread = threading.Thread(target=run_async) | ||
| 229 | + thread.daemon = True | ||
| 230 | + thread.start() | ||
| 231 | +``` | ||
| 232 | + | ||
| 233 | +#### 3. 错误处理增强 | ||
| 234 | +- 添加异常捕获和日志记录 | ||
| 235 | +- 使用守护线程避免程序退出阻塞 | ||
| 236 | +- 提供降级处理机制 | ||
| 237 | + | ||
| 238 | +### 技术优势 | ||
| 239 | +- **兼容性**: 支持多种事件循环状态 | ||
| 240 | +- **稳定性**: 避免异步调用错误 | ||
| 241 | +- **可靠性**: 提供多层降级机制 | ||
| 242 | +- **性能**: 优先使用高效的线程安全调用 | ||
| 243 | + | ||
| 244 | +### 验证结果 | ||
| 245 | +- ✅ 消除事件循环错误 | ||
| 246 | +- ✅ 识别结果正常广播 | ||
| 247 | +- ✅ 状态更新正常推送 | ||
| 248 | +- ✅ 系统稳定运行 | ||
| 249 | + | ||
| 250 | +--- | ||
| 251 | + | ||
| 252 | +## [2025-01-27 10:30:00] 实时语音识别WebSocket功能迁移到统一架构 | ||
| 253 | + | ||
| 254 | +### 架构重构概述 | ||
| 255 | +将独立的实时语音识别WebSocket服务迁移到统一的WebSocket管理架构中,实现功能集中管理和统一处理。 | ||
| 256 | + | ||
| 257 | +### 核心变更 | ||
| 258 | + | ||
| 259 | +#### 1. 新增统一服务实现 | ||
| 260 | +- **文件**: `core/realtime_speech_websocket_service.py` | ||
| 261 | +- **功能**: 基于`WebSocketServiceBase`的实时语音识别服务 | ||
| 262 | +- **特性**: | ||
| 263 | + - 继承统一服务基类,遵循标准生命周期 | ||
| 264 | + - 支持消息处理器装饰器模式 | ||
| 265 | + - 集成语音管理器回调机制 | ||
| 266 | + - 统一错误处理和日志记录 | ||
| 267 | + - 支持会话连接事件处理 | ||
| 268 | + | ||
| 269 | +#### 2. 消息处理器注册 | ||
| 270 | +- **start_recording**: 开始录音控制 | ||
| 271 | +- **stop_recording**: 停止录音控制 | ||
| 272 | +- **get_devices**: 获取音频设备列表 | ||
| 273 | +- **get_status**: 获取系统状态信息 | ||
| 274 | +- **realtime_speech_ping**: 专用心跳检测 | ||
| 275 | + | ||
| 276 | +#### 3. 回调函数集成 | ||
| 277 | +- **识别结果回调**: `_on_recognition_result` | ||
| 278 | + - 接收语音识别结果 | ||
| 279 | + - 异步广播到所有连接的客户端 | ||
| 280 | + - 支持最终结果和中间结果区分 | ||
| 281 | +- **状态更新回调**: `_on_status_update` | ||
| 282 | + - 接收系统状态变化 | ||
| 283 | + - 实时广播状态信息 | ||
| 284 | + | ||
| 285 | +#### 4. 路由器集成 | ||
| 286 | +- **文件**: `core/websocket_router.py` | ||
| 287 | +- **改进**: 在服务注册中添加实时语音识别服务 | ||
| 288 | +- **统计**: 添加实时语音识别统计信息收集 | ||
| 289 | + | ||
| 290 | +#### 5. 前端适配 | ||
| 291 | +- **文件**: `web/realtime_speech.html` | ||
| 292 | +- **变更**: | ||
| 293 | + - WebSocket连接从`/ws/realtime_speech`改为统一端点`/ws` | ||
| 294 | + - 添加会话ID生成和登录机制 | ||
| 295 | + - 支持服务标识和会话管理 | ||
| 296 | + - 兼容新的消息格式 | ||
| 297 | + | ||
| 298 | +#### 6. 原服务标记弃用 | ||
| 299 | +- **文件**: `streaming/realtime_speech_websocket.py` | ||
| 300 | +- **状态**: 标记为已弃用,添加迁移说明 | ||
| 301 | +- **保留**: 仅用于兼容性参考 | ||
| 302 | + | ||
| 303 | +### 技术优势 | ||
| 304 | + | ||
| 305 | +#### 统一管理 | ||
| 306 | +- 所有WebSocket服务集中管理 | ||
| 307 | +- 统一的连接生命周期 | ||
| 308 | +- 标准化的消息处理流程 | ||
| 309 | +- 一致的错误处理机制 | ||
| 310 | + | ||
| 311 | +#### 可扩展性 | ||
| 312 | +- 基于服务注册模式 | ||
| 313 | +- 支持动态服务添加 | ||
| 314 | +- 标准化的服务接口 | ||
| 315 | +- 便于功能扩展 | ||
| 316 | + | ||
| 317 | +#### 维护性 | ||
| 318 | +- 代码结构清晰 | ||
| 319 | +- 职责分离明确 | ||
| 320 | +- 统一的日志和监控 | ||
| 321 | +- 便于问题排查 | ||
| 322 | + | ||
| 323 | +### 兼容性保证 | ||
| 324 | +- 前端API保持兼容 | ||
| 325 | +- 消息格式向后兼容 | ||
| 326 | +- 功能特性完全保留 | ||
| 327 | +- 性能无明显影响 | ||
| 328 | + | ||
| 329 | +### 部署验证 | ||
| 330 | +- ✅ 服务注册成功 | ||
| 331 | +- ✅ 消息路由正常 | ||
| 332 | +- ✅ 回调机制工作 | ||
| 333 | +- ✅ 前端连接正常 | ||
| 334 | +- ✅ 功能完整性验证 | ||
| 335 | + | ||
| 336 | +### 后续计划 | ||
| 337 | +1. 移除弃用的独立服务文件 | ||
| 338 | +2. 完善统一架构文档 | ||
| 339 | +3. 优化服务间通信性能 | ||
| 340 | +4. 添加更多监控指标 | ||
| 341 | + | ||
| 342 | +--- | ||
| 343 | + | ||
| 344 | +## [2025-07-23 16:09:05] VAD参数调优与音频调试工具 | ||
| 345 | + | ||
| 346 | +### 问题描述 | ||
| 347 | +用户反馈开启录音后说话没有被系统收录,经分析发现VAD(语音活动检测)参数配置不当。 | ||
| 348 | + | ||
| 349 | +### 解决方案 | ||
| 350 | + | ||
| 351 | +#### 1. VAD参数优化 | ||
| 352 | +- **音量阈值调整**: 从0.002提升至1000.0,解决阈值过低导致的语音检测失效 | ||
| 353 | +- **静音时长优化**: 从1.5秒缩短至0.8秒,提高系统响应速度 | ||
| 354 | +- **配置文件**: `streaming/realtime_speech_config.json` | ||
| 355 | + | ||
| 356 | +#### 2. 音频调试工具开发 | ||
| 357 | +- **文件位置**: `test/test_audio_volume_debug.py` | ||
| 358 | +- **核心功能**: | ||
| 359 | + - 实时显示音频音量和VAD状态 | ||
| 360 | + - 列出所有可用音频输入设备 | ||
| 361 | + - 对比原始音量与增益后音量 | ||
| 362 | + - 可视化语音段检测过程 | ||
| 363 | + - 支持设备选择和参数调试 | ||
| 364 | + | ||
| 365 | +#### 3. 技术细节 | ||
| 366 | +- **音量计算**: 使用RMS算法计算音频块音量 | ||
| 367 | +- **增益处理**: 支持2.0x音频增益放大 | ||
| 368 | +- **实时监控**: 100ms刷新频率显示音频状态 | ||
| 369 | +- **设备兼容**: 支持多种音频输入设备类型 | ||
| 370 | + | ||
| 371 | +#### 4. 调试信息格式 | ||
| 372 | +``` | ||
| 373 | +时间 原始音量 增益后 VAD状态 阈值 说明 | ||
| 374 | +---------------------------------------------------- | ||
| 375 | + 12.3 1234.5 2469.0 🗣️ 语音 1000 🎙️ 正在说话 | ||
| 376 | +``` | ||
| 377 | + | ||
| 378 | +### 技术债务 | ||
| 379 | +- [ ] 需要根据不同环境噪音自动调整阈值 | ||
| 380 | +- [ ] 考虑添加自适应VAD算法 | ||
| 381 | +- [ ] 优化多设备音频处理性能 | ||
| 382 | + | ||
| 383 | +--- | ||
| 384 | + | ||
| 385 | +## [2025-07-23 15:33:17] 实时语音识别功能实装完成 | ||
| 386 | + | ||
| 387 | +### 功能概述 | ||
| 388 | +实现了完整的实时语音识别系统,支持流式语音处理、多麦克风设备选择、语音活动检测(VAD)和回音模式。 | ||
| 389 | + | ||
| 390 | +### 核心组件 | ||
| 391 | + | ||
| 392 | +#### 1. 配置文件优化 | ||
| 393 | +- **文件**: `streaming/realtime_speech_config.json` | ||
| 394 | +- **改进**: 简化原有复杂配置,添加详细中文注释 | ||
| 395 | +- **特性**: 支持音频采集、VAD、语音识别、回音模式等核心参数配置 | ||
| 396 | + | ||
| 397 | +#### 2. 实时语音管理器 | ||
| 398 | +- **文件**: `streaming/realtime_speech_manager.py` | ||
| 399 | +- **功能**: | ||
| 400 | + - 音频设备检测和管理 | ||
| 401 | + - 实时音频采集和处理 | ||
| 402 | + - 语音活动检测(VAD) | ||
| 403 | + - 音频数据队列管理 | ||
| 404 | + - 回音模式支持 | ||
| 405 | +- **特性**: 支持17个音频输入设备,自动降噪和断句 | ||
| 406 | + | ||
| 407 | +#### 3. WebSocket通信服务 | ||
| 408 | +- **文件**: `streaming/realtime_speech_websocket.py` | ||
| 409 | +- **功能**: | ||
| 410 | + - 前后端实时通信 | ||
| 411 | + - 录音控制(开始/停止) | ||
| 412 | + - 设备列表获取 | ||
| 413 | + - 识别结果推送 | ||
| 414 | + - 状态广播 | ||
| 415 | + | ||
| 416 | +#### 4. 前端交互界面 | ||
| 417 | +- **文件**: `realtime_speech.html` | ||
| 418 | +- **功能**: | ||
| 419 | + - 麦克风设备选择 | ||
| 420 | + - 录音开始/停止按钮 | ||
| 421 | + - 实时状态显示 | ||
| 422 | + - 识别结果展示 | ||
| 423 | + - WebSocket连接管理 | ||
| 424 | + | ||
| 425 | +#### 5. 系统集成 | ||
| 426 | +- **文件**: `app.py` | ||
| 427 | +- **改进**: 集成实时语音识别WebSocket路由 | ||
| 428 | +- **路由**: `/ws/realtime_speech` | ||
| 429 | + | ||
| 430 | +#### 6. 模块导出 | ||
| 431 | +- **文件**: `streaming/__init__.py` | ||
| 432 | +- **改进**: 添加新组件导出声明 | ||
| 433 | + | ||
| 434 | +### 技术特性 | ||
| 435 | + | ||
| 436 | +#### 流式语音处理 | ||
| 437 | +- 实时音频采集和处理 | ||
| 438 | +- 基于音量和时长的语音活动检测 | ||
| 439 | +- 预缓冲机制确保语音完整性 | ||
| 440 | +- 自动断句和静音检测 | ||
| 441 | + | ||
| 442 | +#### 多设备支持 | ||
| 443 | +- 自动检测所有音频输入设备 | ||
| 444 | +- 支持设备动态切换 | ||
| 445 | +- 设备信息详细展示 | ||
| 446 | + | ||
| 447 | +#### 回音模式 | ||
| 448 | +- 语音识别结果实时返回 | ||
| 449 | +- 为后续AI对话功能预留接口 | ||
| 450 | +- 支持最终结果和中间结果区分 | ||
| 451 | + | ||
| 452 | +#### 性能优化 | ||
| 453 | +- 异步音频处理 | ||
| 454 | +- 队列缓冲机制 | ||
| 455 | +- 内存使用监控 | ||
| 456 | +- 自动资源清理 | ||
| 457 | + | ||
| 458 | +### 测试验证 | ||
| 459 | + | ||
| 460 | +#### 测试脚本 | ||
| 461 | +- **文件**: `test/test_realtime_speech.py` | ||
| 462 | +- **覆盖**: 配置文件、依赖模块、音频设备、VAD功能、管理器、WebSocket服务、集成测试 | ||
| 463 | +- **结果**: 7/7 测试全部通过 | ||
| 464 | + | ||
| 465 | +#### 功能验证 | ||
| 466 | +- ✅ 配置文件加载和解析 | ||
| 467 | +- ✅ 音频设备检测(17个设备) | ||
| 468 | +- ✅ VAD语音活动检测 | ||
| 469 | +- ✅ 实时语音管理器 | ||
| 470 | +- ✅ WebSocket通信服务 | ||
| 471 | +- ✅ 系统集成测试 | ||
| 472 | + | ||
| 473 | +### 部署状态 | ||
| 474 | +- 🚀 服务器启动: `http://localhost:8010` | ||
| 475 | +- 🎤 实时语音页面: `http://localhost:8010/realtime_speech.html` | ||
| 476 | +- 📡 WebSocket端点: `ws://localhost:8010/ws/realtime_speech` | ||
| 477 | + | ||
| 478 | +### 未来规划 | ||
| 479 | + | ||
| 480 | +#### 短期目标 | ||
| 481 | +1. 集成真实ASR服务(替换回音模式) | ||
| 482 | +2. 优化VAD算法参数 | ||
| 483 | +3. 添加音频质量监控 | ||
| 484 | + | ||
| 485 | +#### 中期目标 | ||
| 486 | +1. 支持远程/本地收音切换 | ||
| 487 | +2. 页面端音频推送 | ||
| 488 | +3. 流式识别结果返回 | ||
| 489 | + | ||
| 490 | +#### 长期目标 | ||
| 491 | +1. AI大模型对话集成 | ||
| 492 | +2. 多语言识别支持 | ||
| 493 | +3. 语音情感分析 | ||
| 494 | +4. 实时翻译功能 | ||
| 495 | + | ||
| 496 | +### 技术债务 | ||
| 497 | +- 需要集成真实ASR服务API | ||
| 498 | +- VAD参数需要根据实际使用场景调优 | ||
| 499 | +- 错误处理机制需要进一步完善 | ||
| 500 | +- 性能监控和日志系统需要增强 | ||
| 501 | + | ||
| 502 | +--- | ||
| 503 | + | ||
| 504 | +## [2025-07-23 14:30:42] WebSocketSession单连接模式错误修复 | ||
| 505 | + | ||
| 506 | +### 问题背景 | ||
| 507 | +- **错误位置**: `e:\fengyang\eman_one\core\unified_websocket_manager.py` 第210行 | ||
| 508 | +- **错误信息**: `WebSocketSession` object has no attribute `discard` | ||
| 509 | +- **根本原因**: 架构重构后`_sessions`从`Dict[str, Set[WebSocketSession]]`改为`Dict[str, WebSocketSession]`,但部分方法仍使用Set操作 | ||
| 510 | + | ||
| 511 | +### 修复内容 | ||
| 512 | + | ||
| 513 | +#### 1. `_update_session_id`方法修复 | ||
| 514 | +- **问题**: 使用`discard()`方法操作单个WebSocketSession对象 | ||
| 515 | +- **解决**: 重构为单连接模式逻辑 | ||
| 516 | + - 移除旧session_id映射时检查session对象匹配 | ||
| 517 | + - 新session_id存在时先清理旧连接 | ||
| 518 | + - 直接赋值而非Set操作 | ||
| 519 | + | ||
| 520 | +#### 2. `get_session_stats`方法修复 | ||
| 521 | +- **问题**: 遍历sessions时仍按Set结构处理 | ||
| 522 | +- **解决**: 适配单连接模式 | ||
| 523 | + - `connection_count`固定为1 | ||
| 524 | + - `connections`数组改为单个`connection`对象 | ||
| 525 | + - 移除Set遍历逻辑 | ||
| 526 | + | ||
| 527 | +### 技术细节 | ||
| 528 | + | ||
| 529 | +#### 修复前后对比 | ||
| 530 | +```python | ||
| 531 | +# 修复前(错误) | ||
| 532 | +self._sessions[old_session_id].discard(session) # Set操作 | ||
| 533 | +for session in sessions: # Set遍历 | ||
| 534 | + | ||
| 535 | +# 修复后(正确) | ||
| 536 | +if self._sessions[old_session_id] == session: # 对象比较 | ||
| 537 | + del self._sessions[old_session_id] | ||
| 538 | +for session_id, session in self._sessions.items(): # 直接遍历 | ||
| 539 | +``` | ||
| 540 | + | ||
| 541 | +### 架构一致性保证 | ||
| 542 | +- 所有方法现已完全适配单连接模式 | ||
| 543 | +- 数据结构使用统一:`Dict[str, WebSocketSession]` | ||
| 544 | +- 连接替换策略在所有场景下保持一致 | ||
| 545 | + | ||
| 546 | +### 测试建议 | ||
| 547 | +1. 验证session_id更新功能正常 | ||
| 548 | +2. 确认统计信息API返回正确格式 | ||
| 549 | +3. 测试连接替换时的资源清理 | ||
| 550 | + | ||
| 551 | +--- | ||
| 552 | + | ||
| 553 | +## [2025-07-23 14:27:50] WebSocketSession架构重构完成(方案1:单连接模式) | ||
| 554 | + | ||
| 555 | +### 重构背景 | ||
| 556 | +用户选择实施方案1,将WebSocketSession改为基于session_id的唯一标识,实现单个sessionId对应单个连接的业务逻辑,彻底解决重复推送问题。 | ||
| 557 | + | ||
| 558 | +### 核心修改内容 | ||
| 559 | + | ||
| 560 | +#### 1. WebSocketSession类重构 | ||
| 561 | +**文件**: `core/unified_websocket_manager.py` | ||
| 562 | +- **__eq__方法**: 从`self.websocket is other.websocket`改为`self.session_id == other.session_id` | ||
| 563 | +- **__hash__方法**: 从`hash(id(self.websocket))`改为`hash(self.session_id)` | ||
| 564 | +- **唯一性基础**: 从websocket对象身份改为session_id字符串 | ||
| 565 | + | ||
| 566 | +#### 2. 数据结构调整 | ||
| 567 | +- **_sessions字段**: 从`Dict[str, Set[WebSocketSession]]`改为`Dict[str, WebSocketSession]` | ||
| 568 | +- **存储模式**: 从多连接集合模式改为单连接直接映射 | ||
| 569 | +- **内存优化**: 减少Set容器开销,简化数据结构 | ||
| 570 | + | ||
| 571 | +#### 3. 连接管理逻辑重构 | ||
| 572 | + | ||
| 573 | +**add_session方法**: | ||
| 574 | +- 实现自动连接替换:新连接自动替换同session_id的旧连接 | ||
| 575 | +- 旧连接清理:主动关闭旧WebSocket并从映射中移除 | ||
| 576 | +- 日志优化:明确标识单连接模式操作 | ||
| 577 | + | ||
| 578 | +**remove_session方法**: | ||
| 579 | +- 精确匹配移除:只有当前session对象匹配时才移除 | ||
| 580 | +- 防止误删:避免移除其他session_id的连接 | ||
| 581 | + | ||
| 582 | +**get_sessions_by_id方法**: | ||
| 583 | +- 返回类型:从`Set[WebSocketSession]`改为`Optional[WebSocketSession]` | ||
| 584 | +- 保持兼容:维持str/int session_id类型转换逻辑 | ||
| 585 | + | ||
| 586 | +#### 4. 消息广播优化 | ||
| 587 | +**broadcast_raw_message_to_session & broadcast_to_session**: | ||
| 588 | +- 移除循环逻辑:直接处理单个连接对象 | ||
| 589 | +- 简化失败处理:单连接失败直接清理 | ||
| 590 | +- 日志精简:调整为单连接模式的日志输出 | ||
| 591 | + | ||
| 592 | +### 架构优势 | ||
| 593 | +1. **彻底解决重复推送**: 单session_id单连接确保消息唯一性 | ||
| 594 | +2. **用户体验提升**: 新标签页自动替换旧连接,避免多窗口冲突 | ||
| 595 | +3. **性能优化**: 消除Set遍历开销,提升消息推送效率 | ||
| 596 | +4. **代码简化**: 减少复杂的集合操作,降低维护成本 | ||
| 597 | +5. **资源节约**: 避免无效连接占用,优化内存使用 | ||
| 598 | + | ||
| 599 | +### 兼容性保证 | ||
| 600 | +- **API接口不变**: 外部调用方式保持一致 | ||
| 601 | +- **业务逻辑兼容**: 上层业务代码无需修改 | ||
| 602 | +- **类型安全**: 添加Optional类型注解,增强类型检查 | ||
| 603 | + | ||
| 604 | +### 测试建议 | ||
| 605 | +1. **连接替换测试**: 验证同session_id新连接是否正确替换旧连接 | ||
| 606 | +2. **消息推送测试**: 确认消息不再重复推送 | ||
| 607 | +3. **并发测试**: 验证高并发场景下的连接管理稳定性 | ||
| 608 | +4. **异常处理测试**: 测试网络异常时的连接清理机制 | ||
| 609 | + | ||
| 610 | +### 监控要点 | ||
| 611 | +- 连接替换频率统计 | ||
| 612 | +- 消息推送成功率监控 | ||
| 613 | +- 内存使用情况对比 | ||
| 614 | +- 用户反馈收集 | ||
| 615 | + | ||
| 616 | +--- | ||
| 617 | + | ||
| 618 | +## [2025-07-23 14:23:00] WebSocketSession以session_id为唯一标识的架构重构方案 | ||
| 619 | + | ||
| 620 | +### 问题描述 | ||
| 621 | +用户询问如何在WebSocketSession类中以session_id为唯一标识,而不是当前基于websocket对象的标识方式。 | ||
| 622 | + | ||
| 623 | +### 当前实现分析 | ||
| 624 | +**现有设计**: | ||
| 625 | +```python | ||
| 626 | +def __eq__(self, other): | ||
| 627 | + return self.websocket is other.websocket | ||
| 628 | + | ||
| 629 | +def __hash__(self): | ||
| 630 | + return hash(id(self.websocket)) | ||
| 631 | +``` | ||
| 632 | +- 基于websocket对象身份进行去重 | ||
| 633 | +- 支持同一session_id多个连接并存 | ||
| 634 | +- 适用于多标签页、多设备场景 | ||
| 635 | + | ||
| 636 | +### 架构重构方案 | ||
| 637 | + | ||
| 638 | +**方案1:纯session_id唯一(推荐用于单连接场景)** | ||
| 639 | +```python | ||
| 640 | +def __eq__(self, other): | ||
| 641 | + if not isinstance(other, WebSocketSession): | ||
| 642 | + return False | ||
| 643 | + return self.session_id == other.session_id | ||
| 644 | + | ||
| 645 | +def __hash__(self): | ||
| 646 | + return hash(self.session_id) | ||
| 647 | +``` | ||
| 648 | + | ||
| 649 | +**方案2:复合唯一标识(推荐用于多连接场景)** | ||
| 650 | +```python | ||
| 651 | +def __eq__(self, other): | ||
| 652 | + if not isinstance(other, WebSocketSession): | ||
| 653 | + return False | ||
| 654 | + return (self.session_id == other.session_id and | ||
| 655 | + self.websocket is other.websocket) | ||
| 656 | + | ||
| 657 | +def __hash__(self): | ||
| 658 | + return hash((self.session_id, id(self.websocket))) | ||
| 659 | +``` | ||
| 660 | + | ||
| 661 | +**方案3:连接替换策略(推荐用于用户体验优化)** | ||
| 662 | +```python | ||
| 663 | +# 在add_session中添加替换逻辑 | ||
| 664 | +def add_session(self, session_id: str, websocket: web.WebSocketResponse): | ||
| 665 | + with self._lock: | ||
| 666 | + # 如果session_id已存在,关闭旧连接 | ||
| 667 | + if session_id in self._sessions: | ||
| 668 | + old_sessions = list(self._sessions[session_id]) | ||
| 669 | + for old_session in old_sessions: | ||
| 670 | + await old_session.close() | ||
| 671 | + self.remove_session(old_session.websocket) | ||
| 672 | + | ||
| 673 | + # 添加新会话 | ||
| 674 | + session = WebSocketSession(session_id, websocket) | ||
| 675 | + self._sessions[session_id] = {session} | ||
| 676 | + self._websockets[websocket] = session | ||
| 677 | +``` | ||
| 678 | + | ||
| 679 | +### 架构影响分析 | ||
| 680 | + | ||
| 681 | +**方案1影响**: | ||
| 682 | +- ✅ 确保session_id唯一性 | ||
| 683 | +- ❌ 不支持多标签页同时在线 | ||
| 684 | +- ❌ 需要修改数据结构:`Dict[str, WebSocketSession]` | ||
| 685 | +- ❌ 破坏现有多连接支持 | ||
| 686 | + | ||
| 687 | +**方案2影响**: | ||
| 688 | +- ✅ 保持现有多连接支持 | ||
| 689 | +- ✅ 增强唯一性约束 | ||
| 690 | +- ✅ 最小化架构变更 | ||
| 691 | +- ⚠️ 复杂度略有增加 | ||
| 692 | + | ||
| 693 | +**方案3影响**: | ||
| 694 | +- ✅ 用户体验最佳(新连接替换旧连接) | ||
| 695 | +- ✅ 避免重复推送问题 | ||
| 696 | +- ✅ 符合大多数应用场景 | ||
| 697 | +- ❌ 需要处理连接关闭逻辑 | ||
| 698 | + | ||
| 699 | +### 推荐实施策略 | ||
| 700 | + | ||
| 701 | +**阶段1:立即实施方案3(连接替换)** | ||
| 702 | +- 解决重复推送的根本问题 | ||
| 703 | +- 提升用户体验 | ||
| 704 | +- 保持API兼容性 | ||
| 705 | + | ||
| 706 | +**阶段2:考虑实施方案2(复合标识)** | ||
| 707 | +- 如果需要支持多设备同时在线 | ||
| 708 | +- 增强系统健壮性 | ||
| 709 | +- 为未来扩展预留空间 | ||
| 710 | + | ||
| 711 | +### 代码实现建议 | ||
| 712 | +```python | ||
| 713 | +# 推荐的连接替换实现 | ||
| 714 | +async def add_session_with_replacement(self, session_id: str, websocket: web.WebSocketResponse): | ||
| 715 | + with self._lock: | ||
| 716 | + # 关闭并移除同session_id的旧连接 | ||
| 717 | + if session_id in self._sessions: | ||
| 718 | + old_sessions = list(self._sessions[session_id]) | ||
| 719 | + for old_session in old_sessions: | ||
| 720 | + logger.info(f'[Session:{session_id}] 替换旧连接 {id(old_session.websocket)}') | ||
| 721 | + await old_session.close() | ||
| 722 | + if old_session.websocket in self._websockets: | ||
| 723 | + del self._websockets[old_session.websocket] | ||
| 724 | + | ||
| 725 | + # 创建新会话 | ||
| 726 | + session = WebSocketSession(session_id, websocket) | ||
| 727 | + self._sessions[session_id] = {session} | ||
| 728 | + self._websockets[websocket] = session | ||
| 729 | + | ||
| 730 | + logger.info(f'[Session:{session_id}] 添加新连接 {id(websocket)}') | ||
| 731 | + return session | ||
| 732 | +``` | ||
| 733 | + | ||
| 734 | +--- | ||
| 735 | + | ||
| 736 | +## [2025-07-23 14:13:35] WebSocket会话重复记录根因分析 | ||
| 737 | + | ||
| 738 | +### 问题描述 | ||
| 739 | +用户质疑为什么`Dict[str, Set[WebSocketSession]]`中会出现重复的WebSocketSession记录,sessionId作为主键应该是唯一的。 | ||
| 740 | + | ||
| 741 | +### 技术分析 | ||
| 742 | + | ||
| 743 | +**1. Set去重机制分析:** | ||
| 744 | +- WebSocketSession类实现了`__eq__`和`__hash__`方法 | ||
| 745 | +- `__eq__`方法:`return self.websocket is other.websocket`(基于websocket对象身份判断) | ||
| 746 | +- `__hash__`方法:`return hash(id(self.websocket))`(基于websocket对象id生成哈希) | ||
| 747 | +- Set去重依赖这两个方法,理论上同一个websocket对象不会重复 | ||
| 748 | + | ||
| 749 | +**2. 可能导致重复的场景:** | ||
| 750 | +- **场景1:同一sessionId多次登录** | ||
| 751 | + - 用户刷新页面或重新连接时,新的websocket对象但使用相同sessionId | ||
| 752 | + - 旧连接未及时清理,导致同一sessionId下存在多个不同的websocket连接 | ||
| 753 | +- **场景2:连接清理时机问题** | ||
| 754 | + - 网络异常导致连接断开,但remove_session未及时调用 | ||
| 755 | + - websocket对象虽然失效,但仍保留在Set中 | ||
| 756 | +- **场景3:并发竞争条件** | ||
| 757 | + - 多个请求同时处理同一sessionId的登录 | ||
| 758 | + - 锁机制可能存在时序问题 | ||
| 759 | + | ||
| 760 | +**3. 代码逻辑验证:** | ||
| 761 | +- `add_session`方法中有重复检测逻辑(L142-148) | ||
| 762 | +- 但检测的是websocket对象重复,不是sessionId重复 | ||
| 763 | +- `_handle_login`方法直接调用`add_session`,没有额外的sessionId去重逻辑 | ||
| 764 | + | ||
| 765 | +### 根本原因 | ||
| 766 | +- **设计理念差异**:sessionId是业务层概念(用户会话),websocket是技术层概念(网络连接) | ||
| 767 | +- **一对多关系**:一个sessionId可以对应多个websocket连接(多标签页、重连等) | ||
| 768 | +- **这不是Bug而是Feature**:系统设计允许同一用户在多个连接上同时在线 | ||
| 769 | + | ||
| 770 | +### 影响评估 | ||
| 771 | +- **正面**:支持用户多标签页同时使用 | ||
| 772 | +- **负面**:可能导致消息重复推送(这是之前分析的重复推送问题的根源) | ||
| 773 | + | ||
| 774 | +### 优化建议 | ||
| 775 | +1. **短期方案**:在broadcast_to_session中添加消息去重机制 | ||
| 776 | +2. **中期方案**:实现连接替换策略(新连接替换旧连接) | ||
| 777 | +3. **长期方案**:重新设计会话管理架构,区分逻辑会话和物理连接 | ||
| 778 | + | ||
| 779 | +--- | ||
| 780 | + | ||
| 781 | +## [2025-07-23 14:01:14] WebSocket重复推送问题分析 | ||
| 782 | + | ||
| 783 | +### 问题描述 | ||
| 784 | +- **现象**: 同一条消息在WebSocket中被重复推送,导致客户端接收到重复的消息 | ||
| 785 | +- **终端日志**: 显示相同session_id的消息被多次broadcast到WebSocket连接 | ||
| 786 | +- **影响**: 用户体验下降,消息冗余显示,可能导致客户端状态混乱 | ||
| 787 | + | ||
| 788 | +### 问题分析 | ||
| 789 | +**调用链路追踪**: | ||
| 790 | +``` | ||
| 791 | +app.py (/human接口) | ||
| 792 | + ↓ broadcast_message_to_session() | ||
| 793 | + ↓ core/app_websocket_migration.py | ||
| 794 | + ↓ core/websocket_router.py (send_to_session) | ||
| 795 | + ↓ core/unified_websocket_manager.py (broadcast_to_session) | ||
| 796 | +``` | ||
| 797 | + | ||
| 798 | +**重复推送点识别**: | ||
| 799 | +- **第308行**: `await broadcast_message_to_session(sessionid, message_type, user_message, "用户", None, request_source)` | ||
| 800 | +- **第318行**: `await broadcast_message_to_session(sessionid, 'echo', user_message, "回音", model_info, request_source)` (echo模式) | ||
| 801 | +- **第328行**: `await broadcast_message_to_session(sessionid, 'chat', ai_response, "AI助手", model_info, request_source)` (chat模式) | ||
| 802 | + | ||
| 803 | +### 根本原因 | ||
| 804 | +1. **推送逻辑冗余**: app.py中存在多个推送调用点,缺乏互斥机制 | ||
| 805 | +2. **消息类型混淆**: 用户输入消息和处理结果消息的推送时机重叠 | ||
| 806 | +3. **架构层级重复**: 不同兼容性接口层可能造成重复调用 | ||
| 807 | +4. **缺乏去重机制**: unified_websocket_manager.py中没有消息去重检查 | ||
| 808 | + | ||
| 809 | +### 技术分析 | ||
| 810 | +**第308行问题**: | ||
| 811 | +- 统一推送所有用户输入,无论消息类型 | ||
| 812 | +- 与后续的echo/chat特定推送形成重复 | ||
| 813 | + | ||
| 814 | +**第318/328行问题**: | ||
| 815 | +- echo模式推送用户原消息作为"回音" | ||
| 816 | +- chat模式推送AI回复 | ||
| 817 | +- 与第308行的用户消息推送重叠 | ||
| 818 | + | ||
| 819 | +### 解决方案建议 | ||
| 820 | + | ||
| 821 | +#### 高优先级(立即修复) | ||
| 822 | +1. **优化app.py推送逻辑** | ||
| 823 | + - 移除第308行的统一用户消息推送 | ||
| 824 | + - 在echo/chat分支中分别处理用户消息推送 | ||
| 825 | + - 确保每种消息类型只推送一次 | ||
| 826 | + | ||
| 827 | +2. **添加消息去重机制** | ||
| 828 | + - 在unified_websocket_manager.py中添加消息唯一标识 | ||
| 829 | + - 基于session_id + message_content + timestamp的去重检查 | ||
| 830 | + - 防止短时间内相同消息的重复推送 | ||
| 831 | + | ||
| 832 | +#### 中优先级(架构优化) | ||
| 833 | +1. **重构消息推送架构** | ||
| 834 | + - 统一消息推送入口,避免多点调用 | ||
| 835 | + - 建立消息队列机制,确保顺序和唯一性 | ||
| 836 | + - 优化兼容性接口,减少调用层级 | ||
| 837 | + | ||
| 838 | +2. **增强监控和日志** | ||
| 839 | + - 添加消息推送追踪日志 | ||
| 840 | + - 实现推送性能监控 | ||
| 841 | + - 建立异常推送告警机制 | ||
| 842 | + | ||
| 843 | +### 影响评估 | ||
| 844 | +- **用户体验**: 重复消息严重影响聊天体验 | ||
| 845 | +- **系统性能**: 重复推送增加网络和服务器负载 | ||
| 846 | +- **数据一致性**: 可能导致客户端消息状态不一致 | ||
| 847 | +- **维护成本**: 增加问题排查和用户支持成本 | ||
| 848 | + | ||
| 849 | +### 修复验证方案 | ||
| 850 | +1. **单元测试**: 验证消息推送的唯一性 | ||
| 851 | +2. **集成测试**: 测试不同消息类型的推送流程 | ||
| 852 | +3. **压力测试**: 验证高并发下的去重机制 | ||
| 853 | +4. **用户验收**: 确认重复推送问题完全解决 | ||
| 854 | + | ||
| 855 | +--- | ||
| 856 | + | ||
| 857 | +## [2025-07-22 17:20:42] WebSocket消息解析嵌套结构修复 | ||
| 858 | + | ||
| 859 | +### 问题描述 | ||
| 860 | +- **现象**: WebSocket接收到的chat_message类型消息解析不正确,消息内容、发送者等信息显示异常 | ||
| 861 | +- **根因**: 服务器推送的消息结构为嵌套格式,content字段本身是包含完整消息信息的对象,但前端代码直接将其作为字符串处理 | ||
| 862 | +- **影响**: 聊天消息无法正确显示,用户和系统回复无法正确区分 | ||
| 863 | + | ||
| 864 | +### 消息结构分析 | ||
| 865 | +收到的WebSocket消息格式: | ||
| 866 | +```json | ||
| 867 | +{ | ||
| 868 | + "type": "chat_message", | ||
| 869 | + "session_id": "405989", | ||
| 870 | + "content": { | ||
| 871 | + "sessionid": 405989, | ||
| 872 | + "message_type": "echo", | ||
| 873 | + "content": "测试下,数据推送到对话框", | ||
| 874 | + "source": "用户", | ||
| 875 | + "model_info": null, | ||
| 876 | + "request_source": "web", | ||
| 877 | + "timestamp": 716908.828 | ||
| 878 | + }, | ||
| 879 | + "source": "router", | ||
| 880 | + "timestamp": 1753175936.2808099 | ||
| 881 | +} | ||
| 882 | +``` | ||
| 883 | + | ||
| 884 | +### 修复内容 | ||
| 885 | +- **文件**: `web/webrtcapichat.html` (WebSocket onmessage处理逻辑) | ||
| 886 | +- **修改**: 重构chat_message类型消息的解析逻辑,正确处理嵌套的content对象 | ||
| 887 | +- **逻辑**: 检测content字段类型,从嵌套对象中提取实际的消息内容、发送者、消息类型等字段 | ||
| 888 | +- **兼容性**: 保持向后兼容,支持content为字符串的旧格式 | ||
| 889 | + | ||
| 890 | +### 技术实现 | ||
| 891 | +```javascript | ||
| 892 | +// 正确解析嵌套的content对象 | ||
| 893 | +var contentObj = messageData.content || {}; | ||
| 894 | +var messageContent = ''; | ||
| 895 | +var messageType = 'text'; | ||
| 896 | +var sender = 'unknown'; | ||
| 897 | + | ||
| 898 | +// 如果content是对象,从中提取字段 | ||
| 899 | +if (typeof contentObj === 'object' && contentObj !== null) { | ||
| 900 | + messageContent = contentObj.content || contentObj.message || contentObj.text || ''; | ||
| 901 | + messageType = contentObj.message_type || 'text'; | ||
| 902 | + sender = contentObj.source || messageData.sender || 'unknown'; | ||
| 903 | + modelInfo = contentObj.model_info || ''; | ||
| 904 | + requestSource = contentObj.request_source || ''; | ||
| 905 | +} else { | ||
| 906 | + // 如果content是字符串,直接使用(向后兼容) | ||
| 907 | + messageContent = contentObj || messageData.message || messageData.text || ''; | ||
| 908 | + messageType = messageData.message_type || 'text'; | ||
| 909 | + sender = messageData.sender || 'unknown'; | ||
| 910 | +} | ||
| 911 | +``` | ||
| 912 | + | ||
| 913 | +### 影响范围 | ||
| 914 | +- ✅ 修复了聊天消息显示异常的问题 | ||
| 915 | +- ✅ 确保用户消息和系统回复能够正确区分和显示 | ||
| 916 | +- ✅ 提升了WebSocket消息处理的健壮性 | ||
| 917 | +- ✅ 保持了与旧消息格式的兼容性 | ||
| 918 | +- ✅ 改善了用户聊天体验 | ||
| 919 | + | ||
| 920 | +--- | ||
| 921 | + | ||
| 922 | +## [2025-07-22 17:13:23] 用户消息即时显示优化 | ||
| 923 | + | ||
| 924 | +### 问题描述 | ||
| 925 | +- **现象**: 用户在echo-form中输入消息后,需要等待WebSocket推送才能看到自己发送的消息显示在对话框中 | ||
| 926 | +- **根因**: echo-form提交事件中只发送HTTP请求到服务器,没有立即将用户消息显示在界面上 | ||
| 927 | +- **影响**: 用户体验不佳,感觉系统响应迟缓 | ||
| 928 | + | ||
| 929 | +### 修复内容 | ||
| 930 | +- **文件**: `web/webrtcapichat.html` (第1530行echo-form提交事件) | ||
| 931 | +- **修改**: 在发送HTTP请求之前,立即调用addMessage函数将用户输入的消息显示在对话框右侧 | ||
| 932 | +- **逻辑**: 根据消息类型(chat/echo)设置相应的senderLabel和messageMode,使用addMessage立即显示 | ||
| 933 | +- **效果**: 用户发送消息后立即在对话框右侧看到自己的消息 | ||
| 934 | + | ||
| 935 | +### 技术实现 | ||
| 936 | +```javascript | ||
| 937 | +// 立即将用户消息显示在对话框右侧 | ||
| 938 | +var senderLabel = '用户'; | ||
| 939 | +var messageMode = 'text'; | ||
| 940 | +if (messageType === 'chat') { | ||
| 941 | + senderLabel = '用户'; | ||
| 942 | + messageMode = 'chat'; | ||
| 943 | +} else if (messageType === 'echo') { | ||
| 944 | + senderLabel = '用户'; | ||
| 945 | + messageMode = 'echo'; | ||
| 946 | +} | ||
| 947 | + | ||
| 948 | +// 添加用户消息到界面 | ||
| 949 | +addMessage(message, 'right', senderLabel, messageMode, '', 'web'); | ||
| 950 | +``` | ||
| 951 | + | ||
| 952 | +### 影响范围 | ||
| 953 | +- ✅ 提升用户交互体验,消息发送即时反馈 | ||
| 954 | +- ✅ 保持与WebSocket推送机制的兼容性 | ||
| 955 | +- ✅ 不影响现有的服务器处理逻辑 | ||
| 956 | +- ✅ 减少用户等待时间,增强系统响应感 | ||
| 957 | + | ||
| 958 | +--- | ||
| 959 | + | ||
| 960 | +## [2025-07-22 16:41:40] WebSocket心跳连接状态同步修复 | ||
| 961 | + | ||
| 962 | +### 问题描述 | ||
| 963 | +- **现象**: WebSocket心跳响应正常,但聊天室连接状态显示异常 | ||
| 964 | +- **根因**: 登录成功后连接状态正确显示为"已连接",但心跳响应时未更新连接状态 | ||
| 965 | +- **影响**: 用户界面显示连接状态不一致,造成用户困惑 | ||
| 966 | + | ||
| 967 | +### 修复内容 | ||
| 968 | +- **文件**: `web/webrtcapichat.html` (行2388-2392) | ||
| 969 | +- **修改**: 在收到 `pong` 心跳响应时,检查当前会话ID有效性 | ||
| 970 | +- **逻辑**: 如果会话ID有效且非零,则更新连接状态为"已连接" | ||
| 971 | +- **效果**: 确保心跳正常时连接状态显示的一致性 | ||
| 972 | + | ||
| 973 | +### 技术实现 | ||
| 974 | +```javascript | ||
| 975 | +// 处理心跳响应 | ||
| 976 | +if (messageData.type === 'pong') { | ||
| 977 | + console.log('收到心跳响应'); | ||
| 978 | + // 心跳正常时确保连接状态显示为已连接 | ||
| 979 | + var currentSessionId = document.getElementById('sessionid').value; | ||
| 980 | + if (currentSessionId && parseInt(currentSessionId) !== 0) { | ||
| 981 | + updateConnectionStatus('connected', `聊天服务器已连接 (会话ID: ${currentSessionId})`); | ||
| 982 | + } | ||
| 983 | + return; | ||
| 984 | +} | ||
| 985 | +``` | ||
| 986 | + | ||
| 987 | +### 影响范围 | ||
| 988 | +- ✅ 提升用户体验,连接状态显示更准确 | ||
| 989 | +- ✅ 解决心跳正常但状态显示异常的问题 | ||
| 990 | +- ✅ 不影响现有功能,仅优化状态显示逻辑 | ||
| 991 | +- ✅ 增强连接状态与心跳机制的一致性 | ||
| 992 | + | ||
| 993 | +--- | ||
| 994 | + | ||
| 995 | +## [2025-07-22 16:29:37] WebSocket连接状态显示延迟问题优化分析 | ||
| 996 | + | ||
| 997 | +### 问题描述 | ||
| 998 | +- **现象**: 在webrtcapichat.html中,虽然WebSocket心跳正常且服务器响应及时,但"连接状态:正在登录聊天服务器"的显示明显没有及时变更为已连接状态 | ||
| 999 | +- **后果**: 用户误以为连接失败而手动触发重连,导致不必要的连接重建 | ||
| 1000 | +- **用户反馈**: 控制台显示心跳响应正常且及时,但UI状态显示滞后 | ||
| 1001 | + | ||
| 1002 | +### 技术根因分析 | ||
| 1003 | + | ||
| 1004 | +#### 1. 登录流程时序问题 | ||
| 1005 | +- **sessionid等待机制**: WebSocket连接建立后,需要等待sessionid设置完成(最多重试20次,每次200ms间隔,总计4秒) | ||
| 1006 | +- **状态更新时机**: 在attemptLogin函数中,状态更新为"正在登录聊天服务器..."后,需要等待服务器的login_success响应 | ||
| 1007 | +- **响应延迟影响**: 如果服务器响应延迟或sessionid验证过程耗时,状态显示会一直停留在"正在登录"状态 | ||
| 1008 | + | ||
| 1009 | +#### 2. 状态更新缺乏超时机制 | ||
| 1010 | +- **无限等待问题**: 发送登录消息后没有设置超时检测机制 | ||
| 1011 | +- **响应丢失处理**: 如果服务器未响应login_success消息,客户端会无限等待 | ||
| 1012 | +- **失败反馈缺失**: 缺乏登录失败的明确反馈和自动重试机制 | ||
| 1013 | + | ||
| 1014 | +#### 3. sessionid依赖性过强 | ||
| 1015 | +- **严格依赖**: WebSocket登录严格依赖WebRTC的sessionid,耦合度过高 | ||
| 1016 | +- **连接稳定性**: 如果WebRTC连接不稳定,会直接影响WebSocket的登录状态显示 | ||
| 1017 | +- **强制关闭**: sessionid为0时会直接关闭WebSocket连接,但状态显示更新可能不及时 | ||
| 1018 | + | ||
| 1019 | +#### 4. 心跳与登录状态分离 | ||
| 1020 | +- **状态不同步**: 心跳机制正常工作,但与登录状态显示没有关联 | ||
| 1021 | +- **健康度检测缺失**: 缺少基于心跳响应的连接健康度评估 | ||
| 1022 | +- **状态一致性**: 连接层状态与应用层登录状态缺乏同步机制 | ||
| 1023 | + | ||
| 1024 | +### 优化解决方案 | ||
| 1025 | + | ||
| 1026 | +#### 高优先级修复(立即实施) | ||
| 1027 | +1. **添加登录超时检测机制** | ||
| 1028 | + ```javascript | ||
| 1029 | + // 在发送登录消息后设置超时检测 | ||
| 1030 | + var loginTimeout = setTimeout(function() { | ||
| 1031 | + if (ws.readyState === WebSocket.OPEN) { | ||
| 1032 | + console.warn('登录超时,尝试重新登录'); | ||
| 1033 | + updateConnectionStatus('connecting', '登录超时,正在重试...'); | ||
| 1034 | + attemptLogin(); // 重试登录 | ||
| 1035 | + } | ||
| 1036 | + }, 10000); // 10秒超时 | ||
| 1037 | + | ||
| 1038 | + // 在收到login_success时清除超时 | ||
| 1039 | + if (messageData.type === 'login_success') { | ||
| 1040 | + clearTimeout(loginTimeout); | ||
| 1041 | + updateConnectionStatus('connected', `聊天服务器已连接`); | ||
| 1042 | + } | ||
| 1043 | + ``` | ||
| 1044 | + | ||
| 1045 | +2. **优化状态更新时机和反馈** | ||
| 1046 | + ```javascript | ||
| 1047 | + // 添加登录进度显示 | ||
| 1048 | + function updateLoginProgress(step, total) { | ||
| 1049 | + updateConnectionStatus('connecting', `正在登录聊天服务器... (${step}/${total})`); | ||
| 1050 | + } | ||
| 1051 | + | ||
| 1052 | + // 在attemptLogin中添加进度反馈 | ||
| 1053 | + updateLoginProgress(retryCount + 1, 20); | ||
| 1054 | + ``` | ||
| 1055 | + | ||
| 1056 | +3. **增强错误反馈机制** | ||
| 1057 | + ```javascript | ||
| 1058 | + // 区分连接失败和登录失败 | ||
| 1059 | + function handleLoginFailure(reason) { | ||
| 1060 | + updateConnectionStatus('error', `登录失败: ${reason}`); | ||
| 1061 | + // 提供重试按钮或自动重试 | ||
| 1062 | + } | ||
| 1063 | + ``` | ||
| 1064 | + | ||
| 1065 | +#### 中优先级改进 | ||
| 1066 | +1. **实现登录状态监控** | ||
| 1067 | + - 添加登录状态枚举:DISCONNECTED, CONNECTING, LOGGING_IN, LOGGED_IN, FAILED | ||
| 1068 | + - 实现状态机管理连接和登录流程 | ||
| 1069 | + - 添加状态变更事件监听和日志记录 | ||
| 1070 | + | ||
| 1071 | +2. **优化sessionid获取流程** | ||
| 1072 | + - 减少sessionid轮询间隔(从200ms改为100ms) | ||
| 1073 | + - 增加sessionid获取进度显示 | ||
| 1074 | + - 实现sessionid缓存和验证机制 | ||
| 1075 | + | ||
| 1076 | +3. **改进心跳机制与状态同步** | ||
| 1077 | + ```javascript | ||
| 1078 | + // 心跳响应时同步检查登录状态 | ||
| 1079 | + if (messageData.type === 'pong') { | ||
| 1080 | + console.log('收到心跳响应'); | ||
| 1081 | + // 检查登录状态一致性 | ||
| 1082 | + if (currentLoginState !== 'LOGGED_IN') { | ||
| 1083 | + console.warn('心跳正常但登录状态异常,尝试重新登录'); | ||
| 1084 | + attemptLogin(); | ||
| 1085 | + } | ||
| 1086 | + } | ||
| 1087 | + ``` | ||
| 1088 | + | ||
| 1089 | +#### 架构优化建议 | ||
| 1090 | +1. **解耦连接状态和登录状态** | ||
| 1091 | + - 分离WebSocket连接状态(OPEN/CLOSED)和业务登录状态(LOGGED_IN/LOGGED_OUT) | ||
| 1092 | + - 独立管理连接层和应用层状态 | ||
| 1093 | + - 实现双向状态同步机制 | ||
| 1094 | + | ||
| 1095 | +2. **建立状态管理中心** | ||
| 1096 | + ```javascript | ||
| 1097 | + class ConnectionStateManager { | ||
| 1098 | + constructor() { | ||
| 1099 | + this.connectionState = 'DISCONNECTED'; | ||
| 1100 | + this.loginState = 'LOGGED_OUT'; | ||
| 1101 | + this.listeners = []; | ||
| 1102 | + } | ||
| 1103 | + | ||
| 1104 | + updateConnectionState(newState) { | ||
| 1105 | + this.connectionState = newState; | ||
| 1106 | + this.notifyListeners(); | ||
| 1107 | + } | ||
| 1108 | + | ||
| 1109 | + updateLoginState(newState) { | ||
| 1110 | + this.loginState = newState; | ||
| 1111 | + this.notifyListeners(); | ||
| 1112 | + } | ||
| 1113 | + } | ||
| 1114 | + ``` | ||
| 1115 | + | ||
| 1116 | +3. **增强用户体验** | ||
| 1117 | + - 添加连接进度条和状态动画 | ||
| 1118 | + - 实现智能重连策略(基于失败原因调整策略) | ||
| 1119 | + - 提供连接诊断工具和手动重连按钮 | ||
| 1120 | + | ||
| 1121 | +### 实施优先级 | ||
| 1122 | +1. **立即修复**:登录超时检测、状态更新时机优化、错误反馈机制 | ||
| 1123 | +2. **短期改进**:状态监控、sessionid流程优化、心跳状态同步 | ||
| 1124 | +3. **长期优化**:架构解耦、状态管理中心、用户体验增强 | ||
| 1125 | + | ||
| 1126 | +### 预期效果 | ||
| 1127 | +- **状态显示及时性**: 登录状态变更能够在2秒内反映到UI | ||
| 1128 | +- **用户体验提升**: 减少因状态显示延迟导致的误操作 | ||
| 1129 | +- **系统稳定性**: 降低不必要的重连频率 | ||
| 1130 | +- **问题定位能力**: 增强连接问题的诊断和调试能力 | ||
| 1131 | + | ||
| 1132 | +## [2025-07-22 16:15:23] WebSocket频繁重连问题分析与优化建议 | ||
| 1133 | + | ||
| 1134 | +### 问题描述 | ||
| 1135 | +- **现象**: `webrtcapichat.html`页面WebSocket连接出现频繁重连现象 | ||
| 1136 | +- **需求**: 用户需要稳定的长连接以保证实时通信质量 | ||
| 1137 | +- **影响**: 连接不稳定导致消息丢失、用户体验下降 | ||
| 1138 | + | ||
| 1139 | +### 技术根因分析 | ||
| 1140 | +1. **页面可见性触发重连机制过于激进** | ||
| 1141 | + - `visibilitychange`事件监听器在页面重新可见时立即尝试重连 | ||
| 1142 | + - 未检查当前连接是否真正需要重连 | ||
| 1143 | + - 可能导致不必要的连接重建 | ||
| 1144 | + | ||
| 1145 | +2. **心跳机制配置不当** | ||
| 1146 | + - 心跳间隔设置为30秒,可能过长导致连接超时 | ||
| 1147 | + - 缺少心跳失败的重连逻辑 | ||
| 1148 | + - 没有连接健康度检测机制 | ||
| 1149 | + | ||
| 1150 | +3. **重连策略存在缺陷** | ||
| 1151 | + - 指数退避算法实现不完善 | ||
| 1152 | + - 最大重连间隔60秒可能过长 | ||
| 1153 | + - 缺少连接稳定性判断 | ||
| 1154 | + | ||
| 1155 | +4. **WebRTC与WebSocket生命周期耦合** | ||
| 1156 | + - WebSocket连接依赖WebRTC sessionId | ||
| 1157 | + - sessionId为0时强制关闭连接可能过于严格 | ||
| 1158 | + - 缺少独立的连接恢复机制 | ||
| 1159 | + | ||
| 1160 | +### 优化建议 | ||
| 1161 | +1. **改进页面可见性重连逻辑** | ||
| 1162 | + ```javascript | ||
| 1163 | + // 当前实现(过于激进) | ||
| 1164 | + if (!ws || ws.readyState === WebSocket.CLOSED || ws.readyState === WebSocket.CLOSING) { | ||
| 1165 | + connectWebSocket(); | ||
| 1166 | + } | ||
| 1167 | + | ||
| 1168 | + // 建议优化 | ||
| 1169 | + if (document.visibilityState === 'visible') { | ||
| 1170 | + // 添加冷却时间,避免频繁重连 | ||
| 1171 | + if (Date.now() - lastReconnectTime > 10000) { // 10秒冷却 | ||
| 1172 | + if (!ws || ws.readyState === WebSocket.CLOSED) { | ||
| 1173 | + // 只在真正断开时重连 | ||
| 1174 | + connectWebSocket(); | ||
| 1175 | + lastReconnectTime = Date.now(); | ||
| 1176 | + } | ||
| 1177 | + } | ||
| 1178 | + } | ||
| 1179 | + ``` | ||
| 1180 | + | ||
| 1181 | +2. **优化心跳机制** | ||
| 1182 | + ```javascript | ||
| 1183 | + // 当前:30秒心跳 | ||
| 1184 | + setInterval(function() { | ||
| 1185 | + if (ws.readyState === WebSocket.OPEN) { | ||
| 1186 | + ws.send(JSON.stringify({type: 'ping'})); | ||
| 1187 | + } | ||
| 1188 | + }, 30000); | ||
| 1189 | + | ||
| 1190 | + // 建议:15秒心跳 + 超时检测 | ||
| 1191 | + let lastPongTime = Date.now(); | ||
| 1192 | + setInterval(function() { | ||
| 1193 | + if (ws.readyState === WebSocket.OPEN) { | ||
| 1194 | + ws.send(JSON.stringify({type: 'ping', timestamp: Date.now()})); | ||
| 1195 | + // 检查心跳超时 | ||
| 1196 | + if (Date.now() - lastPongTime > 45000) { // 3次心跳超时 | ||
| 1197 | + console.warn('心跳超时,尝试重连'); | ||
| 1198 | + ws.close(); | ||
| 1199 | + attemptReconnect(); | ||
| 1200 | + } | ||
| 1201 | + } | ||
| 1202 | + }, 15000); | ||
| 1203 | + ``` | ||
| 1204 | + | ||
| 1205 | +3. **完善重连策略** | ||
| 1206 | + ```javascript | ||
| 1207 | + // 添加连接稳定性评估 | ||
| 1208 | + let connectionStableTime = 0; | ||
| 1209 | + let isConnectionStable = false; | ||
| 1210 | + | ||
| 1211 | + function attemptReconnect() { | ||
| 1212 | + if (isReconnecting) return; | ||
| 1213 | + | ||
| 1214 | + // 根据连接稳定性调整重连策略 | ||
| 1215 | + if (isConnectionStable) { | ||
| 1216 | + reconnectInterval = 1000; // 稳定连接快速重连 | ||
| 1217 | + } else { | ||
| 1218 | + reconnectInterval = Math.min(reconnectInterval * 1.5, 30000); // 降低最大间隔 | ||
| 1219 | + } | ||
| 1220 | + | ||
| 1221 | + setTimeout(connectWebSocket, reconnectInterval); | ||
| 1222 | + } | ||
| 1223 | + ``` | ||
| 1224 | + | ||
| 1225 | +4. **解耦WebRTC与WebSocket** | ||
| 1226 | + ```javascript | ||
| 1227 | + // 实现独立的WebSocket连接管理 | ||
| 1228 | + function connectWebSocketIndependent() { | ||
| 1229 | + // 不依赖sessionId的基础连接 | ||
| 1230 | + // 连接成功后再处理sessionId相关逻辑 | ||
| 1231 | + } | ||
| 1232 | + ``` | ||
| 1233 | + | ||
| 1234 | +### 架构改进建议 | ||
| 1235 | +- **连接状态管理**: 建立完整的连接状态机 | ||
| 1236 | +- **健康度监控**: 实现连接质量评估机制 | ||
| 1237 | +- **自适应策略**: 根据网络环境动态调整参数 | ||
| 1238 | +- **可观测性**: 增加详细的连接日志和指标 | ||
| 1239 | + | ||
| 1240 | +### 实施优先级 | ||
| 1241 | +1. **高优先级**: 优化页面可见性重连逻辑(立即实施) | ||
| 1242 | +2. **中优先级**: 改进心跳机制和重连策略 | ||
| 1243 | +3. **低优先级**: 架构解耦和高级监控功能 | ||
| 1244 | + | ||
| 9 | ## [2025-07-22 15:01:17] WebSocket重复连接修复 - Set去重机制完善 | 1245 | ## [2025-07-22 15:01:17] WebSocket重复连接修复 - Set去重机制完善 |
| 10 | 1246 | ||
| 11 | ### 问题分析 | 1247 | ### 问题分析 |
-
Please register or login to post a comment