--- title: 聊天系统 updated: 2026-04-22 status: active tags: [module, chat, stream] --- # 聊天系统 > 从 [[index]] 导航。关联模块: [[routing]] [[saas]] [[butler]] ## 1. 设计决策 | 决策 | 原因 | |------|------| | 3 种 ChatStream 实现 | 覆盖 3 种运行环境: KernelClient(Tauri) / SaaSRelay(浏览器) / GatewayClient(外部进程) | | 5 Store 拆分 | 原 908 行 ChatStore → stream/conversation/message/chat/artifact,单一职责 | | 5 分钟超时守护 | 防止流挂起: kernel-chat.ts:76,超时自动 cancelStream | | 统一回调接口 | 3 种实现共享 `{ onDelta, onThinkingDelta, onTool, onHand, onComplete, onError }` | ### ChatStream 实现 | 环境 | 实现 | 传输 | |------|------|------| | Tauri + SaaS (主路径) | KernelClient + relay | Tauri Event → SaaS Token Pool → LLM | | Tauri 本地 | KernelClient | Tauri Event → Kernel → LLM 直连 | | 浏览器端 | SaaSRelayGatewayClient | HTTP SSE → SaaS → LLM | | 外部 Gateway | GatewayClient | WebSocket | ## 2. 关键文件 + 数据流 ### 核心文件 | 文件 | 职责 | |------|------| | `desktop/src/store/chat/streamStore.ts` | 流式消息编排、发送、取消 | | `desktop/src/store/chat/conversationStore.ts` | 会话管理、当前模型、sessionKey | | `desktop/src/store/chat/messageStore.ts` | 消息持久化 (IndexedDB) | | `desktop/src/lib/kernel-chat.ts` | KernelClient ChatStream (Tauri) | | `desktop/src/components/ChatArea.tsx` | 聊天区域 UI | | `crates/zclaw-runtime/src/loop_runner.rs` | Rust 主聊天循环 + 中间件链 | ### 发送消息流 ``` 用户输入 → ChatPanel.tsx → streamStore.sendMessage(content) → effectiveSessionKey = conversationStore.sessionKey || uuid() → effectiveAgentId = resolveGatewayAgentId(currentAgent) → getClient().chatStream(content, callbacks, { sessionKey, agentId, chatMode }) → [KernelClient] Tauri invoke('agent_chat_stream') → Kernel → loop_runner → 14层中间件 → LLM Driver → Tauri Event emit('chat-response-delta') → onDelta → streamStore 追加 delta → UI 渲染 → onComplete → conversationStore 持久化 → messageStore 写 IndexedDB → [SaaSRelay] POST /api/v1/relay/chat/completions → SSE → [GatewayClient] WebSocket send → onmessage → 5 分钟超时守护 (kernel-chat.ts:76) ``` ### 集成契约 | 方向 | 模块 | 接口 | 说明 | |------|------|------|------| | Calls -> | routing | `getClient()` | 确定走哪条 ChatStream | | Calls -> | middleware | Through loop_runner | 14 层 runtime 中间件链 | | Called by <- | UI | ChatPanel.tsx | 用户发送消息、取消流式 | | Emits -> | streamStore | Tauri Event `chat-response-delta` | 流式增量更新 | ## 3. 代码逻辑 ### Store 拆分 (5 Store) | Store | 文件 | 职责 | |-------|------|------| | streamStore | `store/chat/streamStore.ts` | 流式编排、发送、取消 | | conversationStore | `store/chat/conversationStore.ts` | 会话管理、当前模型 | | messageStore | `store/chat/messageStore.ts` | 消息持久化 (IndexedDB) | | chatStore | `store/chat/chatStore.ts` | 聊天通用状态 | | artifactStore | `store/chat/artifactStore.ts` | 聊天产物/附件 | ### 流式事件类型 `agent_chat_stream` Tauri Event emit 的 tagged union: `Delta` / `ThinkingDelta` / `ToolStart` / `ToolEnd` / `HandStart` / `HandEnd` / `SubtaskStatus` / `IterationStart` / `Complete` / `Error` ### 模型切换 ``` UI 选择模型 → conversationStore.currentModel = newModel → 下次 sendMessage → getClient() 读取 currentModel → SaaS 模式: relay 白名单验证 → 可用则切换 → 本地模式: 直接使用用户配置的模型 ``` ### 不变量 - sessionKey 在会话内必须一致 (UUID 生成一次,持久化 IndexedDB) - cancelStream 设置原子标志位,与 onDelta 回调无竞态 - 3 种 ChatStream 共享同一套回调接口,上层代码无需感知实现差异 - 消息持久化走 messageStore → IndexedDB,与流式渲染解耦 ### Tauri 命令 | 命令 | 说明 | |------|------| | `agent_chat_stream` | 流式聊天 (主路径) | | `agent_chat` | 非流式聊天 | | `cancel_stream` | 取消当前流式响应 | | `classroom_chat` | 课堂场景对话 | ## 4. 活跃问题 + 注意事项 | 问题 | 状态 | 说明 | |------|------|------| | B-CHAT-07 混合域截断 | P2 Open | 跨域消息时可能截断上下文 | | SSE Token 统计为 0 | ✅ 已修复 | SseUsageCapture stream_done flag | | Tauri invoke 参数名 | ✅ 已修复 (f6c5dd2) | camelCase 格式 | | Provider Key 解密 | ✅ 已修复 (b69dc61) | warn+skip + heal | **注意事项:** - 辅助 LLM 调用 (记忆摘要/提取、管家路由) 复用 `kernel_init` 的 model+base_url,与聊天同链路 - 课堂聊天是独立 Tauri 命令 (`classroom_chat`),不走 `agent_chat_stream` ## 5. 变更日志 | 日期 | 变更 | |------|------| | 04-22 | Wiki 重写: 5 节模板,增加集成契约和不变量 | | 04-21 | 上一轮更新 | | 04-17 | ChatStore 拆分为 5 Store (stream/conversation/message/chat/artifact) | | 04-16 | Provider Key 解密修复 (b69dc61) | | 04-16 | Tauri invoke 参数名修复 (f6c5dd2) |