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

13 KiB
Raw Blame History

iAs 工具系统设计

核心理念

一切皆 Skill。【todo:向内置工具转变】

没有"内置工具"和"外部技能"的区别——所有工具都是 SKILL.md,区别仅在来源:

  • 系统 skillsresources/skills/ 目录,随 iAs 分发,只读
  • 用户 skillsskills/ 项目目录,用户自己添加

加载时自动合并,同名时用户 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 完全透明                           │    │
│  └──────────────────────────────────────────────────┘    │
└──────────────────────────────────────────────────────────┘

核心类型

/// 风险等级(仅两级)
#[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 文件:

# 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

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 不知道确认码的存在,不知道审批过程。它只知道自己调用了工具,然后得到了结果或拒绝。


数据库表

-- 待审批的工具调用
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 天