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_PRELOAD、OPENSSL_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 技能为例:
OpenClaw:mkdir skills/todo && vim skills/todo/SKILL.md,写入内容,保存——下次启动自动发现。还可以通过插件清单批量声明技能路径:
{
"id": "my-plugin",
"skills": ["skills/todo", "skills/my-other-skill"]
}
Claude Code:mkdir -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 文件就能定义一个完整的自动化工作流。
六、总结
| 维度 | OpenClaw | Claude Code |
|---|---|---|
| 技能载体 | SKILL.md | SKILL.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 基础设施最朴素也最深刻的设计选择。