Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
- MarkdownRenderer: 从 StreamingText 提取共享 react-markdown + remark-gfm 组件 - ArtifactPanel: 替换手写 MarkdownPreview 为完整 GFM 渲染,添加文件选择器下拉菜单 - 数据源: file_write/str_replace 双工具 + sendMessage/initStreamListener 双路径 - 持久化: artifactStore 添加 zustand persist + IndexedDB (复用 idb-storage)
6.7 KiB
6.7 KiB
title, updated, status, tags
| title | updated | status | tags | |||
|---|---|---|---|---|---|---|
| 聊天系统 | 2026-04-23 | active |
|
聊天系统
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 } |
| LLM 动态建议 | 替换硬编码关键词匹配,用 LLM 生成个性化建议(1深入追问+1实用行动+1管家关怀),4路并行预取智能上下文 |
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 |
流式消息编排、发送、取消、LLM 动态建议生成 |
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/lib/suggestion-context.ts |
4路并行智能上下文拉取 (用户画像/痛点/经验/技能匹配) |
desktop/src/lib/cold-start-mapper.ts |
冷启动配置映射 (行业检测/命名/个性/技能) |
desktop/src/components/ChatArea.tsx |
聊天区域 UI |
desktop/src/components/ai/SuggestionChips.tsx |
动态建议芯片展示 |
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,与流式渲染解耦
- 动态建议 4 路并行预取 (userProfile/painPoints/experiences/skillMatch),500ms 超时降级为空串
- 建议生成与 memory extraction 解耦 — 不等 memory LLM 调用完成即启动建议
LLM 动态建议
sendMessage → isStreaming=true + _activeSuggestionContextPrefetch = fetchSuggestionContext(...)
→ 流式响应中 prefetch 在后台执行
onComplete → createCompleteHandler
→ generateLLMSuggestions(prefetchedContext) — 立即启动不等 memory
→ prompt: 1 深入追问 + 1 实用行动 + 1 管家关怀
→ memory/reflection 后台独立运行 (Promise.all)
→ SuggestionChips 渲染
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 - Agent tab 已移除 — 跨会话身份由 soul.md 接管,不再通过 RightPanel 管理
5. 变更日志
| 日期 | 变更 |
|---|---|
| 04-24 | 产物系统优化: MarkdownRenderer 提取共享 + ArtifactPanel react-markdown 渲染 + 文件选择器下拉 + 数据源扩展(file_write/str_replace 两路径) + artifactStore IndexedDB 持久化 |
| 04-23 | 建议 prefetch: sendMessage 时启动 context 预取,流结束后立即消费,不等 memory extraction |
| 04-23 | 建议 prompt 重写: 1深入追问+1实用行动+1管家关怀,上下文窗口 6→20 条 |
| 04-23 | 身份信号: detectAgentNameSuggestion 前端即时检测 + RightPanel 监听 Tauri 事件刷新名称 |
| 04-23 | Agent tab 移除: RightPanel 清理 ~280 行 dead code,身份由 soul.md 接管 |