
一、先说个场景
你好不容易把 hello-world 跑通了,但发现 slash 命令里没出现自己的 skill;或者你希望某个 skill 只在用户主动 /skill 时才触发,不要让 agent 自己乱用;又或者你想做一个固定命令,让 /release_notes 绕过模型直接调用工具。这些都可以通过 SKILL.md 的 frontmatter 字段精准控制。
这篇文章把每一个 frontmatter 字段拆开讲,告诉你默认值是什么、写了会怎样、不写会怎样。
为什么要把”用户能不能叫”和”模型能不能叫”拆成两个独立开关?因为现实里这两种风险不同:有的技能你乐意自己随手触发,但不想让模型自作主张调它(比如会花钱的操作);有的技能你希望模型在合适时默默用,却不想把它塞进 slash 列表添乱(比如内部检索)。两个开关正交组合,正好覆盖”谁可以启动它”的全部四种情况,你按需关掉其中一项即可。
把 frontmatter 想成技能的简历会好懂:name 是姓名,description 是一句话简介,homepage 是个人主页,那几个开关是”是否接受调用””是否允许模型自荐”之类的意向。系统扫一眼简历,就决定要不要把这个技能介绍给会话、要不要让用户直接点名。简历写清楚,机会才多;写含糊,再强的能力也埋没。所以 frontmatter 虽小,却直接决定了技能的”出场率”。
二、为什么是这样
OpenClaw 读取 SKILL.md 时,把文件分成两段:
- 顶部的 YAML frontmatter:给系统看的元数据。
- 下面的 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" } }
---
这里同时使用了:
- 用户可调用;
- 模型也能主动调用;
- 只有装了
geminiCLI 且配了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 决定”这个技能怎么被找到和被调用”,正文决定”被调用后做什么”。两层都写好,才算一个完整、可控、能放心分享的技能。
