Files
iAs/src/tools/DESIGN.md
T
wunianxiao 4d149982c5 refactor: 删除外置 skills 系统,全部工具转为内置
删除:
- resources/skills/ (9个外置 skill, bash/python/node 脚本)
- src/tools/registry.rs (SkillRegistry 加载/执行)
- src/tools/parser.rs (SKILL.md 解析)
- src/skills_importer.rs (skills 导入)
- cli.rs Skills 子命令
- main.rs cmd_skills / global_skills_dir

保留:
- src/tools/builtin.rs (内置工具注册 + 审批流程)
- src/tools/weather.rs (天气查询)
- src/tools/skill.rs (精简为 SkillSpec/RiskLevel/SkillResult/ExecutionContext)
- src/tools/approval.rs (审批管理器)

main.rs 简化: 移除 SkillRegistry 加载, executor 只走 BuiltinRegistry
2026-06-03 10:28:28 +08:00

329 lines
13 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.
# iAs 工具系统设计
## 核心理念
**一切皆 Skill。【todo:向内置工具转变】**
没有"内置工具"和"外部技能"的区别——所有工具都是 SKILL.md,区别仅在来源:
- **系统 skills** — `resources/skills/` 目录,随 iAs 分发,只读
- **用户 skills** — `skills/` 项目目录,用户自己添加
加载时自动合并,同名时用户 skills 覆盖系统 skills。
---
## 架构
```
┌──────────────────────────────────────────────────────────┐
│ LLM │
│ generate_reply() ←→ tool_choice → tool_execute(result) │
└─────────────────────┬────────────────────────────────────┘
┌──────────────────────────────────────────────────────────┐
│ SkillRegistry │
│ │
│ ┌──────────────────┐ ┌────────────────────────────┐ │
│ │ 系统 Skills │ │ 用户 Skills │ │
│ │ resources/skills/ │ │ skills/ │ │
│ │ │ │ │ │
│ │ • datetime │ │ • query_qweather/ │ │
│ │ • weather │ │ • search_tavily/ │ │
│ │ • search_web │ │ • start_pc/ │ │
│ │ • email │ │ • ... │ │
│ │ • shell_exec │ │ │ │
│ │ • schedule │ │ │ │
│ └────────┬──────────┘ └────────────┬──────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Approval Gate │ │
│ │ risk == High → 确认码审批 / Low → 自动执行 │ │
│ │ 审批过程对 LLM 完全透明 │ │
│ └──────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
```
---
## 核心类型
```rust
/// 风险等级(仅两级)
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum RiskLevel {
Low, // 只读/安全,自动执行
High, // 写操作/有风险,需微信确认码审批
}
/// 技能来源
#[derive(Debug, Clone, PartialEq)]
pub enum SkillSource {
System, // resources/skills/
User, // skills/
}
/// 技能元数据(从 SKILL.md 解析)
#[derive(Debug, Clone, Deserialize)]
pub struct SkillSpec {
pub name: String,
pub description: String,
pub risk_level: RiskLevel,
pub parameters: serde_json::Value, // JSON Schema
pub timeout_secs: u64,
}
/// 技能(加载到内存后)
pub struct Skill {
pub spec: SkillSpec,
pub source: SkillSource,
pub dir: PathBuf,
pub execute_command: String,
}
```
---
## SKILL.md 格式
每个技能是一个目录,包含 `SKILL.md` 文件:
```markdown
# Get Current Datetime
获取当前日期时间(北京时间 UTC+8)。
## Risk Level
Low
## Parameters
\`\`\`json
{
"type": "object",
"properties": {
"format": {
"type": "string",
"description": "时间格式: full | date | time",
"enum": ["full", "date", "time"],
"default": "full"
}
}
}
\`\`\`
## Execute
\`\`\`bash
#!/usr/bin/env bash
scripts/datetime.sh
\`\`\`
```
---
## 内置 Skills 一览
随 iAs 分发的系统 skills
| Skill | 风险 | 说明 |
|-------|------|------|
| `get_current_datetime` | Low | 获取当前时间(UTC+8 |
| `query_weather` | Low | 和风天气查询 |
| `search_web` | Low | Tavily 网络搜索 |
| `email` | High | 收发邮件 |
| `execute_shell` | High | 执行 shell 命令 |
| `list_scheduled_tasks` | Low | 列出定时任务 |
| `manage_scheduled_task` | High | 增删改定时任务 |
---
## SkillRegistry
```rust
pub struct SkillRegistry {
skills: HashMap<String, Arc<Skill>>,
}
impl SkillRegistry {
pub async fn load(system_dir: &str, user_dir: &str) -> Result<Self, LoadError>;
pub fn list_specs(&self) -> Vec<&SkillSpec>;
pub fn list_specs_by_risk(&self, level: RiskLevel) -> Vec<&SkillSpec>;
pub fn get(&self, name: &str) -> Option<&Skill>;
/// 执行技能(含审批流程)
pub async fn execute(
&self,
name: &str,
params: serde_json::Value,
ctx: &ExecutionContext,
) -> Result<SkillResult, SkillError>;
}
pub struct ExecutionContext {
pub user_id: String,
pub db: Option<Arc<Database>>,
/// 发送微信消息的回调
pub send_wechat: Option<Box<dyn Fn(&str, &str) -> Result<(), String> + Send + Sync>>,
/// 等待用户回复的回调(返回用户消息文本)
pub wait_for_reply: Option<Box<dyn Fn(&str, Duration) -> Result<Option<String>, String> + Send + Sync>>,
}
```
---
## 审批流程(透明模式)
**核心原则:审批过程对 LLM 完全不可见。** LLM 只看到最终结果。
```
LLM 调用 High 风险技能
SkillRegistry::execute()
├── 查找 Skill
├── risk_level == High
└── YES ──────────────────────────────────────────┐
│ │
▼ │
┌──────────────────────────────────────────┐ │
│ 等待阶段(LLM 不感知) │ │
│ │ │
│ ① 生成 6 位确认码(如 482731) │ │
│ ② SHA-256 哈希存入 pending_approvals │ │
│ ③ 通过微信发送确认消息给用户: │ │
│ │ │
│ ┌─────────────────────────────┐ │ │
│ │ ⚠️ 操作确认 │ │ │
│ │ │ │ │
│ │ 技能:send_email │ │ │
│ │ 参数:收件人 xxx@xxx.com │ │ │
│ │ │ │ │
│ │ 确认码:482731 │ │ │
│ │ 回复确认码以继续 │ │ │
│ │ 回复 0 或「取消」以取消 │ │ │
│ │ (5分钟内有效,最多3次尝试) │ │ │
│ └─────────────────────────────┘ │ │
│ │ │
│ ④ 等待用户回复 │ │
│ │ │ │
│ ├── 回复正确确认码 ──┐ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────┐ │ │
│ │ │ ⑤ 执行技能 │ │ │
│ │ │ ⑥ LLM 看到 │ │ │
│ │ │ 执行结果 │ │ │
│ │ └──────────────┘ │ │
│ │ │ │
│ ├── 回复错误确认码 │ │
│ │ → "确认码错误,还剩 N 次" │ │
│ │ → 重试(最多 3 次) │ │
│ │ → 3 次都错 │ │
│ │ → LLM 看到: │ │
│ │ "用户拒绝了你的调用: │ │
│ │ send_email" │ │
│ │ │ │
│ ├── 回复 0 / 「取消」 │ │
│ │ → LLM 看到: │ │
│ │ "用户拒绝了你的调用: │ │
│ │ send_email" │ │
│ │ │ │
│ └── 5 分钟超时 │ │
│ → LLM 看到: │ │
│ "用户没有确认操作: │ │
│ send_email" │ │
└──────────────────────────────────────────┘
```
### LLM 视角(两种可能)
```
✅ 通过:
LLM: 调用 send_email({to: "xxx", subject: "..."})
系统: { success: true, message: "邮件已发送" }
❌ 拒绝:
LLM: 调用 send_email({to: "xxx", subject: "..."})
系统: { success: false, error: "用户拒绝了你的调用:send_email" }
```
LLM 不知道确认码的存在,不知道审批过程。它只知道自己调用了工具,然后得到了结果或拒绝。
---
## 数据库表
```sql
-- 待审批的工具调用
CREATE TABLE pending_approvals (
id UUID PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ NOT NULL, -- 5分钟后过期
user_id TEXT NOT NULL,
skill_name TEXT NOT NULL,
params JSONB NOT NULL,
code_hash TEXT NOT NULL, -- 确认码 SHA-256(不存明文)
attempts_left INTEGER NOT NULL DEFAULT 3, -- 剩余尝试次数
status TEXT NOT NULL DEFAULT 'pending', -- pending | approved | rejected | expired
result JSONB,
consumed_at TIMESTAMPTZ
);
```
---
## Skills 目录结构
```
resources/skills/ # 系统内置 Skills(随 iAs 分发)
├── get_current_datetime/
│ ├── SKILL.md
│ └── scripts/datetime.sh
├── query_weather/
│ ├── SKILL.md
│ └── scripts/weather.sh
├── search_web/
│ ├── SKILL.md
│ └── scripts/search.sh
├── email/
│ ├── SKILL.md
│ └── scripts/email.sh
├── execute_shell/
│ ├── SKILL.md
│ └── scripts/shell.sh
├── list_scheduled_tasks/
│ ├── SKILL.md
│ └── scripts/list.sh
└── manage_scheduled_task/
├── SKILL.md
└── scripts/manage.sh
skills/ # 用户 Skills(用户自行添加)
├── query_qweather/
│ ├── SKILL.md
│ └── scripts/query.sh
├── search_tavily/
│ ├── SKILL.md
│ └── scripts/search.sh
└── start_pc/
├── SKILL.md
└── scripts/wake.sh
```
---
## 实施步骤
| 步骤 | 内容 | 预估 |
|------|------|------|
| Step 1 | SkillSpec/Skill/SkillRegistry 核心类型 + 注册表 | 半天 |
| Step 2 | SKILL.md 解析器 + 目录扫描(system + user 合并) | 半天 |
| Step 3 | 技能执行器:子进程管理 + 参数 JSON 传递 + 结果解析 | 半天 |
| Step 4 | 系统 Skills 脚本实现(7 个 bash 脚本) | 1 天 |
| Step 5 | 审批系统:pending_approvals 表 + 确认码生成/验证 + 微信透明通知 | 1 天 |
| Step 6 | LLM function calling 集成 + 审计日志 | 1 天 |