拆解 OpenClaw 的 Skills 机制:一个为 AI Agent 设计的"包管理器"

43 阅读15分钟

当大模型的上下文窗口成为稀缺资源,如何让 AI 在 50+ 个专业领域之间按需切换,同时不浪费一个 token?OpenClaw 的 Skills 系统给出了一个工程上极其优雅的答案。

一、背景:AI Agent 的"万能"困局

你一定遇到过这样的场景——

让 AI 帮你查天气,它不知道该调用哪个 API;让它帮你操作 1Password,它不知道 CLI 怎么用;让它帮你生成图片,它连 Python 脚本都要从头写一遍。

问题不在于模型不够聪明,而在于模型缺少特定领域的程序性知识(Procedural Knowledge)。这类知识——"具体用什么命令"、"参数怎么填"、"脚本长什么样"——不是通过预训练能完全覆盖的。

传统的解决方案是写一大段 System Prompt,把所有领域的知识塞进去。但这会导致:

  • 上下文窗口爆炸:50 个领域的详细指令能轻松吃掉 10 万+ tokens
  • 注意力稀释:模型在大段 prompt 中找到相关段落的能力会下降
  • 维护噩梦:增删改一个领域需要触碰整个 prompt

OpenClaw(一个开源的多渠道 AI 助手网关)提出了一种不同的思路:Skills 系统

二、Skills 不只是"文档"

一看到"Skill"这个词,你可能会想到 Custom Instructions 或者 Prompt Template——写一段文字告诉 AI 怎么做。

OpenClaw 的 Skill 远不止于此。它是一个完整的可分发能力包(Capability Package),包含:

skill-name/
├── SKILL.md          # 必需:元数据声明 + 使用指令
├── scripts/          # 可选:可执行脚本(Python/Bash/Node)
├── bin/              # 可选:二进制包装器
├── references/       # 可选:按需加载的参考文档
└── assets/           # 可选:模板、图片等输出资源

让我们逐层拆解。

三、第一层:声明式元数据——"我是谁、我需要什么"

每个 Skill 的 SKILL.md 顶部有一个 YAML frontmatter 块。以本地 TTS(文字转语音)Skill 为例:

---
name: sherpa-onnx-tts
description: Local text-to-speech via sherpa-onnx (offline, no cloud)
metadata:
  openclaw:
    emoji: "🗣️"
    os: ["darwin", "linux", "win32"]
    requires:
      env: ["SHERPA_ONNX_RUNTIME_DIR", "SHERPA_ONNX_MODEL_DIR"]
    install:
      - id: "download-runtime-macos"
        kind: "download"
        os: ["darwin"]
        url: "https://github.com/.../sherpa-onnx-v1.12.23-osx-universal2-shared.tar.bz2"
        archive: "tar.bz2"
        extract: true
        stripComponents: 1
        targetDir: "runtime"
      - id: "download-runtime-linux-x64"
        kind: "download"
        os: ["linux"]
        url: "https://github.com/.../sherpa-onnx-v1.12.23-linux-x64-shared.tar.bz2"
        ...
      - id: "download-model-lessac"
        kind: "download"
        url: "https://github.com/.../vits-piper-en_US-lessac-high.tar.bz2"
        targetDir: "models"
---

这段元数据声明了五件事:

字段含义
os支持哪些操作系统
requires.bins需要哪些命令行工具
requires.env需要哪些环境变量
requires.anyBins多个工具中有一个即可
install依赖缺失时如何自动安装

关键洞察:这些信息是给运行时引擎看的,不是给模型看的。 运行时在 Skill 被触发之前就用这些元数据做资格检查和依赖安装。

四、内置的自动安装系统

这是 Skills 机制最出乎意料的部分——它内建了 5 种安装器

看一下实际的安装命令构建逻辑(src/agents/skills-install.ts):

function buildInstallCommand(spec: SkillInstallSpec, prefs: SkillsInstallPreferences) {
  switch (spec.kind) {
    case "brew":
      return { argv: ["brew", "install", spec.formula] };
    case "node":
      return { argv: buildNodeInstallCommand(spec.package, prefs) };
    case "go":
      return { argv: ["go", "install", spec.module] };
    case "uv":
      return { argv: ["uv", "tool", "install", spec.package] };
    case "download":
      // 单独处理:下载 + 解压
      return { argv: null, error: "download install handled separately" };
  }
}

五种安装器覆盖了主流的包管理生态:

kind用途实际命令示例
brewmacOS Homebrewbrew install python
nodenpm/pnpm/yarn/bun 全局包npm install -g <package>
goGo 模块go install <module>
uvPython 工具uv tool install <package>
download下载归档文件并解压从 GitHub Releases 下载 .tar.bz2

而且,node 安装器还会尊重用户的包管理器偏好:

function buildNodeInstallCommand(packageName: string, prefs: SkillsInstallPreferences): string[] {
  switch (prefs.nodeManager) {
    case "pnpm": return ["pnpm", "add", "-g", "--ignore-scripts", packageName];
    case "yarn": return ["yarn", "global", "add", "--ignore-scripts", packageName];
    case "bun":  return ["bun", "add", "-g", "--ignore-scripts", packageName];
    default:     return ["npm", "install", "-g", "--ignore-scripts", packageName];
  }
}

注意 --ignore-scripts——默认禁用 npm 的 postinstall 脚本,这是一个安全考量。

download 安装器更有意思,它实现了完整的"下载 → 解压 → 安全校验"流程:

// 下载文件到临时目录
const result = await downloadFile({ url, rootDir: safeRoot, relativePath, timeoutMs });

// 自动检测归档类型
const archiveType = resolveArchiveType(spec, filename);  // tar.gz / tar.bz2 / zip

// 解压到目标目录
await extractArchive({ archivePath, archiveType, targetDir, stripComponents, timeoutMs });

所有下载目标都被严格限制在 ~/.openclaw/tools/<skill-name>/ 下,有完整的路径遍历防护。

从用户视角看:你说"帮我用本地 TTS 朗读这段话",系统发现 sherpa-onnx-tts Skill 可用但依赖未安装,自动下载对应平台的运行时和语音模型,然后执行。整个过程对用户透明。

五、运行时资格检查——"这个 Skill 现在能不能用"

在 Skill 被注入到 AI 上下文之前,运行时会做一系列检查(src/agents/skills/config.ts):

function shouldIncludeSkill(params) {
  const { entry, config, eligibility } = params;

  // 1. 用户手动禁用了?
  if (skillConfig?.enabled === false) return false;

  // 2. 不在 bundled allowlist 中?
  if (!isBundledSkillAllowed(entry, allowBundled)) return false;

  // 3. 运行时资格检查
  return evaluateRuntimeEligibility({
    os: entry.metadata?.os,                    // 操作系统匹配
    requires: entry.metadata?.requires,        // 依赖检查
    hasBin: hasBinary,                         // 本机二进制检查
    hasRemoteBin: eligibility?.remote?.hasBin,  // 远程设备二进制检查
    hasEnv: (envName) => Boolean(              // 环境变量检查
      process.env[envName] ||
      skillConfig?.env?.[envName] ||
      (skillConfig?.apiKey && entry.metadata?.primaryEnv === envName)
    ),
  });
}

这意味着:

  • Linux 上不会出现 macOS 专属的 Apple Notes Skill
  • 没有安装 tmux 的机器上不会出现 tmux Skill
  • 没有配置 OPENAI_API_KEY 的环境不会出现 OpenAI 图片生成 Skill
  • 连接了 iOS 设备时,才会出现需要 iOS 的 Skill

这种"按能力过滤"的设计,避免了 AI 去调用一个根本不可能成功的工具。

六、渐进式加载——上下文窗口的精细管理

这是整套设计最精巧的部分。Skills 使用三层渐进式加载(Progressive Disclosure)来管理上下文开销:

                    ┌─────────────────────────────────────────┐
                      Level 1: 元数据(始终在上下文中)       
                      name + description  100 tokens/skill  
                      50  Skill  5000 tokens             
                    └──────────────┬──────────────────────────┘
                                   
                          AI 判断:"这个 Skill 和当前任务相关"
                                   
                    ┌──────────────▼──────────────────────────┐
                      Level 2: SKILL.md 正文(触发后加载)    
                      具体命令、参数、工作流 < 5000 words     
                    └──────────────┬──────────────────────────┘
                                   
                        AI 判断:"需要更多细节" / "需要执行脚本"
                                   
                    ┌──────────────▼──────────────────────────┐
                      Level 3: 附属资源(按需加载/执行)      
                      scripts/  直接执行,不占上下文        
                      references/  读取到上下文             
                      bin/  直接调用,不占上下文            
                    └─────────────────────────────────────────┘

具体的限制参数:

const DEFAULT_MAX_SKILLS_IN_PROMPT = 150;        // 最多 150 个 Skill 的元数据
const DEFAULT_MAX_SKILLS_PROMPT_CHARS = 30_000;   // 元数据总字符上限 30K
const DEFAULT_MAX_SKILL_FILE_BYTES = 256_000;     // 单个 SKILL.md 最大 256KB

为什么这很重要?

假设你有 50 个 Skill。传统做法是把所有 50 个 Skill 的完整文档塞进 System Prompt,可能需要 10 万+ tokens。

而在 OpenClaw 的方案中:

  • Level 1 只需要约 5000 tokens(50 × 100)
  • Level 2 只在用户明确触发某个领域时加载
  • Level 3 中的脚本甚至不需要进入上下文窗口——AI 只需要知道"运行 {baseDir}/scripts/gen.py"就行

节省的 token 可以用来处理更长的对话历史、更丰富的工具输出。

七、Skill 的丰富形态——不只一种"能力"

从 OpenClaw 内置的 50+ 个 Skill 来看,它们呈现出非常不同的形态:

形态 1:纯知识注入型

weather Skill 为例——目录下只有一个 SKILL.md,教 AI 如何用 curl 调用 wttr.in

---
name: weather
description: "Get current weather and forecasts via wttr.in or Open-Meteo."
metadata: { "openclaw": { "requires": { "bins": ["curl"] } } }
---
## Commands

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

### Forecasts
curl "wttr.in/London?format=v2"

AI 天然知道 curl,但不知道 wttr.in 的 API 格式。Skill 补全了这块知识。

形态 2:脚本捆绑型

openai-image-gen Skill 自带了一个完整的 Python 脚本(scripts/gen.py,约 11KB),并声明了自动安装:

metadata:
  openclaw:
    requires: { "bins": ["python3"], "env": ["OPENAI_API_KEY"] }
    install:
      - id: "python-brew"
        kind: "brew"
        formula: "python"
        bins: ["python3"]

AI 不需要从头写图片生成代码——直接调用打包好的脚本:

python3 {baseDir}/scripts/gen.py --prompt "a lobster astronaut" --count 4

脚本还自带测试(test_gen.py),是经过验证的确定性实现。

形态 3:二进制 + 运行时下载型

sherpa-onnx-tts Skill 自带了一个 Node.js 包装脚本(bin/sherpa-onnx-tts),并声明了跨平台的运行时下载——macOS、Linux、Windows 各一份,外加语音模型文件。

这不是简单的"教 AI 用工具",而是在用户不知情的情况下完成了一个完整的软件安装流程

形态 4:参考文档型

1password Skill 有一个 references/ 目录,包含 get-started.mdcli-examples.md。SKILL.md 本体保持精简,只在 AI 判断需要深入了解时才加载 references。

这种设计特别适合复杂 API——SKILL.md 放"概述 + 常用命令",references 放"完整 API 文档"。

形态 5:编排型(Meta Skill)

coding-agent Skill 教 AI 如何 spawn 和管理其他 AI Agent

## The Pattern: workdir + background + pty

# 启动 Codex agent 在后台
bash pty:true workdir:~/project background:true command:"codex exec --full-auto 'Build a snake game'"

# 监控进度
process action:log sessionId:XXX

# 发送输入
process action:submit sessionId:XXX data:"yes"

这不再是"让 AI 用工具",而是让 AI 管理其他 AI

形态 6:自举型(Self-bootstrapping)

skill-creator Skill 教 AI 如何创建新的 Skill。它自带了 init_skill.pypackage_skill.pyquick_validate.py 三个脚本,覆盖了 Skill 的创建、打包、验证全流程。

这意味着系统可以自我扩展——AI 可以根据用户需求创建新的 Skill,打包后分发。

八、安全机制——不信任任何输入

社区贡献的 Skill 可能包含恶意代码。OpenClaw 在多个层面做了防护。

安装时安全扫描

src/security/skill-scanner.ts 在 Skill 安装时扫描所有 JS/TS 文件,检测危险代码模式:

type SkillScanFinding = {
  ruleId: string;
  severity: "info" | "warn" | "critical";
  file: string;
  line: number;
  message: string;
  evidence: string;
};

发现 critical 级别的问题会生成警告,用户可以选择是否继续。

输入净化

所有 frontmatter 中的安装参数都经过严格校验:

// Brew formula 校验——防止命令注入
const BREW_FORMULA_PATTERN = /^[A-Za-z0-9][A-Za-z0-9@+._/-]*$/;

// npm 包名校验
function normalizeSafeNpmSpec(raw) {
  if (!spec || spec.startsWith("-")) return undefined;  // 防止 flag 注入
  if (validateRegistryNpmSpec(spec) !== null) return undefined;
  return spec;
}

// 下载 URL 校验——只允许 http/https
function normalizeSafeDownloadUrl(raw) {
  const parsed = new URL(value);
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return undefined;
  return parsed.toString();
}

路径安全

下载目标目录有严格的边界检查,防止路径遍历攻击:

function resolveDownloadTargetDir(entry, spec) {
  const safeRoot = resolveSkillToolsRootDir(entry);  // ~/.openclaw/tools/<skill>/
  const resolved = path.resolve(safeRoot, raw);

  if (!isWithinDir(safeRoot, resolved)) {
    throw new Error(
      `Refusing to install outside the skill tools directory. ` +
      `targetDir="${raw}" resolves to "${resolved}". Allowed root: "${safeRoot}".`
    );
  }
  return resolved;
}

打包时也会拒绝包含 symlink 的 Skill,防止符号链接逃逸。

九、分发机制——ClawHub 生态

OpenClaw 有一个配套的 Skill 市场——ClawHub(clawhub.com),类似 npm 之于 Node.js。

clawhub Skill(对,它自己也是一个 Skill)提供了完整的生命周期管理:

clawhub search "postgres backups"     # 搜索
clawhub install my-skill              # 安装
clawhub update --all                  # 批量更新
clawhub publish ./my-skill            # 发布

Skill 的打包产物是一个 .skill 文件(本质是 zip),包含验证过的目录结构。

十、与同类方案的对比

维度OpenClaw SkillsCustom GPTs / PromptsMCP ToolsLangChain Tools
知识注入✅ 三层渐进式✅ 全量加载❌ 无❌ 无
可执行脚本✅ 内置✅ 需编码
依赖声明✅ 声明式
自动安装✅ 5 种安装器
平台感知✅ OS/bin/env
上下文优化✅ 按需加载❌ 全量✅ schema 级
安全扫描✅ 内置N/A
社区分发✅ ClawHub✅ GPT Store

OpenClaw Skills 的独特之处在于它将知识注入、工具集成、依赖管理、安全检查统一在一个声明式的框架内,而不是把这些问题留给开发者逐一解决。

十一、设计哲学与启示

从 OpenClaw Skills 系统中,可以提炼出几个值得借鉴的设计原则:

原则 1:上下文窗口是稀缺资源,要按需分配

不要把所有信息一股脑塞给模型。用元数据做路由,让模型自己决定需要什么深度的信息。这是一种信息的"懒加载"

原则 2:AI 不只需要知识,还需要工具

纯文本的 Prompt 只能教 AI "知道"——但 scripts/ 和 bin/ 让 AI 能"做到"。一个经过测试的确定性脚本,比让 AI 每次从头生成代码要可靠得多。

原则 3:声明式优于命令式

不是"请安装 Python",而是 requires.bins: ["python3"] + install: [{ kind: "brew", formula: "python" }]。运行时引擎负责"怎么做",Skill 只负责"需要什么"。

原则 4:安全不是事后补丁

从输入校验(brew formula 正则)、路径约束(isWithinDir)、到代码扫描(skill-scanner),安全考量嵌入在了每一层设计中。

原则 5:系统要能自我扩展

skill-creator Skill 赋予了 AI 创建新 Skill 的能力,ClawHub 提供了分发渠道。这种**自举(bootstrapping)**能力让生态可以有机增长。

十二、总结

OpenClaw 的 Skills 系统本质上是一个 为 AI Agent 设计的声明式包管理器

  • 它用 YAML frontmatter 声明能力和依赖
  • 用三层渐进式加载精细管理上下文开销
  • 用五种安装器自动解决运行时依赖
  • 用脚本和二进制提供确定性的执行能力
  • 用安全扫描和路径约束防御供应链攻击
  • 用 ClawHub 实现社区级的能力分发

在 AI Agent 框架百花齐放的今天,大多数项目都在解决"如何调用工具"的问题。OpenClaw 更进一步——它同时解决了**"怎么知道该用什么工具"、"怎么安装工具"、"怎么在有限上下文中管理几十个工具的知识"**这三个同等重要的问题。

这或许预示了 AI Agent 基础设施的一个方向:不只是工具编排,而是完整的能力生命周期管理。

十三、对 AI 使用者的启发——你不需要写代码,但需要这些思维

前面十二节偏技术实现,但 OpenClaw Skills 系统背后的设计理念,对每一个日常使用 AI 的人都有直接价值。

启发 1:上下文窗口是你最贵的资源,别浪费

很多人把一大堆背景信息、规则、示例全塞进 System Prompt,觉得"给得越多越好"。OpenClaw 的做法恰恰相反——50+ 个 Skill,常驻上下文只放 name + description(每个约 100 tokens),详细指令只在触发时才加载。

你可以这样做:给 AI 的 prompt 也应该分层。把"什么时候用"放在最前面,"怎么用的细节"放在后面或者单独的文件里。不是所有信息都需要一直在场。上下文塞太满,模型反而找不到重点。

启发 2:别让 AI 每次重新发明轮子——给它"预制件"

OpenClaw 的 Skill 不是简单地告诉 AI "你可以查天气",而是直接提供了写好的 Python 脚本、Bash 工具、甚至二进制程序。AI 不需要从头写代码,直接调用即可。

你可以这样做:如果你经常让 AI 做某类重复性任务(生成报告、处理数据、格式转换),与其每次用自然语言描述,不如准备好模板、脚本或 prompt 片段。把"可复用的确定性操作"固化下来,让 AI 做编排和决策,而不是每次都从零开始。

启发 3:自定义指令是刚需,不是可选项

OpenClaw 的核心假设是:模型本身很聪明,但缺少你的领域的程序性知识。它知道 Python 语法,但不知道你公司的 API 怎么调;它知道 SQL,但不知道你的表结构长什么样。

你可以这样做:无论你用 ChatGPT、Claude 还是任何 AI 工具,都应该投入时间构建自己的"Skill 库"——可以是 Custom Instructions、项目级的 CLAUDE.md / .cursorrules、或者 System Prompt 模板。这不是"锦上添花",这是决定 AI 输出质量的关键变量

启发 4:告诉 AI "什么时候不要用"比"什么时候用"更重要

注意 OpenClaw 的每个 Skill 都有明确的 "When NOT to Use" 部分。比如 weather Skill 会说"历史气象数据不要用我,去用专门的 API"。

你可以这样做:在写给 AI 的指令中,边界条件和排除规则往往比正面描述更有价值。"不要用 xxx 方式实现"、"这种情况走另一个流程"——这些约束能大幅减少 AI 的错误决策。

启发 5:上下文管理是你的责任,不是模型的

OpenClaw 花了大量工程投入来管理上下文:渐进式加载、token 预算、Skill 数量上限(150 个)、单文件大小上限(256KB)。它不指望模型自己"聪明地"处理 10 万 token 的 prompt。

你可以这样做:当你和 AI 长对话时,上下文会逐渐堆积和失焦。主动管理它——定期总结、裁剪无关历史、把核心规则放在 System Prompt 而非第 50 轮对话里。模型的注意力机制不是万能的,越精练的输入得到越精准的输出。

启发 6:声明目标和约束,比描述步骤更高效

OpenClaw 的 Skill 用声明式的 YAML 说"我需要 python3、我需要 OPENAI_API_KEY",运行时自动检查和安装。它不说"请先检查有没有 Python,如果没有就用 Homebrew 安装"。

你可以这样做:给 AI 的指令也一样——声明目标和约束,比描述步骤更好。"用 TypeScript 写一个 REST API,需要鉴权、分页、错误处理" 比 "第一步创建文件,第二步写路由,第三步..." 更能发挥模型的能力。让 AI 决定"怎么做",你负责定义"做成什么样"。

启发 7:安全意识要嵌入工作流,不能事后补

OpenClaw 对社区 Skill 做代码扫描、对安装参数做正则校验、对下载路径做边界检查。这些不是出了事才加的,是设计之初就有的。

你可以这样做:当你让 AI 执行代码、访问文件、调用 API 时,养成审查输出的习惯。特别是让 AI 写 shell 命令、数据库操作、或者处理用户输入时——AI 不会主动考虑安全边界,这是你的责任。


本文基于 OpenClaw 项目(github.com/openclaw/op… 源码分析。 项目 MIT 协议开源,欢迎阅读源码深入了解。