docs(wiki): Phase D完成 — 6模块页重构(routing/chat/butler/hands-skills/pipeline/data-model)
- routing.md: 移除Store/lib列表+5节模板 (330→131行) - chat.md: 添加集成契约+不变量 (180→134行) - butler.md: 移除重复→引用memory/hands-skills (215→150行) - hands-skills.md: 5节模板+契约+不变量 (281→170行) - pipeline.md: 添加契约+重组 (157→154行) - data-model.md: 添加契约+双库架构图 (181→153行) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
318
wiki/butler.md
318
wiki/butler.md
@@ -7,208 +7,144 @@ tags: [module, butler, interaction]
|
|||||||
|
|
||||||
# 管家模式 (Butler Mode)
|
# 管家模式 (Butler Mode)
|
||||||
|
|
||||||
> 从 [[index]] 导航。关联模块: [[chat]] [[middleware]] [[memory]]
|
> 从 [[index]] 导航。关联: [[chat]] [[middleware]] [[memory]] [[hands-skills]]
|
||||||
|
|
||||||
## 设计思想
|
## 1. 设计决策
|
||||||
|
|
||||||
**核心问题: 非技术用户(如医院行政)不会写 prompt,需要 AI 主动引导。**
|
**核心问题: 非技术用户(如医院行政)不会写 prompt,需要 AI 主动引导。**
|
||||||
|
|
||||||
设计决策:
|
| 决策 | WHY |
|
||||||
1. **默认激活** — 所有聊天都经过 ButlerRouter,不需要用户手动开启
|
|------|-----|
|
||||||
2. **语义路由** — SemanticSkillRouter 用 TF-IDF 匹配 75 个技能,替代简单关键词
|
| 默认激活 | 所有聊天都经过 ButlerRouter,无需用户手动开启。降低使用门槛到零 |
|
||||||
3. **痛点积累** — 从对话中提取用户痛点,积累后生成方案建议
|
| 语义路由 + 痛点积累 | SemanticSkillRouter 用 TF-IDF 匹配 75 个技能(详见 [[hands-skills]]),从对话中提取痛点并积累后生成方案建议 |
|
||||||
4. **双模式 UI** — simple(纯聊天,默认) / professional(完整功能),渐进式解锁
|
| 双模式 UI | simple(纯聊天) / professional(完整功能),渐进式解锁。简洁模式隐藏高级功能降低认知负担 |
|
||||||
|
| ButlerRouter@80 中间件 | 在 Evolution@78 之后、Memory@150 之前执行。先路由增强 prompt,再检索记忆注入,最后技能索引 |
|
||||||
|
| XML fencing `<butler-context>` | 结构化注入 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<SemanticSkillRouter> }
|
|
||||||
impl ButlerRouterBackend for SemanticRouterAdapter {
|
|
||||||
async fn classify(&self, query: &str) -> Option<RoutingHint> { ... }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
这是 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<RwLock<Vec<IndustryKeywordConfig>>>)
|
|
||||||
|
|
||||||
### 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-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 桥接 (kernel -> skills) |
|
||||||
| `crates/zclaw-kernel/src/kernel/mod.rs:196-231` | SemanticRouterAdapter 桥接 |
|
| `desktop/src-tauri/src/intelligence/pain_storage.rs` | 痛点双写 (内存 Vec + SQLite) |
|
||||||
| `crates/zclaw-kernel/src/intelligence/pain_storage.rs` | 痛点双写 (内存+SQLite) |
|
| `desktop/src-tauri/src/intelligence/solution_generator.rs` | 方案生成 |
|
||||||
| `crates/zclaw-kernel/src/intelligence/solution_generator.rs` | 方案生成 |
|
|
||||||
| `desktop/src/hooks/use-cold-start.ts` | 冷启动 4 阶段 |
|
| `desktop/src/hooks/use-cold-start.ts` | 冷启动 4 阶段 |
|
||||||
| `desktop/src/store/uiModeStore.ts` | 双模式切换 |
|
| `desktop/src/store/uiModeStore.ts` | 双模式切换 |
|
||||||
| `desktop/src/components/SimpleSidebar.tsx` | 简洁模式侧边栏 |
|
| `desktop/src/store/industryStore.ts` | 行业配置 (persist, 离线缓存) |
|
||||||
| `desktop/src/components/ButlerPanel/index.tsx` | 管家面板主组件 (洞察/方案/记忆/行业) |
|
| `desktop/src/components/ButlerPanel/index.tsx` | 管家面板 (洞察/方案/记忆/行业) |
|
||||||
| `desktop/src/components/ButlerPanel/MemorySection.tsx` | 记忆展示+用户画像卡片 (viking_read L1 + agent_get) |
|
| `desktop/src/components/ButlerPanel/MemorySection.tsx` | 记忆展示 + 用户画像卡片 |
|
||||||
| `desktop/src/components/ButlerPanel/InsightsSection.tsx` | 痛点洞察列表 |
|
|
||||||
| `desktop/src/components/ButlerPanel/ProposalsSection.tsx` | 方案建议列表 |
|
|
||||||
|
|
||||||
## 已知问题
|
### ButlerRouter 数据流
|
||||||
|
|
||||||
- ✅ **行业 API 字段名不一致** — BUG-L1 已修复。`pain_seeds` vs `pain_seed_categories`
|
```
|
||||||
- ✅ **industryStore 无组件导入** — V13-GAP-02 已修复 (已连接 ButlerPanel)
|
用户消息
|
||||||
- ✅ **行业选择 500** — 类型不匹配已修复
|
-> ButlerRouter@80 (middleware/butler_router.rs)
|
||||||
- ✅ **桌面端未接入 Knowledge Search** — V13-GAP-03 已修复 (saas-knowledge mixin)
|
-> SemanticRouterAdapter -> SemanticSkillRouter (TF-IDF)
|
||||||
- ⚠️ **SkillIndex 条件注册** — 无技能时中间件不注册,长期观察
|
-> 返回 RoutingHint { category, confidence, skill_id }
|
||||||
|
-> 增强 system prompt (匹配技能上下文 + <butler-context> 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<RwLock<Vec<IndustryKeywordConfig>>>`):
|
||||||
|
- 4 内置行业: 医疗/教育/制衣/电商,各有 keywords/prompt/pain_seed_categories
|
||||||
|
- 动态注入: SaaS `industry/fullConfig` 端点拉取自定义行业
|
||||||
|
- 匹配流程: message -> 关键词命中 -> 识别行业 -> 注入行业 prompt 增强
|
||||||
|
|
||||||
|
### XML fencing 注入格式
|
||||||
|
|
||||||
|
```
|
||||||
|
<butler-context>
|
||||||
|
<active-pain-points>...</active-pain-points>
|
||||||
|
<related-experience>...</related-experience>
|
||||||
|
<industry>医疗</industry>
|
||||||
|
<routing-hint confidence="0.85">data-analysis</routing-hint>
|
||||||
|
</butler-context>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 跨会话连续性 (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** |
|
||||||
|
|||||||
195
wiki/chat.md
195
wiki/chat.md
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 聊天系统
|
title: 聊天系统
|
||||||
updated: 2026-04-21
|
updated: 2026-04-22
|
||||||
status: active
|
status: active
|
||||||
tags: [module, chat, stream]
|
tags: [module, chat, stream]
|
||||||
---
|
---
|
||||||
@@ -9,149 +9,126 @@ tags: [module, chat, stream]
|
|||||||
|
|
||||||
> 从 [[index]] 导航。关联模块: [[routing]] [[saas]] [[butler]]
|
> 从 [[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 }` |
|
||||||
|
|
||||||
| 环境 | 实现 | 传输 | 为什么 |
|
### 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 种实现共享同一套回调:
|
| 环境 | 实现 | 传输 |
|
||||||
|
|------|------|------|
|
||||||
|
| 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
|
## 2. 关键文件 + 数据流
|
||||||
{ onDelta, onThinkingDelta, onTool, onHand, onComplete, onError }
|
|
||||||
```
|
|
||||||
|
|
||||||
## 功能清单
|
### 核心文件
|
||||||
|
|
||||||
| 功能 | 描述 | 入口文件 | 状态 |
|
| 文件 | 职责 |
|
||||||
|------|------|----------|------|
|
|------|------|
|
||||||
| 发送消息 | 流式/非流式,支持 thinking | streamStore.ts | ✅ |
|
| `desktop/src/store/chat/streamStore.ts` | 流式消息编排、发送、取消 |
|
||||||
| 流式响应 | SSE/Tauri Event 实时推送 | streamStore.ts | ✅ |
|
| `desktop/src/store/chat/conversationStore.ts` | 会话管理、当前模型、sessionKey |
|
||||||
| 模型切换 | 运行时切换 LLM 模型 | conversationStore.ts | ✅ |
|
| `desktop/src/store/chat/messageStore.ts` | 消息持久化 (IndexedDB) |
|
||||||
| 上下文管理 | 会话持久化 + 跨会话恢复 | conversationStore.ts | ✅ |
|
| `desktop/src/lib/kernel-chat.ts` | KernelClient ChatStream (Tauri) |
|
||||||
| 取消流式 | 原子标志位中断 | kernel-chat.ts | ✅ |
|
| `desktop/src/components/ChatArea.tsx` | 聊天区域 UI |
|
||||||
| Agent 聊天 | 指定 agent_id 独立对话 | streamStore.ts | ✅ |
|
| `crates/zclaw-runtime/src/loop_runner.rs` | Rust 主聊天循环 + 中间件链 |
|
||||||
| 课堂聊天 | 教育场景专用 | classroomStore.ts | ✅ |
|
|
||||||
| 消息持久化 | IndexedDB 存储 | messageStore.ts | ✅ |
|
|
||||||
| 聊天产物 | 附件/代码块管理 | artifactStore.ts | ✅ |
|
|
||||||
|
|
||||||
## 代码逻辑
|
|
||||||
|
|
||||||
### 发送消息流
|
### 发送消息流
|
||||||
|
|
||||||
入口: `streamStore.sendMessage(content)` → `store/chat/streamStore.ts`
|
|
||||||
|
|
||||||
```
|
```
|
||||||
sendMessage(content)
|
用户输入 → ChatPanel.tsx
|
||||||
|
→ streamStore.sendMessage(content)
|
||||||
→ effectiveSessionKey = conversationStore.sessionKey || uuid()
|
→ effectiveSessionKey = conversationStore.sessionKey || uuid()
|
||||||
→ effectiveAgentId = resolveGatewayAgentId(currentAgent)
|
→ effectiveAgentId = resolveGatewayAgentId(currentAgent)
|
||||||
→ client.chatStream(content, callbacks, { sessionKey, agentId, chatMode })
|
→ getClient().chatStream(content, callbacks, { sessionKey, agentId, chatMode })
|
||||||
→ KernelClient: Tauri invoke('kernel_chat', ...)
|
→ [KernelClient] Tauri invoke('agent_chat_stream')
|
||||||
→ Kernel → loop_runner → LLM Driver
|
→ Kernel → loop_runner → 14层中间件 → LLM Driver
|
||||||
→ 如果 baseUrl 指向 SaaS relay → 请求发往 Token Pool → LLM
|
→ Tauri Event emit('chat-response-delta')
|
||||||
→ 如果 baseUrl 指向 LLM 直连 → 请求直接发往 LLM
|
→ onDelta → streamStore 追加 delta → UI 渲染
|
||||||
→ Tauri Event emit('chat-response-delta', ...)
|
→ onComplete → conversationStore 持久化 → messageStore 写 IndexedDB
|
||||||
→ onDelta(text) → streamStore 追加 delta
|
→ [SaaSRelay] POST /api/v1/relay/chat/completions → SSE
|
||||||
→ onTool(tool) → toolStore 更新
|
→ [GatewayClient] WebSocket send → onmessage
|
||||||
→ onHand(hand) → handStore 更新
|
→ 5 分钟超时守护 (kernel-chat.ts:76)
|
||||||
→ 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)
|
### Store 拆分 (5 Store)
|
||||||
|
|
||||||
原来 908 行的 ChatStore 已拆分为:
|
|
||||||
|
|
||||||
| Store | 文件 | 职责 |
|
| Store | 文件 | 职责 |
|
||||||
|-------|------|------|
|
|-------|------|------|
|
||||||
| streamStore | `store/chat/streamStore.ts` | 流式消息编排、发送、取消 |
|
| streamStore | `store/chat/streamStore.ts` | 流式编排、发送、取消 |
|
||||||
| conversationStore | `store/chat/conversationStore.ts` | 会话管理、当前模型 |
|
| conversationStore | `store/chat/conversationStore.ts` | 会话管理、当前模型 |
|
||||||
| messageStore | `store/chat/messageStore.ts` | 消息持久化 |
|
| messageStore | `store/chat/messageStore.ts` | 消息持久化 (IndexedDB) |
|
||||||
| chatStore | `store/chat/chatStore.ts` | 聊天通用状态 |
|
| chatStore | `store/chat/chatStore.ts` | 聊天通用状态 |
|
||||||
| artifactStore | `store/chat/artifactStore.ts` | 聊天产物/附件 |
|
| artifactStore | `store/chat/artifactStore.ts` | 聊天产物/附件 |
|
||||||
|
|
||||||
### 前端 Tauri 命令映射
|
### 流式事件类型
|
||||||
|
|
||||||
```
|
`agent_chat_stream` Tauri Event emit 的 tagged union:
|
||||||
kernel_chat / agent_chat / agent_chat_stream → 发送消息
|
|
||||||
cancel_stream → 取消流式响应
|
`Delta` / `ThinkingDelta` / `ToolStart` / `ToolEnd` / `HandStart` / `HandEnd` / `SubtaskStatus` / `IterationStart` / `Complete` / `Error`
|
||||||
```
|
|
||||||
|
|
||||||
### 模型切换
|
### 模型切换
|
||||||
|
|
||||||
```
|
```
|
||||||
UI 选择模型 → conversationStore.currentModel = newModel
|
UI 选择模型 → conversationStore.currentModel = newModel
|
||||||
→ 下次 sendMessage 时,connectionStore 读取 currentModel
|
→ 下次 sendMessage → getClient() 读取 currentModel
|
||||||
→ SaaS 模式: relay 白名单验证 → 可用则切换
|
→ SaaS 模式: relay 白名单验证 → 可用则切换
|
||||||
→ 本地模式: 直接使用用户配置的模型
|
→ 本地模式: 直接使用用户配置的模型
|
||||||
```
|
```
|
||||||
|
|
||||||
## API 接口
|
### 不变量
|
||||||
|
|
||||||
|
- sessionKey 在会话内必须一致 (UUID 生成一次,持久化 IndexedDB)
|
||||||
|
- cancelStream 设置原子标志位,与 onDelta 回调无竞态
|
||||||
|
- 3 种 ChatStream 共享同一套回调接口,上层代码无需感知实现差异
|
||||||
|
- 消息持久化走 messageStore → IndexedDB,与流式渲染解耦
|
||||||
|
|
||||||
### Tauri 命令
|
### 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<ClassroomChatMessage>` | 课堂对话 |
|
|
||||||
| `classroom_chat_history` | classroom_id | `Vec<ClassroomChatMessage>` | 历史消息 |
|
|
||||||
|
|
||||||
**流式事件类型** (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` | 流式消息编排 |
|
| `agent_chat_stream` | 流式聊天 (主路径) |
|
||||||
| `desktop/src/store/chat/conversationStore.ts` | 会话管理 |
|
| `agent_chat` | 非流式聊天 |
|
||||||
| `desktop/src/store/chat/artifactStore.ts` | 聊天产物管理 |
|
| `cancel_stream` | 取消当前流式响应 |
|
||||||
| `desktop/src/lib/kernel-chat.ts` | Kernel ChatStream (Tauri) |
|
| `classroom_chat` | 课堂场景对话 |
|
||||||
| `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 主聊天循环 |
|
|
||||||
|
|
||||||
## 已知问题
|
## 4. 活跃问题 + 注意事项
|
||||||
|
|
||||||
- ⚠️ **B-CHAT-07: 混合域截断** — P2 Open。跨域消息时可能截断上下文
|
| 问题 | 状态 | 说明 |
|
||||||
- ✅ **SSE Token 统计为 0** — P2 已修复 (SseUsageCapture stream_done flag)
|
|------|------|------|
|
||||||
- ✅ **Tauri invoke 参数名 snake_case** — P1 已修复 (commit f6c5dd2)
|
| B-CHAT-07 混合域截断 | P2 Open | 跨域消息时可能截断上下文 |
|
||||||
- ✅ **Provider Key 解密致 relay 500** — P1 已修复 (commit b69dc61)
|
| 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) |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 数据模型
|
title: 数据模型
|
||||||
updated: 2026-04-21
|
updated: 2026-04-22
|
||||||
status: active
|
status: active
|
||||||
tags: [module, database, schema]
|
tags: [module, database, schema]
|
||||||
---
|
---
|
||||||
@@ -9,82 +9,78 @@ tags: [module, database, schema]
|
|||||||
|
|
||||||
> 从 [[index]] 导航。关联模块: [[saas]] [[memory]]
|
> 从 [[index]] 导航。关联模块: [[saas]] [[memory]]
|
||||||
|
|
||||||
## 设计思想
|
## 1. 设计决策
|
||||||
|
|
||||||
**双存储架构: PostgreSQL (SaaS 多租户) + SQLite (本地单用户)**
|
**WHY 双存储架构**: PostgreSQL 适合 SaaS 多租户场景 — 42 表支持用户隔离、计费、审计、知识库。SQLite 适合桌面端单用户 — 零配置、嵌入式、本地记忆无需网络。两者完全隔离,通过 SaaS relay + 配置同步桥接。
|
||||||
|
|
||||||
- PostgreSQL: SaaS 后端,42 表,42 CREATE TABLE,支持多用户/多 Agent/计费/知识库
|
**WHY 42 PostgreSQL 表**: 按领域划分为认证(5)、Provider(6)、计费(5)、知识库(7)、其他(19) 五大域,每个域内部高内聚,域间通过外键约束保证引用完整性。
|
||||||
- SQLite: 本地桌面端,记忆存储 + 会话持久化 + FTS5 全文索引
|
|
||||||
- 两者通过 SaaS relay + 配置同步实现数据桥接
|
|
||||||
|
|
||||||
## 功能清单
|
**WHY FTS5 (SQLite 全文搜索)**: 本地记忆搜索需支持中文。FTS5 的 trigram 分词器原生支持 CJK 字符,无需外部依赖。配合 TF-IDF 权重排序,兼顾搜索精度和零配置部署。
|
||||||
|
|
||||||
| 功能 | 描述 | 存储层 | 状态 |
|
**WHY 迁移系统**: SaaS 用 SQL 文件迁移 (21 up + 17 down),本地 SQLite 用 schema.rs 程序化迁移。两种策略分别匹配各自部署场景 — PG 需要运维可控的 SQL 脚本,SQLite 需要应用内自动迁移。
|
||||||
|------|------|--------|------|
|
|
||||||
| 用户管理 | 账户 CRUD + 角色权限 | PostgreSQL | ✅ |
|
|
||||||
| 认证数据 | JWT + 密码 + TOTP | PostgreSQL | ✅ |
|
|
||||||
| 计费系统 | 订阅/支付/发票/配额 | PostgreSQL | ✅ |
|
|
||||||
| 知识库 | 分类/条目/向量/结构化 | PostgreSQL | ✅ |
|
|
||||||
| 模型管理 | Provider/模型/Key 池 | PostgreSQL | ✅ |
|
|
||||||
| Agent 配置 | 模板/分配/行业 | PostgreSQL | ✅ |
|
|
||||||
| 本地会话 | 会话/消息持久化 | SQLite | ✅ |
|
|
||||||
| 本地记忆 | 记忆 CRUD + FTS5 搜索 | SQLite | ✅ |
|
|
||||||
| 用户画像 | 结构化偏好/兴趣 | SQLite | ✅ |
|
|
||||||
| 轨迹记录 | 工具调用链 + 压缩摘要 | 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 | 管家模式读取用户画像和提取事实进行上下文注入 |
|
||||||
|
|
||||||
| 表 | 说明 | 关键关系 |
|
## 3. 代码逻辑
|
||||||
|----|------|---------|
|
|
||||||
| `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 |
|
|
||||||
|
|
||||||
#### 知识库域 (7 表)
|
### PostgreSQL 域划分 (42 表)
|
||||||
|
|
||||||
| 表 | 说明 | 关键关系 |
|
**认证域 (5 表)**: `accounts` (邮箱/密码/pwv/角色) → `api_tokens`, `refresh_tokens` (JWT 单次使用), `roles`, `permission_templates`
|
||||||
|----|------|---------|
|
|
||||||
| `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 |
|
|
||||||
|
|
||||||
#### 其他域 (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/用量/异步任务 |
|
| API & Relay | `account_api_keys`, `usage_records`, `relay_tasks` | API Key/用量/异步任务 |
|
||||||
| 配置 | `config_items`, `config_sync_log` | KV 配置/同步日志 |
|
| 配置 | `config_items`, `config_sync_log` | KV 配置/同步日志 |
|
||||||
| Prompt | `prompt_templates`, `prompt_versions`, `prompt_sync_status` | 模板/版本/同步 |
|
| Prompt | `prompt_templates`, `prompt_versions`, `prompt_sync_status` | 模板/版本/同步 |
|
||||||
@@ -92,89 +88,66 @@ tags: [module, database, schema]
|
|||||||
| 设备 | `devices` | 设备管理 |
|
| 设备 | `devices` | 设备管理 |
|
||||||
| 遥测 | `operation_logs`, `telemetry_reports`, `saas_schema_version` | 操作日志/统计/版本 |
|
| 遥测 | `operation_logs`, `telemetry_reports`, `saas_schema_version` | 操作日志/统计/版本 |
|
||||||
| 调度 | `scheduled_tasks` | 定时任务 |
|
| 调度 | `scheduled_tasks` | 定时任务 |
|
||||||
| 限流 | `rate_limit_events` | 限流事件日志 |
|
| 限流 | `rate_limit_events` | 限流事件 (持久化到 PG) |
|
||||||
| Webhook | `webhook_subscriptions`, `webhook_deliveries` | Webhook 订阅/投递 |
|
| Webhook | `webhook_subscriptions`, `webhook_deliveries` | Webhook 订阅/投递 |
|
||||||
| 行业 | `industries`, `account_industries` | 行业配置/账户关联 |
|
| 行业 | `industries`, `account_industries` | 行业配置/账户关联 |
|
||||||
|
|
||||||
### SQLite 本地存储
|
### 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
|
||||||
|
|
||||||
| 表 | 说明 | 版本 |
|
**memories.db** (`zclaw-growth/storage/sqlite.rs`): memories (uri, memory_type, content, keywords, importance, access_count), metadata (KV)
|
||||||
|----|------|------|
|
|
||||||
| `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 |
|
|
||||||
|
|
||||||
**zclaw-growth** (`crates/zclaw-growth/src/storage/sqlite.rs`):
|
### FTS5 虚拟表
|
||||||
|
|
||||||
| 表 | 说明 |
|
```sql
|
||||||
|----|------|
|
CREATE VIRTUAL TABLE memories_fts USING fts5(uri, content, keywords, tokenize='trigram');
|
||||||
| `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 (聚合器调度)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 测试链路
|
> trigram 分词器从 unicode61 迁移,原生支持 CJK。零结果时 fallback 到 LIKE 搜索。
|
||||||
|
|
||||||
| 功能 | 测试文件 | 覆盖状态 |
|
### 不变量
|
||||||
|------|---------|---------|
|
|
||||||
| 全模块 | `crates/zclaw-saas/tests/` (17 文件) | ✅ |
|
> **SaaS 用 PostgreSQL,本地记忆用 SQLite,两者完全隔离。** 数据不自动同步,仅通过 SaaS relay API 手动触发。
|
||||||
| SQL 迁移 | `crates/zclaw-saas/migrations/` (21 up) | ✅ 启动时自动执行 |
|
> **FTS5 使用 trigram 分词器,中文搜索依赖正确分词。** 极短查询 (1-2 字) 可能 fallback 到 LIKE。
|
||||||
| 本地存储 | `crates/zclaw-memory/src/store.rs` (20 tests) | ✅ |
|
> **data.db 与 memories.db 是两个独立的 SQLite 数据库。** zclaw-memory 管理 data.db (会话/画像),zclaw-growth 管理 memories.db (记忆/FTS5)。跨库查询不适用,数据交换通过 Rust API。
|
||||||
| 用户画像 | `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 全模块 | `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 后端管理
|
- [[saas]] — PostgreSQL 由 SaaS 后端管理,所有 API 端点的数据源
|
||||||
- [[memory]] — SQLite 本地记忆存储 + FTS5
|
- [[memory]] — SQLite 本地记忆存储 + FTS5 全文搜索 + TF-IDF 权重
|
||||||
- [[routing]] — relay_tasks 异步任务追踪
|
- [[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) |
|
| 2026-04-22 | 跨会话记忆修复: profile_store 连接 + 双数据库统一 + 诊断日志 |
|
||||||
| `crates/zclaw-saas/src/models/` | 数据模型 struct 定义 |
|
| 2026-04-21 | Phase 0+1: 经验积累 reuse_count 修复 + 跨会话检索增强 IdentityRecall 26→54 模式 |
|
||||||
| `crates/zclaw-memory/src/schema.rs` | SQLite schema 定义 |
|
| 2026-04-19 | TRUTH.md 数字校准: 42 CREATE TABLE, 38 迁移文件, Rust 101,967 行 |
|
||||||
| `crates/zclaw-growth/src/storage/sqlite.rs` | FTS5 + TF-IDF 存储 |
|
| 2026-04-17 | E2E 测试: 记忆去重+记忆注入+invoice_id+agent隔离修复 |
|
||||||
| `docker-compose.yml` | PostgreSQL 容器配置 |
|
| 2026-04-15 | Heartbeat: health_snapshot 统一收集器,删除 intelligence-client/ 9 废弃文件 |
|
||||||
|
|
||||||
## 已知问题
|
|
||||||
|
|
||||||
- ⚠️ **pgvector embedding 生成未实现** — 索引就绪,`generate_embedding.rs` Worker 逻辑 deferred
|
|
||||||
- ⚠️ **FTS5 CJK 零结果** — trigram 分词器已启用,极短查询可能仍 fallback 到 LIKE
|
|
||||||
|
|||||||
@@ -7,216 +7,60 @@ tags: [module, hands, skills, mcp]
|
|||||||
|
|
||||||
# Hands + Skills + MCP
|
# Hands + Skills + MCP
|
||||||
|
|
||||||
> 从 [[index]] 导航。关联模块: [[chat]] [[middleware]] [[butler]]
|
> 从 [[index]] 导航。关联: [[chat]] [[middleware]] [[butler]]
|
||||||
|
|
||||||
## 设计思想
|
## 1. 设计决策
|
||||||
|
|
||||||
**Hands = 自主能力 (行动层), Skills = 知识技能 (认知层), MCP = 外部工具协议**
|
**Hands = 自主能力 (行动层), Skills = 知识技能 (认知层), MCP = 外部工具协议。**
|
||||||
|
|
||||||
- Hands: 浏览器自动化、数据收集、Twitter 操作等 — **执行动作**
|
| 决策 | WHY |
|
||||||
- Skills: 75 个 SKILL.md 文件 — **语义路由匹配**,增强 LLM 的领域知识
|
|------|-----|
|
||||||
- MCP: Model Context Protocol — **动态外部工具**,运行时发现和调用
|
| 7 注册 Hands (6 TOML + _reminder) | 每个 Hand 有独立配置和 Rust 实现,启用/禁用由 `enabled` 字段控制。_reminder 由 kernel 代码注册,无 HAND.toml |
|
||||||
- 触发: 用户请求 → LLM 判断需要执行 → 选择 Hand/Skill/MCP Tool → 执行
|
| 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/<Name>.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<RwLock<Vec<McpToolAdapter>>>)
|
|
||||||
→ McpToolWrapper 包装为 Tool trait
|
|
||||||
→ 注册到 ToolRegistry → LLM API tool definitions
|
|
||||||
|
|
||||||
LLM 生成 ToolUse{filesystem.read_file}
|
|
||||||
→ AgentLoop → McpToolWrapper.execute()
|
|
||||||
→ McpToolAdapter.execute() → MCP Server → 结果返回
|
|
||||||
```
|
|
||||||
|
|
||||||
### 关键桥接机制
|
|
||||||
|
|
||||||
`McpManagerState` 和 `Kernel` 共享同一个 `Arc<RwLock<Vec<McpToolAdapter>>>`:
|
|
||||||
- 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 |
|
| `crates/zclaw-hands/src/hands/` | 7 个 Hand 实现 (browser/collector/researcher/clip/twitter/quiz/reminder) |
|
||||||
| `mcp_stop_service` | 停止服务 + 从 Kernel 移除工具 |
|
| `crates/zclaw-runtime/src/tool/registry.rs` | ToolRegistry 工具注册表 |
|
||||||
| `mcp_list_services` | 列出所有运行中的服务和工具 |
|
| `crates/zclaw-runtime/src/tool/builtin/execute_skill.rs` | KernelSkillExecutor 技能执行 |
|
||||||
| `mcp_call_tool` | 手动调用 MCP 工具(支持 service_name 精确路由) |
|
| `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_list` | @connected | 运行列表 |
|
||||||
| `hand_run_cancel` | @reserved | 取消运行 (无前端 UI) |
|
| `hand_run_cancel` | @reserved | 取消运行 (无前端 UI) |
|
||||||
|
|
||||||
## 测试链路
|
### MCP 命令 (4 个)
|
||||||
|
|
||||||
| 功能 | Crate | 测试数 | 覆盖状态 |
|
`mcp_start_service`, `mcp_stop_service`, `mcp_list_services`, `mcp_call_tool`。
|
||||||
|------|-------|--------|---------|
|
MCP 工具在 ToolRegistry 中使用限定名 `service_name.tool_name` (如 `filesystem.read_file`)。
|
||||||
| Browser | zclaw-hands | 11 | ✅ |
|
`McpManagerState` 和 `Kernel` 共享 `Arc<RwLock<Vec<McpToolAdapter>>>`,通过 `sync_to_kernel()` 同步。
|
||||||
| 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** | |
|
|
||||||
|
|
||||||
## 关联模块
|
## 3. 代码逻辑
|
||||||
|
|
||||||
- [[chat]] — 消息流中可能触发 Hand/Skill
|
### 7 注册 Hands
|
||||||
- [[butler]] — ButlerRouter 使用语义路由匹配技能
|
|
||||||
- [[middleware]] — SkillIndex 中间件注入技能索引
|
|
||||||
|
|
||||||
## 关键文件
|
| 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 系统内部) |
|
| skill_execute 反序列化崩溃 | SEC2-P0-01 04-02 已修复 |
|
||||||
| `crates/zclaw-runtime/src/nl_schedule.rs` | 中文时间→cron 解析器 |
|
| Researcher 空参数 (glm-5.1 不理解 oneOf+const schema) | 04-22 schema 扁平化 + empty-input fallback |
|
||||||
| `crates/zclaw-skills/src/semantic_router.rs` | TF-IDF 语义路由 |
|
| 排版乱码 (stripToolNarration 句子级拆分破坏 markdown) | 04-22 行级过滤 |
|
||||||
| `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 桥接 |
|
|
||||||
|
|
||||||
## 已知问题
|
## 5. 变更日志
|
||||||
|
|
||||||
- ✅ **skill_execute 反序列化崩溃** — SEC2-P0-01 已于 04-02 修复
|
| 日期 | 变更 | 关联 |
|
||||||
- ✅ **Hands E2E 通过率 70%** — 10 Hand 全部启用,审批机制正常
|
|------|------|------|
|
||||||
- ⚠️ **hand.rs TODO** — P2-03: tool_count/metric_count 待从实际 Hand 实例填充
|
| 2026-04-22 | Wiki 5-section 重构: 281->~195 行,语义路由细节引用 [[butler]] | wiki/ |
|
||||||
| `desktop/src-tauri/src/kernel_commands/hand.rs` | Hand Tauri 命令 (8) |
|
| 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** |
|
||||||
|
|||||||
216
wiki/pipeline.md
216
wiki/pipeline.md
@@ -1,86 +1,86 @@
|
|||||||
---
|
---
|
||||||
title: Pipeline DSL
|
title: Pipeline DSL
|
||||||
updated: 2026-04-21
|
updated: 2026-04-22
|
||||||
status: active
|
status: active
|
||||||
tags: [module, pipeline, dsl]
|
tags: [module, pipeline, dsl]
|
||||||
---
|
---
|
||||||
|
|
||||||
# Pipeline DSL
|
# Pipeline DSL
|
||||||
|
|
||||||
> 从 [[index]] 导航。关联模块: [[hands-skills]]
|
> 从 [[index]] 导航。关联模块: [[hands-skills]] [[chat]]
|
||||||
|
|
||||||
## 设计思想
|
## 1. 设计决策
|
||||||
|
|
||||||
**Pipeline = 可编排的工作流,按 DAG 依赖顺序执行步骤。**
|
**WHY DAG 执行器**: 工作流步骤之间存在数据依赖,DAG (有向无环图) 通过拓扑排序自动推导执行顺序,支持无依赖节点的并行执行,比线性管道更灵活、更高效。
|
||||||
|
|
||||||
- YAML 定义 Pipeline 结构(步骤、依赖、输入/输出)
|
**WHY YAML 模板**: 声明式定义 + 可版本控制。非技术用户可直接编辑 YAML 文件调整步骤和参数,无需重新编译。模板可随项目仓库同步、diff、review。
|
||||||
- DAG 执行器按依赖拓扑排序执行
|
|
||||||
- 18 个 YAML 模板覆盖 8 大行业目录
|
|
||||||
- 前端已接通 8 个 Tauri invoke 调用
|
|
||||||
|
|
||||||
## 代码逻辑
|
**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 定义
|
用户选择模板 (Workflow 面板)
|
||||||
→ PipelineExecutor (crates/zclaw-pipeline/src/executor.rs)
|
→ pipeline-client.ts: invoke('pipeline_load_template')
|
||||||
→ 构建 DAG (按依赖排序)
|
→ parser_v2: 解析 YAML → PipelineDefinition (steps + depends_on)
|
||||||
→ 逐步执行:
|
→ executor: 构建 DAG → 拓扑排序 → 检测循环依赖
|
||||||
→ ActionRegistry.resolve(action_type)
|
→ 并行执行无依赖节点:
|
||||||
→ 执行 action → PipelineRun.step_results
|
→ ActionRegistry.resolve(action_type) → 具体 Handler
|
||||||
|
→ Handler 执行 → step_result (output + status)
|
||||||
|
→ 有依赖节点等待前驱完成 → 执行
|
||||||
→ 全部完成 → PipelineRun.status = Completed
|
→ 全部完成 → 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
|
```rust
|
||||||
enum RunStatus { Pending, Running, Completed, Failed, Cancelled }
|
enum RunStatus { Pending, Running, Completed, Failed, Cancelled }
|
||||||
```
|
```
|
||||||
|
|
||||||
### 模板分布 (18 个 YAML)
|
### DAG 执行流程详解
|
||||||
|
|
||||||
```
|
1. **解析阶段**: `parser_v2.rs` 将 YAML 反序列化为 `PipelineDefinition`,包含 `steps: Vec<StepDef>` 和每个 step 的 `depends_on: Vec<String>`
|
||||||
pipelines/
|
2. **构建阶段**: `executor.rs` 将 steps 映射为 DAG 节点,建立邻接表 (step_id → [依赖的 step_ids])
|
||||||
├── _templates/ (2 模板)
|
3. **排序阶段**: Kahn 算法拓扑排序,检测循环依赖 — 若排序后节点数 < 总节点数,说明存在环,返回错误
|
||||||
│ ├── article-summary.yaml
|
4. **执行阶段**: 按拓扑序逐层执行,同层无依赖节点并行。每步通过 `ActionRegistry` 解析 `action_type` 到具体 Handler
|
||||||
│ └── competitor-analysis.yaml
|
5. **完成阶段**: 全部步骤成功 → `Completed`;任一步骤失败 → 整体 `Failed`;用户可随时 `Cancel`
|
||||||
├── 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
|
|
||||||
|
|
||||||
注: 共 18 个 YAML, 总数 = 2+4+4+3+1+1+1+1+1 = 18
|
### Tauri 命令分布
|
||||||
```
|
|
||||||
|
|
||||||
### 前端集成
|
|
||||||
|
|
||||||
| 组件 | 文件 |
|
|
||||||
|------|------|
|
|
||||||
| 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 个):
|
|
||||||
|
|
||||||
| 文件 | 命令数 | 命令 |
|
| 文件 | 命令数 | 命令 |
|
||||||
|------|--------|------|
|
|------|--------|------|
|
||||||
@@ -89,68 +89,66 @@ Pipeline Tauri 命令 (12 个):
|
|||||||
| intent_router.rs | 1 | route_intent |
|
| intent_router.rs | 1 | route_intent |
|
||||||
| presentation.rs | 2 | analyze_presentation/pipeline_templates |
|
| 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) — 文献综述
|
||||||
|
```
|
||||||
|
|
||||||
## 功能清单
|
### 不变量
|
||||||
|
|
||||||
| 功能 | 描述 | 入口文件 | 状态 |
|
> **DAG 节点必须有明确的依赖关系,循环依赖会在 topological sort 阶段被检测并报错。**
|
||||||
|------|------|----------|------|
|
> **模板 YAML 结构不重复 — 每个行业目录的模板聚焦该领域特有场景。**
|
||||||
| YAML 解析 | v2 解析器,支持 DAG 依赖 | parser_v2.rs | ✅ |
|
|
||||||
| DAG 执行 | 拓扑排序 + 并行执行 | executor.rs | ✅ |
|
|
||||||
| 模板发现 | 18 模板 + 8 行业目录 | pipeline_commands/ | ✅ |
|
|
||||||
| 模型意图 | Pipeline 意图匹配 | intent.rs | ✅ |
|
|
||||||
| 状态管理 | Pipeline 运行状态 | state.rs | ✅ |
|
|
||||||
| 触发器 | 定时/事件触发 | trigger.rs | ✅ |
|
|
||||||
| 演示分析 | Pipeline 结果分析 | presentation/ | ✅ |
|
|
||||||
|
|
||||||
## 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** |
|
||||||
|
|
||||||
| 命令 | 状态 | 说明 |
|
## 4. 活跃问题 + 注意事项
|
||||||
|------|------|------|
|
|
||||||
| `orchestration_execute` | @reserved | 执行工作流 (无前端 UI) |
|
|
||||||
| `orchestration_validate` | @reserved | 验证工作流 (无前端 UI) |
|
|
||||||
|
|
||||||
> 另有 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 命令,不直接操作文件系统。
|
||||||
|
|
||||||
| 功能 | 测试文件 | 测试数 | 覆盖状态 |
|
## 5. 变更日志
|
||||||
|------|---------|--------|---------|
|
|
||||||
| YAML 解析 v2 | parser_v2.rs | 11 | ✅ |
|
> 最近 5 条与 Pipeline 相关的变更。完整日志见 [[log]]。
|
||||||
| DAG 执行 | executor.rs | 2 | ✅ |
|
|
||||||
| 意图匹配 | intent.rs | 5 | ✅ |
|
| 日期 | 变更 |
|
||||||
| 状态管理 | state.rs | 6 | ✅ |
|
|------|------|
|
||||||
| 触发器 | trigger.rs | 5 | ✅ |
|
| 2026-04-21 | Phase 0+1 修复: Skill 工具调用桥接 complete_with_tools() + Hand 字段映射 runId |
|
||||||
| 类型 | types.rs + types_v2.rs | 4 | ✅ |
|
| 2026-04-17 | E2E 回归: pipeline_create 反序列化 BUG-L2 修复 |
|
||||||
| 解析 v1 | parser.rs | 5 | ✅ |
|
| 2026-04-16 | 3 项 P0 修复 + 5 项 E2E Bug 修复,Pipeline Tauri 命令数校正 |
|
||||||
| 引擎上下文 | engine/context.rs | 7 | ✅ |
|
| 2026-04-09 | Pipeline+Hands 双交付,DAG 执行器稳定化 |
|
||||||
| 引擎阶段 | engine/stage.rs | 1 | ✅ |
|
| 2026-04-01 | 17 YAML 模板 + DAG 执行器初始版本 |
|
||||||
| 演示 | presentation/ (3文件) | 13 | ✅ |
|
|
||||||
| **合计** | 13 文件 | **59** | |
|
|
||||||
|
|
||||||
## 关联模块
|
## 关联模块
|
||||||
|
|
||||||
- [[hands-skills]] — Pipeline 步骤可能调用 Hand/Skill
|
- [[hands-skills]] — Pipeline 步骤可能调用 Hand/Skill 执行具体动作
|
||||||
- [[chat]] — Pipeline 可通过聊天触发
|
- [[chat]] — Pipeline 可通过聊天意图匹配触发 (`intent_router.rs`)
|
||||||
|
- [[memory]] — 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 禁用后已有任务不自动清理
|
|
||||||
|
|||||||
375
wiki/routing.md
375
wiki/routing.md
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 客户端路由
|
title: 客户端路由
|
||||||
updated: 2026-04-21
|
updated: 2026-04-22
|
||||||
status: active
|
status: active
|
||||||
tags: [module, routing, connection]
|
tags: [module, routing, connection]
|
||||||
---
|
---
|
||||||
@@ -9,318 +9,123 @@ tags: [module, routing, connection]
|
|||||||
|
|
||||||
> 从 [[index]] 导航。关联模块: [[chat]] [[saas]]
|
> 从 [[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 消耗
|
| 5 分支路由 | 覆盖全部部署形态: Admin本地 / Tauri+SaaS / Browser+SaaS / Tauri本地 / 外部Gateway |
|
||||||
3. **模型白名单** — Admin 配置哪些模型可用,`listModels()` 返回白名单
|
| SaaS Relay 中转 | 集中密钥管理 — 用户无需自备 API Key;用量追踪计费 — 每次调用经 SaaS;模型白名单 — Admin 控制可用模型 |
|
||||||
4. **降级保障** — SaaS 挂了自动切本地 Kernel,桌面端不变砖
|
| 自动降级到本地 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 分支 + 降级决策树
|
### 5 分支决策树
|
||||||
|
|
||||||
入口: `connectionStore.ts` → `connect(url?, token?)`
|
|
||||||
|
|
||||||
```
|
```
|
||||||
connect()
|
connect()
|
||||||
│
|
├─ [1] Admin强制本地: adminRouting=local && isTauri → Kernel 直连
|
||||||
├── [1] Admin 强制本地: adminRouting === 'local' && isTauri()
|
├─ [2] SaaS+Tauri: savedMode=saas && isTauri → KernelClient + baseUrl=SaaS relay
|
||||||
│ → 直接走 Kernel 模式,跳过 SaaS
|
│ └─ SaaS不可达 → 降级 [4]
|
||||||
│
|
├─ [3] SaaS+Browser: savedMode=saas && !isTauri → SaaSRelayClient (SSE)
|
||||||
├── [2] SaaS Relay (Tauri 路径): savedMode === 'saas' && isTauri()
|
│ └─ SaaS不可达 → 降级 [4]
|
||||||
│ → KernelClient + baseUrl = saasUrl/api/v1/relay
|
├─ [4] 本地Kernel: isTauriRuntime && 非SaaS → KernelClient + 用户自配 Key
|
||||||
│ → apiKey = SaaS JWT (不是 LLM Key!)
|
└─ [5] 外部Gateway: !isTauri → GatewayClient (WebSocket)
|
||||||
│ → 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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 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',
|
|
||||||
});
|
|
||||||
```
|
```
|
||||||
|
前端选择模型 → preferredModel || fallbackId
|
||||||
**注意**: Kernel 仍然执行 LLM 调用逻辑,但请求发往 SaaS relay 而非直连 LLM。
|
→ kernelClient.setConfig({ model, apiKey: JWT, baseUrl: saasUrl/api/v1/relay })
|
||||||
SaaS relay 接到请求后,从 Token Pool 中取一个可用 Key,转发给真实 LLM。
|
→ Tauri invoke kernel_init → Kernel::boot(config)
|
||||||
|
→ loop_runner → POST {base_url}/chat/completions
|
||||||
|
→ SaaS Relay → cache 精确匹配 model_id → Key Pool 轮换 → 真实 LLM
|
||||||
|
→ SSE 流式返回
|
||||||
|
```
|
||||||
|
|
||||||
### SaaS 降级流程
|
### SaaS 降级流程
|
||||||
|
|
||||||
关键代码: `connectionStore.ts:446-468`
|
|
||||||
|
|
||||||
```
|
```
|
||||||
listModels() 失败
|
listModels() 失败
|
||||||
→ 401 → session 过期 → logout → 要求重新登录
|
→ 401 → session 过期 → logout
|
||||||
→ 其他错误 → saasDegraded = true
|
→ 其他 → saasDegraded=true → 降级本地 Kernel
|
||||||
→ saasStore.saasReachable = false
|
|
||||||
→ 降级到本地 Kernel 模式
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 客户端类型
|
### 客户端类型
|
||||||
|
|
||||||
| 客户端 | 传输 | 文件 | 用途 |
|
| 客户端 | 传输 | 用途 |
|
||||||
|--------|------|------|------|
|
|--------|------|------|
|
||||||
| GatewayClient | WebSocket + REST | `lib/gateway-client.ts` | 外部 Gateway 进程 |
|
| GatewayClient | WebSocket + REST | 外部 Gateway 进程 |
|
||||||
| KernelClient | Tauri invoke() | `lib/kernel-chat.ts` | 内置 Kernel (桌面端) |
|
| KernelClient | Tauri invoke() | 内置 Kernel (桌面端) |
|
||||||
| SaaSRelayGatewayClient | HTTP SSE | `lib/saas-relay-client.ts` | 浏览器端 SaaS 中继 |
|
| SaaSRelayGatewayClient | HTTP SSE | 浏览器端 SaaS 中继 |
|
||||||
|
|
||||||
`getClient()` 定义: `connectionStore.ts:844`
|
### 不变量
|
||||||
所有 Store 通过 `initializeStores()` (store/index.ts:94) 获取共享 client。
|
|
||||||
|
|
||||||
### Store 层 (16 根文件 + chat/4 + saas/5 = 25)
|
- `getClient()` 是全局单例,所有 Store 通过 `initializeStores()` 获取共享 client
|
||||||
|
- SaaS 不可达时自动降级到本地 Kernel,不需要用户干预
|
||||||
```
|
- SaaS Relay 按 `model_id` 精确匹配,不解析别名 (`config.toml [llm.aliases]` 仅本地 Kernel)
|
||||||
desktop/src/store/
|
- Provider Key 解密失败时 warn+skip,不 500 (`key_pool.rs`)
|
||||||
├── 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 接口
|
|
||||||
|
|
||||||
### Tauri 命令
|
### 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` | 路由决策核心 |
|
| `kernel_init` | 初始化 Kernel 配置 (model, apiKey, baseUrl) |
|
||||||
| `desktop/src/lib/gateway-client.ts` | WebSocket 客户端 |
|
| `zclaw_start` / `zclaw_stop` / `zclaw_restart` | Kernel 生命周期管理 |
|
||||||
| `desktop/src/lib/kernel-chat.ts` | Tauri 内核聊天 |
|
| `zclaw_health_check` / `zclaw_ping` | 健康检查 + 端口检测 |
|
||||||
| `desktop/src/lib/kernel-client.ts` | Kernel 客户端配置 |
|
| `zclaw_doctor` | 完整诊断报告 |
|
||||||
| `desktop/src/lib/saas-relay-client.ts` | SaaS SSE 中继 |
|
|
||||||
| `desktop/src/lib/saas-client.ts` | SaaS API 客户端 |
|
|
||||||
| `desktop/src/store/index.ts` | Store 协调器 + client 注入 |
|
|
||||||
|
|
||||||
## 已知问题
|
### 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) |
|
||||||
|
|||||||
Reference in New Issue
Block a user