Files
zclaw_openfang/wiki/hands-skills.md
iven b60b96225d
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
docs(wiki): Hermes Phase 1-4 wiki 同步
- hands-skills: 新增 concurrency() 声明要求不变量
- log: 追加 Hermes Phase 1-4 变更记录
- index: 更新日期
2026-04-24 08:54:48 +08:00

184 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Hands + Skills + MCP
updated: 2026-04-22
status: active
tags: [module, hands, skills, mcp]
---
# Hands + Skills + MCP
> 从 [[index]] 导航。关联: [[chat]] [[middleware]] [[butler]]
## 1. 设计决策
**Hands = 自主能力 (行动层), Skills = 知识技能 (认知层), MCP = 外部工具协议。**
| 决策 | 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. 关键文件 + 数据流
### 核心文件
| 文件 | 职责 |
|------|------|
| `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 触发流
```
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 更新状态
```
### 集成契约
| 方向 | 模块 | 接口 / 触发点 |
|------|------|---------------|
| 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 个)
| 命令 | 状态 | 说明 |
|------|------|------|
| `hand_list` | @connected | 列出可用 Hands |
| `hand_execute` | @connected | 执行 Hand |
| `hand_approve` | @connected | 审批 Hand 执行 |
| `hand_cancel` | @connected | 取消执行 |
| `hand_get` | @connected | 获取 Hand 详情 |
| `hand_run_status` | @connected | 运行状态查询 |
| `hand_run_list` | @connected | 运行列表 |
| `hand_run_cancel` | @reserved | 取消运行 (无前端 UI) |
### MCP 命令 (4 个)
`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. 代码逻辑
### 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 行
### ⚡ 新增工具/技能必须声明 concurrency 级别
`Tool` trait 的 `concurrency()` 方法决定并行执行策略 (04-24 Hermes Phase 2A):
| 级别 | 含义 | 适用场景 |
|------|------|---------|
| `ReadOnly` (默认) | 只读,始终可并行 | file_read, web_search, calculator |
| `Exclusive` | 有副作用,必须串行 | file_write, shell_exec, send_message, execute_skill, task |
| `Interactive` | 需要用户交互,永不并行 | ask_clarification |
**新增工具时**:在 `impl Tool for YourTool` 中覆盖 `concurrency()` 方法。默认 `ReadOnly`,如果有写操作/副作用必须返回 `ToolConcurrency::Exclusive`。未正确声明会导致并行执行时产生竞态条件。
## 4. 活跃问题 + 陷阱
### 活跃
| 问题 | 状态 | 说明 |
|------|------|------|
| Clip 依赖 FFmpeg | P3 | 用户需本地安装 FFmpeg否则视频处理 Hand 不可用 |
| Hands E2E 通过率 ~70% | P2 | 10 Hand 全部启用,审批机制正常,但部分 Hand 边界场景未覆盖 |
| hand.rs TODO | P2 | tool_count/metric_count 待从实际 Hand 实例填充 |
### 历史 (已修复)
| 问题 | 修复 |
|------|------|
| 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. 变更日志
| 日期 | 变更 | 关联 |
|------|------|------|
| 2026-04-24 | Hermes Phase 2A: ToolConcurrency 枚举 + 并行执行 + concurrency() 声明要求 | commit 9060935 |
| 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** |