OpenClaw 技能系统机制深度解析
仓库地址:https://github.com/openclaw/openclaw
一、概述
在 OpenClaw 中,技能(Skill) 是一种让 AI 智能体获得特定能力的机制。与传统 MCP 或 Function Calling 的工具注册方式不同,OpenClaw 采用声明式设计——技能不是作为自定义工具注册给大模型,而是通过系统提示词让 AI 主动扫描和选择。
技能 vs 传统工具注册
| 特性 | 传统工具注册 | OpenClaw 技能 |
|---|---|---|
| 注册方式 | Function Calling / MCP | System Prompt 强制扫描 |
| 内容加载 | 初始全部加载 | 按需动态读取 |
| 更新机制 | 重新注册 | 文件系统监控 |
| 隔离性 | 全局注册 | 工作区隔离 |
二、技能加载机制
2.1 技能来源与优先级
OpenClaw 支持从多个来源加载技能,按优先级从低到高排列。源码位于 src/agents/skills/workspace.ts:491-510:
// Precedence: extra < bundled < managed < agents-skills-personal < agents-skills-project < workspace
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); } // 最高优先级
| 优先级 | 来源 | 路径 |
|---|---|---|
| 1 (最低) | extra | config.skills.load.extraDirs + 插件内置 |
| 2 | bundled | packageRoot/skills |
| 3 | managed | ~/.openclaw/skills |
| 4 | agents-skills-personal | ~/.agents/skills |
| 5 | agents-skills-project | {workspace}/.agents/skills |
| 6 (最高) | workspace | {workspace}/skills |
关键特性:同名技能会按照优先级覆盖,高优先级来源的同名技能会替代低优先级的。
2.2 技能快照机制
每个会话在启动时会生成一个技能快照(SkillSnapshot),这个快照在会话期间保持固定。源码位于 src/agents/skills/types.ts:86-93:
export type SkillSnapshot = {
prompt: string; // 给系统提示词用的技能简介字符串
skills: Array<{ name: string; primaryEnv?: string; requiredEnv?: string[] }>; // 技能元数据列表
skillFilter?: string[]; // 技能过滤器
resolvedSkills?: Skill[]; // 解析后的技能对象
version?: number; // 版本号
};
关键点:
prompt:用于插入系统提示词的技能列表字符串skills:技能元数据列表- 会话启动时生成,运行期间固定,不会"累积"更多技能
三、渐进式披露原理
3.1 第一层:系统提示词中的技能列表
在会话开始时,AI 会在系统提示词中看到所有可用技能的简介列表(名称 + 描述 + 路径)。源码位于 src/agents/system-prompt.ts:21-37:
function buildSkillsSection(params: { skillsPrompt?: string; readToolName: string }) {
const trimmed = params.skillsPrompt?.trim();
if (!trimmed) {
return [];
}
return [
"## Skills (mandatory)",
// 中文:## 技能(强制)
"Before replying: scan <available_skills> <description> entries.",
// 中文:在回复之前,请扫描 <available_skills> 中的 <description> 条目。
`- If exactly one skill clearly applies: read its SKILL.md at <location> with \`${params.readToolName}\`, then follow it.`,
// 中文:如果正好有一个技能明显适用,使用 `Read` 工具读取该技能位于 <location> 的 SKILL.md,然后遵循执行。
"- If multiple could apply: choose the most specific one, then read/follow it.",
// 中文:如果有多个可能适用,选择最具体的一个,然后读取/遵循执行。
"- If none clearly apply: do not read any SKILL.md.",
// 中文:如果没有明显适用的技能,不要读取任何 SKILL.md。
"Constraints: never read more than one skill up front; only read after selecting.",
// 中文:约束:永远不要预先读取多个技能,只能在选择之后读取。
"- When a skill drives external API writes, assume rate limits: prefer fewer larger writes, avoid tight one-item loops, serialize bursts when possible, and respect 429/Retry-After.",
// 中文:当技能驱动外部 API 写入时,需要考虑速率限制:优先选择较少的较大写入,避免紧密的单项循环,在可能的情况下对突发请求进行序列化,并遵守 429/Retry-After 响应。
trimmed,
"",
];
}
3.2 第二层:按需读取完整 SKILL.md
AI 判断需要某个技能后,使用 Read 工具 读取完整的 SKILL.md 文件内容。这个内容作为 Tool Result 返回给 AI,作为执行任务的参考。
// 系统提示词告诉 AI:
"read its SKILL.md at <location> with `Read`, then follow it."
// 中文:使用 Read 工具读取该技能位于 <location> 的 SKILL.md,然后遵循执行
// AI 调用 Read 工具 → 返回 SKILL.md 内容作为 tool_result
// AI 根据 tool_result 中的技能指令执行任务
3.3 两种格式
技能列表在系统提示词中有两种格式,源码位于 src/agents/skills/workspace.ts:544-562:
完整格式(技能数量少时使用):
<available_skills>
<skill>
<name>github</name>
<description>Manage GitHub repositories, issues, PRs...</description>
<location>~/project/skills/github/SKILL.md</location>
</skill>
</available_skills>
紧凑格式(技能数量多时使用,只保留名称和路径):
<available_skills>
<skill><name>github</name><location>~/project/skills/github/SKILL.md</location></skill>
<skill><name>weather</name><location>~/project/skills/weather/SKILL.md</location></skill>
...
</available_skills>
四、工作区隔离设计
4.1 为什么需要隔离
如果没有工作区隔离,所有技能都会加载到系统提示词中,导致:
- Token 爆炸:技能列表占用大量上下文空间
- 效果下降:AI 需要在大量不相关的技能中做选择
- 隐私问题:项目 A 的技能可能泄露到项目 B
4.2 隔离机制
每个工作区有独立的 skills/ 目录,技能列表专属于该工作区:
project-a/
└── skills/ # 只包含项目 A 相关的技能
├── github/
└── docker/
project-b/
└── skills/ # 只包含项目 B 相关的技能
├── k8s/
└── terraform/
优先级确保专一性:当前工作区的技能会优先被使用,同名技能会覆盖其他来源的同名技能。
五、技能刷新机制
5.1 文件监控
当运行期间用户新增、修改或删除 SKILL.md 文件时,系统会通过 chokidar 监控并触发刷新。源码位于 src/agents/skills/refresh.ts:98-207:
export function registerSkillsChangeListener(listener: (event: SkillsChangeEvent) => void) {
listeners.add(listener);
return () => {
listeners.delete(listener);
};
}
export function bumpSkillsSnapshotVersion(params?: {
workspaceDir?: string;
reason?: SkillsChangeEvent["reason"];
changedPath?: string;
}): number {
// 触发技能快照版本更新
}
5.2 刷新是替换,非累积
关键点:刷新是替换技能列表,不是累积更多技能。
旧快照 (10个技能) → 文件变化 → 新快照 (可能是 8 个或 12 个技能)
- 删除了一个
SKILL.md→ 新快照少一个技能 ✓ - 新增了一个
SKILL.md→ 新快照多一个技能 ✓ - 不会因为执行了任务就自动"学会"新技能
六、关键源码解读
6.1 技能加载入口
src/agents/skills/workspace.ts:440-527:
// 加载所有来源的技能并合并
const bundledSkills = bundledSkillsDir
? loadSkills({ dir: bundledSkillsDir, source: "openclaw-bundled" })
: [];
const extraSkills = mergedExtraDirs.flatMap((dir) => loadSkills({...}));
const managedSkills = loadSkills({ dir: managedSkillsDir, source: "openclaw-managed" });
const workspaceSkills = loadSkills({ dir: workspaceSkillsDir, source: "openclaw-workspace" });
// 按优先级合并
const merged = new Map<string, Skill>();
// ... 按优先级顺序合并
6.2 快照生成与持久化
src/agents/agent-command.ts:902-932:
const needsSkillsSnapshot = isNewSession || !sessionEntry?.skillsSnapshot;
const skillsSnapshotVersion = getSkillsSnapshotVersion(workspaceDir);
const skillsSnapshot = needsSkillsSnapshot
? buildWorkspaceSkillSnapshot(workspaceDir, {...})
: sessionEntry?.skillsSnapshot; // 复用已有快照
// 持久化到会话存储
if (skillsSnapshot && sessionStore && sessionKey && needsSkillsSnapshot) {
await persistSessionEntry({ sessionStore, sessionKey, storePath, entry: next });
}
6.3 技能提示词解析
src/agents/skills/workspace.ts:695-713:
export function resolveSkillsPromptForRun(params: {...}): string {
// 优先使用快照中的 prompt
const snapshotPrompt = params.skillsSnapshot?.prompt?.trim();
if (snapshotPrompt) {
return snapshotPrompt;
}
// 否则重新构建
if (params.entries && params.entries.length > 0) {
return buildWorkspaceSkillsPrompt(params.workspaceDir, {...});
}
return "";
}
七、总结
OpenClaw 的技能系统采用了独特的声明式设计:
- 渐进式披露:系统提示词中只加载技能简介,按需通过 Read 工具读取完整内容
- 静态快照:会话启动时生成技能列表,运行期间固定不变
- 工作区隔离:每个工作区有独立的技能列表,避免"技能爆炸"
- 刷新替换:文件变化触发快照重建,是替换而非累积
这种设计既保证了灵活性,又避免了传统工具注册方式的诸多问题。
参考资料
- 源码仓库:https://github.com/openclaw/openclaw
- 技能相关源码:
src/agents/skills/workspace.ts- 技能加载与合并src/agents/skills/types.ts- 类型定义src/agents/skills/refresh.ts- 文件监控与刷新src/agents/system-prompt.ts- 系统提示词构建src/agents/agent-command.ts- 会话与快照管理
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)