Files
Cyrene/docs/debug_log/2026-05-19-round7-continuous-debugging-report.md
T
AskaEth b123a36aae fix: 第四轮调试 — 回复去重/消息时序/UI布局/自主思考深度优化 + 文档重整
后端修复:
- main.go: 恢复 /api/v1/chat 路由中丢失的 handleChat 调用 (空响应回归)
- orchestrator.go: splitChatByLines 改为双换行分割, 避免单换行误拆
- chat_handler.go: multi_message 增加 !hasReview 守卫, 消息延迟 200→800ms
- thinker.go: RecordUserMessage 追踪活跃会话ID, 推送主动消息到正确会话
- thinker.go: 增强思考提示词 — 禁止在用户休息/离开时发送主动消息

前端修复:
- useWebSocket.ts: stream_segments 不再创建消息气泡, 消除重复回复
- MessageBubble.tsx: 动作消息居左对齐无头像, 时间戳移至气泡外侧 hover 显示
- ChatInput.tsx: 昔涟输入提示移至输入框上方, 波点动画效果
- MessageList/TypingIndicator/ChatContainer: 清理冗余 isTyping 传递
- MemoryPanel.tsx: 新增记忆面板组件

文档重整:
- docs/debug/ → docs/debug_log/ 重命名统一
- 新增 debug_log/README.md 索引
- .gitignore: 新增 android/ 排除规则

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 13:09:18 +08:00

226 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Cyrene 持续性调试报告 — 10轮汇总
**日期**: 2026-05-19
**时间范围**: UTC+8 约21:30 — 23:35
**总轮次**: 10
**环境**: 本地开发 (非Docker)7个核心服务 + PostgreSQL + DevTools
---
## 一、调试轮次总览
| 轮次 | 角度 | 关键发现数 |
|------|------|-----------|
| 第1轮 | 服务间通信与API契约验证 | 4 |
| 第2轮 | IoT设备控制与工具链验证 | 2 |
| 第3轮 | 端到端功能测试与安全验证 | 2 |
| 第4轮 | LLM思维记忆优化与工具扩展 | 3 |
| 第5轮 | 独立服务抽取与调用记录 | 4 |
| 第6轮 | 多会话架构、自主思考重构与综合Bug修复 | 8 |
| 第7轮 | 认证安全与前端消息类型完整性 | 2 |
| 第8轮 | 并发安全与跨服务韧性 | 3 |
| 第9轮 | 部署配置与Docker完整性 | 4 |
| 第10轮 | 编译产物与构建完整性 | 2 |
---
## 二、所有Bug汇总 (按优先级)
### 🔴 P0 — 严重/阻塞 (5个)
| # | 来源轮次 | 问题 | 文件位置 | 影响 |
|---|---------|------|---------|------|
| 1 | R6 | Session [`randomID()`](backend/gateway/internal/handler/session_handler.go:576-582) 确定性输出导致ID冲突 | [`session_handler.go:576-582`](backend/gateway/internal/handler/session_handler.go:576) | 使用 `letters[i%len(letters)]` 而非 `crypto/rand`,每次生成相同序列;会话/消息ID可预测且可能碰撞 |
| 2 | R6 | TTS fallback不可达 | [`tts_handler.go:55-65`](backend/voice-service/internal/handler/tts_handler.go:55) | handler层 [`IsAvailable()`](backend/voice-service/internal/handler/tts_handler.go:55) 检查在第55行就返回503,导致 [`voice_handler.go`](backend/gateway/internal/handler/voice_handler.go) 中Gateway配置的fallback逻辑永远不会被触发 |
| 3 | R8 | 编排器/子会话goroutine缺少 `defer recover()` | [`orchestrator.go:81`](backend/ai-core/internal/orchestrator/orchestrator.go:81), [`manager.go:90/137`](backend/ai-core/internal/subsession/manager.go:90) | 3处 `go func()` 启动的goroutine中任何panic都会导致整个进程崩溃,无优雅恢复机制 |
| 4 | R9 | 管理员权限中间件逻辑错误 | [`router.go:231`](backend/gateway/internal/router/router.go:231) | [`adminAuth()`](backend/gateway/internal/router/router.go:231) 检查 `admin_` 前缀但实际 user_id 为 `user_admin` 格式([`auth_handler.go:97`](backend/gateway/internal/handler/auth_handler.go:97)),导致管理员永远无法通过中间件鉴权 |
| 5 | R10 | 40MB编译产物被Git追踪 | [`voice-service/main`](backend/voice-service/main), [`voice-service/main2`](backend/voice-service/main2), [`voice-service/voice-svc-new`](backend/voice-service/voice-svc-new), [`gateway/cmd/gateway`](backend/gateway/cmd/gateway) | [`.gitignore`](.gitignore) 未覆盖 `voice-service/main``voice-service/main2``voice-service/voice-svc-new``gateway/cmd/gateway` 四个二进制文件;每次提交携带约40MB无用二进制 |
### 🟡 P1 — 高优先级 (5个)
| # | 来源轮次 | 问题 | 文件位置 | 影响 |
|---|---------|------|---------|------|
| 6 | R6 | Gateway缺少IoT代理路由 | [`router.go`](backend/gateway/internal/router/router.go) | Gateway未注册IoT设备查询/控制的代理路由,前端无法通过Gateway统一入口访问IoT功能 |
| 7 | R7 | 普通用户登录不验证密码 | [`auth_handler.go:99-101`](backend/gateway/internal/handler/auth_handler.go:99) | MVP阶段注释写明"简化逻辑",任意用户名无需密码即可登录获取JWT Token;需实现bcrypt密码哈希验证 |
| 8 | R8 | IoT Client不使用context | [`iot_client.go:71`](backend/ai-core/internal/tools/iot_client.go:71) | 使用 [`client.Get()`](backend/ai-core/internal/tools/iot_client.go:71) 而非 `NewRequestWithContext`,无法传递超时/取消信号;IoT服务不可用时调用方会无限阻塞 |
| 9 | R8 | 跨服务HTTP调用无重试/熔断 | Gateway → ai-core / memory-service / tool-engine 等多处 | 所有跨服务HTTP调用均为单次请求,无重试策略、无熔断器、无降级逻辑;下游服务短暂不可用即导致请求失败 |
| 10 | R9 | 登录用户名无输入校验 | [`auth_handler.go:91`](backend/gateway/internal/handler/auth_handler.go:91) | 用户名接受任意字符包括SQL注入payload (`'; DROP TABLE--`);虽然使用参数化查询但无长度/字符白名单限制,存在潜在风险 |
### 🟢 P2 — 中优先级 (4个)
| # | 来源轮次 | 问题 | 文件位置 | 影响 |
|---|---------|------|---------|------|
| 11 | R6 | Briefing AI摘要降级 | AI-Core `/api/v1/briefing/summarize` 端点 | AI-Core返回404时前端无友好降级提示;摘要生成失败静默 |
| 12 | R7 | 前端缺少 `multi_message`/`stream_segments` 消息类型 | [`types/chat.ts`](frontend/web/src/types/chat.ts), [`MessageBubble.tsx`](frontend/web/src/components/chat/MessageBubble.tsx) | 第6轮引入的子会话架构产生的 [`MultiMessage`](backend/ai-core/internal/model/sub_session.go) 和 [`StreamEvent`](backend/ai-core/internal/model/sub_session.go) 类型在前端无对应渲染组件;多段消息被当作普通文本显示 |
| 13 | R9 | Docker Compose缺少3个服务 | [`docker-compose.dev.yml`](docker-compose.dev.yml), [`docker-compose.yml`](docker-compose.yml) | memory (8091)、tool (8092)、voice (8093) 三个服务未加入Compose编排;无法一键启动完整环境 |
| 14 | R10 | PWA缺少192x192图标 | [`manifest.json`](frontend/web/public/manifest.json), [`public/images/`](frontend/web/public/images/) | PWA manifest中引用的192x192图标文件不存在;影响PWA安装体验 |
### 🔵 P3 — 低优先级/改进建议 (5个)
| # | 来源轮次 | 问题 | 文件位置 | 影响 |
|---|---------|------|---------|------|
| 15 | R5 | 2个fire-and-forget goroutine无错误处理 | [`thinker.go`](backend/ai-core/internal/background/thinker.go), [`hub.go`](backend/gateway/internal/ws/hub.go) | 后台goroutine中的错误仅log.Printf,无告警/重试/降级;出现问题后运维难以发现 |
| 16 | R5 | .gitignore需添加gateway编译产物 | [`.gitignore`](.gitignore) | 缺失 `backend/gateway/cmd/gateway` 条目;已在P0#5中汇总 |
| 17 | R6 | Reminder `created_at` 为零值 | [`reminder_store.go`](backend/gateway/internal/store/reminder_store.go) | 创建提醒时未设置 `created_at` 字段,数据库中为 `0001-01-01`;影响提醒列表排序和展示 |
| 18 | R9 | voice-service无Dockerfile | [`voice-service/`](backend/voice-service/) | 其他5个Go服务均有Dockerfilevoice-service缺失;无法通过Docker部署 |
| 19 | R9 | Dockerfile Go版本与go.mod不一致 | [`ai-core/Dockerfile`](backend/ai-core/Dockerfile), [`gateway/Dockerfile`](backend/gateway/Dockerfile) 等多处 | 部分Dockerfile使用 `golang:1.22` 但 [`go.mod`](backend/go.work) 声明 `go 1.26`;可能导致编译失败或运行时行为差异 |
---
## 三、已验证通过的功能清单
### 编译验证 (6/6 Go服务 + 1前端)
| 模块 | 命令 | 结果 |
|------|------|------|
| `backend/ai-core` | `go vet ./...` | ✅ 通过 |
| `backend/gateway` | `go vet ./...` | ✅ 通过 |
| `backend/iot-debug-service` | `go vet ./...` | ✅ 通过 |
| `backend/memory-service` | `go vet ./...` | ✅ 通过 |
| `backend/tool-engine` | `go vet ./...` | ✅ 通过 |
| `backend/voice-service` | `go vet ./...` | ✅ 通过 |
| `frontend/web` | `tsc --noEmit` | ✅ 通过 |
| `frontend/web` | `vite build` | ✅ 构建成功 |
### API端点测试 (已验证通过)
- ✅ Gateway 健康检查: `GET /api/v1/health`
- ✅ 管理员登录: `POST /api/v1/auth/login` → 正确校验密码
- ✅ 错误密码拒绝: `POST /api/v1/auth/login` → 401
- ✅ 创建/列出/获取/删除会话
- ✅ 消息持久化 (分页加载)
- ✅ 管理员端点 (所有会话/活跃会话)
- ✅ Memory-Service 记忆 CRUD + 语义查询 + 合并 + 衰减
- ✅ Tool-Engine 工具列表 (13个工具) + 调用记录 + 统计
- ✅ IoT Debug Service 设备列表 + 属性设置 (8种操作)
- ✅ DevTools 仪表盘 + 数据库管理 + STT日志面板
- ✅ WebSocket 实时通信 + SSE流式降级
- ✅ 前端URL哈希路由 + 会话切换 + 竞态条件修复
### 架构功能验证
- ✅ 子会话并行分发架构 (IntentAnalyzer → Manager.Dispatch → Synthesizer)
- ✅ 事件驱动自主思考 (`post_chat` + `silence` 触发)
- ✅ 记忆三层注入 (核心/常用/其他)
- ✅ Gateway代理层 (memory + tool-engine)
- ✅ ai-core HTTP客户端调用外部服务
- ✅ 前端MediaRecorder降级方案 (STT)
- ✅ 前端双重WebSocket修复
- ✅ 数据库模式演化 (ALTER TABLE ADD COLUMN IF NOT EXISTS)
---
## 四、统计
| 指标 | 数值 |
|------|------|
| 测试覆盖的API端点 | 30+ |
| 通过的API测试 | 25+ |
| 发现Bug总数 | 19 (P0: 5, P1: 5, P2: 4, P3: 5) |
| Go服务编译 | 6/6 通过 |
| 前端TypeScript编译 | 通过 |
| 前端Vite构建 | 通过 |
| 涉及Go源文件 | 50+ |
| 涉及前端文件 | 20+ |
---
## 五、建议优先修复路线
### 第一阶段: 安全与稳定性 (P0)
1. **R9-P0#4 — 管理员权限中间件**: 修改 [`router.go:231`](backend/gateway/internal/router/router.go) `adminAuth()``HasPrefix` 检查,使其匹配 [`auth_handler.go:97`](backend/gateway/internal/handler/auth_handler.go:97) 生成的 `admin_` 前缀格式;当前因 `user_admin` 格式不匹配导致管理员鉴权完全失效
2. **R6-P0#1 — Session randomID**: 将 [`session_handler.go:576-582`](backend/gateway/internal/handler/session_handler.go:576) 的确定性伪随机替换为 `crypto/rand``uuid.New()`
3. **R8-P0#3 — Goroutine panic recovery**: 在 [`orchestrator.go:81`](backend/ai-core/internal/orchestrator/orchestrator.go:81)、[`manager.go:90`](backend/ai-core/internal/subsession/manager.go:90)、[`manager.go:137`](backend/ai-core/internal/subsession/manager.go:137) 三处 `go func()` 开头添加 `defer recover()`
4. **R6-P0#2 — TTS fallback**: 将 [`tts_handler.go:55`](backend/voice-service/internal/handler/tts_handler.go:55) 的 `IsAvailable()` 检查移除或改为返回特定错误码,让Gateway [`voice_handler.go`](backend/gateway/internal/handler/voice_handler.go) 的fallback逻辑可触发
5. **R10-P0#5 — 二进制文件清理**: 在 [`.gitignore`](.gitignore) 添加缺失条目并使用 `git rm --cached` 移除已追踪的二进制文件
### 第二阶段: 功能完善 (P1)
6. **R7-P1#7 — 普通用户密码验证**: 在 [`auth_handler.go:99`](backend/gateway/internal/handler/auth_handler.go:99) 实现bcrypt密码哈希存储与验证
7. **R8-P1#8 — IoT Client context传递**: 修改 [`iot_client.go:71`](backend/ai-core/internal/tools/iot_client.go:71) 使用 `NewRequestWithContext`
8. **R8-P1#9 — HTTP重试/熔断**: 引入重试库 (如 `go-resiliency`) 包装跨服务HTTP调用
9. **R6-P1#6 — Gateway IoT代理路由**: 在 [`router.go`](backend/gateway/internal/router/router.go) 注册IoT代理端点
10. **R9-P1#10 — 用户名输入校验**: 在 [`auth_handler.go:91`](backend/gateway/internal/handler/auth_handler.go:91) 添加长度和字符白名单验证
### 第三阶段: 体验与运维 (P2/P3)
11. **R9-P2#13 — Docker Compose补全**: 在 [`docker-compose.dev.yml`](docker-compose.dev.yml) 和 [`docker-compose.yml`](docker-compose.yml) 添加 memory/tool/voice 三个服务
12. **R7-P2#12 — 前端多消息类型渲染**: 在 [`types/chat.ts`](frontend/web/src/types/chat.ts) 和 [`MessageBubble.tsx`](frontend/web/src/components/chat/MessageBubble.tsx) 添加 `multi_message`/`stream_segments` 渲染支持
13. **R10-P2#14 — PWA 192x192图标**: 生成并放置缺失的图标文件
14. **R9-P3#18 — voice-service Dockerfile**: 参照其他服务为voice-service创建Dockerfile
15. **R6-P2#11、R6-P3#17、R9-P3#19、R5-P3#15** — 其余P2/P3项择机修复
---
## 六、附录: 各轮次详细发现
### 第1轮 — 服务间通信与API契约验证
- **发现1**: WebSocket [`Message`](backend/gateway/internal/ws/hub.go:37) 结构体缺少 `ID` 字段 → 前端React key为空无法渲染
- **发现2**: [`chat_handler.go`](backend/gateway/internal/handler/chat_handler.go:163) 缓存消息时未生成ID
- **发现3**: 前端 `sessionStore``useWebSocket` 缺少消息fallback ID生成
- **发现4**: 跨会话切换时旧消息闪现 (缺少 `activeSessionRef`)
### 第2轮 — IoT设备控制与工具链验证
- **发现1**: AI-Core只注册了 [`IoTQueryTool`](backend/ai-core/internal/tools/iot_tools.go) 未注册控制工具
- **发现2**: IoT Debug Service [`Toggle()`](backend/iot-debug-service/cmd/main.go:212) 写锁内调用读锁导致死锁
### 第3轮 — 端到端功能测试与安全验证
- **发现1**: [`auth_handler.go:89`](backend/gateway/internal/handler/auth_handler.go:89) 管理员密码错误时静默降级为普通用户
- **发现2**: 会话列表查询 `user_id` 匹配方式与存储不一致
### 第4轮 — LLM思维记忆优化与工具扩展
- **发现1**: 记忆表缺少 `importance`/`keywords`/`source` 列 → AutoMigrate不自动补列
- **发现2**: [`iot_tools.go:88`](backend/ai-core/internal/tools/iot_tools.go:88) `fmt.Sprintf` 格式字符串参数不足
- **发现3**: DevTools数据库管理缺少预检和重连机制
### 第5轮 — 独立服务抽取与调用记录
- **发现1**: Gateway代理记忆失败 → 缺少 `MEMORY_SERVICE_URL``TOOL_ENGINE_URL` 环境变量
- **发现2**: 前端会话切换3个竞态条件
- **发现3**: 二进制文件未被 `.gitignore` 完全覆盖
- **发现4**: DevTools `startAllSequential` 未包含新服务
### 第6轮 — 多会话架构、自主思考重构与综合Bug修复
- **发现1**: 前端双重WebSocket → [`ChatContainer.tsx`](frontend/web/src/components/chat/ChatContainer.tsx) 和 [`App.tsx`](frontend/web/src/App.tsx) 各自调用 `useChat()`
- **发现2**: Memory-Service数据库迁移失败 → `CREATE TABLE IF NOT EXISTS` 不补列
- **发现3**: 语音STT不可用 → MediaRecorder降级方案
- **发现4**: DevTools仪表盘数据库按钮失效 → 缺少 `id` 属性
- **发现5**: Session `randomID()` 确定性输出 (P0#1)
- **发现6**: TTS fallback不可达 (P0#2)
- **发现7**: Gateway缺少IoT代理路由 (P1#6)
- **发现8**: Briefing AI摘要降级 (P2#11)
### 第7轮 — 认证安全与前端消息类型完整性
- **发现1**: 普通用户登录不验证密码 (P1#7)
- **发现2**: 前端缺少 `multi_message`/`stream_segments` 消息类型 (P2#12)
### 第8轮 — 并发安全与跨服务韧性
- **发现1**: 编排器/子会话goroutine缺少 `defer recover()` (P0#3)
- **发现2**: IoT Client不使用context (P1#8)
- **发现3**: 跨服务HTTP调用无重试/熔断 (P1#9)
### 第9轮 — 部署配置与Docker完整性
- **发现1**: 管理员权限中间件逻辑错误 (P0#4)
- **发现2**: 登录用户名无输入校验 (P1#10)
- **发现3**: Docker Compose缺少3个服务 (P2#13)
- **发现4**: voice-service无Dockerfile (P3#18)
- **发现5**: Dockerfile Go版本与go.mod不一致 (P3#19)
### 第10轮 — 编译产物与构建完整性
- **发现1**: 40MB编译产物被Git追踪 (P0#5)
- **发现2**: PWA缺少192x192图标 (P2#14)
---
*报告由Cyrene持续性调试流程自动生成 — 2026-05-19*