Skip to content

OpenClaw 源码解读(十九)技能系统与 ClawHub ​

本篇深入分析 OpenClaw 的技能(Skill)系统——一种基于 Markdown 的声明式 Agent 能力扩展机制。从 SKILL.md 文件格式到六源加载引擎,从 Frontmatter 解析到安全扫描器,从 Prompt 构建到 ClawHub 生态。技能系统让 Agent 从通用 AI 助手进化为领域专家,而无需编写一行代码。


目录 ​


一、技能系统全景 ​

1.1 技能是什么? ​

技能(Skill)是 OpenClaw 的声明式能力扩展机制。与插件(Plugin)不同,技能不需要编写任何代码——它只是一个 Markdown 文件(SKILL.md),告诉 Agent "你现在拥有这个领域的知识和能力"。

Plugin(插件)= TypeScript 代码 → 运行时注册新功能
Skill(技能)= Markdown 文档 → 注入到 Agent 的 System Prompt 中

核心理念: Agent(LLM)已经非常聪明了,大多数任务它只需要正确的指导(而非新代码)就能完成。技能就是提供这种指导的标准化载体。

1.2 三层渐进式加载 ​

技能使用渐进式上下文披露设计,最大化利用有限的上下文窗口:

Layer 1: 元数据(name + description)
├── 始终存在于 Agent 上下文中
├── ~100 tokens
└── Agent 用它决定"是否需要触发这个技能"

Layer 2: SKILL.md 正文
├── 技能被触发后加载
├── <5000 words
└── 包含具体的操作指令和工作流

Layer 3: 捆绑资源(scripts/ + references/ + assets/)
├── Agent 按需加载
├── 无大小限制(脚本可直接执行而不读入上下文)
└── 只加载当前任务需要的部分

![技能系统全景:Plugin vs Skill 与三层渐进式加载](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(十九)技能系统与 ClawHub/01-infographic-skill-system-overview-1775150757559.png)

1.3 核心文件清单 ​

文件职责
src/agents/skills/types.ts核心类型定义(SkillEntry, SkillSnapshot, SkillMetadata 等)
src/agents/skills/workspace.ts主入口 — 六源加载 + 合并 + Prompt 构建 + 沙箱同步
src/agents/skills/config.ts资格判定 + 配置解析 + 白名单过滤
src/agents/skills/frontmatter.tsYAML Frontmatter 解析 + 元数据提取
src/security/skill-scanner.ts安全扫描(危险代码检测)
skills/*/SKILL.md52 个内置技能定义
skills/clawhub/SKILL.mdClawHub CLI 技能
skills/skill-creator/SKILL.md技能创建指南(元技能)

二、SKILL.md 文件格式 ​

2.1 基本结构 ​

每个技能都是一个目录,包含一个必需的 SKILL.md 文件和可选的捆绑资源:

weather/
├── SKILL.md           ← 必需:技能定义
├── scripts/           ← 可选:可执行脚本
├── references/        ← 可选:参考文档
└── assets/            ← 可选:输出资源(模板、图标等)

2.2 SKILL.md 格式 ​

markdown
---
name: weather
description: "Get current weather and forecasts via wttr.in. Use when: 
  user asks about weather, temperature, or forecasts for any location."
homepage: https://wttr.in/:help
metadata: { "openclaw": { "emoji": "🌤️", "requires": { "bins": ["curl"] } } }
---

# Weather Skill

Get current weather conditions and forecasts.

## When to Use

✅ **USE this skill when:**
- "What's the weather?"
- "Will it rain today/tomorrow?"

## Commands

### Current Weather
​```bash
curl "wttr.in/London?format=3"
​```

2.3 Frontmatter 字段说明 ​

字段必需说明
name✅技能唯一标识(用于去重、过滤、命令名)
description✅技能描述(LLM 用它决定何时触发,非常重要)
homepage❌技能主页 URL
metadata❌OpenClaw 扩展元数据(环境要求、安装方式等)
command-dispatch❌斜杠命令分发方式("tool" = 直接调用工具)
command-tool❌配合 command-dispatch: tool 指定目标工具名

2.4 元数据结构 (metadata.openclaw) ​

jsonc
{
  "openclaw": {
    "emoji": "🌤️",                   // UI 显示的表情符号
    "primaryEnv": "WEATHER_API_KEY",  // 主要环境变量(用于资格判定)
    "requires": {
      "env": ["WEATHER_API_KEY"],     // 必需的环境变量
      "bins": ["curl"],               // 必需的二进制命令
      "platforms": ["darwin", "linux"] // 支持的平台
    },
    "install": [                      // 安装指南
      {
        "id": "brew",
        "kind": "brew",
        "package": "curl",
        "label": "Install via Homebrew"
      }
    ]
  }
}

2.5 正文设计原则 ​

技能正文遵循**"少即是多"**原则:

默认假设:Agent 已经非常聪明了。 只添加 Agent 不知道的信息。每一段文字都要问自己:"Agent 真的需要这个解释吗?" "这段话值得它占用的 token 吗?"

最佳实践:

  • 正文控制在 500 行以内
  • 用简洁的代码示例替代冗长的文字说明
  • 复杂内容拆分到 references/ 目录
  • 包含明确的 "When to Use" 和 "When NOT to Use" 章节

![SKILL.md 文件格式:目录结构与 Frontmatter 规范](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(十九)技能系统与 ClawHub/02-infographic-skillmd-format-1775150758383.png)


三、Frontmatter 解析引擎 ​

3.1 解析流程 (src/agents/skills/frontmatter.ts) ​

typescript
export function parseFrontmatter(raw: string): ParsedSkillFrontmatter {
  // 1. 提取 YAML frontmatter(--- 包裹的部分)
  const match = raw.match(/^---\n([\s\S]*?)\n---/);
  if (!match) return {};

  // 2. 解析 YAML
  const yamlText = match[1];
  const parsed = parseYaml(yamlText);  // 使用 yaml 库

  // 3. 安全验证和类型转换
  return {
    name: readString(parsed, "name"),
    description: readString(parsed, "description"),
    homepage: readString(parsed, "homepage"),
    metadata: readObject(parsed, "metadata"),
    // ... 斜杠命令相关字段
  };
}

3.2 OpenClaw 元数据提取 ​

typescript
export function resolveOpenClawMetadata(
  frontmatter: ParsedSkillFrontmatter,
): OpenClawSkillMetadata | undefined {
  const raw = frontmatter?.metadata;
  if (!raw || typeof raw !== "object") return undefined;

  // metadata.openclaw 是 OpenClaw 特有的命名空间
  const openclawMeta = (raw as Record<string, unknown>)["openclaw"];
  if (!openclawMeta || typeof openclawMeta !== "object") return undefined;

  const oc = openclawMeta as Record<string, unknown>;
  return {
    emoji: readString(oc, "emoji"),
    primaryEnv: readString(oc, "primaryEnv"),
    requires: resolveRequires(oc["requires"]),     // { env, bins, platforms }
    install: resolveInstallSpecs(oc["install"]),   // 安装方式数组
  };
}

3.3 调用策略解析 ​

typescript
export function resolveSkillInvocationPolicy(
  frontmatter: ParsedSkillFrontmatter,
): SkillInvocationPolicy {
  return {
    // disableModelInvocation: true → 技能不出现在 Agent 的 prompt 中
    // (仅通过斜杠命令或 API 触发)
    disableModelInvocation: readBool(frontmatter, "disable-model-invocation"),

    // userInvocable: false → 不生成斜杠命令
    userInvocable: readBool(frontmatter, "user-invocable") ?? true,
  };
}

![Frontmatter 解析引擎:从原始文本到结构化数据](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(十九)技能系统与 ClawHub/03-infographic-frontmatter-parser-1775150759172.png)


四、六源加载与优先级合并 ​

4.1 六个技能来源 ​

这是技能系统最核心的设计之一——技能可以来自 6 个不同的来源,按优先级合并:

来源 1: extra(最低优先级)
├── 配置中的 skills.load.extraDirs
└── 插件提供的 skill 目录

来源 2: bundled(内置)
├── OpenClaw 安装目录下的 skills/
└── 52 个内置技能

来源 3: managed(托管)
├── ~/.openclaw/skills/
└── 通过 ClawHub 安装的技能

来源 4: agents-skills-personal(个人全局)
├── ~/.agents/skills/
└── 跨项目的个人技能

来源 5: agents-skills-project(项目级)
├── <工作区>/.agents/skills/
└── 项目特有的技能

来源 6: workspace(最高优先级)
├── <工作区>/skills/
└── 用户手动放置的技能

4.2 合并规则 ​

typescript
const merged = new Map<string, Skill>();

// 低优先级先写入,高优先级覆盖
for (const skill of extraSkills)           merged.set(skill.name, skill);  // 最低
for (const skill of bundledSkills)         merged.set(skill.name, skill);
for (const skill of managedSkills)         merged.set(skill.name, skill);
for (const skill of personalAgentsSkills)  merged.set(skill.name, skill);
for (const skill of projectAgentsSkills)   merged.set(skill.name, skill);
for (const skill of workspaceSkills)       merged.set(skill.name, skill);  // 最高

为什么这样设计? 用户最近放置的技能应该覆盖内置技能。例如,用户可以在 <工作区>/skills/weather/SKILL.md 中放置一个自定义的天气技能,覆盖内置的 weather 技能。

4.3 加载安全限制 ​

每个来源的加载都有严格的资源限制:

typescript
const DEFAULT_LIMITS = {
  maxSkillsLoadedPerSource: 200,     // 每个来源最多 200 个技能
  maxCandidatesPerRoot: 500,         // 每个根目录最多扫描 500 个子目录
  maxSkillFileBytes: 256 * 1024,     // 单个 SKILL.md 最大 256KB
  maxSkillsInPrompt: 80,             // Prompt 中最多包含 80 个技能
  maxSkillsPromptChars: 200_000,     // Prompt 中技能总字符数上限 200K
};

为什么需要限制?

  • 防止恶意技能目录(包含数万个子目录)导致加载超时
  • 防止超大 SKILL.md 文件(如 1GB 的二进制数据伪装成 Markdown)导致 OOM
  • 防止 Prompt 注入超过 LLM 上下文窗口

4.4 异常目录检测 ​

typescript
const childDirs = listChildDirectories(baseDir);
const suspicious = childDirs.length > limits.maxCandidatesPerRoot;

if (suspicious) {
  skillsLogger.warn("Skills root looks suspiciously large, truncating discovery.", {
    dir: params.dir,
    childDirCount: childDirs.length,
  });
}

如果一个技能根目录下有超过 500 个子目录,会被标记为可疑并截断扫描。

![六源加载与优先级合并:从低到高的技能来源层级](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(十九)技能系统与 ClawHub/04-infographic-six-source-loading-1775150759950.png)


五、技能资格判定与过滤 ​

5.1 过滤流程 (src/agents/skills/config.ts) ​

加载完所有技能后,需要根据当前环境决定哪些技能有资格被包含到 Agent 的 Prompt 中:

加载的全部技能
    │
    ├─ Filter 1: skillFilter(配置指定的技能白名单)
    │   └─ 如果配置了 skills.filter: ["weather", "github"],只保留这两个
    │
    ├─ Filter 2: bundled allowlist(内置技能白名单)
    │   └─ 如果配置了 skills.bundled.allowlist: ["weather"]
    │       则只保留内置技能中的 weather
    │
    ├─ Filter 3: 平台检查
    │   └─ requires.platforms 不包含当前 OS?→ 排除
    │
    ├─ Filter 4: 环境变量检查
    │   └─ requires.env 中有未设置的变量?→ 排除
    │
    ├─ Filter 5: 二进制命令检查
    │   └─ requires.bins 中有未安装的命令?→ 排除
    │
    └─ 输出:eligible 技能列表

5.2 资格上下文 ​

typescript
export type SkillEligibilityContext = {
  platform?: NodeJS.Platform;          // "darwin" | "linux" | "win32"
  envKeys?: Set<string>;               // 当前已设置的环境变量名
  availableBins?: Set<string>;         // 已安装的二进制命令名
  remote?: {                            // 远程执行上下文
    note?: string;                      // 远程注入的说明
    envKeys?: Set<string>;
  };
};

5.3 shouldIncludeSkill 决策函数 ​

typescript
export function shouldIncludeSkill(
  entry: SkillEntry,
  config?: OpenClawConfig,
  skillFilter?: string[],
  eligibility?: SkillEligibilityContext,
): boolean {
  // 1. 名称过滤
  if (skillFilter && !skillFilter.includes(entry.skill.name)) {
    return false;
  }

  // 2. 内置技能白名单
  if (entry.skill.source === "openclaw-bundled") {
    if (!isBundledSkillAllowed(entry.skill.name, config)) {
      return false;
    }
  }

  // 3. 平台要求
  const platforms = entry.metadata?.requires?.platforms;
  if (platforms?.length && eligibility?.platform) {
    if (!platforms.includes(eligibility.platform)) {
      return false;
    }
  }

  // 4. 环境变量要求
  const envReqs = entry.metadata?.requires?.env;
  if (envReqs?.length && eligibility?.envKeys) {
    const allMet = envReqs.every(e => eligibility.envKeys!.has(e));
    if (!allMet) return false;
  }

  // 5. 二进制命令要求
  const binReqs = entry.metadata?.requires?.bins;
  if (binReqs?.length && eligibility?.availableBins) {
    const allMet = binReqs.every(b => eligibility.availableBins!.has(b));
    if (!allMet) return false;
  }

  return true;
}

![技能资格判定:五层过滤漏斗](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(十九)技能系统与 ClawHub/05-infographic-eligibility-filter-1775150760821.png)


六、Prompt 构建与上下文预算 ​

6.1 构建流程 ​

eligible 技能列表
    │
    ├─ 排除 disableModelInvocation=true 的技能
    │   (这些技能只通过斜杠命令触发,不占用 Prompt 空间)
    │
    ├─ 应用数量上限(maxSkillsInPrompt = 80)
    │
    ├─ 应用字符预算(maxSkillsPromptChars = 200,000)
    │   └─ 二分搜索找到不超预算的最大前缀
    │
    ├─ 路径压缩(compactSkillPaths)
    │   └─ 将绝对路径压缩为相对路径,节省 token
    │
    └─ 格式化输出

6.2 二分搜索字符预算 ​

当技能总量超过字符预算时,使用二分搜索找到能装入预算的最大技能数:

typescript
function applySkillsPromptLimits(params) {
  const { skills, config } = params;
  const limits = resolveSkillsLimits(config);

  // 先按数量截断
  let skillsForPrompt = skills.slice(0, limits.maxSkillsInPrompt);

  const fits = (skills: Skill[]): boolean => {
    const block = formatSkillsForPrompt(skills);
    return block.length <= limits.maxSkillsPromptChars;
  };

  if (!fits(skillsForPrompt)) {
    // 二分搜索:找到最大的 N 使得前 N 个技能的 Prompt 长度 ≤ 预算
    let lo = 0;
    let hi = skillsForPrompt.length;
    while (lo < hi) {
      const mid = Math.ceil((lo + hi) / 2);
      if (fits(skillsForPrompt.slice(0, mid))) {
        lo = mid;
      } else {
        hi = mid - 1;
      }
    }
    skillsForPrompt = skillsForPrompt.slice(0, lo);
  }

  return { skillsForPrompt, truncated: ... };
}

为什么用二分搜索而非逐个添加? 因为 formatSkillsForPrompt() 需要序列化整个列表(开销较大),二分搜索只需要 O(log N) 次调用。

6.3 SkillSnapshot 快照 ​

Prompt 构建完成后生成一个快照,供运行时使用:

typescript
type SkillSnapshot = {
  prompt: string;                    // 最终注入 Agent Prompt 的文本
  skills: Array<{
    name: string;                    // 技能名
    primaryEnv?: string;             // 主要环境变量(UI 显示用)
    requiredEnv?: string[];          // 所需环境变量(UI 显示用)
  }>;
  skillFilter?: string[];            // 应用的过滤器
  resolvedSkills: Skill[];           // 完整的技能对象(含文件路径等)
  version?: number;                  // 快照版本号
};

![Prompt 构建与上下文预算:二分搜索最大装入量](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(十九)技能系统与 ClawHub/06-infographic-prompt-budget-1775150761549.png)


七、技能安全扫描器 ​

7.1 为什么需要安全扫描? ​

技能可以来自第三方(ClawHub 或手动安装),可能包含恶意代码。虽然 SKILL.md 本身只是 Markdown,但技能目录中可能包含 scripts/ 下的可执行脚本。

7.2 扫描目标 ​

扫描器检查技能目录中的 JavaScript/TypeScript 文件:

typescript
const SCANNABLE_EXTENSIONS = new Set([
  ".js", ".ts", ".mjs", ".cjs", ".mts", ".cts", ".jsx", ".tsx",
]);

7.3 扫描规则 ​

行级规则(逐行匹配):

规则 ID严重性检测内容正则模式
dangerous-exec🔴 criticalShell 命令执行exec|spawn|execFile + child_process
dynamic-code-execution🔴 critical动态代码执行eval( | new Function(
crypto-mining🔴 critical加密货币挖矿stratum+tcp|coinhive|xmrig
suspicious-network🟡 warn非标准端口 WebSocketnew WebSocket("wss://...:PORT")

源码级规则(全文匹配):

规则 ID严重性检测内容检测逻辑
potential-exfiltration🟡 warn数据泄露readFile + fetch/post/http.request 同时出现
obfuscated-code🟡 warn混淆代码连续 6+ 个 \xHH 或 200+ 字符的 base64
env-harvesting🔴 critical凭证窃取process.env + fetch/post/http.request 同时出现

7.4 扫描架构 ​

scanSkillDirectory(dirPath)
    │
    ├─ 递归遍历目录(最大深度限制)
    │   ├─ 跳过 node_modules/
    │   ├─ 跳过 .git/
    │   └─ 限制最多 500 个文件
    │
    ├─ 对每个可扫描文件:
    │   ├─ 检查文件大小(≤ 1MB)
    │   ├─ 检查缓存(mtimeMs + size 未变 → 跳过)
    │   ├─ 读取文件内容
    │   ├─ 执行行级规则
    │   ├─ 执行源码级规则
    │   └─ 缓存结果
    │
    └─ 返回 SkillScanSummary
        { scannedFiles, critical, warn, info, findings }

7.5 缓存机制 ​

扫描结果使用文件指纹缓存(size + mtimeMs),避免重复扫描未修改的文件:

typescript
function getCachedFileScanResult(params: {
  filePath: string;
  size: number;
  mtimeMs: number;
  maxFileBytes: number;
}): FileScanCacheEntry | undefined {
  const cached = FILE_SCAN_CACHE.get(params.filePath);
  if (!cached) return undefined;
  if (
    cached.size !== params.size ||
    cached.mtimeMs !== params.mtimeMs ||
    cached.maxFileBytes !== params.maxFileBytes
  ) {
    FILE_SCAN_CACHE.delete(params.filePath);
    return undefined;
  }
  return cached;
}

缓存上限 5000 条,超出时采用 FIFO 淘汰。

7.6 上下文关联规则 ​

某些规则需要两个模式同时出现才触发(减少误报):

typescript
{
  ruleId: "env-harvesting",
  severity: "critical",
  pattern: /process\.env/,                    // 主模式
  requiresContext: /\bfetch\b|\bpost\b|http\.request/i,  // 上下文模式
  // 只有当文件同时包含 process.env 和网络请求时才报告
}

单独使用 process.env 是完全正常的(读取配置),但如果同时存在网络请求,可能是在窃取环境变量中的凭证。

![技能安全扫描器:双层规则与缓存机制](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(十九)技能系统与 ClawHub/07-infographic-security-scanner-1775150762287.png)


八、斜杠命令生成 ​

8.1 从技能到斜杠命令 ​

每个有资格的技能都会自动生成一个斜杠命令,供用户在消息渠道中触发:

技能 "weather" → 斜杠命令 /weather
技能 "gh-issues" → 斜杠命令 /gh-issues
技能 "openai-image-gen" → 斜杠命令 /openai-image-gen

8.2 命令名归一化 ​

typescript
function sanitizeSkillCommandName(rawName: string): string {
  return rawName
    .toLowerCase()
    .replace(/[^a-z0-9_-]/g, "-")     // 非法字符替换为 "-"
    .replace(/^-+|-+$/g, "")           // 去掉前后 "-"
    .slice(0, 32);                      // 最大 32 字符
}

8.3 命令去重 ​

如果多个技能归一化后命名相同,会自动添加数字后缀:

typescript
function resolveUniqueSkillCommandName(base: string, used: Set<string>): string {
  if (!used.has(base.toLowerCase())) return base;
  for (let i = 2; i < 10_000; i++) {
    const candidate = `${base}-${i}`;
    if (!used.has(candidate.toLowerCase())) return candidate;
  }
  // 极端情况的 fallback...
}

8.4 工具分发命令 ​

某些技能可以配置为直接调用工具而非注入 Prompt:

yaml
---
name: camsnap
description: Take a photo using the phone camera
command-dispatch: tool
command-tool: nodes
command-arg-mode: raw
---

当用户输入 /camsnap front 时,系统直接调用 nodes 工具的 camera_snap 操作,而非将消息发给 Agent 处理。

8.5 保留命令名 ​

系统命令(如 /help、/model、/session)的名称被预留,技能命令不能与它们冲突:

typescript
const specs = buildWorkspaceSkillCommandSpecs(workspaceDir, {
  reservedNames: new Set(["help", "model", "session", "status", ...]),
});

![斜杠命令生成:从技能名到 /command](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(十九)技能系统与 ClawHub/08-infographic-slash-commands-1775150763304.png)


九、沙箱同步机制 ​

9.1 为什么需要同步? ​

当 Agent 在 Docker 沙箱中运行时,沙箱内没有主机上的技能文件。syncSkillsToWorkspace 将技能从主机复制到沙箱工作区:

主机文件系统                          Docker 沙箱
├── ~/.openclaw/skills/weather/       
├── ~/workspace/skills/github/        
├── /usr/lib/.../skills/canvas/       
│                                     ├── /sandbox/skills/weather/
│         syncSkillsToWorkspace() ──→ ├── /sandbox/skills/github/
│                                     └── /sandbox/skills/canvas/

9.2 同步流程 ​

typescript
export async function syncSkillsToWorkspace(params: {
  sourceWorkspaceDir: string;    // 主机工作区
  targetWorkspaceDir: string;    // 沙箱工作区
  config?: OpenClawConfig;
}) {
  // 1. 如果源和目标相同,跳过(非沙箱模式)
  if (sourceDir === targetDir) return;

  // 2. 序列化执行(防止并发同步)
  await serializeByKey(`syncSkills:${targetDir}`, async () => {
    // 3. 加载所有来源的技能
    const entries = loadSkillEntries(sourceDir, { ... });

    // 4. 清空目标目录
    await fsp.rm(targetSkillsDir, { recursive: true, force: true });
    await fsp.mkdir(targetSkillsDir, { recursive: true });

    // 5. 逐个复制(安全路径解析 + 名称去重)
    for (const entry of entries) {
      const dest = resolveSyncedSkillDestinationPath({ ... });
      await fsp.cp(entry.skill.baseDir, dest, { recursive: true });
    }
  });
}

9.3 路径安全 ​

目标路径经过沙箱路径解析(resolveSandboxPath),确保技能文件不会被复制到沙箱工作区之外:

typescript
const dest = resolveSandboxPath({
  filePath: uniqueDirName,
  cwd: targetSkillsDir,
  root: targetSkillsDir,   // 不能逃出这个目录
}).resolved;

![沙箱同步机制:主机到 Docker 的技能迁移](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(十九)技能系统与 ClawHub/09-infographic-sandbox-sync-1775150764104.png)


十、ClawHub 生态 ​

10.1 ClawHub 是什么? ​

ClawHub(clawhub.com)是 OpenClaw 的技能分发平台——类似于 npm 之于 Node.js,或 Docker Hub 之于容器。用户可以在 ClawHub 上搜索、安装、更新和发布技能。

10.2 ClawHub CLI ​

ClawHub 本身也是一个技能(skills/clawhub/SKILL.md),通过 npm 安装:

bash
npm i -g clawhub

10.3 核心命令 ​

命令说明
clawhub search "postgres backups"搜索技能
clawhub install my-skill安装技能到 ~/.openclaw/skills/
clawhub install my-skill --version 1.2.3安装指定版本
clawhub update --all更新所有已安装技能
clawhub update my-skill --force强制更新
clawhub list列出已安装技能
clawhub publish ./my-skill --slug my-skill发布技能
clawhub login登录(发布需要)

10.4 版本管理 ​

ClawHub 使用基于文件哈希的版本匹配:

bash
clawhub update my-skill
# 1. 计算本地技能文件的哈希
# 2. 在 registry 中找到匹配的版本
# 3. 如果有更新版本,下载并替换

10.5 三种分发方式 ​

方式目录来源说明
Bundled<安装目录>/skills/内置于 OpenClaw随版本更新
Managed~/.openclaw/skills/ClawHub 安装独立于版本更新
Workspace<工作区>/skills/手动放置最高优先级

![ClawHub 生态:技能分发平台架构](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(十九)技能系统与 ClawHub/10-infographic-clawhub-ecosystem-1775150765073.png)


十一、内置技能巡览 ​

11.1 52 个内置技能分类 ​

🖥️ 开发工具(12 个):

技能说明
githubGitHub CLI 操作(issues/PRs/repos)
gh-issuesGitHub Issues 管理
coding-agent编码代理工作流
tmuxTmux 终端复用器操作
oracle数据库查询辅助
sagSearch-and-grep 代码搜索
session-logs会话日志分析
skill-creator创建新技能的元技能
gogGo 开发辅助
eightctl8base 平台操作
blucliBlueink CLI 操作
nano-banana-proBanana 推理平台

🌐 Web & API(6 个):

技能说明
weather天气查询(wttr.in)
xurlURL 处理工具
blogwatcher博客/RSS 监控
goplaces地点搜索
healthcheckHTTP 健康检查
gifgrepGIF 搜索

📱 消息与社交(8 个):

技能说明
discordDiscord 操作增强
slackSlack 操作增强
imsgiMessage 操作
bluebubblesBlueBubbles iMessage 桥
wacliWhatsApp CLI
voice-call语音通话
telegram*(通过插件提供)
songsee歌曲识别

📝 笔记与生产力(7 个):

技能说明
apple-notesApple Notes 操作
apple-remindersApple Reminders 操作
bear-notesBear 笔记操作
notionNotion 操作
obsidianObsidian 笔记操作
trelloTrello 看板操作
things-macThings 任务管理

🎨 媒体与 AI(8 个):

技能说明
openai-image-genOpenAI 图像生成(DALL·E)
openai-whisper本地语音转文字
openai-whisper-apiWhisper API 语音转文字
geminiGoogle Gemini 增强
canvasCanvas UI 操作
camsnap手机拍照
peekaboo截图分析
video-frames视频帧提取
nano-pdfPDF 处理

🏠 智能家居 & 设备(4 个):

技能说明
openhuePhilips Hue 灯光控制
sonoscliSonos 音响控制
spotify-playerSpotify 播放控制
1password1Password 密码管理

🔧 系统与运维(5 个):

技能说明
clawhubClawHub 技能管理
model-usage模型用量统计
summarize文本总结
himalaya邮件客户端
mcporterMC Porter 工具
ordercliOrder CLI 工具
sherpa-onnx-tts本地 TTS 引擎

11.2 技能结构示例 ​

以 weather 技能为例,展示一个设计良好的技能:

weather/
└── SKILL.md (112 行)
    ├── Frontmatter:
    │   ├── name: weather
    │   ├── description: 清晰的使用场景描述
    │   └── metadata: { requires: { bins: ["curl"] } }
    │
    ├── When to Use: 5 个匹配场景
    ├── When NOT to Use: 5 个排除场景
    │
    └── Commands: 具体的 curl 命令示例
        ├── 当前天气
        ├── 预报
        ├── 格式选项
        └── 快速响应模板

![52 个内置技能分类全景图](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(十九)技能系统与 ClawHub/11-infographic-builtin-skills-1775150765898.png)


十二、设计模式与架构总结 ​

12.1 技能系统数据流 ​

                    ┌──────────────────────────────────────┐
                    │          6 个技能来源                  │
                    │  extra < bundled < managed <          │
                    │  personal < project < workspace       │
                    └───────────────┬──────────────────────┘
                                    │ loadSkillEntries()
                    ┌───────────────▼──────────────────────┐
                    │        合并 (Map.set 覆盖)             │
                    │        + Frontmatter 解析              │
                    │        + 元数据提取                    │
                    └───────────────┬──────────────────────┘
                                    │ filterSkillEntries()
                    ┌───────────────▼──────────────────────┐
                    │           资格过滤                     │
                    │  平台 / 环境变量 / 二进制命令 / 白名单  │
                    └──┬────────────────────────────────┬──┘
                       │                                │
          ┌────────────▼──────────┐      ┌──────────────▼──────────┐
          │  buildSkillsPrompt()  │      │  buildSkillCommandSpecs │
          │  → Agent System Prompt│      │  → 斜杠命令注册          │
          │                       │      │                         │
          │  二分搜索字符预算     │      │  名称归一化+去重        │
          │  路径压缩             │      │  保留名冲突检查         │
          └───────────────────────┘      └─────────────────────────┘

                    ┌──────────────────────────────────────┐
                    │        安全扫描器 (skill-scanner)      │
                    │                                      │
                    │  行级规则: exec/eval/mining/websocket │
                    │  源码级规则: exfiltration/obfuscation │
                    │  缓存: 文件指纹 (size+mtime)          │
                    └──────────────────────────────────────┘

12.2 关键设计模式 ​

模式应用说明
声明式扩展SKILL.md无需代码,Markdown 即能力
渐进式披露三层加载元数据 → 正文 → 资源,最小化 token 消耗
优先级覆盖六源合并用户本地 > 托管 > 内置
二分搜索Prompt 预算O(log N) 找到最大装入量
静态分析安全扫描器无需执行即可检测恶意代码
资源限制加载限制防止恶意技能目录 DoS
缓存失效文件指纹size + mtimeMs 变化即重新扫描
序列化执行沙箱同步serializeByKey 防止并发写入

![设计模式总结:技能系统 8 大设计模式](https://qn.huat.xyz/blog/article-Illustration/OpenClaw 源码解读(十九)技能系统与 ClawHub/12-infographic-design-patterns-1775150766700.png)

12.3 推荐阅读顺序 ​

优先级文件行数难度
🔴 必读skills/skill-creator/SKILL.md~200★☆☆☆☆
🔴 必读agents/skills/types.ts~89★★☆☆☆
🔴 必读agents/skills/workspace.ts~760★★★★☆
🟡 推荐agents/skills/frontmatter.ts~228★★★☆☆
🟡 推荐agents/skills/config.ts~102★★☆☆☆
🟡 推荐security/skill-scanner.ts~260★★★☆☆
🟢 选读skills/weather/SKILL.md~112★☆☆☆☆
🟢 选读skills/clawhub/SKILL.md~77★☆☆☆☆
🟢 选读skills/github/SKILL.md(示例)★☆☆☆☆

12.4 思考题 ​

  1. 为什么技能系统选择 Markdown 文件而非 JSON/YAML 配置?

    提示:考虑 LLM 的"母语"——它最擅长理解什么格式的文本?

  2. 六源加载中,为什么 workspace(工作区)优先级最高,而 bundled(内置)只排第二?

    提示:考虑用户需要覆盖内置技能行为的场景。

  3. 安全扫描器为什么用 requiresContext 来减少误报?单模式匹配有什么问题?

    提示:process.env 在正常代码中非常常见,仅匹配它会产生大量误报。

  4. Prompt 字符预算为什么用二分搜索而非逐个添加?

    提示:考虑 formatSkillsForPrompt() 的计算开销——它需要序列化整个列表。

  5. 如果你要创建一个新技能,应该放在哪个目录?为什么?

    提示:根据技能的使用范围选择——个人全局 vs 项目特有 vs 工作区。


📌 小结: 技能系统是 OpenClaw 最具创新性的设计之一——它用最朴素的载体(Markdown 文件)实现了最强大的效果(领域专家能力)。52 个内置技能覆盖了从天气查询到智能家居控制的广泛场景,而 ClawHub 生态则让社区可以共享和分发技能。整个系统的工程设计体现了"简单但不简陋"的哲学——六源加载、渐进式披露、二分搜索预算、静态安全分析,每一个细节都经过精心考量。对于想要理解"如何让 AI Agent 拥有领域知识"的开发者来说,这是一个值得反复学习的参考实现。

读文档、看源码、写代码,理解 AI Agent 本质 🤖