Skill(技能)正在成为 AI Agent 平台的核心抽象——一个可装载、可组合的能力单元,让 Agent 按需获得领域专业性。

Claude Code 把 Skill 作为扩展能力的一等公民:一个 Markdown 文件就是一个技能,放进目录就自动生效1。最近比较火的开源项目 OpenClaw 同样以 Skill 为核心组织 Agent 能力2。两者不约而同选择了同一个载体——SKILL.md,却在机制设计上走向了不同的方向。

本文以一个 Todo List 技能为贯穿案例,对比两者从设计到实现的差异。

一、一个技能,两种实现

需求很简单:用本地 JSON 文件存储待办事项,支持增删改查。

两个平台做了同一个决定:一个 SKILL.md 文件 = 一个技能。YAML frontmatter 声明元数据,Markdown body 就是技能本身——写给 AI 的”操作手册”。下面分别是 OpenClaw 和 Claude Code 两个版本的完整实现:

OpenClaw 版 — skills/todo/SKILL.md

---
name: todo
description: "Local todo list management with JSON file storage"
metadata:
  openclaw:
    emoji: "✅"
    requires: { bins: ["jq"] }
    install:
      - kind: brew
        formula: jq
---
# Todo List Skill

Manage a local todo list stored in `~/.todo.json`.

## File Format
JSON array of objects: `[{"id": 1, "text": "买牛奶", "done": false, "created": "2025-01-01"}]`

## Operations
- **Add**: append new item with auto-increment id
- **List**: show all items, optionally filter by done/undone
- **Done**: mark item as completed by id
- **Remove**: delete item by id

Use `jq` for all JSON manipulation. Example:
`jq '. += [{"id": (map(.id) | max + 1), "text": $text, "done": false}]' --arg text "$TEXT" ~/.todo.json`

Claude Code 版 — .claude/skills/todo/SKILL.md

---
name: todo
description: >
  Use this skill when the user wants to manage tasks or todos.
  Trigger phrases: "add a todo", "my task list", "mark as done",
  "what's left to do", "帮我记一下", "还有什么没做",
  "待办事项", "任务列表".
user-invocable: true
---
# Todo List Skill

Manage a local todo list stored in `~/.todo.json`.

## File Format
JSON array: `[{"id": 1, "text": "买牛奶", "done": false, "created": "2025-01-01"}]`

## Operations
- **Add**: Read file → append item with auto-increment id → write back
- **List**: Read file → format as table, group by status
- **Done**: Read file → flip `done` flag by id → write back
- **Remove**: Read file → filter out by id → write back

Always use the Read and Write tools for file operations. Create `~/.todo.json` with `[]` if it doesn't exist.

For detailed schema and migration guide, see `references/schema.md`.
For bulk import/export, run `scripts/import-export.sh`.

两个文件做的事一模一样,但 frontmatter 的侧重点已经显露差异:OpenClaw 声明运行时依赖(jq)和安装方式;Claude Code 填满触发短语,并引用 references/scripts/ 做渐进式披露。

接下来我们围绕这个案例,逐层拆解两者在发现机制、元数据设计、触发逻辑、Token 管理、安全模型和扩展生态上的差异。

二、技能的骨架:定义、发现与组织

发现机制

两者都选择了”基于文件系统的自动发现”,不需要显式注册代码。但发现的来源和优先级差异很大。

OpenClaw 从 6 个目录逐层加载技能,后者覆盖前者:

// workspace.ts — 优先级从低到高
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/ 下放一个同名技能覆盖内置版本。内置技能达 54 个,涵盖 GitHub、Discord、Slack、Notion、1Password、Spotify 等。

Claude Code 同样以目录为单位组织,支持全局和项目两层:

~/.claude/skills/               # 全局技能(跨项目可用)
└── skill-name/
    ├── SKILL.md                # 技能定义
    ├── references/             # 参考文档
    └── scripts/                # 可执行脚本

.claude/skills/                 # 项目级技能(仅当前项目)
└── skill-name/
    └── SKILL.md

放一个 SKILL.md 到对应目录,技能就自动生效——支持热重载,自动出现在 / 斜杠命令菜单中。此外,Claude Code 还有一个插件机制,可以将 commands、agents、skills、hooks 打包成一个可分发的整体,但这是可选的高级功能,普通用户不需要理解插件概念。

核心差异:两者都是目录级独立实体,都支持多层覆盖。OpenClaw 有 6 层优先级叠加 + 54 个内置技能,覆盖面更广;Claude Code 是全局/项目两层 + 插件可选扩展,结构更简洁。

比如我们的 todo 技能,OpenClaw 用户放到 skills/todo/SKILL.md,它就出现在 6 层覆盖的某一层——如果内置也有同名技能,项目级的会覆盖内置版本;Claude Code 用户放到 ~/.claude/skills/todo/SKILL.md(全局)或 .claude/skills/todo/SKILL.md(项目级),下次对话自动生效。两种放法,同一个结果:目录即注册。

元数据设计

两者的 frontmatter 反映了截然不同的关注点。

OpenClaw 关注”能不能跑”——面向运行时环境:

// types.ts
export type OpenClawSkillMetadata = {
  always?: boolean;        // 始终加载,无视配置
  os?: string[];           // 操作系统限制
  requires?: {
    bins?: string[];       // 必须存在的二进制(AND 关系)
    anyBins?: string[];    // 至少存在一个(OR 关系)
    env?: string[];        // 必须的环境变量
  };
  install?: SkillInstallSpec[];  // 自动安装指令
};

技能声明”我需要 gh 命令行工具”,不存在时 OpenClaw 还知道如何帮你装——brew、npm、go、uv 或直接下载:

export type SkillInstallSpec = {
  kind: "brew" | "node" | "go" | "uv" | "download";
  formula?: string;   // brew install gh
  package?: string;   // npm install xxx
  url?: string;       // 直接下载
};

Claude Code 关注”该不该用”——面向触发语义,并采用渐进式披露设计:

description: This skill should be used when the user asks to
  "create a hook", "add a PreToolUse hook", "validate tool use",
  or mentions hook events (PreToolUse, PostToolUse, Stop).
层级内容何时加载大小限制
元数据name + description始终在上下文中~100 词
SKILL.md body核心指导触发短语匹配时1500–2000 词
references/详细文档按需读取无限制
scripts/可执行脚本直接执行,无需读入无限制

元数据永远加载(开销极低),完整技能只在被触发时才注入 prompt——一种上下文窗口友好型设计。

回到我们的 todo 技能:OpenClaw 版声明 requires: { bins: ["jq"] }——如果机器上没有 jq,它知道用 brew install jq 帮你装。Claude Code 版则在 description 里写满触发词——用户说”帮我记一下”、“我的任务列表”、“还有什么没做”,模型都能匹配上。同一个技能,一个操心”能不能跑”,一个操心”该不该触发”。

三、技能的触发与 Token 管理

调用机制

OpenClaw 是命令驱动——把每个技能注册为斜杠命令,用户直接输入 /github pr list

// skill-commands.ts
export function resolveSkillCommandInvocation(params) {
  // 解析 /skillname args 或 /skill skillname args
  const match = trimmed.match(/^\/([^\s]+)(?:\s+([\s\S]+))?$/);
  if (commandName === "skill") {
    // /skill github pr list → 查找 github 技能
  }
}

还有一个双通道设计——技能可以被用户调用,也可以被模型自动调用,两个方向独立控制:

export type SkillInvocationPolicy = {
  userInvocable: boolean;           // 用户能否用 /命令 调用
  disableModelInvocation: boolean;  // 模型能否自动使用
};

Claude Code 同样支持斜杠命令——技能默认 user-invocable: true,自动注册为 /skill-name 命令出现在菜单中,用户可以直接调用。但它还多了一层语义匹配——模型会根据 description 中的触发短语自动判断是否需要加载:

description: This skill should be used when the user asks to
  "create an agent", "add an agent", "write a subagent",
  "agent frontmatter", "agent examples", "agent tools"...

也支持在命令或 agent 流程中显式加载:

<!-- create-plugin 命令中 -->
**MUST load plugin-structure skill** using Skill tool before this phase.

核心差异:两者都支持 /命令 式显式调用。不同的是,OpenClaw 仅靠命令派发 + 模型调用双通道;Claude Code 在此基础上增加了语义匹配——即使用户没有输入 / 命令,模型也能根据意图自动加载相关技能。

拿 todo 技能举例:OpenClaw 用户输入 /todo add 买牛奶,命令解析器匹配到 todo 技能,注入 prompt。Claude Code 用户可以 /todo add 买牛奶(走斜杠命令),也可以直接说”帮我记一下,明天要买牛奶”——模型看到 description 里的触发短语匹配,自动加载技能。同一个意图,一个靠用户记住命令名,一个靠模型理解自然语言。

Token 管理策略

两者都面临同一个工程问题:技能越多,prompt 越长,token 越贵

OpenClaw:全量注入 + 硬上限截断。 设置明确的上限,超出时用二分搜索找到能放进去的最大子集:

const DEFAULT_MAX_SKILLS_IN_PROMPT = 150;       // 最多 150 个技能
const DEFAULT_MAX_SKILLS_PROMPT_CHARS = 30_000;  // 最多 3 万字符

if (!fits(skillsForPrompt)) {
  let lo = 0, hi = skillsForPrompt.length;
  while (lo < hi) {
    const mid = Math.ceil((lo + hi) / 2);
    fits(skillsForPrompt.slice(0, mid)) ? lo = mid : hi = mid - 1;
  }
  skillsForPrompt = skillsForPrompt.slice(0, lo);
}

还有一个小优化——路径压缩,把 /Users/alice/.bun/.../SKILL.md 缩为 ~/.bun/.../SKILL.md,每个路径省 5–6 token。

Claude Code:三层按需加载。 不是”全塞进去再截断”,而是分级注入:

skill-name/
├── SKILL.md              ← 触发时加载(~2000 词)
├── references/            ← 按需读取(无限制)
│   ├── patterns.md
│   └── advanced.md
└── scripts/               ← 直接执行,不读入上下文
    └── validate.sh

以 todo 技能为例,三层加载的实际内容是:SKILL.md 只写核心操作指南——增删改查的 JSON 操作方式、文件路径约定;references/schema.md 放详细的数据格式文档(字段定义、状态枚举、版本兼容规则);scripts/migrate.sh 放版本迁移脚本——用户说”升级我的 todo 格式”时直接执行,不占上下文窗口。同一个技能,只有用户实际触发时才加载核心指南,深层文档和脚本始终按需。

四、安全与配置

安全模型

OpenClaw 是预防型——安装前扫描代码。 内置安全扫描器检测技能目录中的可疑模式:

// skill-scanner.ts
const LINE_RULES = [
  { ruleId: "dangerous-exec", severity: "critical",
    pattern: /\b(exec|execSync|spawn|spawnSync)\s*\(/,
    requiresContext: /child_process/ },
  { ruleId: "dynamic-code-execution", severity: "critical",
    pattern: /\beval\s*\(|new\s+Function\s*\(/ },
  { ruleId: "crypto-mining", severity: "critical",
    pattern: /stratum\+tcp|coinhive|cryptonight|xmrig/i },
];

const SOURCE_RULES = [
  { ruleId: "env-harvesting", severity: "critical",
    message: "Environment variable access + network send",
    pattern: /process\.env/,
    requiresContext: /\bfetch\b|\bpost\b|http\.request/i },
];

还阻止技能设置 LD_PRELOADOPENSSL_CONF 等危险环境变量。

Claude Code 是拦截型——执行时验证行为。 安全模型建立在 Hooks 机制上,在工具执行前/后插入检查点:

{
  "PreToolUse": [{
    "matcher": "Write|Edit",
    "hooks": [{
      "type": "prompt",
      "prompt": "Validate file write safety. Check: system paths,
        credentials, path traversal. Return 'approve' or 'deny'."
    }]
  }]
}

钩子可以是 prompt(让 LLM 判断)或 command(执行脚本),还有权限清单从全局层面控制工具访问:

{ "permissions": { "deny": ["WebSearch", "WebFetch"], "ask": ["Bash"] } }

回到 todo 技能:OpenClaw 扫描 skills/todo/ 目录,如果发现技能文件中有 exec() 调用或网络请求,会在安装时报警。Claude Code 则通过 Hooks 在运行时拦截——比如 todo 技能尝试写入 ~/.todo.json 以外的路径时,PreToolUse 钩子可以阻止操作。

配置管理

OpenClaw 用 YAML 集中管理所有技能的开关、API Key、环境变量:

skills:
  allowBundled: [github, discord]
  entries:
    notion:
      enabled: true
      apiKey: secret_xxx
      env: { NOTION_TOKEN: ntn_xxx }
    discord:
      enabled: false

背后的类型系统支撑了细粒度控制:

export type SkillsConfig = {
  allowBundled?: string[];                // 内置技能白名单
  load?: { extraDirs?: string[]; watch?: boolean };
  limits?: SkillsLimitsConfig;
  entries?: Record<string, SkillConfig>;  // 逐技能配置
};

Claude Code 的配置跟随插件,通过 plugin.json 清单声明,运行时配置用 .claude/plugin-name.local.md 或环境变量,路径引用统一使用 ${CLAUDE_PLUGIN_ROOT} 保持可移植性。

五、扩展生态与命令模板

创建与分发

创建一个技能有多简单?以我们的 todo 技能为例:

OpenClawmkdir skills/todo && vim skills/todo/SKILL.md,写入内容,保存——下次启动自动发现。还可以通过插件清单批量声明技能路径:

{
  "id": "my-plugin",
  "skills": ["skills/todo", "skills/my-other-skill"]
}

Claude Codemkdir -p .claude/skills/todo && vim .claude/skills/todo/SKILL.md,写入内容,保存——热重载,立刻出现在 / 斜杠命令菜单。无需任何注册代码。

对于需要批量分发的场景,Claude Code 提供了插件作为高级打包机制——一个插件可以同时包含命令、agent、技能、钩子和 MCP 服务器:

my-plugin/
├── .claude-plugin/plugin.json  # 清单
├── commands/review.md          # /review 斜杠命令
├── agents/code-reviewer.md     # 专业子 agent
├── skills/api-testing/SKILL.md # 技能
├── hooks/hooks.json            # 事件钩子
└── .mcp.json                   # MCP 服务器集成

其中 MCP 集成值得一提——插件可以启动外部 MCP 服务器,将外部 API 变成 Claude 可调用的工具:

{
  "database-tools": {
    "command": "${CLAUDE_PLUGIN_ROOT}/servers/db-server",
    "args": ["--config", "${CLAUDE_PLUGIN_ROOT}/config.json"],
    "env": { "DB_URL": "${DB_URL}" }
  }
}

但请注意:插件是可选的分发机制,不是使用技能的前提。绝大多数用户只需要 SKILL.md 这一个文件。

命令模板:Claude Code 的独特设计

Claude Code 有一个 OpenClaw 中没有的概念——Command 模板。命令是写给 Claude 的指令,支持动态内容插值:

---
allowed-tools: Bash(git add:*), Bash(git status:*), Bash(git commit:*)
description: Create a git commit
---

## Context
- Current git status: !`git status`
- Current git diff: !`git diff HEAD`
- Recent commits: !`git log --oneline -10`

## Your task
Based on the above changes, create a single git commit.

注意 !`git status` 语法——backtick 包裹的命令会在模板渲染时实际执行,结果内联到 prompt 中。$ARGUMENTS 变量承载用户传入的参数。一个 Markdown 文件就能定义一个完整的自动化工作流。

六、总结

维度OpenClawClaude Code
技能载体SKILL.mdSKILL.md
技能组织独立实体,多来源层叠独立实体,全局/项目两层
发现机制6 层目录,显式优先级覆盖全局/项目目录扫描 + 插件可选
元数据重点运行时依赖(bins/env/os)语义触发(trigger phrases)
调用方式/命令 派发 + 模型调用/命令 + 模型语义匹配 + Skill tool
Token 管理全量注入 + 上限截断三层渐进式加载
安全模型静态代码扫描 + 环境变量阻断事件钩子 + 权限清单
配置方式YAML 集中配置目录约定 + 插件清单(可选)
扩展模型插件声明技能路径目录放置即生效 + 全栈插件可选
内置技能数54 个少量内置 + 插件市场扩展
依赖管理声明依赖 + 自动安装手动确保依赖
命令模板支持动态插值

两个项目从同一个起点出发——SKILL.md——在用户层面的体验其实相当接近:都是往目录里放一个 Markdown 文件,技能就自动生效,都支持 /命令 调用,都不需要编写注册代码。

差异不在组织方式,而在元数据设计Token 管理策略

OpenClaw 的元数据面向运行时环境——技能声明自己需要哪些二进制、哪些环境变量、在哪些操作系统上可用,甚至还知道如何帮你安装缺失的依赖。54 个内置技能覆盖开发者和消费者场景,让 AI Agent 成为跨平台的多面手。Token 管理策略是”全量注入 + 硬上限截断”——简单直接。

Claude Code 的元数据面向语义触发——description 中写满触发短语,让模型自主判断何时加载。Token 管理策略是”三层渐进式披露”——元数据始终在场,SKILL.md 按需加载,references 延迟读取——一种上下文窗口友好型设计。插件机制则提供了可选的高级打包能力。

一个强调运行时能力,一个强调认知效率

但最终它们共享了同一个洞见:Skill 是 AI Agent 从”通用”走向”专业”的关键抽象。不是靠更大的模型、更长的上下文,而是靠一个个 Markdown 文件,把人类的领域知识和工作流程,变成 AI 可以按需装载的能力模块。

技能即能力。文件系统即能力仓库。这或许是 AI Agent 基础设施最朴素也最深刻的设计选择。

Footnotes

  1. Extend Claude with skills — Claude Code

  2. Skills — OpenClaw