OpenClaw Tools 与 Skills 系统深度解析

引言

OpenClaw 作为一个功能强大的 AI 助手平台,其核心能力来自于两个关键系统:Tools(工具)Skills(技能)。工具为 Agent 提供了与外部世界交互的能力,而技能则是教会 Agent 如何组合使用这些工具完成复杂任务的知识库。

本文将深入探讨:

  1. OpenClaw 设计了哪些工具及其功能
  2. Skills 的注入时机和完整流程
  3. Tools 与 Skills 的协作关系

第一部分:OpenClaw 工具体系全览

1.1 工具设计理念

OpenClaw 的工具系统遵循以下设计原则:

核心理念

  • 最小权限原则:工具按功能分组,支持细粒度权限控制
  • 组合优于单体:小而专注的工具,通过组合实现复杂功能
  • 安全第一:多层策略过滤,防止滥用
  • 可扩展性:插件系统支持自定义工具

工具定义

typescript 复制代码
interface Tool {
  name: string;              // 唯一标识符(如 "read", "exec")
  description: string;       // 功能描述
  parameters: JSONSchema;    // TypeBox定义的参数schema
  execute: (params) => Promise<Result>;  // 执行函数
}

1.2 内置工具完整清单

OpenClaw 提供了 28 个核心工具,按 8 个功能类别组织:

📁 文件系统工具(File System)

工具名 功能描述 典型用例 权限级别
read 读取文件内容 查看代码、读取配置、检查日志 基础
write 创建或覆盖文件 生成代码、保存数据、写入配置 中等
edit 精确字符串替换编辑 修改代码片段、更新配置项 中等
apply_patch 应用 OpenAI 风格补丁 批量代码修改、多文件编辑

设计考量

  • readwrite 是最基础的操作
  • edit 提供精确的字符串替换(避免覆盖整个文件)
  • apply_patch 支持复杂的多文件编辑(仅限特定模型)

安全机制

typescript 复制代码
// 工作空间根目录保护
wrapToolWorkspaceRootGuard(tool)
// 防止修改:.git, .openclaw, node_modules 等

⚙️ 运行时工具(Runtime)

工具名 功能描述 典型用例 权限级别
exec 执行 shell 命令 运行脚本、编译代码、系统操作
process 后台进程管理 启动服务、监控任务

关键特性

  • exec 支持超时、信号中断、输出捕获
  • process 支持后台守护进程
  • 默认拒绝:HTTP网关默认禁止这两个工具(安全考虑)

使用示例

typescript 复制代码
// 执行Python脚本
exec("python3 script.py --input data.json")

// 启动开发服务器
process({
  command: "npm run dev",
  background: true
})

🌐 网络工具(Web)

工具名 功能描述 典型用例 权限级别
web_search Web 搜索 查找最新信息、技术文档 基础
web_fetch 获取网页内容 抓取文章、解析API文档 基础

多提供商支持

  • web_search:支持 Tavily、Brave、Perplexity 等
  • web_fetch:自动转换HTML为Markdown,提取主要内容

返回格式

json 复制代码
{
  "results": [
    {
      "title": "OpenClaw Documentation",
      "url": "https://docs.openclaw.ai",
      "snippet": "OpenClaw is a self-hosted AI assistant...",
      "content": "Full page content in markdown"
    }
  ]
}

🧠 内存工具(Memory)

工具名 功能描述 典型用例 权限级别
memory_search 语义搜索记忆文件 查找历史对话、检索知识 基础
memory_get 按路径读取记忆 精确获取特定记忆片段 基础

核心能力

  • 向量搜索 + 关键词搜索混合
  • 支持 MEMORY.md、memory/.md、sessions/.jsonl
  • RRF(Reciprocal Rank Fusion)融合算法

查询流程

lua 复制代码
memory_search("Discord配置")
  ↓
返回:
  - path: MEMORY.md
  - startLine: 10
  - endLine: 15
  - score: 0.85
  ↓
memory_get(path="MEMORY.md", from=10, lines=5)
  ↓
返回完整内容

详细机制参考openclaw-memory-system-blog-zh-CN.md


💬 会话工具(Sessions)

工具名 功能描述 典型用例 权限级别
sessions_list 列出所有会话 查看活跃对话、管理会话 基础
sessions_history 读取会话历史 回顾对话、分析上下文 基础
sessions_send 发送消息到会话 跨会话通信、批量通知 中等
sessions_spawn 生成子代理 并行任务、委托工作
subagents 管理子代理 监控、停止子任务
session_status 查询会话状态 健康检查、负载监控 基础

会话架构

scss 复制代码
Parent Agent (主代理)
  ├─ Spawn → Child Agent 1 (子代理1)
  │   └─ Task: 分析代码
  ├─ Spawn → Child Agent 2 (子代理2)
  │   └─ Task: 写文档
  └─ 汇总结果

子代理限制

typescript 复制代码
// 子代理被拒绝的工具(安全考虑)
const SUBAGENT_TOOL_DENY_ALWAYS = [
  "memory_search",    // 不能访问父代理记忆
  "memory_get",
  "gateway",          // 不能控制系统
  "cron",             // 不能创建定时任务
];

🎨 UI工具(User Interface)

工具名 功能描述 典型用例 权限级别
browser 浏览器自动化 网页截图、自动化测试
canvas Canvas/HTML渲染 生成图表、可视化 中等

browser 工具能力

  • Playwright 驱动
  • 支持导航、点击、输入、截图
  • JavaScript 执行

canvas 工具能力

  • HTML/SVG → PNG/JPEG
  • 支持 Chart.js、D3.js 等库
  • 自定义尺寸和样式

示例

typescript 复制代码
// 生成饼图
canvas({
  html: `
    <canvas id="chart"></canvas>
    <script src="chart.js"></script>
    <script>
      new Chart(ctx, {
        type: 'pie',
        data: { labels: ['A', 'B'], datasets: [{ data: [30, 70] }] }
      });
    </script>
  `,
  width: 800,
  height: 600
})

📧 消息工具(Messaging)

工具名 功能描述 典型用例 权限级别
message 发送消息到当前渠道 主动通知、状态更新 基础

支持的渠道

  • Discord、Telegram、Slack
  • WhatsApp、Signal、iMessage
  • Line、GoogleChat 等

使用示例

typescript 复制代码
message({
  text: "✅ 任务完成!共处理了 1000 条记录。",
  channelId: "channel_123"  // 可选,默认当前渠道
})

🤖 自动化工具(Automation)

工具名 功能描述 典型用例 权限级别
cron 创建定时任务 定期报告、自动备份 高(仅所有者)
gateway 网关系统控制 重启服务、更新配置 高(仅所有者)

cron 工具示例

typescript 复制代码
cron({
  schedule: "0 9 * * *",  // 每天早上9点
  command: "node report-generator.js",
  description: "Daily sales report"
})

gateway 工具功能

  • 重启网关
  • 重新加载配置
  • 查看系统状态
  • 清理缓存

📦 其他工具

工具名 功能描述 典型用例 权限级别
nodes 节点和设备管理 列出连接的设备 中等
agents_list 列出所有代理 查看可用代理 基础
image 图像理解(多模态) 分析图片、OCR 基础
tts 文本转语音 生成语音消息 基础

1.3 工具配置文件(Tool Profiles)

OpenClaw 提供了 4 个预定义配置文件,简化工具权限配置:

1. minimal - 最小配置

json5 复制代码
{
  "tools": {
    "profile": "minimal"
  }
}

包含工具

  • session_status(仅会话状态)

适用场景

  • 受限环境
  • 只读监控

2. coding - 编程配置 ⭐

json5 复制代码
{
  "tools": {
    "profile": "coding"
  }
}

包含工具

  • 文件系统:read, write, edit, apply_patch
  • 运行时:exec, process
  • 内存:memory_search, memory_get
  • 会话:sessions_list, sessions_send, sessions_spawn
  • 自动化:cron
  • 媒体:image

适用场景

  • 代码开发
  • 自动化脚本
  • 技术支持

3. messaging - 消息配置

json5 复制代码
{
  "tools": {
    "profile": "messaging"
  }
}

包含工具

  • sessions_list, sessions_send
  • session_status
  • message

适用场景

  • 客服机器人
  • 通知助手
  • 会话管理

4. full - 完整配置

json5 复制代码
{
  "tools": {
    "profile": "full"
  }
}

包含工具:所有工具(无限制)

适用场景

  • 开发环境
  • 超级管理员
  • 完全信任的环境

1.4 工具策略系统

OpenClaw 使用多层策略控制工具访问:

策略应用顺序

markdown 复制代码
1. 配置文件策略         tools.profile
2. 提供商配置文件       tools.byProvider[provider].profile
3. 全局允许列表         tools.allow
4. 全局拒绝列表         tools.deny
5. 代理允许列表         agents[id].tools.allow
6. 代理拒绝列表         agents[id].tools.deny
7. 组策略               (从会话推导)
8. 子代理策略           (深度限制)
9. 网关HTTP拒绝列表     (硬编码: exec, process)

策略配置示例

json5 复制代码
{
  "tools": {
    // 基础配置文件
    "profile": "coding",

    // 全局允许(可使用工具组)
    "allow": [
      "group:openclaw",    // 所有核心工具
      "group:fs",          // 仅文件系统工具
      "my_custom_tool"     // 自定义工具
    ],

    // 全局拒绝(覆盖允许列表)
    "deny": ["exec", "gateway"],

    // 基于提供商的策略
    "byProvider": {
      "anthropic": {
        "profile": "full"  // Claude模型允许所有工具
      },
      "openai": {
        "allow": ["read", "write", "web_search"]
      }
    }
  },

  "agents": {
    "list": [
      {
        "id": "coding-assistant",
        "tools": {
          "profile": "coding",
          "alsoAllow": ["browser", "canvas"]
        }
      },
      {
        "id": "read-only",
        "tools": {
          "allow": ["read", "memory_search"]
        }
      }
    ]
  }
}

工具组(Tool Groups)

typescript 复制代码
const CORE_TOOL_GROUPS = {
  "group:openclaw": [/* 所有主要工具 */],
  "group:fs": ["read", "write", "edit", "apply_patch"],
  "group:runtime": ["exec", "process"],
  "group:web": ["web_search", "web_fetch"],
  "group:memory": ["memory_search", "memory_get"],
  "group:sessions": ["sessions_list", "sessions_send", ...],
  "group:plugins": "<all-plugin-tools>",
};

使用示例

json5 复制代码
{
  "tools": {
    "allow": [
      "group:fs",        // 所有文件操作
      "group:memory",    // 所有内存操作
      "web_search"       // 单个工具
    ]
  }
}

1.5 插件工具扩展

OpenClaw 支持通过插件注册自定义工具:

插件工具注册

typescript 复制代码
// my-plugin/src/tools.ts
import { Type } from "@sinclair/typebox";

export default function registerTools(api) {
  // 必需工具(始终可用)
  api.registerTool({
    name: "analytics_query",
    description: "Query analytics data",
    parameters: Type.Object({
      metric: Type.String(),
      timeRange: Type.Optional(Type.String()),
    }),
    async execute(toolCallId, params) {
      const data = await fetchAnalytics(params.metric);
      return {
        content: [{ type: "text", text: JSON.stringify(data) }]
      };
    },
  });

  // 可选工具(需要白名单)
  api.registerTool(
    {
      name: "advanced_analytics",
      description: "Advanced analytics with ML",
      parameters: Type.Object({ query: Type.String() }),
      async execute(toolCallId, params) {
        // 实现
      },
    },
    { optional: true }  // 标记为可选
  );
}

启用插件工具

json5 复制代码
{
  "agents": {
    "list": [
      {
        "id": "main",
        "tools": {
          "allow": [
            "analytics_query",      // 启用特定工具
            "my-analytics-plugin",  // 或启用插件的所有工具
          ]
        }
      }
    ]
  }
}

第二部分:Skills 注入机制详解

2.1 什么是 Skill?

定义:Skill 是 Markdown 格式的知识文档,教会 Agent 如何组合使用工具完成复杂任务。

核心组成

ini 复制代码
Skill = Prompt(教学内容)+ Tool References(工具引用)

示例

markdown 复制代码
---
name: git
description: Git version control operations
user-invocable: true
---

# Git Operations Skill

Use the `exec` tool for git commands:  ← Tool引用

## Steps
1. Check status: `exec git status`
2. Stage changes: `exec git add .`
3. Commit: `exec git commit -m "message"`
4. Push: `exec git push`

## Best Practices
- Always check status first
- Write clear commit messages

与 Tool 的本质区别

维度 Tool(工具) Skill(技能)
形式 代码(TypeScript函数) 文档(Markdown文本)
作用 提供原子能力 教学如何使用工具
注册 代码注册(启动时) 文件扫描(启动时)
传递 Function calling API 系统提示注入
执行 直接调用 LLM读取并遵循

2.2 Skill 注入的完整时间线

时间线概览

yaml 复制代码
系统启动/配置变更
    ↓
┌─────────────────────────────────────────────────────┐
│ 阶段1:加载阶段(构建时)                            │
│ 时机:启动或配置变更                                 │
└─────────────────────────────────────────────────────┘
    ↓
    1.1 扫描技能文件
        位置:workspace/skills/, ~/.openclaw/skills/, ...
        优先级:workspace > managed > bundled

    1.2 解析 frontmatter
        提取:name, description, user-invocable, ...

    1.3 检查运行时资格
        - OS匹配?(metadata.os)
        - 二进制可用?(metadata.requires.bins)
        - 环境变量存在?(metadata.requires.env)

    1.4 应用配置过滤
        - skills.entries[name].enabled !== false
        - skills.allowBundled 白名单
        - 代理级 skillFilter

    1.5 生成技能快照(SkillSnapshot)
        prompt: 格式化后的技能列表
        skills: 技能元数据
        version: 1

    ↓
┌─────────────────────────────────────────────────────┐
│ 阶段2:系统提示构建(每次会话开始)                  │
│ 时机:会话创建时                                     │
└─────────────────────────────────────────────────────┘
    ↓
    2.1 创建工具列表(独立流程)
        tools = createOpenClawTools()
        filtered = applyToolPolicyPipeline(tools)
        toolNames = filtered.map(t => t.name)

    2.2 格式化技能块
        过滤:disable-model-invocation !== true
        限制:maxSkillsInPrompt=150, maxChars=30000

        生成:
        <available_skills>
        - python: Execute Python code
          Location: ~/.openclaw/skills/python/SKILL.md
        - git: Git version control
          Location: ~/.openclaw/skills/git/SKILL.md
        </available_skills>

    2.3 注入系统提示
        buildAgentSystemPrompt({
          toolNames,      // ← 已过滤的工具列表
          skillsPrompt,   // ← 格式化的技能块
        })

        生成:
        ## Tooling
        - read: Read file
        - write: Write file
        - exec: Execute command
        ...

        ## Skills (mandatory)
        Before replying: scan <available_skills>...
        <available_skills>
        - python: ...
        - git: ...
        </available_skills>

    ↓
┌─────────────────────────────────────────────────────┐
│ 阶段3:LLM 处理(运行时)                            │
│ 时机:每次用户消息                                   │
└─────────────────────────────────────────────────────┘
    ↓
    3.1 LLM接收系统提示
        包含:工具列表 + 技能列表

    3.2 LLM分析用户请求
        判断:是否需要使用技能?

    3.3 LLM选择技能(可选)
        扫描:<available_skills>
        选择:最相关的技能

    3.4 LLM读取技能文档
        Tool call: read("~/.openclaw/skills/python/SKILL.md")
        返回:完整的Markdown说明

    3.5 LLM遵循技能执行
        按照技能步骤调用工具

2.3 阶段1:技能加载详解

加载位置和优先级

typescript 复制代码
// 技能搜索路径(优先级从高到低)
const skillDirs = [
  "workspace/skills/",           // 1. 工作空间(最高优先级)
  ".agents/skills/",             // 2. 项目级代理目录
  "~/.agents/skills/",           // 3. 个人代理目录
  "~/.openclaw/skills/",         // 4. 本地管理目录
  "node_modules/.../skills/",    // 5. 捆绑技能(最低优先级)
  // + extraDirs from config
];

位置src/agents/skills/workspace.ts:221-406

typescript 复制代码
export function loadSkillEntries(params: {
  workspaceDir?: string;
  extraDirs?: string[];
}): SkillEntry[] {
  const dirs: string[] = [];

  // 1. 额外目录(最低优先级)
  if (params.extraDirs) {
    dirs.push(...params.extraDirs);
  }

  // 2. 捆绑技能
  const bundledDir = resolveBundledSkillsDir();
  if (bundledDir) {
    dirs.push(bundledDir);
  }

  // 3. 本地管理
  dirs.push(path.join(os.homedir(), ".openclaw", "skills"));

  // 4. 个人代理
  dirs.push(path.join(os.homedir(), ".agents", "skills"));

  // 5. 项目代理
  if (params.workspaceDir) {
    dirs.push(path.join(params.workspaceDir, ".agents", "skills"));
  }

  // 6. 工作空间(最高优先级)
  if (params.workspaceDir) {
    dirs.push(path.join(params.workspaceDir, "skills"));
  }

  // 从所有目录加载技能
  const allEntries: SkillEntry[] = [];
  for (const dir of dirs) {
    const entries = loadSkillEntriesFromDir(dir);
    allEntries.push(...entries);
  }

  // 按名称合并(后加载的覆盖先加载的)
  const merged = mergeSkillsByName(allEntries);
  return merged;
}

合并策略

  • 同名技能:后加载的覆盖先加载的
  • 允许工作空间覆盖捆绑技能
  • 实现自定义和扩展

运行时资格检查

位置src/agents/skills/config.ts:50-120

typescript 复制代码
export function shouldIncludeSkill(params: {
  entry: SkillEntry;
  config?: OpenClawConfig;
  eligibility?: SkillEligibilityContext;
}): boolean {
  const { entry, config, eligibility } = params;

  // 1. 检查配置启用
  const skillConfig = resolveSkillConfig(config, entry.skill.name);
  if (skillConfig?.enabled === false) {
    return false;  // 配置明确禁用
  }

  // 2. 检查捆绑技能白名单
  if (entry.skill.source === "openclaw-bundled") {
    const allowBundled = config?.skills?.allowBundled ?? [];
    if (!allowBundled.includes(entry.skill.name)) {
      return false;  // 不在白名单中
    }
  }

  // 3. 运行时资格评估
  return evaluateRuntimeEligibility({
    // 操作系统检查
    os: entry.metadata?.os,
    currentPlatform: process.platform,

    // always标志(强制加载)
    always: entry.metadata?.always,

    // 二进制文件检查
    requires: entry.metadata?.requires,
    hasBin: (bin) => hasBinary(bin),

    // 环境变量检查
    hasEnv: (envName) =>
      !!process.env[envName] ||
      !!skillConfig?.env?.[envName],

    // 配置路径检查
    isConfigPathTruthy: (path) =>
      isConfigPathTruthy(config, path),
  });
}

资格检查示例

yaml 复制代码
# Python技能(需要python3二进制)
---
metadata:
  openclaw:
    requires:
      bins: ["python3"]
---

检查逻辑:

typescript 复制代码
if (!hasBinary("python3")) {
  return false;  // Python技能不加载
}
yaml 复制代码
# OpenAI技能(需要API密钥)
---
metadata:
  openclaw:
    primaryEnv: "OPENAI_API_KEY"
    requires:
      env: ["OPENAI_API_KEY"]
---

检查逻辑:

typescript 复制代码
if (!process.env.OPENAI_API_KEY && !skillConfig?.apiKey) {
  return false;  // OpenAI技能不加载
}

2.4 阶段2:系统提示注入详解

技能块格式化

位置src/agents/skills/format.ts

typescript 复制代码
function formatSkillsForPrompt(entries: SkillEntry[]): string {
  // 1. 过滤:排除 disable-model-invocation
  const forPrompt = entries.filter(
    e => e.invocation?.disableModelInvocation !== true
  );

  // 2. 应用大小限制
  const limited = applySkillsPromptLimits(forPrompt, {
    maxSkillsInPrompt: 150,         // 最多150个技能
    maxSkillsPromptChars: 30000,    // 最多30000字符
  });

  // 3. 格式化为列表
  const lines = limited.map(entry => {
    const name = entry.skill.name;
    const desc = entry.skill.description || "(no description)";
    const location = entry.skill.filePath;
    return `- ${name}: ${desc}\n  Location: ${location}`;
  });

  return `
<available_skills>
${lines.join("\n")}
</available_skills>
`;
}

生成示例

javascript 复制代码
<available_skills>
- python: Execute Python code
  Location: ~/.openclaw/skills/python/SKILL.md
- git: Git version control operations
  Location: ~/.openclaw/skills/git/SKILL.md
- node: Node.js development
  Location: ~/.openclaw/skills/node/SKILL.md
</available_skills>

系统提示构建

位置src/agents/system-prompt.ts:396-473

typescript 复制代码
export function buildAgentSystemPrompt(params: {
  toolNames: string[];      // 已过滤的工具名列表
  skillsPrompt?: string;    // 格式化的技能块
  workspaceDir?: string;
  // ... 其他参数
}): string {
  const sections = [
    buildModelSection(),
    buildRoleSection(),
    buildToolingSection(params.toolNames),  // ← Tools部分
    buildSafetySection(),
    buildSkillsSection(params.skillsPrompt), // ← Skills部分
    buildMemorySection(),
    buildWorkspaceSection(params.workspaceDir),
    buildOutputSection(),
  ];

  return sections.flat().join("\n");
}

技能部分构建

typescript 复制代码
function buildSkillsSection(params: {
  skillsPrompt?: string;
  readToolName: string;
}): string[] {
  const trimmed = params.skillsPrompt?.trim();
  if (!trimmed) {
    return [];  // 没有技能,跳过
  }

  return [
    "## Skills (mandatory)",
    "Before replying: scan <available_skills> <description> entries.",
    "- If exactly one skill clearly applies: read its SKILL.md at <location> with `read`, then follow it.",
    "- If multiple could apply: choose the most specific one, then read/follow it.",
    "- If none clearly apply: do not read any SKILL.md.",
    "Constraints: never read more than one skill up front; only read after selecting.",
    trimmed,  // 注入技能块
    "",
  ];
}

最终系统提示

markdown 复制代码
## Tooling

Available tools:
- read: Read file contents
- write: Create or overwrite file
- exec: Execute shell command
- memory_search: Search memory files
...

## Skills (mandatory)

Before replying: scan <available_skills> <description> entries.
- If exactly one skill clearly applies: read its SKILL.md at <location> with `read`, then follow it.
- If multiple could apply: choose the most specific one, then read/follow it.
- If none clearly apply: do not read any SKILL.md.
Constraints: never read more than one skill up front; only read after selecting.

<available_skills>
- python: Execute Python code
  Location: ~/.openclaw/skills/python/SKILL.md
- git: Git version control operations
  Location: ~/.openclaw/skills/git/SKILL.md
</available_skills>

## Memory

Search MEMORY.md before answering questions about history...

关键点

  • Skills部分在Tools部分之后
  • 包含明确的选择指令("scan", "read", "follow")
  • 使用 "mandatory" 强调重要性
  • 限制只读取一个技能(避免过度加载)

2.5 阶段3:运行时执行详解

LLM 自主选择流程

perl 复制代码
用户消息:"用Python写一个hello world程序"
    ↓
LLM接收系统提示:
    ## Tooling
    - read, write, exec, ...

    ## Skills
    - python: Execute Python code
    - git: Git operations
    ↓
LLM推理:
    1. 用户要求:写Python程序
    2. 扫描技能列表
    3. 判断:"python" skill clearly applies
    4. 决定:read python/SKILL.md
    ↓
LLM调用工具:
    Tool: read
    Args: { path: "~/.openclaw/skills/python/SKILL.md" }
    ↓
返回技能文档:
    # Python Skill

    Use the `exec` tool to run Python code:

    ## Steps
    1. Write Python code using `write` tool
    2. Execute with `exec python3 script.py`
    3. Handle errors and output

    ## Example
    User: "write hello world"
    → write("hello.py", "print('hello world')")
    → exec("python3 hello.py")
    ↓
LLM遵循技能步骤:
    1. Tool: write
       Args: { path: "hello.py", content: "print('hello world')" }

    2. Tool: exec
       Args: { command: "python3 hello.py" }
    ↓
返回结果:
    "✅ 已创建并执行 hello.py,输出:hello world"

关键点

  • LLM 自主判断是否需要技能
  • LLM 自主选择使用哪个技能
  • 技能文档通过 read 工具动态获取
  • 技能只是指导,最终执行仍依赖工具

用户显式调用(Slash Commands)

scss 复制代码
用户消息:"/python print('hello')"
    ↓
解析命令:
    commandName = "python"
    args = "print('hello')"
    ↓
加载技能命令列表:
    listSkillCommandsForWorkspace()
    ↓
    返回:[      {        name: "python",        skillName: "python",        dispatch: { kind: "tool", toolName: "exec" }      },      ...    ]
    ↓
匹配命令:
    resolveSkillCommandInvocation("python", args)
    ↓
检查 dispatch 配置:
    dispatch = { kind: "tool", toolName: "exec" }
    ↓
    有 tool-dispatch → 直接调用工具
    ↓
查找工具:
    tools = createOpenClawTools()  // 应用Tool策略
    tool = tools.find(t => t.name === "exec")
    ↓
执行工具(绕过LLM):
    result = tool.execute({
      command: "print('hello')",
      commandName: "python",
      skillName: "python"
    })
    ↓
返回结果:
    "hello"

关键点

  • 用户通过 /skillname 显式调用
  • 支持 tool-dispatch(直接调用工具)
  • 不经过LLM推理(确定性执行)
  • 仍受Tool策略约束

第三部分:Tools 与 Skills 的协作关系

3.1 依赖关系图

markdown 复制代码
┌─────────────────────────────────────────────────────┐
│ Tools(工具层)                                      │
│ - 提供原子能力                                       │
│ - 代码实现                                           │
│ - 启动时注册                                         │
│ - 独立于Skills                                       │
└───────────────┬─────────────────────────────────────┘
                │
                │ 被引用
                │
                ↓
┌─────────────────────────────────────────────────────┐
│ Skills(技能层)                                     │
│ - 教学如何使用工具                                   │
│ - Markdown文档                                       │
│ - 启动时加载                                         │
│ - 引用工具名称(字符串)                             │
└───────────────┬─────────────────────────────────────┘
                │
                │ 指导
                │
                ↓
┌─────────────────────────────────────────────────────┐
│ LLM(执行层)                                        │
│ - 读取技能文档                                       │
│ - 理解步骤                                           │
│ - 调用工具执行                                       │
│ - 组合工具完成任务                                   │
└─────────────────────────────────────────────────────┘

3.2 执行时序图

bash 复制代码
启动阶段(一次性):
    ├─ 注册所有Tools
    │   ├─ 内置工具
    │   └─ 插件工具
    │
    ├─ 加载所有Skills
    │   ├─ 扫描文件
    │   └─ 检查资格
    │
    └─ 生成快照
        ├─ 工具列表
        └─ 技能列表

会话开始:
    ├─ 应用Tool策略
    │   └─ 过滤工具列表
    │
    ├─ 构建系统提示
    │   ├─ ## Tooling: [过滤后的工具]
    │   └─ ## Skills: [所有技能]
    │
    └─ 发送给LLM

用户消息:
    ├─ LLM分析请求
    │
    ├─ 决定:需要技能?
    │   ├─ 需要 → 选择技能
    │   │   ├─ read技能文档
    │   │   ├─ 理解步骤
    │   │   └─ 调用工具执行
    │   │
    │   └─ 不需要 → 直接调用工具
    │
    └─ 返回结果

3.3 关键约束

1. Tool 必须提前注册

复制代码
❌ 错误理解:读取Skill时会加载Skill中提到的工具
✅ 正确理解:Skill只能引用已注册的工具

证明

typescript 复制代码
// read工具的执行函数
execute: async (toolCallId, params) => {
  const content = fs.readFileSync(params.path, "utf-8");
  return { content: [{ type: "text", text: content }] };

  // ❌ 没有工具注册逻辑
  // ❌ 没有解析技能中的工具引用
  // ✅ 只是返回文本给LLM
}

2. Tool策略不影响Skill可见性

perl 复制代码
配置:
  tools.deny = ["exec"]      // 拒绝exec工具
  skills.allow = ["python"]  // 但允许Python技能

结果:
  系统提示包含:
    ## Tooling
    - read ✅
    - write ✅
    - exec ❌(不在列表中)

    ## Skills
    - python ✅(技能仍然可见)

执行:
  LLM读取python技能 → 尝试调用exec → 失败 ❌

原因

  • Tool过滤和Skill过滤是独立的
  • Skill可见性不受Tool策略影响
  • 但Skill执行依赖Tool可用性

3. 配置一致性很重要

反例(会失败)

json5 复制代码
{
  "tools": { "deny": ["exec"] },
  "skills": { "allowBundled": ["python", "git", "node"] }
}

所有这些技能都依赖 exec,会执行失败。

正例(一致)

json5 复制代码
{
  "tools": {
    "allow": ["read", "write", "exec", "memory_search"]
  },
  "skills": {
    "allowBundled": ["python", "git", "node"]
  }
}

或者使用元数据声明依赖:

yaml 复制代码
---
metadata:
  openclaw:
    requires:
      config: ["tools.allow"]  # 要求工具策略允许必要工具
---

3.4 最佳实践

1. 使用配置文件简化管理

json5 复制代码
{
  "tools": {
    "profile": "coding"  // 一次性允许所有编程工具
  },
  "skills": {
    "allowBundled": ["python", "git", "node"]
  }
}

2. 为Skill声明工具依赖

yaml 复制代码
---
metadata:
  openclaw:
    requires:
      bins: ["python3"]           # 二进制要求
      config: ["tools.allow"]     # 配置要求
---

这样技能会在依赖不满足时自动不加载。


3. 使用 tool-dispatch 确保一致性

yaml 复制代码
---
command-dispatch: tool
command-tool: exec
---

用户输入 /python code 时:

  • 直接调用 exec 工具
  • 绕过LLM推理
  • 确保行为一致

4. 监控技能执行失败

如果用户报告"技能不工作",检查清单:

scss 复制代码
□ 技能是否在系统提示中?
  → 查看 buildWorkspaceSkillSnapshot() 输出

□ 技能依赖的工具是否可用?
  → 查看 createOpenClawTools() 输出
  → 查看 applyToolPolicyPipeline() 结果

□ Tool策略是否拒绝了必要工具?
  → 检查 config.tools.deny
  → 检查 config.agents[id].tools.deny

□ 运行时资格是否满足?
  → 检查 OS、二进制、环境变量

第四部分:实战场景

场景1:代码审查助手

目标:自动审查代码变更

配置

json5 复制代码
{
  "tools": {
    "profile": "coding",
    "alsoAllow": ["web_search"]  // 查找最佳实践
  },
  "skills": {
    "allowBundled": ["git"]
  }
}

工作流

scss 复制代码
用户:"审查最近的commit"
  ↓
LLM选择技能:git
  ↓
LLM读取git技能
  ↓
LLM执行步骤:
  1. exec("git diff HEAD~1")  ← 查看变更
  2. read(...modified files...)  ← 读取完整文件
  3. web_search("best practices for ...")  ← 查找规范
  4. 生成审查报告
  ↓
返回:
  "审查报告:
   - ✅ 代码质量良好
   - ⚠️ 建议添加错误处理
   - 💡 参考资料:[链接]"

场景2:自动化报告生成

目标:每天生成销售报告

配置

json5 复制代码
{
  "tools": {
    "profile": "coding",
    "alsoAllow": ["cron", "message"]
  },
  "skills": {
    "entries": {
      "daily-report": {
        "enabled": true
      }
    }
  }
}

自定义技能

yaml 复制代码
# workspace/skills/daily-report/SKILL.md
---
name: daily-report
user-invocable: true
command-dispatch: tool
command-tool: cron
---

# Daily Report Skill

Generate and send daily sales reports.

## Steps
1. Query database: `exec psql -c "SELECT ..."`
2. Generate chart: `canvas <chart html>`
3. Send to Slack: `message "Daily Report: ..."`

触发

bash 复制代码
方式1:手动触发
  /daily-report

方式2:定时任务
  cron("0 9 * * *", "/daily-report")

场景3:多语言开发助手

目标:支持Python、Node.js、Go开发

配置

json5 复制代码
{
  "tools": {
    "profile": "coding"
  },
  "skills": {
    "allowBundled": ["python", "node", "go"],
    "entries": {
      "python": {
        "env": { "PYTHON_BIN": "/usr/local/bin/python3.11" }
      },
      "node": {
        "env": { "NODE_BIN": "/usr/local/bin/node" }
      }
    }
  }
}

智能选择

scss 复制代码
用户:"写一个REST API"
  ↓
LLM扫描技能:
  - python(Django/Flask)
  - node(Express)
  - go(Gin)
  ↓
LLM询问:
  "您想使用哪种语言?Python (Flask)、Node.js (Express) 还是 Go (Gin)?"
  ↓
用户:"Node.js"
  ↓
LLM选择:node技能
  ↓
LLM执行:
  1. write("package.json", ...)
  2. write("server.js", ...)
  3. exec("npm install")
  4. exec("node server.js")

总结

核心要点

  1. 工具是能力,技能是智慧

    • 工具:28个核心工具 + 插件扩展
    • 技能:Markdown文档,教学如何使用工具
  2. 注入发生在启动和会话开始

    • 启动时:加载技能和工具
    • 会话时:构建系统提示
    • 运行时:LLM读取和执行
  3. 工具和技能独立但关联

    • 独立注册和加载
    • 技能引用工具名称
    • 技能执行依赖工具可用性
  4. 多层策略确保安全

    • 工具策略:控制工具可用性
    • 技能策略:控制技能可见性
    • 两者配合保证系统安全

架构优势

  1. 灵活性:工具和技能分离,易于扩展
  2. 安全性:多层策略控制,细粒度权限
  3. 可组合性:小工具组合成复杂功能
  4. 可维护性:技能是文档,易于更新

设计哲学

"Give the agent tools to do things, and skills to know how to do them well."

"给代理工具去做事,给技能教它如何做好。"

相关推荐
树獭叔叔3 小时前
OpenClaw Memory 系统深度解析:从文件到向量的完整实现
后端·aigc·openai
程序猿阿越3 小时前
Kafka4源码(二)创建Topic
java·后端·源码阅读
悟空码字3 小时前
Spring Boot 整合 MongoDB 最佳实践:CRUD、分页、事务、索引全覆盖
java·spring boot·后端
开心就好20253 小时前
iOS App 安全加固流程记录,代码、资源与安装包保护
后端·ios
省长3 小时前
Sa-Token v1.45.0 发布 🚀,正式支持 Spring Boot 4、新增 Jackson3/Snack4 插件适配
java·后端·开源
开心就好20253 小时前
iOS App 性能测试工具怎么选?使用克魔助手(Keymob)结合 Instruments 完成
后端·ios
神奇小汤圆4 小时前
牛客网Java面试题总结(金三银四最新版)
后端
Cache技术分享4 小时前
346. Java IO API - 操作文件和目录
前端·后端
sTone873754 小时前
web后端开发概念: VO 和 PO
java·后端·架构