diff --git a/wiki/butler.md b/wiki/butler.md index 7918f08..d8a5c86 100644 --- a/wiki/butler.md +++ b/wiki/butler.md @@ -7,208 +7,144 @@ tags: [module, butler, interaction] # 管家模式 (Butler Mode) -> 从 [[index]] 导航。关联模块: [[chat]] [[middleware]] [[memory]] +> 从 [[index]] 导航。关联: [[chat]] [[middleware]] [[memory]] [[hands-skills]] -## 设计思想 +## 1. 设计决策 **核心问题: 非技术用户(如医院行政)不会写 prompt,需要 AI 主动引导。** -设计决策: -1. **默认激活** — 所有聊天都经过 ButlerRouter,不需要用户手动开启 -2. **语义路由** — SemanticSkillRouter 用 TF-IDF 匹配 75 个技能,替代简单关键词 -3. **痛点积累** — 从对话中提取用户痛点,积累后生成方案建议 -4. **双模式 UI** — simple(纯聊天,默认) / professional(完整功能),渐进式解锁 +| 决策 | WHY | +|------|-----| +| 默认激活 | 所有聊天都经过 ButlerRouter,无需用户手动开启。降低使用门槛到零 | +| 语义路由 + 痛点积累 | SemanticSkillRouter 用 TF-IDF 匹配 75 个技能(详见 [[hands-skills]]),从对话中提取痛点并积累后生成方案建议 | +| 双模式 UI | simple(纯聊天) / professional(完整功能),渐进式解锁。简洁模式隐藏高级功能降低认知负担 | +| ButlerRouter@80 中间件 | 在 Evolution@78 之后、Memory@150 之前执行。先路由增强 prompt,再检索记忆注入,最后技能索引 | +| XML fencing `` | 结构化注入 system prompt,避免与用户消息混淆。LLM 可区分管家上下文和用户输入 | +| 冷启动 4 阶段 hook | idle -> greeting_sent -> waiting_response -> completed,自动检测新用户并发送欢迎引导 | +| 4 内置行业 + 自定义关键词 | 医疗/教育/制衣/电商开箱即用,ButlerRouter 动态行业关键词注入支持扩展 | -## 代码逻辑 +## 2. 关键文件 + 数据流 -### 数据流 - -``` -用户消息 - → ButlerRouter 中间件 (middleware/butler_router.rs) - → ButlerRouterBackend trait → SemanticRouterAdapter - → SemanticSkillRouter (zclaw-skills/src/semantic_router.rs) - → TF-IDF 计算与 75 个技能的相似度 - → 返回 RoutingHint { category, confidence, skill_id } - → 增强 system prompt (匹配的技能上下文) - → LLM 响应 - → PainAggregator 提取痛点 - → PainStorage (内存 Vec 热缓存 + SQLite 持久层) - → 全局 PAIN_STORAGE 单例 - → SolutionGenerator - → 基于痛点生成解决方案提案 -``` - -### 语义路由桥接(kernel 层) - -```rust -// crates/zclaw-kernel/src/kernel/mod.rs:196-231 -struct SemanticRouterAdapter { router: Arc } -impl ButlerRouterBackend for SemanticRouterAdapter { - async fn classify(&self, query: &str) -> Option { ... } -} -``` - -这是 kernel 依赖 zclaw-runtime + zclaw-skills 的桥接点。 - -### 冷启动 (新用户引导) - -入口: `desktop/src/hooks/use-cold-start.ts`(lib/ 下有同名文件) - -``` -idle → (检测新用户) → greeting_sent → waiting_response → completed -``` - -4 个阶段,自动检测用户是否需要引导,发送欢迎消息,等待响应后完成。 - -### UI 双模式 - -| 模式 | Store | 特点 | -|------|-------|------| -| simple (默认) | `uiModeStore.ts` | 纯聊天界面,隐藏高级功能 | -| professional | `uiModeStore.ts` | 完整功能面板 | - -切换文件: `desktop/src/store/uiModeStore.ts` -简洁侧边栏: `desktop/src/components/SimpleSidebar.tsx` -管家面板: `desktop/src/components/ButlerPanel.tsx` (3 区: 洞察/方案/记忆 + 行业专长卡片) - -### 管家Tab记忆展示(2026-04-22 增强) - -> ButlerPanel 的 MemorySection 组件负责向用户展示管家了解的信息。 - -``` -ButlerPanel (index.tsx) - ├── InsightsSection — 痛点洞察 - ├── ProposalsSection — 方案建议 - ├── MemorySection — 记忆 + 用户画像 (增强后) - │ ├── 用户画像卡片 — agent_get → UserProfileStore (data.db) - │ │ ├── 行业/角色/沟通风格 (profile_store.update_field) - │ │ ├── 近期话题标签 (profile_store.add_recent_topic, 上限10) - │ │ └── 常用工具标签 (profile_store.add_preferred_tool, 上限10) - │ └── 记忆分组列表 — viking_ls + viking_read(L1) (memories.db) - │ ├── 偏好 (preferences) — 默认展开 - │ ├── 知识 (knowledge) — 默认展开 - │ ├── 经验 (experience) — 折叠 - │ └── 会话 (sessions) — 折叠 - └── 行业专长卡片 — industryStore - -数据源: - 记忆列表: listVikingResources("agent://{agent_id}/") → viking_ls - 记忆摘要: readVikingResource(uri, "L1") → viking_read → L1 摘要 (并行加载) - 用户画像: agent_get(agentId) → kernel.memory() → UserProfileStore.get() → data.db - -关键文件: - desktop/src/components/ButlerPanel/MemorySection.tsx 记忆+画像展示组件 - desktop/src/components/ButlerPanel/index.tsx 管家面板主组件 - desktop/src/lib/viking-client.ts viking_ls/viking_read 客户端 - desktop/src/lib/kernel-types.ts AgentInfo.userProfile 类型 -``` - -### 行业配置 (V13 已接通) - -- `desktop/src/store/industryStore.ts` — 行业配置 Zustand Store (persist, 离线缓存) -- ButlerPanel 展示行业专长卡片 + 自动拉取行业配置 -- SaaS API: `industry/list` / `industry/fullConfig` / `industry/accountIndustries` -- 4 内置行业: 医疗/教育/制衣/电商 (keywords/prompt/pain_seed_categories) -- ButlerRouter 动态行业关键词注入 (Arc>>) - -### Tauri 命令 - -5 个 butler 命令 (已标注 @reserved): - -```rust -// desktop/src-tauri/src/intelligence/pain_aggregator.rs -butler_list_pain_points -butler_record_pain_point -butler_generate_solution -butler_list_proposals -butler_update_proposal_status -``` - -### Intelligence 层文件结构 (16 .rs 文件) - -``` -desktop/src-tauri/src/intelligence/ -├── compactor.rs (5 commands: token estimation + compaction) -├── heartbeat.rs (10 commands: heartbeat engine CRUD) -├── identity.rs (16 commands: agent identity manager) -├── pain_aggregator.rs (5 commands: butler pain points) -├── reflection.rs (7 commands: reflection engine) -├── experience.rs 经验管理桥接 -├── extraction_adapter.rs 记忆提取适配器 -├── health_snapshot.rs 统一健康快照 -├── mod.rs 模块入口 -├── pain_storage.rs 痛点持久化 -├── personality_detector.rs 人格检测 -├── solution_generator.rs 方案生成 -├── trajectory_compressor.rs 轨迹压缩 -├── triggers.rs 触发信号管理 -├── user_profiler.rs 用户画像 -└── validation.rs 验证逻辑 -``` - -## 功能清单 - -| 功能 | 描述 | 入口文件 | 状态 | -|------|------|----------|------| -| 语义路由 | TF-IDF 匹配 75 技能关键词 | butler_router.rs | ✅ | -| 管家主动引导 | 冷启动 4 阶段 + 追问 | use-cold-start.ts | ✅ | -| 痛点积累 | 对话中提取痛点 → 方案建议 | pain_storage.rs | ✅ | -| 双模式 UI | simple/professional 渐进式 | uiModeStore.ts | ✅ | -| 行业配置 | 4 内置行业 + 自定义 | industryStore.ts | ✅ | -| 跨会话连续 | 痛点回访 + 经验检索 | butlerStore.ts | ✅ | -| ButlerContext 注入 | XML fencing 增强系统提示 | ButlerRouter@80 | ✅ | -| 个性化检测 | personality_detector 自动分类 | personality_detector.rs | ✅ | -| 方案生成 | 痛点 → 解决方案建议 | solution_generator.rs | ✅ | - -## 测试链路 - -| 功能 | 测试文件 | 测试数 | 覆盖状态 | -|------|---------|--------|---------| -| 管家路由 | intelligence/butler_router.rs (middleware/) | 12 | ✅ | -| 冷启动 | intelligence/cold_start_prompt.rs | 7 | ✅ | -| 痛点聚合 | intelligence/pain_aggregator.rs | 9 | ✅ | -| 痛点存储 | intelligence/pain_storage.rs | 11 | ✅ | -| 方案生成 | intelligence/solution_generator.rs | 5 | ✅ | -| 个性化 | intelligence/personality_detector.rs | 8 | ✅ | -| 触发信号 | intelligence/triggers.rs | 7 | ✅ | -| 用户画像 | intelligence/user_profiler.rs | 9 | ✅ | -| 经验 | intelligence/experience.rs | 9 | ✅ | -| 身份 | intelligence/identity.rs | 5 | ✅ | -| 反思 | intelligence/reflection.rs | 4 | ✅ | -| 压缩 | intelligence/trajectory_compressor.rs | 11 | ✅ | -| 验证 | intelligence/validation.rs | 5 | ✅ | -| 提取适配 | intelligence/extraction_adapter.rs | 3 | ✅ | -| **合计** | 15 文件 | **99** | | - -## 关联模块 - -- [[middleware]] — ButlerRouter 是中间件链中的第一层 -- [[chat]] — 消息流经过管家路由增强 -- [[memory]] — 痛点存储在 memory 子系统 -- [[hands-skills]] — 语义路由使用 75 个技能的 TF-IDF - -## 关键文件 +### 核心文件 | 文件 | 职责 | |------|------| | `crates/zclaw-runtime/src/middleware/butler_router.rs` | 管家路由器 + ButlerRouterBackend trait | -| `crates/zclaw-skills/src/semantic_router.rs` | SemanticSkillRouter TF-IDF 实现 | -| `crates/zclaw-kernel/src/kernel/mod.rs:196-231` | SemanticRouterAdapter 桥接 | -| `crates/zclaw-kernel/src/intelligence/pain_storage.rs` | 痛点双写 (内存+SQLite) | -| `crates/zclaw-kernel/src/intelligence/solution_generator.rs` | 方案生成 | +| `crates/zclaw-kernel/src/kernel/mod.rs:196-231` | SemanticRouterAdapter 桥接 (kernel -> skills) | +| `desktop/src-tauri/src/intelligence/pain_storage.rs` | 痛点双写 (内存 Vec + SQLite) | +| `desktop/src-tauri/src/intelligence/solution_generator.rs` | 方案生成 | | `desktop/src/hooks/use-cold-start.ts` | 冷启动 4 阶段 | | `desktop/src/store/uiModeStore.ts` | 双模式切换 | -| `desktop/src/components/SimpleSidebar.tsx` | 简洁模式侧边栏 | -| `desktop/src/components/ButlerPanel/index.tsx` | 管家面板主组件 (洞察/方案/记忆/行业) | -| `desktop/src/components/ButlerPanel/MemorySection.tsx` | 记忆展示+用户画像卡片 (viking_read L1 + agent_get) | -| `desktop/src/components/ButlerPanel/InsightsSection.tsx` | 痛点洞察列表 | -| `desktop/src/components/ButlerPanel/ProposalsSection.tsx` | 方案建议列表 | +| `desktop/src/store/industryStore.ts` | 行业配置 (persist, 离线缓存) | +| `desktop/src/components/ButlerPanel/index.tsx` | 管家面板 (洞察/方案/记忆/行业) | +| `desktop/src/components/ButlerPanel/MemorySection.tsx` | 记忆展示 + 用户画像卡片 | -## 已知问题 +### ButlerRouter 数据流 -- ✅ **行业 API 字段名不一致** — BUG-L1 已修复。`pain_seeds` vs `pain_seed_categories` -- ✅ **industryStore 无组件导入** — V13-GAP-02 已修复 (已连接 ButlerPanel) -- ✅ **行业选择 500** — 类型不匹配已修复 -- ✅ **桌面端未接入 Knowledge Search** — V13-GAP-03 已修复 (saas-knowledge mixin) -- ⚠️ **SkillIndex 条件注册** — 无技能时中间件不注册,长期观察 +``` +用户消息 + -> ButlerRouter@80 (middleware/butler_router.rs) + -> SemanticRouterAdapter -> SemanticSkillRouter (TF-IDF) + -> 返回 RoutingHint { category, confidence, skill_id } + -> 增强 system prompt (匹配技能上下文 + XML fencing) + -> LLM 响应 + -> PainAggregator 提取痛点 -> PainStorage (内存+SQLite) + -> SolutionGenerator 基于痛点生成方案 +``` + +### 集成契约 + +| 方向 | 模块 | 接口 / 触发点 | +|------|------|---------------| +| Registered as | middleware: ButlerRouter@80 | `kernel/mod.rs:create_middleware_chain()` | +| Calls -> | hands-skills: SemanticSkillRouter | TF-IDF 技能路由,返回 RoutingHint | +| Calls -> | memory: ExperienceStore, UserProfileStore | 痛点/经验检索,pre_hook 注入活跃痛点 | +| Called by <- | middleware chain | Every chat request | + +### Intelligence 层 (16 .rs 文件) + +`desktop/src-tauri/src/intelligence/` 包含: compactor(5 cmd), heartbeat(10 cmd), identity(16 cmd), pain_aggregator(5 cmd), reflection(7 cmd), experience, extraction_adapter, health_snapshot, pain_storage, personality_detector, solution_generator, trajectory_compressor, triggers, user_profiler, validation。 + +管家相关 Tauri 命令 (5 个, @reserved): `butler_list_pain_points`, `butler_record_pain_point`, `butler_generate_solution`, `butler_list_proposals`, `butler_update_proposal_status`。 + +## 3. 代码逻辑 + +### 语义关键词匹配 + +ButlerRouter 维护行业关键词配置 (`Arc>>`): +- 4 内置行业: 医疗/教育/制衣/电商,各有 keywords/prompt/pain_seed_categories +- 动态注入: SaaS `industry/fullConfig` 端点拉取自定义行业 +- 匹配流程: message -> 关键词命中 -> 识别行业 -> 注入行业 prompt 增强 + +### XML fencing 注入格式 + +``` + + ... + ... + 医疗 + data-analysis + +``` + +### 跨会话连续性 (pre_hook) + +新会话开始时,ButlerRouter 的 pre_hook 注入: +1. 活跃痛点: 从 PainStorage 加载未解决痛点 +2. 相关经验: 通过 ExperienceStore FTS5 检索 +3. 用户画像: UserProfileStore (data.db) 提供行业/角色/偏好 +4. 记忆展示: ButlerPanel -> MemorySection -> viking_ls + viking_read(L1) (详见 [[memory]]) + +### 冷启动 4 阶段 + +``` +idle -> (检测新用户) -> greeting_sent -> waiting_response -> completed +``` + +自动检测 -> 发送欢迎消息 -> 等待响应 -> 完成引导。入口: `use-cold-start.ts`。 + +### 不变量 + +- ButlerRouter@80 在所有能力类中间件之前执行,确保 routing hint 可被后续中间件利用 +- PainStorage 双写: 内存 Vec 热缓存 + SQLite 持久层,通过全局 PAIN_STORAGE 单例 +- UI 双模式: simple(默认) 隐藏高级功能,professional 完整面板。切换: `uiModeStore.ts` + +## 4. 活跃问题 + 陷阱 + +### 活跃 + +| 问题 | 状态 | 说明 | +|------|------|------| +| SkillIndex 条件注册 | 长期观察 | 无技能时中间件不注册,需关注空技能场景一致性 | + +### 历史 (已修复) + +| 问题 | 修复 | +|------|------| +| 行业 API 字段名不一致 (pain_seeds vs pain_seed_categories) | BUG-L1 已修复 | +| industryStore 无组件导入 | V13-GAP-02 已修复,ButlerPanel 已连接 | +| 行业选择 500 | 类型不匹配已修复 | +| 桌面端未接入 Knowledge Search | V13-GAP-03 已修复 | +| DataMasking 中间件过度匹配 | 04-22 移除整个中间件 | + +## 5. 变更日志 + +| 日期 | 变更 | 关联 | +|------|------|------| +| 2026-04-22 | Wiki 5-section 重构: 215->~190 行,移除重复内容引用 [[memory]]/[[hands-skills]] | wiki/ | +| 2026-04-22 | 跨会话记忆断裂修复: profile_store 连接 + 双数据库统一 | commit adf0251 | +| 2026-04-17 | E2E 全系统验证 129 链路: 7 项 Bug 修复含行业字段/记忆去重 | 79.1% 通过率 | +| 2026-04-12 | 行业配置+管家主动性全栈: 4内置行业+动态关键词+跨会话连续性+XML fencing | 全栈 5 Phase | +| 2026-04-09 | 管家模式 6 交付物完成: ButlerRouter+冷启动+简洁UI+桥测试+文档 | 43 tests PASS | + +### 测试概览 + +| 功能 | 测试文件 | 测试数 | +|------|---------|--------| +| 管家路由 | intelligence/butler_router.rs | 12 | +| 冷启动 | cold_start_prompt.rs | 7 | +| 痛点聚合+存储 | pain_aggregator + pain_storage | 20 | +| 方案生成 | solution_generator.rs | 5 | +| 个性化 | personality_detector.rs | 8 | +| 其他 (触发/画像/经验/身份/反思/压缩/验证/提取) | 8 文件 | 47 | +| **合计** | **15 文件** | **99** | diff --git a/wiki/chat.md b/wiki/chat.md index 04fe204..91159b1 100644 --- a/wiki/chat.md +++ b/wiki/chat.md @@ -1,6 +1,6 @@ --- title: 聊天系统 -updated: 2026-04-21 +updated: 2026-04-22 status: active tags: [module, chat, stream] --- @@ -9,149 +9,126 @@ tags: [module, chat, stream] > 从 [[index]] 导航。关联模块: [[routing]] [[saas]] [[butler]] -## 设计思想 +## 1. 设计决策 -**核心问题: 3 种运行环境需要 3 种 ChatStream 实现。** +| 决策 | 原因 | +|------|------| +| 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 }` | -| 环境 | 实现 | 传输 | 为什么 | -|------|------|------|--------| -| 桌面端 (Tauri) | KernelClient | Tauri Event | 内置 Kernel,但 baseUrl 可指向 SaaS relay | -| 桌面端 (Tauri + SaaS) | KernelClient + relay | Tauri Event → SaaS | 主路径: Token Pool 中转 | -| 浏览器端 | SaaSRelayGatewayClient | HTTP SSE | 无 Tauri 运行时 | -| 外部 Gateway | GatewayClient | WebSocket | 独立进程部署 | +### ChatStream 实现 -**统一接口**: 3 种实现共享同一套回调: +| 环境 | 实现 | 传输 | +|------|------|------| +| Tauri + SaaS (主路径) | KernelClient + relay | Tauri Event → SaaS Token Pool → LLM | +| Tauri 本地 | KernelClient | Tauri Event → Kernel → LLM 直连 | +| 浏览器端 | SaaSRelayGatewayClient | HTTP SSE → SaaS → LLM | +| 外部 Gateway | GatewayClient | WebSocket | -```ts -{ onDelta, onThinkingDelta, onTool, onHand, onComplete, onError } -``` +## 2. 关键文件 + 数据流 -## 功能清单 +### 核心文件 -| 功能 | 描述 | 入口文件 | 状态 | -|------|------|----------|------| -| 发送消息 | 流式/非流式,支持 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 | ✅ | - -## 代码逻辑 +| 文件 | 职责 | +|------|------| +| `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 主聊天循环 + 中间件链 | ### 发送消息流 -入口: `streamStore.sendMessage(content)` → `store/chat/streamStore.ts` +``` +用户输入 → 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) +``` -``` -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) 防止流挂起 -``` +### 集成契约 + +| 方向 | 模块 | 接口 | 说明 | +|------|------|------|------| +| 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) -原来 908 行的 ChatStore 已拆分为: - | Store | 文件 | 职责 | |-------|------|------| -| streamStore | `store/chat/streamStore.ts` | 流式消息编排、发送、取消 | +| streamStore | `store/chat/streamStore.ts` | 流式编排、发送、取消 | | conversationStore | `store/chat/conversationStore.ts` | 会话管理、当前模型 | -| messageStore | `store/chat/messageStore.ts` | 消息持久化 | +| messageStore | `store/chat/messageStore.ts` | 消息持久化 (IndexedDB) | | chatStore | `store/chat/chatStore.ts` | 聊天通用状态 | | artifactStore | `store/chat/artifactStore.ts` | 聊天产物/附件 | -### 前端 Tauri 命令映射 +### 流式事件类型 -``` -kernel_chat / agent_chat / agent_chat_stream → 发送消息 -cancel_stream → 取消流式响应 -``` +`agent_chat_stream` Tauri Event emit 的 tagged union: + +`Delta` / `ThinkingDelta` / `ToolStart` / `ToolEnd` / `HandStart` / `HandEnd` / `SubtaskStatus` / `IterationStart` / `Complete` / `Error` ### 模型切换 ``` UI 选择模型 → conversationStore.currentModel = newModel - → 下次 sendMessage 时,connectionStore 读取 currentModel + → 下次 sendMessage → getClient() 读取 currentModel → SaaS 模式: relay 白名单验证 → 可用则切换 → 本地模式: 直接使用用户配置的模型 ``` -## API 接口 +### 不变量 + +- sessionKey 在会话内必须一致 (UUID 生成一次,持久化 IndexedDB) +- cancelStream 设置原子标志位,与 onDelta 回调无竞态 +- 3 种 ChatStream 共享同一套回调接口,上层代码无需感知实现差异 +- 消息持久化走 messageStore → IndexedDB,与流式渲染解耦 ### 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 主聊天循环 | +| `agent_chat_stream` | 流式聊天 (主路径) | +| `agent_chat` | 非流式聊天 | +| `cancel_stream` | 取消当前流式响应 | +| `classroom_chat` | 课堂场景对话 | -## 已知问题 +## 4. 活跃问题 + 注意事项 -- ⚠️ **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) +| 问题 | 状态 | 说明 | +|------|------|------| +| 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) | diff --git a/wiki/data-model.md b/wiki/data-model.md index f3b9040..723aabc 100644 --- a/wiki/data-model.md +++ b/wiki/data-model.md @@ -1,6 +1,6 @@ --- title: 数据模型 -updated: 2026-04-21 +updated: 2026-04-22 status: active tags: [module, database, schema] --- @@ -9,82 +9,78 @@ tags: [module, database, schema] > 从 [[index]] 导航。关联模块: [[saas]] [[memory]] -## 设计思想 +## 1. 设计决策 -**双存储架构: PostgreSQL (SaaS 多租户) + SQLite (本地单用户)** +**WHY 双存储架构**: PostgreSQL 适合 SaaS 多租户场景 — 42 表支持用户隔离、计费、审计、知识库。SQLite 适合桌面端单用户 — 零配置、嵌入式、本地记忆无需网络。两者完全隔离,通过 SaaS relay + 配置同步桥接。 -- PostgreSQL: SaaS 后端,42 表,42 CREATE TABLE,支持多用户/多 Agent/计费/知识库 -- SQLite: 本地桌面端,记忆存储 + 会话持久化 + FTS5 全文索引 -- 两者通过 SaaS relay + 配置同步实现数据桥接 +**WHY 42 PostgreSQL 表**: 按领域划分为认证(5)、Provider(6)、计费(5)、知识库(7)、其他(19) 五大域,每个域内部高内聚,域间通过外键约束保证引用完整性。 -## 功能清单 +**WHY FTS5 (SQLite 全文搜索)**: 本地记忆搜索需支持中文。FTS5 的 trigram 分词器原生支持 CJK 字符,无需外部依赖。配合 TF-IDF 权重排序,兼顾搜索精度和零配置部署。 -| 功能 | 描述 | 存储层 | 状态 | -|------|------|--------|------| -| 用户管理 | 账户 CRUD + 角色权限 | PostgreSQL | ✅ | -| 认证数据 | JWT + 密码 + TOTP | PostgreSQL | ✅ | -| 计费系统 | 订阅/支付/发票/配额 | PostgreSQL | ✅ | -| 知识库 | 分类/条目/向量/结构化 | PostgreSQL | ✅ | -| 模型管理 | Provider/模型/Key 池 | PostgreSQL | ✅ | -| Agent 配置 | 模板/分配/行业 | PostgreSQL | ✅ | -| 本地会话 | 会话/消息持久化 | SQLite | ✅ | -| 本地记忆 | 记忆 CRUD + FTS5 搜索 | SQLite | ✅ | -| 用户画像 | 结构化偏好/兴趣 | SQLite | ✅ | -| 轨迹记录 | 工具调用链 + 压缩摘要 | SQLite | ✅ | +**WHY 迁移系统**: SaaS 用 SQL 文件迁移 (21 up + 17 down),本地 SQLite 用 schema.rs 程序化迁移。两种策略分别匹配各自部署场景 — PG 需要运维可控的 SQL 脚本,SQLite 需要应用内自动迁移。 -## 代码逻辑 +**WHY pgvector (deferred)**: knowledge_chunks 表已创建 pgvector 索引,为将来 embedding 语义搜索预留。当前记忆搜索走 FTS5 + TF-IDF,足够满足中文场景。embedding 激活需要 LLM embedding API 调用,尚未排期。 -### PostgreSQL (SaaS) — 42 表 +## 2. 关键文件 + 数据流 -**迁移文件**: `crates/zclaw-saas/migrations/` (21 up + 17 down) +### 核心文件 -#### 认证与账户域 (5 表) +| 文件 | 职责 | +|------|------| +| `crates/zclaw-saas/migrations/` | 21 up SQL 迁移 (42 CREATE TABLE) | +| `crates/zclaw-saas/src/models/` | PostgreSQL 数据模型 struct 定义 | +| `crates/zclaw-memory/src/schema.rs` | SQLite schema 定义 + 程序化迁移 | +| `crates/zclaw-memory/src/store.rs` | SQLite 会话/消息存储 (20 tests) | +| `crates/zclaw-memory/src/user_profile_store.rs` | 用户画像存储 (20 tests) | +| `crates/zclaw-memory/src/trajectory_store.rs` | 轨迹存储 (9 tests) | +| `crates/zclaw-growth/src/storage/sqlite.rs` | FTS5 + TF-IDF 记忆存储 (6 tests) | +| `docker-compose.yml` | PostgreSQL 容器配置 | -| 表 | 说明 | 关键关系 | -|----|------|---------| -| `accounts` | 用户账户 (邮箱/密码/pwv/角色) | → api_tokens, operation_logs, subscriptions | -| `api_tokens` | API 访问令牌 | FK → accounts | -| `roles` | 角色定义 | — | -| `permission_templates` | 权限模板 | — | -| `refresh_tokens` | JWT 刷新令牌 (单次使用) | FK → accounts | +### 双库架构 -#### Provider 与模型域 (6 表) +``` +PostgreSQL (SaaS, 42 表) SQLite (本地, 2 库) +┌─────────────────────────┐ ┌──────────────────────────┐ +│ 认证: accounts/tokens │ │ data.db: │ +│ Provider: keys/models │ │ agents, sessions, │ +│ 计费: plans/invoices │ │ messages, kv_store, │ +│ 知识库: items/chunks │ │ facts, user_profiles, │ +│ 配置/日志/Webhook │ │ trajectory_events, │ +│ 行业: industries │ │ hand_runs │ +└─────────────────────────┘ ├──────────────────────────┤ + ↑ SaaS API │ memories.db: │ + │ relay_tasks │ memories (FTS5), │ + │ usage_records │ metadata │ + └──────────────────────────┘ + ↑ 本地 API +``` -| 表 | 说明 | 关键关系 | -|----|------|---------| -| `providers` | LLM Provider 配置 | → models, provider_keys | -| `models` | 模型定义 (白名单) | FK → providers | -| `provider_keys` | 加密 API Key 池 | FK → providers, accounts | -| `key_usage_window` | Key RPM/TPM 滑动窗口 | — | -| `model_groups` | 模型组 (故障转移) | → model_group_members | -| `model_group_members` | 组成员 | FK → model_groups, providers | +### 集成契约 -#### 计费域 (5 表) +| 方向 | 接口 | 说明 | +|------|------|------| +| Called by ← saas | PostgreSQL 连接池 (sqlx) | 所有 SaaS API 端点通过连接池访问,Pool 大小可配置 | +| Called by ← memory | SQLite/FTS5 连接 (rusqlite) | 记忆提取/检索/注入 通过 zclaw-memory + zclaw-growth | +| Provides → growth | TF-IDF + FTS5 索引 | zclaw-growth 调用 FTS5 全文搜索 + TF-IDF 语义评分 | +| Called by ← kernel | schema.rs 程序化迁移 | 桌面端启动时自动执行 SQLite 迁移,版本由 schema_version 表跟踪 | +| Provides → butler | user_profiles + facts | 管家模式读取用户画像和提取事实进行上下文注入 | -| 表 | 说明 | 关键关系 | -|----|------|---------| -| `billing_plans` | 计费计划目录 | → subscriptions | -| `billing_subscriptions` | 用户订阅 | FK → accounts, billing_plans | -| `billing_invoices` | 发票 | FK → accounts, subscriptions, plans | -| `billing_payments` | 支付记录 | FK → billing_invoices, accounts | -| `billing_usage_quotas` | 用量配额 | FK → accounts, billing_plans | +## 3. 代码逻辑 -#### 知识库域 (7 表) +### PostgreSQL 域划分 (42 表) -| 表 | 说明 | 关键关系 | -|----|------|---------| -| `knowledge_categories` | 分类 (自引用 parent_id) | → knowledge_items | -| `knowledge_items` | 知识条目 | FK → categories, accounts | -| `knowledge_chunks` | 向量分块 (pgvector) | FK → knowledge_items | -| `knowledge_versions` | 版本历史 | FK → items, chunks, accounts | -| `knowledge_usage` | 使用统计 | FK → knowledge_items | -| `structured_sources` | 结构化数据源 | FK → accounts | -| `structured_rows` | 结构化行数据 | FK → structured_sources | +**认证域 (5 表)**: `accounts` (邮箱/密码/pwv/角色) → `api_tokens`, `refresh_tokens` (JWT 单次使用), `roles`, `permission_templates` -#### 其他域 (19 表) +**Provider 域 (6 表)**: `providers` → `models` (白名单), `provider_keys` (加密 API Key 池), `key_usage_window` (RPM/TPM 滑动窗口), `model_groups` → `model_group_members` (故障转移组) -| 域 | 表 | 说明 | -|----|----|------| +**计费域 (5 表)**: `billing_plans` → `billing_subscriptions` (用户订阅) → `billing_invoices` → `billing_payments`, `billing_usage_quotas` (实时递增配额) + +**知识库域 (7 表)**: `knowledge_categories` (自引用树) → `knowledge_items` → `knowledge_chunks` (pgvector 向量), `knowledge_versions`, `knowledge_usage`, `structured_sources` → `structured_rows` + +**其他域 (19 表)**: + +| 子域 | 表 | 说明 | +|------|-----|------| | API & Relay | `account_api_keys`, `usage_records`, `relay_tasks` | API Key/用量/异步任务 | | 配置 | `config_items`, `config_sync_log` | KV 配置/同步日志 | | Prompt | `prompt_templates`, `prompt_versions`, `prompt_sync_status` | 模板/版本/同步 | @@ -92,89 +88,66 @@ tags: [module, database, schema] | 设备 | `devices` | 设备管理 | | 遥测 | `operation_logs`, `telemetry_reports`, `saas_schema_version` | 操作日志/统计/版本 | | 调度 | `scheduled_tasks` | 定时任务 | -| 限流 | `rate_limit_events` | 限流事件日志 | +| 限流 | `rate_limit_events` | 限流事件 (持久化到 PG) | | Webhook | `webhook_subscriptions`, `webhook_deliveries` | Webhook 订阅/投递 | | 行业 | `industries`, `account_industries` | 行业配置/账户关联 | ### SQLite 本地存储 -**zclaw-memory** (`crates/zclaw-memory/src/schema.rs`): +**data.db** (`zclaw-memory/schema.rs`): agents, sessions, messages, kv_store, facts, user_profiles, trajectory_events, compressed_trajectories, hand_runs, schema_version -| 表 | 说明 | 版本 | -|----|------|------| -| `agents` | Agent 定义 | v1 | -| `sessions` | 聊天会话 | v1, FK → agents | -| `messages` | 会话消息 | v1, FK → sessions | -| `kv_store` | Agent KV 存储 | v1, FK → agents | -| `facts` | 提取的事实 | v2, FK → agents | -| `user_profiles` | 用户画像 | v2 | -| `trajectory_events` | 工具调用链事件 | v3 | -| `compressed_trajectories` | 压缩轨迹摘要 | v3 | -| `hand_runs` | Hand 执行追踪 | v1 | -| `schema_version` | 迁移版本 | v1 | +**memories.db** (`zclaw-growth/storage/sqlite.rs`): memories (uri, memory_type, content, keywords, importance, access_count), metadata (KV) -**zclaw-growth** (`crates/zclaw-growth/src/storage/sqlite.rs`): +### FTS5 虚拟表 -| 表 | 说明 | -|----|------| -| `memories` | 记忆条目 (uri, memory_type, content, keywords, importance, access_count) | -| `metadata` | KV 元数据 | - -**FTS5 虚拟表**: - -| 虚拟表 | 定义 | 分词器 | -|--------|------|--------| -| `memories_fts` | `fts5(uri, content, keywords)` | `trigram` (CJK 支持) | - -> FTS5 使用 `trigram` 分词器(从 `unicode61` 迁移)支持中文/日文/韩文。CJK 查询零结果时 fallback 到 LIKE 搜索。 - -## 数据流 - -``` -桌面端聊天 - → SQLite: sessions + messages (本地持久化) - → SaaS Relay: relay_tasks (异步任务追踪) - → PostgreSQL: usage_records (用量记录) - -记忆管道 - → SQLite: memories + memories_fts (FTS5 全文索引) - → SQLite: facts + user_profiles (结构化提取) - → PostgreSQL: knowledge_chunks (pgvector 向量, embedding deferred) - -计费闭环 - → PostgreSQL: billing_usage_quotas (实时递增) - → PostgreSQL: billing_subscriptions → invoices → payments - → Worker: aggregate_usage (聚合器调度) +```sql +CREATE VIRTUAL TABLE memories_fts USING fts5(uri, content, keywords, tokenize='trigram'); ``` -## 测试链路 +> trigram 分词器从 unicode61 迁移,原生支持 CJK。零结果时 fallback 到 LIKE 搜索。 -| 功能 | 测试文件 | 覆盖状态 | -|------|---------|---------| -| 全模块 | `crates/zclaw-saas/tests/` (17 文件) | ✅ | -| SQL 迁移 | `crates/zclaw-saas/migrations/` (21 up) | ✅ 启动时自动执行 | -| 本地存储 | `crates/zclaw-memory/src/store.rs` (20 tests) | ✅ | -| 用户画像 | `crates/zclaw-memory/src/user_profile_store.rs` (20 tests) | ✅ | -| 轨迹存储 | `crates/zclaw-memory/src/trajectory_store.rs` (9 tests) | ✅ | -| 记忆存储 | `crates/zclaw-growth/src/storage/sqlite.rs` (6 tests) | ✅ | +### 不变量 + +> **SaaS 用 PostgreSQL,本地记忆用 SQLite,两者完全隔离。** 数据不自动同步,仅通过 SaaS relay API 手动触发。 +> **FTS5 使用 trigram 分词器,中文搜索依赖正确分词。** 极短查询 (1-2 字) 可能 fallback 到 LIKE。 +> **data.db 与 memories.db 是两个独立的 SQLite 数据库。** zclaw-memory 管理 data.db (会话/画像),zclaw-growth 管理 memories.db (记忆/FTS5)。跨库查询不适用,数据交换通过 Rust API。 + +### 测试链路 + +| 功能 | 测试文件 | 测试数 | +|------|---------|--------| +| SaaS 全模块 | `crates/zclaw-saas/tests/` (17 文件) | 集成测试 | +| SQL 迁移 | `crates/zclaw-saas/migrations/` (21 up) | 启动时自动执行 | +| 本地存储 | `zclaw-memory/src/store.rs` | 20 | +| 用户画像 | `zclaw-memory/src/user_profile_store.rs` | 20 | +| 轨迹存储 | `zclaw-memory/src/trajectory_store.rs` | 9 | +| 记忆存储 | `zclaw-growth/src/storage/sqlite.rs` | 6 | + +## 4. 活跃问题 + 注意事项 + +| 优先级 | 问题 | 说明 | +|--------|------|------| +| P2 | pgvector embedding 未激活 | knowledge_chunks 表索引就绪,generate_embedding Worker 逻辑 deferred | +| P3 | FTS5 CJK 极短查询 | trigram 已启用,1-2 字查询可能 fallback 到 LIKE,待真实用户反馈 | +| — | 迁移幂等性 | PG 迁移用 `IF NOT EXISTS`,SQLite 迁移用 schema_version 表跟踪 | + +**注意事项**: PostgreSQL 连接需要 `ZCLAW_DATABASE_URL` 或 `saas-config.toml` 中的 `database_url` 配置。本地 SQLite 数据库文件存储在 Tauri 应用数据目录下,卸载应用会丢失数据。`schema.rs` 中的 SQLite 迁移通过 `schema_version` 表跟踪版本号,仅执行增量迁移,不会重建已存在的表。环境变量 `DB_PASSWORD` 支持 `saas-config.toml` 中 `${VAR_NAME}` 插值引用。 ## 关联模块 -- [[saas]] — PostgreSQL 由 SaaS 后端管理 -- [[memory]] — SQLite 本地记忆存储 + FTS5 -- [[routing]] — relay_tasks 异步任务追踪 +- [[saas]] — PostgreSQL 由 SaaS 后端管理,所有 API 端点的数据源 +- [[memory]] — SQLite 本地记忆存储 + FTS5 全文搜索 + TF-IDF 权重 +- [[routing]] — relay_tasks 异步任务追踪,连接前端请求和 SaaS 后端 +- [[security]] — 数据加密: provider_keys AES-256-GCM, 密码 Argon2id, TOTP 独立加密密钥 -## 关键文件 +## 5. 变更日志 -| 文件 | 职责 | +> 最近 5 条与数据模型相关的变更。完整日志见 [[log]]。 + +| 日期 | 变更 | |------|------| -| `crates/zclaw-saas/migrations/` | 21 up SQL 迁移 (42 CREATE TABLE) | -| `crates/zclaw-saas/src/models/` | 数据模型 struct 定义 | -| `crates/zclaw-memory/src/schema.rs` | SQLite schema 定义 | -| `crates/zclaw-growth/src/storage/sqlite.rs` | FTS5 + TF-IDF 存储 | -| `docker-compose.yml` | PostgreSQL 容器配置 | - -## 已知问题 - -- ⚠️ **pgvector embedding 生成未实现** — 索引就绪,`generate_embedding.rs` Worker 逻辑 deferred -- ⚠️ **FTS5 CJK 零结果** — trigram 分词器已启用,极短查询可能仍 fallback 到 LIKE +| 2026-04-22 | 跨会话记忆修复: profile_store 连接 + 双数据库统一 + 诊断日志 | +| 2026-04-21 | Phase 0+1: 经验积累 reuse_count 修复 + 跨会话检索增强 IdentityRecall 26→54 模式 | +| 2026-04-19 | TRUTH.md 数字校准: 42 CREATE TABLE, 38 迁移文件, Rust 101,967 行 | +| 2026-04-17 | E2E 测试: 记忆去重+记忆注入+invoice_id+agent隔离修复 | +| 2026-04-15 | Heartbeat: health_snapshot 统一收集器,删除 intelligence-client/ 9 废弃文件 | diff --git a/wiki/hands-skills.md b/wiki/hands-skills.md index a54013d..134de62 100644 --- a/wiki/hands-skills.md +++ b/wiki/hands-skills.md @@ -7,216 +7,60 @@ tags: [module, hands, skills, mcp] # Hands + Skills + MCP -> 从 [[index]] 导航。关联模块: [[chat]] [[middleware]] [[butler]] +> 从 [[index]] 导航。关联: [[chat]] [[middleware]] [[butler]] -## 设计思想 +## 1. 设计决策 -**Hands = 自主能力 (行动层), Skills = 知识技能 (认知层), MCP = 外部工具协议** +**Hands = 自主能力 (行动层), Skills = 知识技能 (认知层), MCP = 外部工具协议。** -- Hands: 浏览器自动化、数据收集、Twitter 操作等 — **执行动作** -- Skills: 75 个 SKILL.md 文件 — **语义路由匹配**,增强 LLM 的领域知识 -- MCP: Model Context Protocol — **动态外部工具**,运行时发现和调用 -- 触发: 用户请求 → LLM 判断需要执行 → 选择 Hand/Skill/MCP Tool → 执行 +| 决策 | WHY | +|------|-----| +| 7 注册 Hands (6 TOML + _reminder) | 每个 Hand 有独立配置和 Rust 实现,启用/禁用由 `enabled` 字段控制。_reminder 由 kernel 代码注册,无 HAND.toml | +| 75 Skills + 语义路由 | SKILL.md 定义领域知识,SemanticSkillRouter 用 TF-IDF 匹配(详见 [[butler]] 路由细节),增强 LLM 领域能力而不硬编码 | +| MCP bridge | 运行时发现外部工具服务器,McpToolWrapper 适配 Tool trait,让 LLM 在对话中直接调用 filesystem/database 等外部工具 | +| LLM Tool Calling 触发 | Skill 调用通过 LLM 生成 ToolUse,不是直接函数调用。保持 LLM 主导决策权 | +| 定时提醒链路 | NlScheduleParser 中文时间->cron + _reminder Hand + TriggerManager,支持"每天早上9点提醒我查房" | -## 代码逻辑 +## 2. 关键文件 + 数据流 -### Hands (7 注册: 6 TOML + 1 系统内部) +### 核心文件 -每个 Hand 有独立的 `hands/.HAND.toml` 配置和 `crates/zclaw-hands/src/hands/` 下的 Rust 实现。 - -| Hand | 功能 | 依赖 | 测试数 | 配置 | -|------|------|------|--------|------| -| Browser | 浏览器自动化 (23 Tauri命令) | WebDriver | 8 | `hands/browser.HAND.toml` | -| Collector | 数据收集聚合 | — | 8 | `hands/collector.HAND.toml` | -| Researcher | 深度研究 + 网络搜索 | 网络 | 22 | `hands/researcher.HAND.toml` | -| Clip | 视频处理 | FFmpeg | 30 | `hands/clip.HAND.toml` | -| Twitter | Twitter 自动化 (12 API v2) | OAuth 1.0a | 25 | `hands/twitter.HAND.toml` | -| Quiz | 测验生成 | — | — | `hands/quiz.HAND.toml` | -| _reminder | 定时提醒 (系统内部) | — | — | 无 TOML(代码注册) | - -Hands 测试分布(前 5): Clip(30), Twitter(25), Researcher(22), Browser(8), Collector(8) - -### Researcher 搜索能力(04-22 修复) - -Researcher 是 ZCLAW 的核心搜索 Hand,支持从对话中直接触发网络搜索和网页获取。 - -**搜索引擎**: Baidu + Bing CN 并行(国内用户可用),DuckDuckGo 作为 fallback -**网页获取**: Jina Reader API(优先,返回干净 Markdown)→ 直接 HTTP fetch(降级) -**LLM 兼容**: 扁平化 input_schema(action/query/url/urls/engine),兼容 glm-5.1 等国产模型 -**空参数回退**: 当 LLM 发送空 `{}` 时,loop_runner 自动注入用户消息作为 `_fallback_query` - -``` -用户消息 "搜索今天的新闻" - → LLM 生成 ToolUse{hand_researcher, {action:"search", query:"今日新闻"}} - → AgentLoop → ResearcherHand.execute() - → execute_search() → search_native() → Baidu + Bing CN 并行 - → 搜索结果 (10条) → ToolResult → LLM 基于结果生成回复 - → 前端 stripToolNarration 过滤内部叙述 + ReactMarkdown 渲染排版 -``` - -关键修复 (commit 5816f56 + 81005c3): -- **schema 简化**: `oneOf`+`const` → 扁平属性,解决 glm-5.1 不理解复杂 schema 导致空参数 -- **empty-input 回退**: loop_runner 检测 `{}` → 注入 `_fallback_query` → researcher 自动搜索 -- **排版修复**: stripToolNarration 从句子级拆分改为行级过滤,保留 markdown 结构 - -### 已删除 Hands (04-17 Phase 5 空壳清理) - -| Hand | 原状态 | 删除原因 | -|------|--------|----------| -| Whiteboard | 有 HAND.toml + Rust (422行) | 空壳实现,无真实功能,已删除 | -| Slideshow | 有 HAND.toml + Rust (797行) | 空壳实现,无真实功能,已删除 | -| Speech | 有 HAND.toml + Rust (442行) | 空壳实现,无真实功能,已删除 | - -净减 ~5400 行。 - -### 禁用 Hands - -| Hand | 状态 | 说明 | -|------|------|------| -| Predictor | 禁用 | 无 TOML/无 Rust 实现,仅概念定义 | -| Lead | 禁用 | 无 TOML/无 Rust 实现,仅概念定义 | - -### 触发流 - -``` -UI 触发 → handStore.trigger(handName, params) - → Tauri invoke('hand_execute', { handName, params }) - → Kernel → Hand 执行 - → needs_approval? → 等待 approvalStore 确认 - → 执行结果 → Tauri Event emit - → handStore 更新状态 + 记录日志 -``` - -### 定时提醒链路(NlScheduleParser → _reminder Hand) - -用户在聊天中输入包含定时意图的消息(如"每天早上9点提醒我查房"),系统自动拦截并创建定时触发器: - -``` -用户消息 "每天早上9点提醒我查房" - → agent_chat_stream (chat.rs) - → has_schedule_intent() 检测关键词(提醒我/定时/每天/每周等) - → parse_nl_schedule() 解析为 cron 表达式 - → ScheduleParseResult::Exact (confidence >= 0.8) - → TriggerConfig { hand_id: "_reminder", trigger_type: Schedule { cron: "0 9 * * *" } } - → kernel.create_trigger() → TriggerManager 存储 - → LoopEvent::Delta(确认消息) → 前端流式显示 - → 跳过 LLM 调用(省 token) - → SchedulerService 每60秒轮询 - → should_fire_cron() 匹配 → execute_hand_with_source("_reminder") - → ReminderHand.execute() → 记录日志 -``` - -关键组件: -- `crates/zclaw-runtime/src/nl_schedule.rs` — 中文时间→cron 转换(支持6种模式) -- `crates/zclaw-hands/src/hands/reminder.rs` — 系统内部 Hand(id=`_reminder`) -- `crates/zclaw-kernel/src/trigger_manager.rs` — 触发器 CRUD(`_` 前缀 hand_id 免验证) -- `crates/zclaw-kernel/src/scheduler.rs` — 60秒轮询 + cron 匹配 -- `desktop/src-tauri/src/kernel_commands/chat.rs` — 定时意图拦截入口 - -Hand 相关 Tauri 命令 (8 个): -`hand_list, hand_execute, hand_approve, hand_cancel, hand_get, hand_run_status, hand_run_list, hand_run_cancel` - -### Skills (75 个目录) - -``` -skills/ -├── accessibility-auditor/ api-tester/ -├── agentic-identity-trust/ app-store-optimizer/ -├── agents-orchestrator/ backend-architect/ -├── ai-engineer/ brand-guardian/ -├── analytics-reporter/ chart-visualization/ -├── chinese-writing/ classroom-generator/ -├── code-review/ consulting-analysis/ -├── content-creator/ data-analysis/ -├── data-consolidation-agent/ deep-research/ -├── devops-automator/ evidence-collector/ -├── ... (75 个目录,每个含 SKILL.md) -``` - -每个 SKILL.md 定义: -- 技能名称和描述 -- 触发条件 -- 执行步骤 -- 输入/输出格式 - -### 语义路由 - -`crates/zclaw-skills/src/semantic_router.rs` - -``` -用户消息 → SemanticSkillRouter - → TF-IDF 计算消息与 75 个技能的相似度 - → 返回 { skill_id, confidence } - → ButlerRouter 使用 RoutingHint 增强 system prompt -``` - -在 kernel 中通过 `SemanticRouterAdapter` 桥接到 `ButlerRouterBackend` trait。 - -### Skill 调用链路(LLM Tool Calling) - -``` -Skills 目录 → SkillRegistry 加载 → SkillIndexMiddleware(P200) 注入系统提示 - → LLM 看到 skill_load + execute_skill 工具定义 - → LLM 生成 ToolUse{skill_load} → AgentLoop 执行 → 返回技能详情 - → LLM 生成 ToolUse{execute_skill} → AgentLoop 执行 → KernelSkillExecutor - → Skill 执行结果 → ToolResult → LLM 继续对话 -``` - -关键路径: -- `kernel/mod.rs:create_tool_registry()` 注册 7 个内置工具(含 skill_load, execute_skill) -- `runtime/loop_runner.rs` 检测 `ContentBlock::ToolUse` → 调用 `Tool::execute()` -- `runtime/tool/builtin/execute_skill.rs` → `KernelSkillExecutor::execute_skill()` -- Anthropic Driver: ToolResult 必须用 `ContentBlock::ToolResult{tool_use_id, content}` 格式 - -## MCP (Model Context Protocol) - -### 概述 - -MCP 允许 ZCLAW 在运行时连接外部工具服务器(如 filesystem、database、custom tools),让 LLM 在对话中直接调用这些工具。 - -### 架构 - -``` -前端 UI (MCPServices.tsx) - → mcp-client.ts → Tauri invoke('mcp_start_service', {config}) - → McpManagerState → McpServiceManager → BasicMcpClient (stdio transport) - → MCP Server 进程 → list_tools → 注册 adapters - -LLM 对话调用: - Kernel.create_tool_registry() - → 遍历 mcp_adapters (Arc>>) - → McpToolWrapper 包装为 Tool trait - → 注册到 ToolRegistry → LLM API tool definitions - - LLM 生成 ToolUse{filesystem.read_file} - → AgentLoop → McpToolWrapper.execute() - → McpToolAdapter.execute() → MCP Server → 结果返回 -``` - -### 关键桥接机制 - -`McpManagerState` 和 `Kernel` 共享同一个 `Arc>>`: -- Kernel boot 时,`kernel_init` 将 `McpManagerState.kernel_adapters` Arc 注入到 Kernel -- MCP 服务启动/停止时,`sync_to_kernel()` 更新共享列表 -- `create_tool_registry()` 每次对话时读取最新 adapters - -### MCP Tauri 命令 (4 个) - -| 命令 | 功能 | +| 文件 | 职责 | |------|------| -| `mcp_start_service` | 启动 MCP 服务 + 发现工具 + 同步到 Kernel | -| `mcp_stop_service` | 停止服务 + 从 Kernel 移除工具 | -| `mcp_list_services` | 列出所有运行中的服务和工具 | -| `mcp_call_tool` | 手动调用 MCP 工具(支持 service_name 精确路由) | +| `crates/zclaw-hands/src/hands/` | 7 个 Hand 实现 (browser/collector/researcher/clip/twitter/quiz/reminder) | +| `crates/zclaw-runtime/src/tool/registry.rs` | ToolRegistry 工具注册表 | +| `crates/zclaw-runtime/src/tool/builtin/execute_skill.rs` | KernelSkillExecutor 技能执行 | +| `crates/zclaw-skills/src/semantic_router.rs` | TF-IDF 语义路由 (路由细节见 [[butler]]) | +| `crates/zclaw-skills/src/` | 技能解析、索引、WASM runner | +| `crates/zclaw-runtime/src/nl_schedule.rs` | 中文时间->cron 解析器 (6 种模式) | +| `crates/zclaw-protocols/src/mcp_tool_adapter.rs` | MCP 工具适配器 + 服务管理 | +| `crates/zclaw-protocols/src/mcp.rs` | MCP 协议类型 + BasicMcpClient (stdio transport) | +| `crates/zclaw-runtime/src/tool/builtin/mcp_tool.rs` | McpToolWrapper (Tool trait 桥接) | +| `desktop/src/store/handStore.ts` | 前端 Hand 状态 | +| `desktop/src/lib/mcp-client.ts` | 前端 MCP 客户端 | -### 限定名规则 +### Hand 触发流 -MCP 工具在 ToolRegistry 中使用限定名 `service_name.tool_name` 避免冲突。 -例如:`filesystem.read_file`, `database.query`。 +``` +LLM 生成 ToolUse{hand_name, params} + -> AgentLoop (loop_runner.rs) 检测 ContentBlock::ToolUse + -> ToolRegistry.get(hand_name) -> HandExecutor + -> needs_approval? -> 等待 approvalStore 确认 -> 用户批准 + -> Hand.execute(params) -> 结果 + -> ToolResult -> LLM 继续对话 + -> Tauri Event emit -> handStore 更新状态 +``` -## API 接口 +### 集成契约 -### Hand Tauri 命令 (`desktop/src-tauri/src/kernel_commands/hand.rs`) +| 方向 | 模块 | 接口 / 触发点 | +|------|------|---------------| +| Called by <- | loop_runner | Tool 执行 | Every tool call during chat | +| Calls -> | browser/Twitter/etc | External APIs | Hand-specific operations | +| Provides -> | middleware: SkillIndex@200 | `skill_index.rs` | 技能索引注入 system prompt | +| Provides -> | mcp: McpToolWrapper | `Tool` trait | 外部工具桥接到 ToolRegistry | + +### Hand Tauri 命令 (8 个) | 命令 | 状态 | 说明 | |------|------|------| @@ -229,52 +73,98 @@ MCP 工具在 ToolRegistry 中使用限定名 `service_name.tool_name` 避免冲 | `hand_run_list` | @connected | 运行列表 | | `hand_run_cancel` | @reserved | 取消运行 (无前端 UI) | -## 测试链路 +### MCP 命令 (4 个) -| 功能 | Crate | 测试数 | 覆盖状态 | -|------|-------|--------|---------| -| Browser | zclaw-hands | 11 | ✅ | -| Clip (视频) | zclaw-hands | 32 | ✅ | -| Collector | zclaw-hands | 9 | ✅ | -| DailyReport | zclaw-hands | 5 | ✅ | -| Quiz | zclaw-hands | 5 | ✅ | -| Researcher | zclaw-hands | 25 | ✅ | -| Twitter | zclaw-hands | 30 | ✅ | -| **Hands 小计** | | **117** | | -| 语义路由 | zclaw-skills | 7 | ✅ | -| WASM Runner | zclaw-skills | 2 | ✅ | -| 编排 (7 文件) | zclaw-skills | 17 | ✅ | -| **Skills 小计** | | **26** | | -| **合计** | | **143** | | +`mcp_start_service`, `mcp_stop_service`, `mcp_list_services`, `mcp_call_tool`。 +MCP 工具在 ToolRegistry 中使用限定名 `service_name.tool_name` (如 `filesystem.read_file`)。 +`McpManagerState` 和 `Kernel` 共享 `Arc>>`,通过 `sync_to_kernel()` 同步。 -## 关联模块 +## 3. 代码逻辑 -- [[chat]] — 消息流中可能触发 Hand/Skill -- [[butler]] — ButlerRouter 使用语义路由匹配技能 -- [[middleware]] — SkillIndex 中间件注入技能索引 +### 7 注册 Hands -## 关键文件 +| Hand | 功能 | 依赖 | 测试 | 配置 | +|------|------|------|------|------| +| Browser | 浏览器自动化 (23 Tauri 命令) | WebDriver | 11 | `hands/browser.HAND.toml` | +| Collector | 数据收集聚合 | -- | 9 | `hands/collector.HAND.toml` | +| Researcher | 深度研究 + 网络搜索 | 网络 | 25 | `hands/researcher.HAND.toml` | +| Clip | 视频处理 | FFmpeg | 32 | `hands/clip.HAND.toml` | +| Twitter | Twitter 自动化 (12 API v2) | OAuth 1.0a | 30 | `hands/twitter.HAND.toml` | +| Quiz | 测验生成 | -- | 5 | `hands/quiz.HAND.toml` | +| _reminder | 定时提醒 (系统内部) | -- | -- | 无 TOML (代码注册) | -| 文件 | 职责 | +### Researcher 搜索能力 (04-22 修复) + +- **搜索引擎**: Baidu + Bing CN 并行 (国内可用),DuckDuckGo fallback +- **网页获取**: Jina Reader API (优先,干净 Markdown) -> HTTP fetch (降级) +- **LLM 兼容**: 扁平化 input_schema (action/query/url/urls/engine),兼容 glm-5.1 等国产模型 +- **空参数回退**: LLM 发送空 `{}` 时,loop_runner 注入 `_fallback_query` 自动搜索 + +### 定时提醒链路 + +``` +用户消息 "每天早上9点提醒我查房" + -> agent_chat_stream (chat.rs) + -> has_schedule_intent() 关键词检测 (提醒我/定时/每天/每周等) + -> parse_nl_schedule() -> cron 表达式 + -> ScheduleParseResult::Exact (confidence >= 0.8) + -> TriggerConfig { hand_id: "_reminder", trigger_type: Schedule { cron } } + -> kernel.create_trigger() -> TriggerManager 存储 + -> 跳过 LLM 调用 (省 token) + -> SchedulerService 每60秒轮询 -> should_fire_cron() -> ReminderHand.execute() +``` + +### Skill 调用链路 (LLM Tool Calling) + +``` +skills/ -> SkillRegistry 加载 -> SkillIndexMiddleware@200 注入系统提示 + -> LLM 看到 skill_load + execute_skill 工具定义 + -> LLM 生成 ToolUse{skill_load} -> AgentLoop -> 返回技能详情 + -> LLM 生成 ToolUse{execute_skill} -> KernelSkillExecutor -> Skill 执行 + -> ToolResult -> LLM 继续对话 +``` + +关键: Anthropic Driver 要求 ToolResult 必须用 `ContentBlock::ToolResult{tool_use_id, content}` 格式。 + +### 不变量 + +- Hand 配置中 `enabled=false` 的 Hand 不会注册到 ToolRegistry +- Skill 调用通过 LLM Tool Calling,不是直接函数调用 +- MCP 限定名 `service_name.tool_name` 避免与内置工具冲突 +- 已删除空壳 Hands (04-17): Whiteboard/Slideshow/Speech,净减 ~5400 行 + +## 4. 活跃问题 + 陷阱 + +### 活跃 + +| 问题 | 状态 | 说明 | +|------|------|------| +| Clip 依赖 FFmpeg | P3 | 用户需本地安装 FFmpeg,否则视频处理 Hand 不可用 | +| Hands E2E 通过率 ~70% | P2 | 10 Hand 全部启用,审批机制正常,但部分 Hand 边界场景未覆盖 | +| hand.rs TODO | P2 | tool_count/metric_count 待从实际 Hand 实例填充 | + +### 历史 (已修复) + +| 问题 | 修复 | |------|------| -| `crates/zclaw-hands/src/hands/` | 7 个 Hand 实现 (6 有 TOML + _reminder 系统内部) | -| `crates/zclaw-runtime/src/nl_schedule.rs` | 中文时间→cron 解析器 | -| `crates/zclaw-skills/src/semantic_router.rs` | TF-IDF 语义路由 | -| `crates/zclaw-skills/src/` | 技能解析和索引 | -| `skills/*/SKILL.md` | 75 个技能定义 | -| `hands/*.HAND.toml` | 6 个 Hand 配置 | -| `crates/zclaw-protocols/src/mcp_tool_adapter.rs` | MCP 工具适配器 + 服务管理 | -| `crates/zclaw-protocols/src/mcp.rs` | MCP 协议类型 + BasicMcpClient | -| `crates/zclaw-runtime/src/tool/builtin/mcp_tool.rs` | McpToolWrapper (Tool trait 桥接) | -| `crates/zclaw-runtime/src/driver/anthropic.rs` | Anthropic Driver (含 ToolResult 格式) | -| `desktop/src/store/handStore.ts` | 前端 Hand 状态 | -| `desktop/src/store/browserHandStore.ts` | Browser Hand 专用 | -| `desktop/src/lib/mcp-client.ts` | 前端 MCP 客户端 | -| `desktop/src-tauri/src/kernel_commands/mcp.rs` | MCP Tauri 命令 (4) + Kernel 桥接 | +| skill_execute 反序列化崩溃 | SEC2-P0-01 04-02 已修复 | +| Researcher 空参数 (glm-5.1 不理解 oneOf+const schema) | 04-22 schema 扁平化 + empty-input fallback | +| 排版乱码 (stripToolNarration 句子级拆分破坏 markdown) | 04-22 行级过滤 | -## 已知问题 +## 5. 变更日志 -- ✅ **skill_execute 反序列化崩溃** — SEC2-P0-01 已于 04-02 修复 -- ✅ **Hands E2E 通过率 70%** — 10 Hand 全部启用,审批机制正常 -- ⚠️ **hand.rs TODO** — P2-03: tool_count/metric_count 待从实际 Hand 实例填充 -| `desktop/src-tauri/src/kernel_commands/hand.rs` | Hand Tauri 命令 (8) | +| 日期 | 变更 | 关联 | +|------|------|------| +| 2026-04-22 | Wiki 5-section 重构: 281->~195 行,语义路由细节引用 [[butler]] | wiki/ | +| 2026-04-22 | Researcher 搜索修复: schema 扁平化 + 空参数回退 + 排版修复 | commit 5816f56+81005c3 | +| 2026-04-17 | 空壳 Hand 清理: Whiteboard/Slideshow/Speech 删除,净减 ~5400 行 | Phase 5 清理 | +| 2026-04-16 | 3 项 P0 修复 + 5 项 E2E Bug 修复 | 三端联调测试 | +| 2026-04-09 | 管家模式交付: 语义路由 TF-IDF 接入 ButlerRouter | 6 交付物完成 | + +### 测试概览 + +| 功能 | Crate | 测试数 | +|------|-------|--------| +| Hands (7 实现) | zclaw-hands | 117 | +| 语义路由 + WASM + 编排 | zclaw-skills | 26 | +| **合计** | | **143** | diff --git a/wiki/pipeline.md b/wiki/pipeline.md index 974eefb..0d63821 100644 --- a/wiki/pipeline.md +++ b/wiki/pipeline.md @@ -1,86 +1,86 @@ --- title: Pipeline DSL -updated: 2026-04-21 +updated: 2026-04-22 status: active tags: [module, pipeline, dsl] --- # Pipeline DSL -> 从 [[index]] 导航。关联模块: [[hands-skills]] +> 从 [[index]] 导航。关联模块: [[hands-skills]] [[chat]] -## 设计思想 +## 1. 设计决策 -**Pipeline = 可编排的工作流,按 DAG 依赖顺序执行步骤。** +**WHY DAG 执行器**: 工作流步骤之间存在数据依赖,DAG (有向无环图) 通过拓扑排序自动推导执行顺序,支持无依赖节点的并行执行,比线性管道更灵活、更高效。 -- YAML 定义 Pipeline 结构(步骤、依赖、输入/输出) -- DAG 执行器按依赖拓扑排序执行 -- 18 个 YAML 模板覆盖 8 大行业目录 -- 前端已接通 8 个 Tauri invoke 调用 +**WHY YAML 模板**: 声明式定义 + 可版本控制。非技术用户可直接编辑 YAML 文件调整步骤和参数,无需重新编译。模板可随项目仓库同步、diff、review。 -## 代码逻辑 +**WHY 18 模板覆盖 8 行业**: 管家模式面向行业垂直场景。每个行业目录包含该领域典型工作流(如汕头设计的供应链采集、医疗的政策合规),用户可直接使用或定制。 -### 架构 +**WHY v2 解析器**: v1 仅支持线性步骤序列,v2 引入 DAG 依赖声明 (`depends_on` 字段),支持复杂分支和并行。v1 解析器保留用于向后兼容。 + +**WHY ActionRegistry**: Pipeline 步骤与具体执行逻辑解耦。`action_type` 字符串映射到注册的处理函数,新增步骤类型只需注册新 action,不改动执行器核心。 + +## 2. 关键文件 + 数据流 + +### 核心文件 + +| 文件 | 职责 | +|------|------| +| `crates/zclaw-pipeline/src/executor.rs` | DAG 执行器 — 拓扑排序 + 并行执行 | +| `crates/zclaw-pipeline/src/parser_v2.rs` | YAML v2 解析器 (11 tests) | +| `crates/zclaw-pipeline/src/parser.rs` | YAML v1 解析器 (兼容) | +| `crates/zclaw-pipeline/src/state.rs` | 运行状态管理 | +| `crates/zclaw-pipeline/src/intent.rs` | Pipeline 意图匹配 | +| `crates/zclaw-pipeline/src/trigger.rs` | 定时/事件触发器 | +| `desktop/src/lib/pipeline-client.ts` | 前端 Pipeline 客户端 | +| `desktop/src/store/workflowBuilderStore.ts` | 工作流编辑器状态 | +| `desktop/src/components/pipeline/` | Pipeline UI 组件 | +| `desktop/src-tauri/src/pipeline_commands/` | 12 个 Tauri 命令 (4 文件) | +| `pipelines/` | 18 个 YAML 模板 (8 目录) | + +### 架构流程 ``` -YAML Pipeline 定义 - → PipelineExecutor (crates/zclaw-pipeline/src/executor.rs) - → 构建 DAG (按依赖排序) - → 逐步执行: - → ActionRegistry.resolve(action_type) - → 执行 action → PipelineRun.step_results - → 全部完成 → PipelineRun.status = Completed +用户选择模板 (Workflow 面板) + → pipeline-client.ts: invoke('pipeline_load_template') + → parser_v2: 解析 YAML → PipelineDefinition (steps + depends_on) + → executor: 构建 DAG → 拓扑排序 → 检测循环依赖 + → 并行执行无依赖节点: + → ActionRegistry.resolve(action_type) → 具体 Handler + → Handler 执行 → step_result (output + status) + → 有依赖节点等待前驱完成 → 执行 + → 全部完成 → PipelineRun.status = Completed + → 前端轮询 progress 或 Tauri Event 推送状态 ``` +### 集成契约 + +| 方向 | 接口 | 说明 | +|------|------|------| +| Called by ← UI | `pipelineStore.ts` / `workflowBuilderStore.ts` | 工作流面板交互: 列出模板、创建/运行/取消 Pipeline | +| Calls → runtime | Tauri invoke (discovery 12 命令) | pipeline_commands/ 转发到 DAG executor | +| Calls → skills/hands | `ActionRegistry.resolve(action_type)` | Pipeline 步骤可能调用 Skill 或 Hand 执行具体动作 | +| Called by ← chat | `intent_router.rs` | 聊天消息意图匹配到 Pipeline 模板 | +| Calls → memory | 记忆检索 (via runtime) | Pipeline 执行时可检索历史记忆增强步骤上下文 | + +## 3. 代码逻辑 + ### 运行状态 ```rust enum RunStatus { Pending, Running, Completed, Failed, Cancelled } ``` -### 模板分布 (18 个 YAML) +### DAG 执行流程详解 -``` -pipelines/ -├── _templates/ (2 模板) -│ ├── article-summary.yaml -│ └── competitor-analysis.yaml -├── design-shantou/ (4 模板) — 汕头玩具/服装行业 -│ ├── client-communication.yaml -│ ├── competitor-research.yaml -│ ├── supply-chain-collect.yaml -│ └── trend-to-design.yaml -├── education/ (4 模板) -│ ├── classroom.yaml -│ ├── lesson-plan.yaml -│ ├── research-to-quiz.yaml -│ └── student-analysis.yaml -├── healthcare/ (3 模板) -│ ├── data-report.yaml -│ ├── meeting-minutes.yaml -│ └── policy-compliance.yaml -├── legal/ (1 模板) -│ └── contract-review.yaml -├── marketing/ (1 模板) -│ └── campaign.yaml -├── productivity/ (1 模板) -│ └── meeting-summary.yaml -└── research/ (1 模板) - └── literature-review.yaml +1. **解析阶段**: `parser_v2.rs` 将 YAML 反序列化为 `PipelineDefinition`,包含 `steps: Vec` 和每个 step 的 `depends_on: Vec` +2. **构建阶段**: `executor.rs` 将 steps 映射为 DAG 节点,建立邻接表 (step_id → [依赖的 step_ids]) +3. **排序阶段**: Kahn 算法拓扑排序,检测循环依赖 — 若排序后节点数 < 总节点数,说明存在环,返回错误 +4. **执行阶段**: 按拓扑序逐层执行,同层无依赖节点并行。每步通过 `ActionRegistry` 解析 `action_type` 到具体 Handler +5. **完成阶段**: 全部步骤成功 → `Completed`;任一步骤失败 → 整体 `Failed`;用户可随时 `Cancel` -注: 共 18 个 YAML, 总数 = 2+4+4+3+1+1+1+1+1 = 18 -``` - -### 前端集成 - -| 组件 | 文件 | -|------|------| -| PipelineClient | `desktop/src/lib/pipeline-client.ts` | -| WorkflowBuilderStore | `desktop/src/store/workflowBuilderStore.ts` | -| Pipeline UI | `desktop/src/components/pipeline/` | -| Tauri 命令 | `desktop/src-tauri/src/pipeline_commands/` | - -Pipeline Tauri 命令 (12 个): +### Tauri 命令分布 | 文件 | 命令数 | 命令 | |------|--------|------| @@ -89,68 +89,66 @@ Pipeline Tauri 命令 (12 个): | intent_router.rs | 1 | route_intent | | presentation.rs | 2 | analyze_presentation/pipeline_templates | -前端 invoke 匹配: 8 个调用对应 8 个 discovery 命令,完整可用。 +前端 invoke 匹配: 8 个调用对应 8 个 discovery 命令。另有 2 个 @reserved (`orchestration_execute`/`orchestration_validate`,无前端 UI)。 -### 测试 +### 模板分布 (18 YAML, 8 目录) -`parser_v2.rs`: 11 tests — YAML 解析和 DAG 构建验证。 +``` +pipelines/ +├── _templates/ (2) — article-summary, competitor-analysis +├── design-shantou/ (4) — 汕头玩具/服装: 通信/竞品/供应链/趋势 +├── education/ (4) — 课堂/教案/研究→测验/学生分析 +├── healthcare/ (3) — 数据报告/会议纪要/政策合规 +├── legal/ (1) — 合同审查 +├── marketing/ (1) — 营销活动 +├── productivity/ (1) — 会议摘要 +└── research/ (1) — 文献综述 +``` -## 功能清单 +### 不变量 -| 功能 | 描述 | 入口文件 | 状态 | -|------|------|----------|------| -| YAML 解析 | v2 解析器,支持 DAG 依赖 | parser_v2.rs | ✅ | -| DAG 执行 | 拓扑排序 + 并行执行 | executor.rs | ✅ | -| 模板发现 | 18 模板 + 8 行业目录 | pipeline_commands/ | ✅ | -| 模型意图 | Pipeline 意图匹配 | intent.rs | ✅ | -| 状态管理 | Pipeline 运行状态 | state.rs | ✅ | -| 触发器 | 定时/事件触发 | trigger.rs | ✅ | -| 演示分析 | Pipeline 结果分析 | presentation/ | ✅ | +> **DAG 节点必须有明确的依赖关系,循环依赖会在 topological sort 阶段被检测并报错。** +> **模板 YAML 结构不重复 — 每个行业目录的模板聚焦该领域特有场景。** -## API 接口 +### 测试链路 -### Tauri 命令 +| 功能 | 测试文件 | 测试数 | +|------|---------|--------| +| YAML 解析 v2 | parser_v2.rs | 11 | +| DAG 执行 | executor.rs | 2 | +| 意图匹配 | intent.rs | 5 | +| 状态管理 | state.rs | 6 | +| 触发器 | trigger.rs | 5 | +| 类型 | types.rs + types_v2.rs | 4 | +| 解析 v1 | parser.rs | 5 | +| 引擎上下文/阶段 | engine/ | 8 | +| 演示 | presentation/ | 13 | +| **合计** | 13 文件 | **59** | -| 命令 | 状态 | 说明 | -|------|------|------| -| `orchestration_execute` | @reserved | 执行工作流 (无前端 UI) | -| `orchestration_validate` | @reserved | 验证工作流 (无前端 UI) | +## 4. 活跃问题 + 注意事项 -> 另有 12 个 pipeline discovery 命令在 `desktop/src-tauri/src/pipeline_commands/`,8 个已接通前端。 +| 优先级 | 问题 | 说明 | +|--------|------|------| +| P2 | Pipeline+Skill E2E 通过率 37.5% | Tauri IPC 可用但参数格式问题,非核心链路 | +| P3 | Deepseek 中继任务卡 processing | Provider Key 禁用后已有任务不自动清理 | +| — | pipeline_create 反序列化 | BUG-L2 已修复 (04-17 回归),需持续关注 | -## 测试链路 +**注意事项**: Pipeline 步骤中的 `action_type` 必须在 `ActionRegistry` 中注册,未注册的 action 会导致步骤 Failed。模板 YAML 的 `depends_on` 字段引用的 step_id 必须存在,否则解析阶段报错。前端 `workflowBuilderStore.ts` 负责编辑器状态,但 CRUD 操作通过 `pipeline-client.ts` 调用 Tauri 命令,不直接操作文件系统。 -| 功能 | 测试文件 | 测试数 | 覆盖状态 | -|------|---------|--------|---------| -| YAML 解析 v2 | parser_v2.rs | 11 | ✅ | -| DAG 执行 | executor.rs | 2 | ✅ | -| 意图匹配 | intent.rs | 5 | ✅ | -| 状态管理 | state.rs | 6 | ✅ | -| 触发器 | trigger.rs | 5 | ✅ | -| 类型 | types.rs + types_v2.rs | 4 | ✅ | -| 解析 v1 | parser.rs | 5 | ✅ | -| 引擎上下文 | engine/context.rs | 7 | ✅ | -| 引擎阶段 | engine/stage.rs | 1 | ✅ | -| 演示 | presentation/ (3文件) | 13 | ✅ | -| **合计** | 13 文件 | **59** | | +## 5. 变更日志 + +> 最近 5 条与 Pipeline 相关的变更。完整日志见 [[log]]。 + +| 日期 | 变更 | +|------|------| +| 2026-04-21 | Phase 0+1 修复: Skill 工具调用桥接 complete_with_tools() + Hand 字段映射 runId | +| 2026-04-17 | E2E 回归: pipeline_create 反序列化 BUG-L2 修复 | +| 2026-04-16 | 3 项 P0 修复 + 5 项 E2E Bug 修复,Pipeline Tauri 命令数校正 | +| 2026-04-09 | Pipeline+Hands 双交付,DAG 执行器稳定化 | +| 2026-04-01 | 17 YAML 模板 + DAG 执行器初始版本 | ## 关联模块 -- [[hands-skills]] — Pipeline 步骤可能调用 Hand/Skill -- [[chat]] — Pipeline 可通过聊天触发 - -## 关键文件 - -| 文件 | 职责 | -|------|------| -| `crates/zclaw-pipeline/src/executor.rs` | DAG 执行器 | -| `crates/zclaw-pipeline/src/parser_v2.rs` | YAML 解析 (11 tests) | -| `pipelines/` | 18 个 YAML 模板 (8 目录) | -| `desktop/src/lib/pipeline-client.ts` | 前端 Pipeline 客户端 | -| `desktop/src-tauri/src/pipeline_commands/` | 12 个 Tauri 命令 (4 文件) | - -## 已知问题 - -- ✅ **pipeline_create 反序列化失败** — BUG-L2 已修复 (04-17 回归) -- ⚠️ **Pipeline+Skill E2E 通过率 37.5%** — Tauri IPC 可用但参数格式问题 -- ⚠️ **Deepseek 中继任务卡 processing** — P3,Provider Key 禁用后已有任务不自动清理 +- [[hands-skills]] — Pipeline 步骤可能调用 Hand/Skill 执行具体动作 +- [[chat]] — Pipeline 可通过聊天意图匹配触发 (`intent_router.rs`) +- [[memory]] — Pipeline 执行时可检索记忆增强上下文 diff --git a/wiki/routing.md b/wiki/routing.md index b296fb0..db23aaa 100644 --- a/wiki/routing.md +++ b/wiki/routing.md @@ -1,6 +1,6 @@ --- title: 客户端路由 -updated: 2026-04-21 +updated: 2026-04-22 status: active tags: [module, routing, connection] --- @@ -9,318 +9,123 @@ tags: [module, routing, connection] > 从 [[index]] 导航。关联模块: [[chat]] [[saas]] -## 设计思想 +## 1. 设计决策 -**核心决策: Tauri 桌面端通过 SaaS Token Pool 中转访问 LLM,不直连。** +**核心: Tauri 桌面端通过 SaaS Token Pool 中转访问 LLM,不直连。** -为什么? -1. **集中密钥管理** — 用户不需要自己的 API Key,SaaS 维护共享 Key 池 -2. **用量追踪 + 计费** — 每次调用经过 SaaS,`record_usage` worker 记录 token 消耗 -3. **模型白名单** — Admin 配置哪些模型可用,`listModels()` 返回白名单 -4. **降级保障** — SaaS 挂了自动切本地 Kernel,桌面端不变砖 +| 决策 | 原因 | +|------|------| +| 5 分支路由 | 覆盖全部部署形态: Admin本地 / Tauri+SaaS / Browser+SaaS / Tauri本地 / 外部Gateway | +| SaaS Relay 中转 | 集中密钥管理 — 用户无需自备 API Key;用量追踪计费 — 每次调用经 SaaS;模型白名单 — Admin 控制可用模型 | +| 自动降级到本地 Kernel | SaaS 不可达时桌面端不变砖,无感切换,不需要用户干预 | +| Kernel 不直连 LLM | 直连是降级后备。主路径经 SaaS Token Pool 做 RPM/TPM 轮换 + 故障转移 | +| getClient() 全局单例 | 所有 Store 通过 `initializeStores()` 获取共享 client,避免重复连接 | -## 功能清单 +## 2. 关键文件 + 数据流 -| 功能 | 描述 | 入口文件 | 状态 | -|------|------|----------|------| -| 连接管理 | 5 分支路由决策 + 自动降级 | connectionStore.ts | ✅ | -| SaaS Relay 中转 | Tauri 通过 SaaS Token Pool 中转 LLM | connectionStore.ts | ✅ | -| 浏览器模式 | SSE 连接 SaaS relay | saas-relay-client.ts | ✅ | -| 本地 Kernel | Tauri 内置 Kernel 直连 LLM | kernel-client.ts | ✅ | -| 外部 Gateway | WebSocket 独立进程 | gateway-client.ts | ✅ | -| Gateway 进程管理 | 启动/停止/重启/状态/诊断 | gateway/commands.rs | ✅ | -| 健康检查 | 端口检测 + 完整诊断 | health_check.rs | ✅ | -| 设备配对 | 设备审批 + 公钥交换 | gateway/commands.rs | ✅ | -| 模型路由 | 白名单验证 + fallback + 别名解析 | connectionStore.ts | ✅ | +### 核心文件 -## 代码逻辑 +| 文件 | 职责 | +|------|------| +| `desktop/src/store/connectionStore.ts` | 路由决策核心: 5 分支 + 降级 + 模型路由 | +| `desktop/src/lib/kernel-chat.ts` | KernelClient ChatStream (Tauri Event) | +| `desktop/src/lib/kernel-client.ts` | Kernel 客户端配置 (setConfig/boot) | +| `desktop/src/lib/saas-relay-client.ts` | SaaS Relay ChatStream (SSE) | +| `desktop/src/lib/gateway-client.ts` | External Gateway ChatStream (WebSocket) | +| `desktop/src/store/index.ts` | Store 协调器 + client 注入 | -### 5 分支 + 降级决策树 - -入口: `connectionStore.ts` → `connect(url?, token?)` +### 5 分支决策树 ``` connect() - │ - ├── [1] Admin 强制本地: adminRouting === 'local' && isTauri() - │ → 直接走 Kernel 模式,跳过 SaaS - │ - ├── [2] SaaS Relay (Tauri 路径): savedMode === 'saas' && isTauri() - │ → KernelClient + baseUrl = saasUrl/api/v1/relay - │ → apiKey = SaaS JWT (不是 LLM Key!) - │ → SaaS 不可达 → 降级到本地 Kernel - │ - ├── [3] SaaS Relay (Browser 路径): savedMode === 'saas' && !isTauri() - │ → SaaSRelayGatewayClient (SSE) - │ → SaaS 不可达 → 降级到本地 Kernel - │ - ├── [4] 本地 Kernel: isTauriRuntime() && 非 SaaS 模式 - │ → KernelClient + 用户自定义模型配置 - │ → 用户需要自己的 API Key - │ - └── [5] External Gateway (fallback): !isTauri() - → GatewayClient via WebSocket/REST + ├─ [1] Admin强制本地: adminRouting=local && isTauri → Kernel 直连 + ├─ [2] SaaS+Tauri: savedMode=saas && isTauri → KernelClient + baseUrl=SaaS relay + │ └─ SaaS不可达 → 降级 [4] + ├─ [3] SaaS+Browser: savedMode=saas && !isTauri → SaaSRelayClient (SSE) + │ └─ SaaS不可达 → 降级 [4] + ├─ [4] 本地Kernel: isTauriRuntime && 非SaaS → KernelClient + 用户自配 Key + └─ [5] 外部Gateway: !isTauri → GatewayClient (WebSocket) ``` -### SaaS Relay 主路径 (Tauri 桌面端) +### 集成契约 -关键代码: `connectionStore.ts:482-535` +| 方向 | 模块 | 接口 | 说明 | +|------|------|------|------| +| Calls -> | saas | relay URL + JWT | Chat relay, model list, 用量上报 | +| Calls -> | kernel | Tauri invoke | Kernel boot, chat, config | +| Called by <- | all stores | `getClient()` | 每个 API 调用都经过路由决策 | +| Provides -> | UI | Connection status, model list | 所有聊天依赖组件消费 | + +## 3. 代码逻辑 + +### 模型路由链 (SaaS Relay 主路径) -```ts -kernelClient.setConfig({ - provider: 'custom', - model: modelToUse, // 从 SaaS listModels() 获取 - apiKey: session.token, // SaaS JWT,不是 LLM Key - baseUrl: `${session.saasUrl}/api/v1/relay`, // 指向 SaaS relay - apiProtocol: 'openai', -}); ``` - -**注意**: Kernel 仍然执行 LLM 调用逻辑,但请求发往 SaaS relay 而非直连 LLM。 -SaaS relay 接到请求后,从 Token Pool 中取一个可用 Key,转发给真实 LLM。 +前端选择模型 → preferredModel || fallbackId + → kernelClient.setConfig({ model, apiKey: JWT, baseUrl: saasUrl/api/v1/relay }) + → Tauri invoke kernel_init → Kernel::boot(config) + → loop_runner → POST {base_url}/chat/completions + → SaaS Relay → cache 精确匹配 model_id → Key Pool 轮换 → 真实 LLM + → SSE 流式返回 +``` ### SaaS 降级流程 -关键代码: `connectionStore.ts:446-468` - ``` listModels() 失败 - → 401 → session 过期 → logout → 要求重新登录 - → 其他错误 → saasDegraded = true - → saasStore.saasReachable = false - → 降级到本地 Kernel 模式 + → 401 → session 过期 → logout + → 其他 → saasDegraded=true → 降级本地 Kernel ``` ### 客户端类型 -| 客户端 | 传输 | 文件 | 用途 | -|--------|------|------|------| -| GatewayClient | WebSocket + REST | `lib/gateway-client.ts` | 外部 Gateway 进程 | -| KernelClient | Tauri invoke() | `lib/kernel-chat.ts` | 内置 Kernel (桌面端) | -| SaaSRelayGatewayClient | HTTP SSE | `lib/saas-relay-client.ts` | 浏览器端 SaaS 中继 | +| 客户端 | 传输 | 用途 | +|--------|------|------| +| GatewayClient | WebSocket + REST | 外部 Gateway 进程 | +| KernelClient | Tauri invoke() | 内置 Kernel (桌面端) | +| SaaSRelayGatewayClient | HTTP SSE | 浏览器端 SaaS 中继 | -`getClient()` 定义: `connectionStore.ts:844` -所有 Store 通过 `initializeStores()` (store/index.ts:94) 获取共享 client。 +### 不变量 -### Store 层 (16 根文件 + chat/4 + saas/5 = 25) - -``` -desktop/src/store/ -├── index.ts Store 协调器 + client 注入 -├── agentStore.ts Agent 分身管理 -├── browserHandStore.ts 浏览器 Hand 状态 -├── chatStore.ts 聊天通用状态 -├── classroomStore.ts 课堂模式 -├── configStore.ts 配置读写 -├── connectionStore.ts 路由决策核心 -├── handStore.ts Hand 状态管理 -├── industryStore.ts 行业配置 (已接通 ButlerPanel) -├── memoryGraphStore.ts 记忆图谱 -├── offlineStore.ts 离线队列 -├── saasStore.ts SaaS 认证 (re-export barrel) -├── securityStore.ts 安全状态 -├── sessionStore.ts 会话管理 -├── uiModeStore.ts 双模式 UI -├── workflowStore.ts 工作流状态 -├── chat/ -│ ├── artifactStore.ts 聊天产物 -│ ├── conversationStore.ts 会话管理 -│ ├── messageStore.ts 消息持久化 -│ └── streamStore.ts 流式编排 -└── saas/ (拆分子模块, 04-17 refactor) - ├── index.ts 子模块入口 - ├── auth.ts 认证逻辑 - ├── billing.ts 计费逻辑 - ├── shared.ts 共享状态/工具 - └── types.ts 类型定义 -``` - -### lib/ 工具层 (75 个 .ts 文件) - -关键分类: - -| 类别 | 文件 | 数量 | -|------|------|------| -| Kernel 通信 | kernel-client/kernel-chat/kernel-agent/kernel-skills/kernel-triggers/kernel-hands/... | 8 | -| SaaS 通信 | saas-client/saas-auth/saas-billing/saas-relay/saas-industry/saas-knowledge/... | 12 | -| Gateway | gateway-client/gateway-api/gateway-auth/gateway-config/... | 9 | -| Intelligence | intelligence-backend/intelligence-client/embedding-client/memory-extractor | 4 | -| Viking | viking-client | 1 | -| Pipeline | pipeline-client/pipeline-recommender | 2 | -| Security | crypto-utils/secure-storage/security-audit/security-index/api-key-storage | 5 | -| 工具 | config-parser/logger/utils/error-types/error-utils/json-utils/... | 10+ | -| Tauri 集成 | safe-tauri/tauri-gateway | 2 | -| 工作流 | workflow-builder/ (index + types + yaml-converter) | 3 | - -## 模型路由 - -### 完整链路 (Tauri SaaS Relay 主路径) - -``` -前端模型选择 - │ - ├─ conversationStore.currentModel (用户上次选择的模型) - │ 持久化到 IndexedDB,跨会话保留 - │ - ├─ connectionStore 连接时获取 SaaS 可用模型 - │ saasClient.listModels() → [{id: "deepseek-chat"}, {id: "GLM-4.7"}, ...] - │ relayModels[0]?.id 作为 fallback - │ - └─ 最终: preferredModel || fallbackId - │ - ▼ -kernelClient.setConfig({ model: modelToUse }) - │ - ▼ -kernel_init (Tauri Command) - │ KernelConfigRequest { model, api_key, base_url } - │ base_url = "https://saas-host/api/v1/relay" - │ api_key = SaaS JWT (不是 LLM Key!) - │ - ▼ -Kernel::boot(config) - │ config.llm.model = modelToUse - │ config.llm.base_url = SaaS relay URL - │ - ▼ -loop_runner → LLM Driver (OpenAI compatible) - │ POST {base_url}/chat/completions - │ body: { model: modelToUse, messages: [...] } - │ header: Authorization: Bearer {SaaS JWT} - │ - ▼ -SaaS Relay Handler (handlers.rs) - │ cache.get_model(model_name) → 精确匹配 model_id - │ ⚠️ 无别名解析! "glm-4-flash" ≠ "deepseek-chat" - │ 找不到 → 400 "模型 xxx 不存在或未启用" - │ - ▼ -Key Pool 轮换 - │ priority ASC → last_used_at ASC → cooldown 检查 → RPM/TPM 滑动窗口 - │ - ▼ -真实 LLM API - │ 429 → mark cooldown → 切换 key - │ 5xx → exponential backoff - │ model_group → 跨 Provider 故障转移 - │ - ▼ -响应 → SSE 流式返回 → 前端 -``` - -### 辅助 LLM 调用 (非聊天主路径) - -这些 Rust 端组件也通过同一个 relay 发起 LLM 请求: - -| 组件 | 文件 | 模型来源 | 触发时机 | -|------|------|----------|----------| -| 记忆摘要 | `summarizer_adapter.rs` | kernel_init 传入的 model | 定期 L0/L1 摘要生成 | -| 记忆提取 | `extraction_adapter.rs` | kernel_init 传入的 model | 中间件触发提取 | -| 管家路由 | ButlerRouter via loop_runner | 同聊天模型 | 聊天中间件链 | - -**关键**: `summarizer_adapter.rs` 和 `extraction_adapter.rs` 在 `kernel_init` 时配置, -使用与聊天相同的 `model` 和 `base_url`。未配置时会明确报错,不会静默 fallback 到错误模型。 - -### SaaS Relay 模型匹配规则 - -``` -前端发送 model: "deepseek-chat" - → SaaS cache 按 model_id 精确匹配 - → 匹配: cache.models["deepseek-chat"] → 命中 - → 不匹配: cache.models["glm-4-flash"] → null → 400 错误 - -⚠️ config.toml 中的 [llm.aliases] 仅用于本地 Kernel,SaaS relay 不解析别名! -``` - -### Browser 模式模型路由 - -``` -createSaaSRelayGatewayClient(saasUrl, getModel) - │ getModel() 回调 → conversationStore.currentModel || relayModels[0]?.id - │ - ▼ -chatStream() → saasClient.chatCompletion({ model: getModel() }) - │ 未获取到模型时 → onError 报错,不发请求 - │ - ▼ -POST /api/v1/relay/chat/completions → SSE 流 -``` - -## API 接口 +- `getClient()` 是全局单例,所有 Store 通过 `initializeStores()` 获取共享 client +- SaaS 不可达时自动降级到本地 Kernel,不需要用户干预 +- SaaS Relay 按 `model_id` 精确匹配,不解析别名 (`config.toml [llm.aliases]` 仅本地 Kernel) +- Provider Key 解密失败时 warn+skip,不 500 (`key_pool.rs`) ### Tauri 命令 -**Gateway 管理** (`desktop/src-tauri/src/gateway/commands.rs`): - -| 命令 | 参数 | 返回值 | 说明 | -|------|------|--------|------| -| `zclaw_status` | — | `LocalGatewayStatus` | Kernel 运行状态 | -| `zclaw_start` | — | `LocalGatewayStatus` | 启动 Kernel | -| `zclaw_stop` | — | `LocalGatewayStatus` | 停止 Kernel | -| `zclaw_restart` | — | `LocalGatewayStatus` | 重启 Kernel | -| `zclaw_local_auth` | — | `LocalGatewayAuth` | 获取本地认证 token | -| `zclaw_prepare_for_tauri` | — | `LocalGatewayPrepareResult` | 更新 Tauri allowed origins | -| `zclaw_approve_device_pairing` | device_id, public_key_base64, url? | `PairingApprovalResult` | 设备配对审批 | -| `zclaw_doctor` | — | `String` | 诊断报告 | -| `zclaw_process_list` | — | `ProcessListResponse` | 进程列表 | -| `zclaw_process_logs` | pid?, lines? | `ProcessLogsResponse` | 进程日志 | -| `zclaw_version` | — | `VersionResponse` | 版本信息 | - -**健康检查** (`desktop/src-tauri/src/health_check.rs`): - -| 命令 | 参数 | 返回值 | 说明 | -|------|------|--------|------| -| `zclaw_health_check` | port?, timeout_ms? | `HealthCheckResponse` | 完整健康检查 | -| `zclaw_ping` | — | `bool` | 快速端口检测 | - -### SaaS Relay 路由 (`crates/zclaw-saas/src/relay/`) - -| 方法 | 路径 | 权限 | 说明 | -|------|------|------|------| -| POST | `/api/v1/relay/chat/completions` | 认证+配额 | 主聊天中转 | -| GET | `/api/v1/relay/models` | 认证 | 可用模型列表 | -| GET | `/api/v1/relay/tasks` | 认证 | 任务列表 | -| GET | `/api/v1/relay/tasks/:id` | 认证 | 任务详情 | -| POST | `/api/v1/relay/tasks/:id/retry` | admin | 重试失败任务 | - -### Provider Key 管理 (`crates/zclaw-saas/src/relay/handlers.rs`) - -| 方法 | 路径 | 权限 | 说明 | -|------|------|------|------| -| GET | `/api/v1/providers/:id/keys` | admin | 列出 Provider Key | -| POST | `/api/v1/providers/:id/keys` | admin | 添加加密 Key | -| PUT | `/api/v1/providers/:id/keys/:kid/toggle` | admin | 启停 Key | -| DELETE | `/api/v1/providers/:id/keys/:kid` | admin | 删除 Key | - -## 测试链路 - -| 功能 | 测试文件 | 测试数 | 覆盖状态 | -|------|---------|--------|---------| -| Admin 路由解析 | `tests/desktop/connectionStore.adminRouting.test.ts` | — | ✅ parseAdminRouting() 纯函数 | -| 连接流程 | `tests/desktop/gatewayStore.test.ts` | — | ✅ connect() + 数据加载 | -| GatewayClient | `tests/gateway/ws-client.test.ts` | — | ✅ WS 连接/事件/断连 | -| Mock Server | `tests/fixtures/zclaw-mock-server.ts` | — | ✅ HTTP+WS 模拟 | - -## 关联模块 - -- [[chat]] — 路由决定使用哪种 ChatStream -- [[saas]] — Token Pool、认证、模型管理 -- [[middleware]] — 请求经过中间件链处理 -- [[butler]] — 管家模式通过 ButlerRouter 中间件介入 - -## 关键文件 - -| 文件 | 职责 | +| 命令 | 说明 | |------|------| -| `desktop/src/store/connectionStore.ts` | 路由决策核心 | -| `desktop/src/lib/gateway-client.ts` | WebSocket 客户端 | -| `desktop/src/lib/kernel-chat.ts` | Tauri 内核聊天 | -| `desktop/src/lib/kernel-client.ts` | Kernel 客户端配置 | -| `desktop/src/lib/saas-relay-client.ts` | SaaS SSE 中继 | -| `desktop/src/lib/saas-client.ts` | SaaS API 客户端 | -| `desktop/src/store/index.ts` | Store 协调器 + client 注入 | +| `kernel_init` | 初始化 Kernel 配置 (model, apiKey, baseUrl) | +| `zclaw_start` / `zclaw_stop` / `zclaw_restart` | Kernel 生命周期管理 | +| `zclaw_health_check` / `zclaw_ping` | 健康检查 + 端口检测 | +| `zclaw_doctor` | 完整诊断报告 | -## 已知问题 +### SaaS Relay 路由 -- ✅ **Tauri invoke 参数名 snake_case vs camelCase** — P1 已修复 (commit f6c5dd2)。Tauri 2.x 默认 `rename_all = "camelCase"`,所有 invoke 调用必须用 camelCase -- ✅ **Provider Key 解密失败导致 relay 500** — P1 已修复 (commit b69dc61)。`key_pool.rs` 现在 decrypt 失败时 warn+skip 到下一个 key,启动时 `heal_provider_keys()` 自动重新加密有效 key +| 路径 | 说明 | +|------|------| +| `POST /api/v1/relay/chat/completions` | 主聊天中转 (认证+配额) | +| `GET /api/v1/relay/models` | 可用模型列表 | + +## 4. 活跃问题 + 注意事项 + +| 问题 | 状态 | 说明 | +|------|------|------| +| Tauri invoke 参数名 snake_case | ✅ 已修复 (f6c5dd2) | Tauri 2.x 默认 `rename_all="camelCase"`,invoke 必须用 camelCase | +| Provider Key 解密致 relay 500 | ✅ 已修复 (b69dc61) | decrypt 失败 warn+skip,启动时 `heal_provider_keys()` 自动重新加密 | +| Tauri 命令孤儿 | ~0 (差异来自内部调用) | 190 定义 / 104 invoke / 97 @reserved | + +**注意事项:** +- `summarizer_adapter.rs` 和 `extraction_adapter.rs` 在 `kernel_init` 时配置,使用与聊天相同的 model+base_url。未配置时明确报错,不静默 fallback +- Browser 模式 `getModel()` 未获取到模型时 onError 报错,不发请求 + +## 5. 变更日志 + +| 日期 | 变更 | +|------|------| +| 04-22 | Wiki 重写: 5 节模板,移除 Store/lib 全量列表 | +| 04-21 | 上一轮更新 | +| 04-19 | TRUTH.md 数字校准: 190 命令 / 104 invoke / 97 @reserved | +| 04-16 | Provider Key 解密修复 (b69dc61) | +| 04-16 | Tauri invoke 参数名修复 (f6c5dd2) |