Day 11 / 共 20 天 · 第 3 周 技能与渠道

技能系统 skills

第 2 周讲完了"大脑",第 3 周讲"手和嘴"。今天开篇:skills/ 里 52 个技能怎么定义(SKILL.md + frontmatter)、三层发现加载、以及一个反直觉的事实——技能不是可调用的工具,而是"渐进式披露的提示词片段"。它呼应 Day 09 系统提示里的"技能目录段",也为明天(Day 12)讲"扩展"做对照。

📍 你在整门课的位置(第 3 周 · 技能与渠道)
W2 大脑· Day11 技能 Day12 扩展 Day13 渠道抽象 Day14 渠道实现 Day15 归一化· W4 安全/部署
💡 用一个类比先兜住今天("图书馆 + 操作手册"世界观) 今天两个核心类比:技能是"操作手册"、工具是"按钮"——手册本身不执行,它教模型"遇到这类需求怎么一步步做",模型再用已有按钮(工具)照做;渐进式披露像逛图书馆——先看书目(技能目录),需要哪本才借来读全文(read SKILL.md),而不是把整个图书馆搬回家(全塞进提示)。抓住这两个类比,今天就通了。
L01

技能 vs 工具

🤔 痛点:想给助理加个新本领(比如查股价),难道要改源码、重新编译发布? 如果每加一个玩法都得写代码、跑构建、重启,那"教助理新本领"就成了程序员的专利,普通人根本插不上手。能不能像写一篇 Word 说明书那样,零代码就给助理加本领?
💡 本质:技能 = 一篇 markdown 操作手册,靠"提示词 + 模型读文件照做"生效 技能不是可执行代码,而是一份 SKILL.md——里面用大白话(+几行 curl/bash)教模型"遇到这类需求怎么办"。加一个技能 = 加一篇说明书,零代码。这让非程序员也能给助理"教新本领"。工具则相反:它是真能跑的函数(按钮)。

OpenClaw 里"技能(skill)"和"工具(tool)"是两种不同机制,别混:

  • 工具:一段可执行代码({name, description, parameters, execute}),模型调用它、它跑函数返回结果。
  • 技能:一个 SKILL.md markdown 文件(数据,无可执行代码),靠"提示词 + 模型自己读文件照做"生效。
工具是"按钮",技能是"操作手册" 工具像遥控器上的按钮——按下去(调用)就执行一个确定动作。技能像一本操作手册——它本身不"执行",而是教模型"遇到这类需求时该怎么一步步做"(通常是"用 curl 调这个 API""按这个格式输出")。模型读了手册,用已有的工具(如 exec 跑 curl)去照做。所以加一个技能 = 加一篇 markdown 说明书,零代码;这让非程序员也能给助理"教新本领"。

👶 小白:既然技能不是代码、不能"被调用",那我写完 SKILL.md,助理到底怎么就"会"了?

👨‍🏫 老师:靠"读",不靠"调"。系统提示里会列出所有技能的目录(名字+简介+文件路径)。模型看到你的问题,觉得某个技能对口,就用它已有的 read 工具把那份 SKILL.md 读进来,然后照着手册里的步骤,用它已有的工具(跑 curl、写文件等)去做。所以技能本身零可执行代码,"执行力"全借自模型已有的通用工具——手册负责教方法,工具负责出力。

L02

SKILL.md 长啥样

skills/ 下 52 个目录,每个核心是 SKILL.md(YAML frontmatter + markdown 正文)。真实例子 skills/weather/SKILL.md:1-6

---
name: weather
description: "Get current weather and forecasts via wttr.in ... Use when: ... NOT for: ..."
homepage: https://wttr.in/:help
metadata: { "openclaw": { "emoji": "☔", "requires": { "bins": ["curl"] } } }
---

(正文:教模型怎么用 curl 调 wttr.in、怎么解析、怎么呈现)
读法:frontmatter 是"元信息"(名字、描述、依赖),正文是"给模型的操作说明"。description 里的 "Use when / NOT for" 很关键——它帮模型判断"这个技能该不该用"。skills/canvas/SKILL.md 甚至没有 frontmatter(说明 frontmatter 可选)。
L03

frontmatter 门控

metadata.openclaw.requires 声明技能的"启用前提"(Schema 在 src/agents/skills/types.ts):

  • requires.bins: ["curl"]:需要某命令存在(weather)。
  • requires.anyBins + install:缺了可自动装(spotify-player,brew 公式)。
  • requires.config: ["channels.slack"]:需要某配置(skills/slack)。
  • os / requires.env:限定系统/环境变量。
读法:门控让技能"条件启用":没装 curl 的机器就不给 weather 技能(免得模型调了报错)。资格过滤在 shouldIncludeSkillskills/config.ts:71-103)。frontmatter 解析 + 安全校验在 frontmatter.ts(含防 shell 注入的 normalizeSafeBrewFormula——防止恶意技能借"自动安装"跑任意命令)。
L04

三层发现加载

loadSkillEntriesskills/workspace.ts:292-527)扫多个 root:

  • bundled:仓库自带 skills/resolveBundledSkillsDir)。
  • managed~/.openclaw/skills(用户装的)。
  • workspace<workspace>/skills(本项目专属)。
  • 外加 ~/.agents/skills、插件贡献目录。
读法:三层来源让技能可以"自带一批 + 用户全局装一批 + 每个工作区再加一批"。含符号链接逃逸防护、体积/数量上限(防恶意撑爆)、chokidar 热重载(skills/refresh.ts,改了 SKILL.md 立即生效)。
L05

优先级覆盖

同名技能冲突时的优先级(后者覆盖前者,workspace.ts:491):

extra < bundled < managed < agents-personal < agents-project < workspace
"就近覆盖"——本地定制赢 如果仓库自带一个 weather 技能,但你想在自己的工作区改它,只要在 <workspace>/skills/weather/SKILL.md 放一个同名的,它就覆盖内置版——因为 workspace 优先级最高。这就像 CSS 的层叠、或配置文件的"本地覆盖全局":越"贴近你当前项目"的定义越优先。让你能定制内置技能而不用改仓库源码。
L06

渐进式披露(技能怎么"调用")

关键:技能不是可调用工具。运行流程是(呼应 Day 09 技能段):

  1. 系统提示里只放技能目录(name+description+location,XML 形式)。
  2. 模型按 description 判断该不该用某技能。
  3. 命中 → 模型用 read 工具读那个 SKILL.md<location>
  4. 模型遵循 markdown 正文(通常执行其中的 bash/curl)。
为什么不一次性全塞进提示? 52 个技能,每个 SKILL.md 可能几百行。全塞进每次请求 = 巨量 token 浪费 + 撑爆上下文。渐进式披露(progressive disclosure):先只给"目录+简介"(几行),模型真需要某个才去读全文。好比图书馆——你先看书目,需要哪本才借来读,而不是把整个图书馆搬回家。运行时取技能在 skills-runtime.ts,注入提示在 attempt.ts:1417/1654,格式化由 Pi 的 formatSkillsForPrompt 生成。
渐进式披露:先看菜单,需要才读全文 ① 提示里只放目录 <skill>weather 查天气·location> (52 个只占几十行) ② 模型看描述 "该用 weather" ③ read 工具 读 SKILL.md 全文 ④ 照做 curl wttr.in 只有命中的那 1 个技能才读全文,其余 51 个从不进入上下文 → 省 token
图注:技能不是"被调用",而是"被模型读"——目录常驻、全文按需,这就是渐进式披露。
📝 举个例子:/weather 北京 vs "北京天气?",两条路径 确定性路径(L07 斜杠命令):你发 /weather 北京 → 直接触发绑定的工具,不经过 LLM 推理,快且省。
智能路径(本讲渐进式披露):你发 "北京天气?" → 模型看目录里 weather 的描述觉得对口 → 用 readskills/weather/SKILL.md → 按手册执行 curl wttr.in/北京 → 整理成人话回你。
L07

斜杠命令直达

第二条路径:技能可注册成用户斜杠命令,甚至绕过 LLM 直接调工具(skills/workspace.ts:775-881 + auto-reply/skill-commands.ts):

// frontmatter 设 command-dispatch: tool + command-tool: <name>
// → 用户发 /skillname,直接调用指定工具,不经过 LLM 推理
//   (SkillCommandDispatchSpec,types.ts:40-49)
读法:这是技能唯一"变成真工具"的场景:确定性任务(如 /weather 北京)不需要 LLM 绕一圈,直接触发工具,快且省。两条路径并存:智能路径(模型判断+读文件)和确定性路径(斜杠命令直达)。安装/扫描 CLI 在 skills-install.ts(含 security/skill-scanner 安全扫描,Day 17)。
L08

今日小结 + 动手

🧠 今天你应该能回答

  • 技能和工具的本质区别?
  • SKILL.md 的 frontmatter + 正文各是什么?
  • requires 门控解决什么问题?
  • 三层发现 + 优先级覆盖怎么让技能可定制?
  • 渐进式披露是什么?为什么要它?斜杠命令直达又是什么?

✋ 动手

cd /Users/bitmart/work/codes/github/openclaw
ls skills/ | head -30
sed -n '1,10p' skills/weather/SKILL.md
grep -n 'extra.*bundled.*managed\|shouldIncludeSkill' src/agents/skills/workspace.ts src/agents/skills/config.ts | head
明天预告 · Day 12扩展系统 extensions——extension(=plugin)是可执行 TS 模块,能注入 provider/channel/tool/hook。它和技能的区别、register(api) 加载流水线、生命周期钩子。
← Day 10 记忆 Day 12 · 扩展系统 →