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."

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

相关推荐
桦说编程7 小时前
AI 真的让写代码变快了吗?
后端
AskHarries8 小时前
openclaw升级和参数调整
后端·ai编程
creaDelight9 小时前
基于 Django 5.x 的全功能博客系统 DjangoBlog 深度解析
后端·python·django
树獭叔叔9 小时前
FFN 激活函数深度解析:从 ReLU 到 SwiGLU 的演进之路
算法·aigc·openai
Rust语言中文社区9 小时前
【Rust日报】 Danube Messaging - 云原生消息平台
开发语言·后端·rust
菜鸟程序员专写BUG10 小时前
SpringBoot 接口返回异常全集|JSON解析失败/响应乱码/状态码错误完美解决
spring boot·后端·json
希望永不加班10 小时前
SpringBoot 编写第一个 REST 接口(Get/Post/Put/Delete)
java·spring boot·后端·spring
vx-程序开发11 小时前
springboot智慧农业信息服务平台-计算机毕业设计源码65287
spring boot·后端·课程设计
小雷君11 小时前
SpringBoot 接口开发5个高频踩坑总结
java·spring boot·后端·面试
陈随易11 小时前
农村程序员聊五险一金
前端·后端·程序员