第 18 章 Skills
18.1 Skills 的本质
Skills 不是代码插件,而是给 Agent 看的操作手册。每个 Skill 是一个 Markdown 文件(SKILL.md),描述一项特定能力的操作方法:调用哪些工具、按什么顺序、用什么参数。
用户:"帮我查天气"
→ Agent 扫描 system prompt 中的 <available_skills>
→ 发现 weather skill 的描述匹配
→ read("/path/to/weather/SKILL.md")
→ 按 SKILL.md 指引调用 web_fetch 获取数据
→ 格式化返回给用户这是纯提示词层面的扩展,不需要修改代码,不需要重启服务。任何人都可以写一个 SKILL.md 来扩展 Agent 的能力。
18.2 Skill 文件结构
一个完整的 Skill 目录:
skills/weather/
├── SKILL.md # 主文件(必须)
├── assets/ # 可选:辅助资源
└── scripts/ # 可选:可执行脚本SKILL.md 的 frontmatter
---
name: weather
description: >
Get current weather and forecasts via wttr.in or Open-Meteo.
Use when user asks about weather, temperature, or forecasts.
NOT for: historical data, severe alerts.
always: false
skillKey: weather
primaryEnv: OPENWEATHER_KEY
emoji: 🌤
homepage: https://clawhub.com/skills/weather
os: [darwin, linux]
requires:
bins: [curl]
anyBins: [wget, curl]
env: [OPENWEATHER_KEY]
config: [weather.apiKey]
install:
- kind: brew
formula: wttr
bins: [wttr]
- kind: node
package: weather-cli
bins: [weather]
---
# Weather Skill
## 使用方法
当用户询问天气时...
(以下是完整的操作说明)18.3 完整类型定义
type OpenClawSkillMetadata = {
always?: boolean; // true = 始终注入完整内容,无需 Agent 主动读取
skillKey?: string; // 唯一标识符(用于过滤和 env 覆盖)
primaryEnv?: string; // 主依赖环境变量
emoji?: string;
homepage?: string;
os?: string[]; // ["darwin", "linux", "win32"](平台限制)
requires?: {
bins?: string[]; // 全部都要有(AND 关系)
anyBins?: string[]; // 有一个就行(OR 关系)
env?: string[]; // 需要的环境变量
config?: string[]; // 需要的 openclaw.json config 字段
};
install?: SkillInstallSpec[];
};
type SkillInstallSpec = {
kind: "brew" | "node" | "go" | "uv" | "download";
label?: string;
bins?: string[]; // 安装后提供的命令行工具
os?: string[]; // 限定操作系统
formula?: string; // brew formula
package?: string; // npm/go/uv 包名
module?: string; // node 模块名(与 package 不同时使用)
url?: string; // 下载 URL(kind=download)
archive?: string; // 压缩包内路径
extract?: boolean; // 是否解压
stripComponents?: number; // tar --strip-components
targetDir?: string; // 安装目标目录
};18.4 Skills 的四个来源与优先级
Skills 来自四个来源:
// 1. 内置 Skills(随 OpenClaw 打包)
resolveBundledAllowlist() // 按 config 的 allowlist 过滤
isBundledSkillAllowed() // 是否在白名单中
// 2. Workspace Skills(用户自定义)
loadWorkspaceSkillEntries() // 扫描 ~/workspace/skills/*/SKILL.md
// 3. 插件 Skills
plugin-skills.ts // 插件通过 SDK 注册
// 4. 远程 Skills(从 clawhub.com 安装)
syncSkillsToWorkspace() // 同步到本地 workspace同名冲突优先级(高 → 低): Workspace > 插件 > 内置
这允许用户通过在 workspace 放置同名 SKILL.md 来覆盖内置 Skills,实现个性化定制。
18.5 按需加载设计
Step 1:构建描述摘要(system prompt 构建时)
buildWorkspaceSkillsPrompt()输出:
<available_skills>
<skill>
<name>weather</name>
<description>Get current weather and forecasts...</description>
<location>/Users/claw/.openclaw/workspace/skills/weather/SKILL.md</location>
</skill>
<skill>
<name>discord</name>
<description>Discord ops via the message tool...</description>
<location>/opt/homebrew/.../skills/discord/SKILL.md</location>
</skill>
...(最多几十个条目,只含名称 + 描述 + 路径)
</available_skills>Step 2:Agent 按需读取(运行时)
Agent 判断需要 weather skill
→ read("/path/to/weather/SKILL.md") // 消耗约 500-2000 token
→ 获得完整操作手册
→ 按手册操作Token 节省效果
| 场景 | 消耗 |
|---|---|
| 预加载所有 50 个 Skills 的完整内容 | ~50,000 token/次 |
| 按需加载(通常只需 1-2 个) | ~2,000 + 1,000 × n token |
在 Agent 只需要 2 个 Skills 的典型场景下,按需加载节省约 46,000 token——约节省 23% 的 200k context window。
约束:每次最多预读一个
System prompt 中有明确约束:
"Constraints: never read more than one skill up front; only read after selecting."防止 Agent 一次性读取多个 Skills 导致 context 被快速消耗。
18.6 always: true 的 Skills
标记 always: true 的 Skills 会在每次请求时自动注入完整内容,不需要 Agent 主动读取:
---
always: true
---适用场景:
- Skill 内容很短(< 500 token)
- Agent 几乎每次请求都需要它
- 单看描述无法判断是否需要,需要读完整内容才能决策
例如 memory 相关的 Skills 通常标记为 always: true,因为 Agent 在几乎每次请求前都需要执行 memory recall。
18.7 Skill Commands(命令快捷方式)
每个 Skill 可以注册用户可调用的 slash 命令:
type SkillCommandSpec = {
name: string; // 命令名(如 "weather")
skillName: string; // 对应的 Skill 名
description: string;
dispatch?: SkillCommandDispatchSpec;
};
type SkillCommandDispatchSpec = {
kind: "tool";
toolName: string; // 直接调用这个工具
argMode?: "raw"; // 将用户参数原样传入
};短路 dispatch
dispatch.kind = "tool" + argMode = "raw" 实现了"绕过 LLM 直接调工具"的路径:
用户:/weather 北京
↓ 不经过 LLM
→ 直接调用 weather_fetch(args="北京")
→ 返回结果
→ 节省一次 LLM 调用对高频、低复杂度的操作(查天气、查汇率)有显著的延迟和成本优势。
18.8 Skills 调用策略
type SkillInvocationPolicy = {
userInvocable: boolean; // 用户可以用 slash 命令触发
disableModelInvocation: boolean; // 禁止 Agent 自主触发
};disableModelInvocation = true 用于高权限或高成本的 Skills(发邮件、发 Twitter、修改系统配置),确保只有用户明确要求时才执行,Agent 不会自作主张。
18.9 Skills 的环境变量覆盖
文件: src/agents/skills/env-overrides.ts
Skills 可以声明依赖的环境变量(通过 primaryEnv),用户可以在 config 中为特定 Skill 配置覆盖值:
{
"skills": {
"weather": {
"env": {
"OPENWEATHER_KEY": "your_api_key_here"
}
}
}
}applySkillEnvOverrides 在 Skill 执行时将这些变量注入到工具的执行环境中,不污染全局进程环境。
18.10 Skills 的资格检查
在将 Skills 包含到 system prompt 之前,filterWorkspaceSkillEntries 检查每个 Skill 的资格:
type SkillEligibilityContext = {
remote?: {
platforms: string[];
hasBin: (bin: string) => boolean;
hasAnyBin: (bins: string[]) => boolean;
note?: string;
};
};检查项:
- 平台匹配:Skill 的
os字段是否包含当前平台 - 依赖工具:
requires.bins(ALL)/requires.anyBins(ANY)是否存在 - 环境变量:
requires.env中的变量是否已设置 - Config 字段:
requires.config中的字段是否已配置
不满足条件的 Skills 不出现在 <available_skills> 列表中,避免 Agent 尝试使用注定失败的 Skill。
18.11 本章要点
- Skills 是纯 Markdown 的操作手册,不是代码插件——零代码扩展能力
- 按需加载设计在典型场景下节省约 46,000 token/次
- 四个来源,Workspace 优先级最高,允许用户覆盖内置 Skills
- Skill Commands 支持短路 dispatch,绕过 LLM 直接调用工具
disableModelInvocation保护高权限 Skills 不被 Agent 自主触发- 资格检查确保 Agent 不会尝试使用不满足依赖条件的 Skill
推荐阅读的源文件
| 文件 | 优先级 | 说明 |
|---|---|---|
src/agents/skills/types.ts | ★★★ | 完整类型定义 |
src/agents/skills/workspace.ts | ★★★ | Skills 加载 + prompt 生成 |
src/agents/skills/config.ts | ★★ | 内置 Skills 配置与过滤 |
src/agents/skills/filter.ts | ★★ | 资格检查逻辑 |
src/agents/skills/env-overrides.ts | ★ | 环境变量覆盖 |
src/agents/skills/frontmatter.ts | ★ | frontmatter 解析 |