老周的工具笔记

老周的工具笔记

已发布 24 篇文章

SKILL.md 的 frontmatter 字段详解:必填两个,剩下这六个决定技能的行为

SKILL.md 的 frontmatter 字段详解:必填两个,剩下这六个决定技能的行为
SKILL.md 的 frontmatter 字段详解:必填两个,剩下这六个决定技能的行为

一、先说个场景

你好不容易把 hello-world 跑通了,但发现 slash 命令里没出现自己的 skill;或者你希望某个 skill 只在用户主动 /skill 时才触发,不要让 agent 自己乱用;又或者你想做一个固定命令,让 /release_notes 绕过模型直接调用工具。这些都可以通过 SKILL.md 的 frontmatter 字段精准控制。

这篇文章把每一个 frontmatter 字段拆开讲,告诉你默认值是什么、写了会怎样、不写会怎样。

为什么要把”用户能不能叫”和”模型能不能叫”拆成两个独立开关?因为现实里这两种风险不同:有的技能你乐意自己随手触发,但不想让模型自作主张调它(比如会花钱的操作);有的技能你希望模型在合适时默默用,却不想把它塞进 slash 列表添乱(比如内部检索)。两个开关正交组合,正好覆盖”谁可以启动它”的全部四种情况,你按需关掉其中一项即可。

把 frontmatter 想成技能的简历会好懂:name 是姓名,description 是一句话简介,homepage 是个人主页,那几个开关是”是否接受调用””是否允许模型自荐”之类的意向。系统扫一眼简历,就决定要不要把这个技能介绍给会话、要不要让用户直接点名。简历写清楚,机会才多;写含糊,再强的能力也埋没。所以 frontmatter 虽小,却直接决定了技能的”出场率”。

二、为什么是这样

OpenClaw 读取 SKILL.md 时,把文件分成两段:

  1. 顶部的 YAML frontmatter:给系统看的元数据。
  2. 下面的 Markdown 正文:给模型看的指令。

frontmatter 决定技能能否被发现、以什么形式被发现、用户能不能直接调用、模型能不能主动调用、斜杠命令要不要绕过模型。正文决定技能被触发后具体怎么做。二者各司其职,不能互相替代。

三、跟着做:分步操作

1. 必填字段

---
name: hello-world
description: A simple skill that prints a greeting.
---
  • name:技能的 slug。只能用小写字母、数字、连字符。目录名最好和 name 一致,方便调试。
  • description:一句话描述。给 agent 和 slash 命令发现看的,160 字符以内。这行字直接决定你的 skill 会不会被 model 想起。

2. 可选字段逐个看

homepage

---
name: my-skill
description: Search and summarize PDF documents.
homepage: https://example.com/my-skill
---

在 macOS 的 Skills UI 里显示为 “Website”。让别人能点进去看你的文档或源码。

user-invocable

---
name: my-skill
description: ...
user-invocable: false
---

默认值 true。设为 false 后,用户不能直接打 /my-skill 触发,但模型仍可能在需要时调用它。适合”不希望用户手动触发,但希望 agent 在合适场景自己用”的技能。

disable-model-invocation

---
name: my-skill
description: ...
disable-model-invocation: true
---

默认值 false。设为 true 后,模型不会在正常提示里主动选这个 skill,但用户显式 /skill my-skill 或 $my-skill 引用仍然可以调用。适合”后台能力”或”危险操作”,让用户明确授权再执行。

command-dispatch、command-tool、command-arg-mode

这三个要一起用:

---
name: release-notes
description: Print the latest release notes.
command-dispatch: tool
command-tool: print_release_notes
command-arg-mode: raw
---
  • command-dispatch: tool:斜杠命令绕过模型,直接派发给工具。
  • command-tool:指定要调用的工具名。
  • command-arg-mode: raw:把用户输入的原始参数字符串传给工具。工具收到的对象包含 command、commandName、skillName。

这意味着 /release_notes v1.2.0 会直接调用 print_release_notes 工具,不会经过模型理解,延迟更低、结果更确定。

把六个可选字段归个类会更清楚:一类管”可见性”,如 homepage、user-invocable;一类管”调用权”,如 disable-model-invocation;一类管”调用方式”,如 command-dispatch / command-tool / command-arg-mode。前两类决定”谁能碰这个技能”,第三类决定”碰的时候怎么走”。写 frontmatter 时心里有这张图,就不会把”想让模型别调”错写成只藏了 slash 入口。

command-dispatch 这一族适合”确定性极高、不希望模型自由发挥”的场景。比如查版本号、打构建、调一个输入输出都很固定的内部工具,让模型先理解再转述,既慢又可能改坏参数;直接绕过模型、把原话交给工具,结果更稳更可预期。但它不是默认推荐:多数技能仍该让模型参与,因为只有模型能处理自然语言里的模糊与变化。把它当成”特殊通道”,只在确需精确时使用。

四、可以复制的指令

---
name: github-summary
description: Summarize open issues and PRs from a GitHub repo.
homepage: https://github.com/me/github-summary
user-invocable: true
disable-model-invocation: false
---
---
name: dangerous-cleanup
description: Delete old build artifacts. Only use when explicitly requested.
user-invocable: false
disable-model-invocation: true
---
---
name: release-notes
description: Print the latest release notes.
command-dispatch: tool
command-tool: print_release_notes
command-arg-mode: raw
---

五、实战案例

假设你有三个 skill:

  • weather:普通能力,用户可调用,模型也能主动用。
  • internal-search:只能让 agent 在合适场景用,不希望用户手动触发。
  • db-reset:高危操作,必须用户显式 /skill db-reset 才能执行,agent 不能自己决定。

配置如下:

---
name: weather
description: Check local weather.
user-invocable: true
disable-model-invocation: false
---
---
name: internal-search
description: Search internal knowledge base.
user-invocable: false
disable-model-invocation: false
---
---
name: db-reset
description: Reset the local database. Requires explicit user invocation.
user-invocable: true
disable-model-invocation: true
---

组合效果:

skill 用户可直接触发 模型可主动调用
weather 是 是
internal-search 否 是
db-reset 是(显式) 否

六、常见误区

  • 误区 1:user-invocable: false 会阻止模型调用。不会。它只是不让用户用 slash 命令触发,模型仍可能主动调用。要同时阻止模型,用 disable-model-invocation: true。
  • 误区 2:disable-model-invocation: true 会让 skill 彻底失效。不会。/skill name 或 $name 显式引用仍然可以调用。
  • 误区 3:command-dispatch 可以和任意字段自由组合。一旦设为 tool,就要同时给 command-tool,并且确保工具已注册。
  • 误区 4:连字符名字不加引号。在 openclaw.json 的 skills.entries 里引用连字符 skill key 时,要加引号,例如 "my-skill": { enabled: false }。
  • 误区 5: frontmatter 字段名用中文。name、description 等字段必须严格用英文官方命名,不要用中文别名。

说到进阶,容易把 frontmatter 顶层字段和 metadata.openclaw 搞混。简单说:顶层字段是”行为开关”,系统据此决定怎么调用;metadata.openclaw 里的是”运行前提”,系统据此决定能不能加载。比如 requires.bins 声明缺了哪个二进制就别加载,primaryEnv 声明主密钥叫什么——这些是前置条件,不满足就压根不出现。两层一前一后,先把”能不能跑”卡住,再规定”怎么跑”,技能才既稳又可控。

七、进阶玩法

把 frontmatter 和 gating 结合:

---
name: gemini-search
description: Search using Gemini CLI.
user-invocable: true
disable-model-invocation: false
metadata: { "openclaw": { "requires": { "bins": ["gemini"] }, "primaryEnv": "GEMINI_API_KEY" } }
---

这里同时使用了:

  • 用户可调用;
  • 模型也能主动调用;
  • 只有装了 gemini CLI 且配了 GEMINI_API_KEY 才会加载。

metadata.openclaw 与 homepage 的两种写法:homepage 可以写 frontmatter 顶层,也可以写进 metadata.openclaw.homepage。后者更集中。

写 frontmatter 的顺序建议是:先把 name 和 description 两个必填项写对,让技能能加载、能被发现;等跑通了,再按需要加 user-invocable、disable-model-invocation 去收紧;最后才考虑 command-dispatch、门控与 metadata.openclaw 的依赖声明。一层层加,每加一项就测一次,比一次性堆满字段更容易定位”哪项写错了”。

写完一个技能后,隔周回看一次 frontmatter,常常会有新发现:某个开关其实该关、某句 description 能写得更准。字段虽少,但越用越能体会每个开关的分量。把 frontmatter 当成技能的门面,常看常调,它的行为就会越来越贴合你的预期。

八、小结

字段 默认值 作用
name 必填 slug,小写 + 数字 + 连字符
description 必填 160 字符内的一句话
homepage — 在 macOS Skills UI 显示网站
user-invocable true 是否暴露为 slash 命令
disable-model-invocation false 是否从模型提示中隐藏
command-dispatch — 设为 tool 绕过模型直接派发给工具
command-tool — 绕过模型时要调用的工具名
command-arg-mode raw 原始参数字符串传给工具

必填的只有 name 和 description,剩下六个决定了你的 skill 被发现、被调用、被隔离的方式。合理组合它们,是写出安全且易用 skill 的基本功。记住一条心法:frontmatter 决定”这个技能怎么被找到和被调用”,正文决定”被调用后做什么”。两层都写好,才算一个完整、可控、能放心分享的技能。