--- title: 聊天系统 updated: 2026-04-21 status: active tags: [module, chat, stream] --- # 聊天系统 > 从 [[index]] 导航。关联模块: [[routing]] [[saas]] [[butler]] ## 设计思想 **核心问题: 3 种运行环境需要 3 种 ChatStream 实现。** | 环境 | 实现 | 传输 | 为什么 | |------|------|------|--------| | 桌面端 (Tauri) | KernelClient | Tauri Event | 内置 Kernel,但 baseUrl 可指向 SaaS relay | | 桌面端 (Tauri + SaaS) | KernelClient + relay | Tauri Event → SaaS | 主路径: Token Pool 中转 | | 浏览器端 | SaaSRelayGatewayClient | HTTP SSE | 无 Tauri 运行时 | | 外部 Gateway | GatewayClient | WebSocket | 独立进程部署 | **统一接口**: 3 种实现共享同一套回调: ```ts { onDelta, onThinkingDelta, onTool, onHand, onComplete, onError } ``` ## 功能清单 | 功能 | 描述 | 入口文件 | 状态 | |------|------|----------|------| | 发送消息 | 流式/非流式,支持 thinking | streamStore.ts | ✅ | | 流式响应 | SSE/Tauri Event 实时推送 | streamStore.ts | ✅ | | 模型切换 | 运行时切换 LLM 模型 | conversationStore.ts | ✅ | | 上下文管理 | 会话持久化 + 跨会话恢复 | conversationStore.ts | ✅ | | 取消流式 | 原子标志位中断 | kernel-chat.ts | ✅ | | Agent 聊天 | 指定 agent_id 独立对话 | streamStore.ts | ✅ | | 课堂聊天 | 教育场景专用 | classroomStore.ts | ✅ | | 消息持久化 | IndexedDB 存储 | messageStore.ts | ✅ | | 聊天产物 | 附件/代码块管理 | artifactStore.ts | ✅ | ## 代码逻辑 ### 发送消息流 入口: `streamStore.sendMessage(content)` → `store/chat/streamStore.ts` ``` sendMessage(content) → effectiveSessionKey = conversationStore.sessionKey || uuid() → effectiveAgentId = resolveGatewayAgentId(currentAgent) → client.chatStream(content, callbacks, { sessionKey, agentId, chatMode }) → KernelClient: Tauri invoke('kernel_chat', ...) → Kernel → loop_runner → LLM Driver → 如果 baseUrl 指向 SaaS relay → 请求发往 Token Pool → LLM → 如果 baseUrl 指向 LLM 直连 → 请求直接发往 LLM → Tauri Event emit('chat-response-delta', ...) → onDelta(text) → streamStore 追加 delta → onTool(tool) → toolStore 更新 → onHand(hand) → handStore 更新 → onComplete() → conversationStore 持久化 → SaaSRelay: HTTP POST /api/v1/relay/chat/completions → SSE → GatewayClient: WebSocket send → onmessage → 5 分钟超时守护 (kernel-chat.ts:76) 防止流挂起 ``` ### Store 拆分 (5 Store) 原来 908 行的 ChatStore 已拆分为: | Store | 文件 | 职责 | |-------|------|------| | streamStore | `store/chat/streamStore.ts` | 流式消息编排、发送、取消 | | conversationStore | `store/chat/conversationStore.ts` | 会话管理、当前模型 | | messageStore | `store/chat/messageStore.ts` | 消息持久化 | | chatStore | `store/chat/chatStore.ts` | 聊天通用状态 | | artifactStore | `store/chat/artifactStore.ts` | 聊天产物/附件 | ### 前端 Tauri 命令映射 ``` kernel_chat / agent_chat / agent_chat_stream → 发送消息 cancel_stream → 取消流式响应 ``` ### 模型切换 ``` UI 选择模型 → conversationStore.currentModel = newModel → 下次 sendMessage 时,connectionStore 读取 currentModel → SaaS 模式: relay 白名单验证 → 可用则切换 → 本地模式: 直接使用用户配置的模型 ``` ## API 接口 ### Tauri 命令 **聊天核心** (`desktop/src-tauri/src/kernel_commands/chat.rs`): | 命令 | 参数 | 返回值 | 说明 | |------|------|--------|------| | `agent_chat` | ChatRequest { agent_id, message, thinking_enabled?, model? } | `ChatResponse` | 非流式聊天 | | `agent_chat_stream` | StreamChatRequest { +session_id } | Tauri Event 流 | 流式聊天(主路径) | | `cancel_stream` | session_id | `()` | 取消当前流式 | **课堂聊天** (`desktop/src-tauri/src/classroom_commands/chat.rs`): | 命令 | 参数 | 返回值 | 说明 | |------|------|--------|------| | `classroom_chat` | { classroom_id, user_message, scene_context? } | `Vec` | 课堂对话 | | `classroom_chat_history` | classroom_id | `Vec` | 历史消息 | **流式事件类型** (agent_chat_stream emit): `Delta` / `ThinkingDelta` / `ToolStart` / `ToolEnd` / `HandStart` / `HandEnd` / `SubtaskStatus` / `IterationStart` / `Complete` / `Error` ### SaaS Relay 路由 | 方法 | 路径 | 说明 | |------|------|------| | POST | `/api/v1/relay/chat/completions` | OpenAI 兼容格式,支持 session_key/agent_id 透传 | ## 测试链路 | 功能 | 测试文件 | 测试数 | 覆盖状态 | |------|---------|--------|---------| | ChatStore 完整流程 | `tests/desktop/chatStore.test.ts` | 11 | ✅ sendMessage/sessionKey/agent隔离/stream相关 | | 类型契约测试 | `tests/seam/chat-seam.test.ts` | 8 | ✅ StreamChatRequest/ChatResponse camelCase/Event tagged union | ## 关联模块 - [[routing]] — 路由决定使用哪种 client - [[saas]] — Token Pool 提供模型和 API Key - [[butler]] — ButlerRouter 中间件增强 system prompt - [[middleware]] — 消息经过 15 层 runtime 中间件处理 - [[memory]] — 对话内容可能触发记忆提取 ## 关键文件 | 文件 | 职责 | |------|------| | `desktop/src/store/chat/streamStore.ts` | 流式消息编排 | | `desktop/src/store/chat/conversationStore.ts` | 会话管理 | | `desktop/src/store/chat/artifactStore.ts` | 聊天产物管理 | | `desktop/src/lib/kernel-chat.ts` | Kernel ChatStream (Tauri) | | `desktop/src/lib/saas-relay-client.ts` | SaaS Relay ChatStream | | `desktop/src/lib/gateway-client.ts` | Gateway ChatStream (WS) | | `desktop/src/components/ChatArea.tsx` | 聊天区域 UI | | `crates/zclaw-runtime/src/loop_runner.rs` | Rust 主聊天循环 | ## 已知问题 - ⚠️ **B-CHAT-07: 混合域截断** — P2 Open。跨域消息时可能截断上下文 - ✅ **SSE Token 统计为 0** — P2 已修复 (SseUsageCapture stream_done flag) - ✅ **Tauri invoke 参数名 snake_case** — P1 已修复 (commit f6c5dd2) - ✅ **Provider Key 解密致 relay 500** — P1 已修复 (commit b69dc61)