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:
iven
2026-04-22 21:53:17 +08:00
parent 5d88d129d1
commit c10e50d58e
6 changed files with 646 additions and 1067 deletions

View File

@@ -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/<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_schemaaction/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` — 系统内部 Handid=`_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 |
| `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<RwLock<Vec<McpToolAdapter>>>`,通过 `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** |