4d149982c5
删除: - 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
329 lines
13 KiB
Markdown
329 lines
13 KiB
Markdown
# 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 天 |
|