游乐游手机版
首页/AI教程/文章详情

OpenClaw深度解析八:Skill系统让LLM按需学习工作流

时间:2026-06-07 16:29
Skill系统通过SKILL md文件为LLM提供操作手册,采用元数据与正文分离、资格过滤仅暴露可用工具、渐进式披露只放摘要至系统提示等机制,解决文档规模、工具可用性及工作流标准化问题,使LLM精准按需执行技能。

场景:AI 怎么知道用哪个命令?

当你向 OpenClaw 提问「帮我查一下上海今天的天气」时,AI 是如何确定该执行哪条指令的?

OpenClaw 深度解析(八):Skill 系统——让 LLM 按需学习工作流

AI 随后返回了一段 curl "wttr.in/Shanghai?format=3" 的命令,执行后准确获取了天气数据。

然而,这背后隐藏着一个值得深入探讨的核心问题:LLM 本质上只是一个语言模型,它并非天生知晓“查天气需要调用 wttr.in”,也不熟悉“管理 GitHub PR 需使用 gh CLI”,更不曾了解“控制 Spotify 要借助 spotify-player”这类具体操作。

因此,必定有一种机制在“教导”它掌握这些技能。但若将 50 个工具的完整文档一股脑塞进系统提示,单是文档本身就可能撑爆上下文窗口。

这正是 Skill 系统要解决的核心矛盾:

  1. 文档规模难题:50 多个工具,每个都附带详尽的文档。若全部预加载,LLM 的上下文内存根本不堪重负。
  2. 工具可用性问题gh CLI 未安装,spotify-player 的环境变量也未配置——将这类不可用的工具暴露给 LLM,只会引发不可预知的错误。
  3. 工作流标准化需求:工具的用法必须让 LLM 精准理解并严格执行,绝不能依靠“猜测”来运作。
  4. 用户体验优化:用户更期望直接输入 /weather 上海 来触发查询,而非每次都用完整的自然语言表述。

一、SKILL.md:专为 LLM 设计的文档格式

为什么选用 Markdown 而非代码?

Skill 并非传统的程序代码——它本质上是一份“面向 LLM 的操作手册”。LLM 最擅长理解自然语言与 Markdown 结构,因此最合理的形式就是带有 YAML frontmatter 的 Markdown 文件。

每个 skill 对应一个目录,内部包含一个 SKILL.md

--- name: weather description: "Get current weather and forecasts via wttr.in or Open-Meteo. Use when: user asks about weather, temperature, or forecasts for any location. NOT for: historical weather data, severe weather alerts." metadata: { "openclaw": { "emoji": "?️", "requires": { "bins": ["curl"] } } } --- # Weather Skill ## When to Use ✅ **USE this skill when:** - "What's the weather?" - "Will it rain today/tomorrow?" ## Commands ```bash # One-line summary curl "wttr.in/London?format=3" ```

文件被划分为两个主要部分。首先是 frontmatter(供机器读取的部分)

```typescript // src/agents/skills/types.ts type OpenClawSkillMetadata = { always?: boolean; // 是否绕过资格检查,强制包含 emoji?: string; // 显示用 primaryEnv?: string; // 主要依赖的环境变量 requires?: { bins?: string[]; // 需要哪些可执行文件 anyBins?: string[]; // 满足其中一个即可 env?: string[]; // 需要哪些环境变量 config?: string[]; // 需要哪些配置键 }; install?: SkillInstallSpec[]; // 如何安装依赖 }; ```

frontmatter 中包含两个关键字段。其一是 description,它如同系统提示中唯一的“代言人”,LLM 仅凭这一行描述来决定是否启用该 skill。其二是 metadata.openclaw.requires.bins,它声明了依赖的可执行文件;若运行时文件缺失,整个 skill 将从系统提示中移除。

其次是 正文(供 LLM 阅读的部分),其中详细说明了“何时使用”、“何时不用”、命令模板及注意事项。这部分不会直接进入系统提示,只有 LLM 主动读取时才会加载到上下文中。

这种分离设计是整个系统的核心:元数据给机器解析,正文给 LLM 参考,摘要则作为中间层传递决策信号。

二、多来源发现与优先级(workspace.ts

问题:skill 从哪里来?

用户可能同时拥有系统内置的 skill、自行安装的 skill,以及项目级别的 skill。系统必须能发现这些 skill,并且在同名时遵循明确的覆盖规则。

loadSkillEntries() 会扫描六个来源,按优先级从低到高排列:

extra(openclaw.yml 中 skills.load.extraDirs 指定)< bundled(核心内置,代码库 skills/ 目录,随 OpenClaw 发布)< managed(~/.openclaw/skills/,用户通过 openclaw skills install 安装)< agents-skills-personal(~/.agents/skills/,个人全局 skill)< agents-skills-project(工作区 .agents/skills/,项目级 skill)< workspace(工作区 skills/,最高优先级)

优先级通过 Map 实现——后赋值的项会覆盖先赋值的项:

// src/agents/skills/workspace.ts const merged = new Map(); 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/github/SKILL.md,它将完全覆盖系统内置的 github skill,而不是合并。用户可以为特定项目定制任意 skill 的行为。

嵌套目录探测

resolveNestedSkillsRoot() 采用了一套精巧的启发式逻辑:如果 dir/skills/*/SKILL.md 存在,则将 dir/skills 视为真正的 skill 根目录。这样一来,~/.openclaw/skills/ 目录下既可以直接存放 github/SKILL.md,也可以放置一个包含 skills/ 子目录的完整工具包——两种结构都能被正确识别。

三、资格过滤:仅暴露可用的 skill

问题:gh CLI 未安装,还需要向 LLM 展示 GitHub skill 吗?

shouldIncludeSkill() 在加载后会执行运行时资格检查:

// 检查 requires.bins:这些可执行文件存在吗? // 检查 requires.anyBins:至少有一个存在吗? // 检查 requires.env:这些环境变量设置了吗? // 检查 requires.config:配置文件中有这些键吗? // 检查 os:当前操作系统匹配吗?(如仅限 macOS 的 skill) // always: true → 跳过所有检查,强制包含

如果 gh 未安装,requires.bins: ["gh"] 检查会失败,GitHub skill 便直接从列表中移除——LLM 的系统提示中完全不会出现任何关于它的信息。

过滤之后还有第二步:剔除那些 disable-model-invocation: true 的 skill。这类 skill 只能通过 /命令 显式触发,LLM 自主决策时无法看到它们。

资格上下文:远端信息

SkillEligibilityContext.remote 支持注入远端节点的状态:

type SkillEligibilityContext = { remote?: { platforms: string[]; hasBin: (bin: string) => boolean; // 目标节点上 curl 存在吗? hasAnyBin: (bins: string[]) => boolean; note?: string; }; };

当 Agent 在远端 Node Host 上执行时,资格检查针对的是目标节点的环境,而非 Gateway 所在的机器。因此,即使你的远端 Linux 服务器有 gh 而本地 Mac 没有,GitHub skill 仍然会展示给 LLM。

四、渐进式披露:系统提示中仅包含摘要

问题:150 个 skill 的完整文档有多大?

假设每个 SKILL.md 平均 2000 字节,150 个 skill 就是 300KB 纯文本——这远超大多数模型的上下文窗口限制。

解决方案就是渐进式披露:系统提示中只放置每个 skill 的三个字段——name、description、location,正文则留到 LLM 决定使用时再去读取。

formatSkillsForPrompt() 将过滤后的 skill 列表格式化为:

weather Get current weather and forecasts via wttr.in or Open-Meteo. Use when: user asks about weather, temperature, or forecasts for any location. NOT for: historical weather data, severe weather alerts. ~/.openclaw/skills/weather/SKILL.md github GitHub operations via gh CLI: issues, PRs, CI runs, code review. Use when: (1) checking PR status or CI, (2) creating/commenting on issues... ~/.openclaw/skills/github/SKILL.md

注意 location 字段中的路径:/Users/alice/.openclaw/skills/weather/SKILL.md 被压缩为 ~/.openclaw/skills/weather/SKILL.md。这一细节在 compactSkillPaths() 中实现,每个路径大约能节省 5-6 个 token,150 个 skill 合计可节省 600-900 token。

Token 预算控制

// src/agents/skills/workspace.ts const DEFAULT_MAX_SKILLS_IN_PROMPT = 150; const DEFAULT_MAX_SKILLS_PROMPT_CHARS = 30_000; const DEFAULT_MAX_SKILL_FILE_BYTES = 256_000; // 超出字符限制时,用二分搜索找最大可容纳前缀 if (!fits(skillsForPrompt)) { let lo = 0, hi = skillsForPrompt.length; while (lo < hi) { const mid = Math.ceil((lo + hi) / 2); if (fits(skillsForPrompt.slice(0, mid))) lo = mid; else hi = mid - 1; } skillsForPrompt = skillsForPrompt.slice(0, lo); }

五、系统提示中的元指令:指导 LLM 正确使用

问题:LLM 看到 skill 列表后,知道该怎么做吗?

仅仅提供列表还不够——LLM 还需要明确的行为规则。buildSkillsSection() 的作用就是将列表与指令一同注入系统提示:

// src/agents/system-prompt.ts function buildSkillsSection(params: { skillsPrompt?: string; readToolName: string }) { return [ "## Skills (mandatory)", "Before replying: scan entries.", `- If exactly one skill clearly applies: read its SKILL.md at with ${readToolName}, then follow it.`, "- If multiple could apply: choose the most specific one, then read/follow it.", "- If none clearly apply: do not read any SKILL.md.", "Constraints: never read more than one skill up front; only read after selecting.", trimmed, // ← 摘要块 ]; }

这段指令的设计有几个关键要点:

  1. (mandatory) 标记为“强制”,意味着 LLM 每次回复前都必须扫描,而非“偶尔参考”。
  2. 明确指定使用 read 工具加载 处的 SKILL.md,LLM 无需自行猜测路径。
  3. “never read more than one skill up front” 这条规则防止 LLM 一次性读取所有可能相关的 skill,从而浪费大量 token。
  4. “then follow it” 表明读取后需遵循其中的指示,而不仅仅是作为参考信息。

最终效果如何?用户提问「查一下上海天气」→ LLM 扫描摘要 → 匹配到 weather skill 的 description → 调用 read("~/.openclaw/skills/weather/SKILL.md") → 读取完整工作流 → 成功执行 curl "wttr.in/Shanghai?format=3"

在整个过程中,LLM 是主动参与者,而非被动执行脚本的机器。Skill 系统通过“摘要 + 路径”为 LLM 提供恰好足够的信息来做出决策,完整内容仅在真正需要时才加载。

六、/命令:用户显式触发路径

问题:用户想输入 /weather 上海 而非完整自然语言

buildWorkspaceSkillCommandSpecs() 会扫描所有 user-invocable: true 的 skill(默认为 true),然后为消息平台注册斜杠命令:

// src/auto-reply/skill-commands.ts // /weather → weather skill // /github → github skill // 冲突时自动加 _2 后缀

命令名还会经过规范化处理:

function sanitizeSkillCommandName(raw: string): string { return raw .toLowerCase() .replace(/[^a-z0-9_]+/g, "_") .replace(/_+/g, "_") .replace(/^_+|_+$/g, "") .slice(0, 32); // Discord 限制:命令名最长 32 字符 }

两种触发模式

用户发送 /weather 上海 后,系统会查找 weather 对应的 SkillCommandSpec,随后执行其中一条路径:

模式一:经过 LLM(默认)

/weather 上海 → resolveSkillCommandInvocation() 识别命令 → 将 "weather 上海" 作为用户消息注入会话 → LLM 正常处理(仍然会读 SKILL.md 并做决策)

模式二:确定性工具分发(command-dispatch: tool

如果 SKILL.md 的 frontmatter 中声明了:

command-dispatch: tool command-tool: exec command-arg-mode: raw

那么触发将完全绕过 LLM:

/weather 上海 → dispatch.kind === "tool" → 直接调用 exec 工具,args = "上海"(原样转发) → LLM 不参与任何决策

这种模式适用于“输入明确、工具已知、无需推理”的场景,执行速度更快,且行为完全可预期。

七、沙盒环境下的 skill 同步

当 Agent 在 Docker 沙盒中运行时,skill 文件需要从宿主机同步到容器内:

// src/agents/skills/workspace.ts export async function syncSkillsToWorkspace(params: { sourceWorkspaceDir: string; // 宿主机工作区 targetWorkspaceDir: string; // 容器内工作区 }) { // 1. 加载宿主机的 skill 列表 // 2. 清空容器内的 skills/ 目录 // 3. 把每个 skill 目录 cp 进容器 // 4. 路径安全检查(防路径遍历) }

同步完成后,容器内的 read 工具读取的是容器内的 SKILL.md 副本,而非宿主机路径。resolveSandboxPath() 会确保每个 skill 目录名都是安全的,不会通过 ../.. 这类名称逃逸到容器外部。

小结:渐进式披露驱动的 LLM 工作流

Skill 系统的核心,实则是一个极为简洁的设计哲学:不将文档转变为代码,而是将文档传授给 LLM,让 LLM 依据文档行动。

阶段机制目的
发现六来源扫描 + Map 优先级覆盖让用户/项目可以覆盖系统内置 skill
过滤bins/env/os 资格检查只向 LLM 暴露当前环境真正可用的 skill
摘要注入name + description + location,字符预算控制最小 token 开销让 LLM 能决策
元指令## Skills (mandatory) + read 工具路径告诉 LLM 如何用这些信息
渐进式披露LLM 决策后主动调用 read(SKILL.md)完整文档只在真正需要时才进入上下文
/命令buildWorkspaceSkillCommandSpecs() 注册斜杠命令用户显式触发,绕过自然语言推理
确定性分发command-dispatch: tool执行路径完全不经过 LLM

这一设计使得 skill 作者只需编写 Markdown,而无需了解 LLM 推理、工具注册或消息平台的细节——一份 SKILL.md 文件,即可让 AI 按照作者的意图精准行动。

来源:https://juejin.cn/post/7614889731939909659
上一篇AI视频在中国每天烧140万亿Token是内容泡沫还是生产力 下一篇用AI编程助手从零开发安卓应用的真实体验心得
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Windows Docker Desktop RabbitMQ生产级部署完整指南
AI教程 · 2026-06-29

Windows Docker Desktop RabbitMQ生产级部署完整指南

前言 在 Windows 本地开发环境中,直接安装 RabbitMQ 确实颇为周折:需要单独配置 Erlang 运行环境、手动管理环境变量、服务启停全凭手工操作。更令人困扰的是,版本兼容冲突、端口占用、环境不一致等问题层出不穷。笔者见过不少开发者为搭建环境就得耗费整整半天时间。 相比之下,借助 Do

AI搜索重构制造业采购逻辑的阿里云企业级GEOCMS优化实践
AI教程 · 2026-06-29

AI搜索重构制造业采购逻辑的阿里云企业级GEOCMS优化实践

先分享一个切实感受。过去两年,我们与福建制造企业合作较为频繁,发现一个非常突出的现象:超过80%的企业官网,产品参数仍然存放在PDF或图片中。AI爬虫?根本无法抓取。这些企业技术实力不弱、资质证照齐全、应用案例也丰富,但在AI搜索这一全新战场上,它们几乎处于隐身状态。 一、一个正在发生的行业变化 A

阿里云Token Plan团队版功能价格与省钱购买指南
AI教程 · 2026-06-29

阿里云Token Plan团队版功能价格与省钱购买指南

阿里云百炼近期推出了名为“Token Plan 团队版”的全新服务,这一服务专为企业与开发者量身打造,定位为AI大模型订阅平台。通过引入Credits作为统一计量单位,将文本生成、图像生成等多模态AI能力纳入单一计费体系,同时无缝兼容主流AI编程工具及智能体(Agent)生态系统。其核心亮点包括:全

阿里云物联网.NET Core客户端位置信息上报
AI教程 · 2026-06-29

阿里云物联网.NET Core客户端位置信息上报

阿里云物联网平台的位置服务并非一个完全独立的功能模块。位置信息可包含二维坐标与三维坐标,而位置数据的来源本质上是借助设备属性进行上传。换言之,若要让设备上报位置,您需先将其视为一个普通属性进行处理。 1)添加二维位置数据 操作过程十分简洁。进入数据分析 → 空间数据可视化 → 二维数据,点击添加,将

年阿里云服务器选型配置与网站部署全攻略
AI教程 · 2026-06-29

年阿里云服务器选型配置与网站部署全攻略

2026年,阿里云服务器生态已高度成熟,形成了清晰的轻量应用服务器与ECS云服务器两大产品阵营。无论你是计划搭建个人博客、企业官网,还是运营电商平台、进行应用开发,基本都能找到理想的解决方案。本指南将从服务器选型、配置选择、部署流程到安全运维,系统梳理2026年最实用的操作要点,帮助你少走弯路,让网