
一、先说个场景
你写了一个调用 gemini CLI 的技能,兴冲冲地发给同事。结果同事装上后一聊,OpenClaw 报错了:”找不到 gemini 命令”。原来他机器上根本没装 Gemini CLI。如果能在技能加载前就判断”依赖是否满足”,这种尴尬就不会发生。
OpenClaw 的 Gating(门控) 就是做这件事的:让某个 skill 只在条件满足时才被加载。条件可以是平台、二进制、环境变量、配置文件,甚至是一个”总是加载”的强制开关。
再设想一个场景:你维护一份内部工具技能,只在公司内网、且配了 INTERNAL_API_TOKEN 的机器上才能用。如果强行让所有人加载,一半同事会看到一堆用不了的灰按钮,体验极差。有了 gating,这份技能会”智能隐身”——不满足条件的人根本不会在列表里看到它,自然也不会去点、去报错。这就是 gating 存在的价值:把”能不能用”的判断前移到加载期,而不是等到运行时才爆炸。
二、为什么是这样
不同用户的机器环境不一样。有的装了 Docker,有的没有;有的配了 OPENAI_API_KEY,有的没配;有的在 macOS 上跑,有的在 Windows 上跑。如果让同一个 skill 在所有环境下都加载,不是功能残缺就是报错连连。
Gating 让 skill 作者声明”我的技能需要这些前提”。OpenClaw 在加载阶段就做过滤,不满足条件的 skill 根本不会出现在 agent 的视野里。这样模型不会调用一个注定失败的技能,用户也不会看到一堆灰掉的按钮。
把过滤放在”加载期”而不是”运行时”,还有一个好处:agent 的上下文更干净。如果所有技能无差别加载,模型要在几十个用不了的技能里挑挑拣拣,既浪费 token,也容易被”看起来相关但其实跑不起来”的技能误导。gating 相当于在门口先筛掉不合格者,让进门的每一个技能都是”当前环境真能用”的。对技能作者来说,这也是一种责任:你声明了依赖,框架就帮你兜住”环境不对”的底线,你不必在正文里写一堆”请先确认你装了某某”。
三、跟着做:分步操作
1. 官方示例拆解
---
name: gemini-search
description: Search using Gemini CLI.
metadata: { "openclaw": { "requires": { "bins": ["gemini"] }, "primaryEnv": "GEMINI_API_KEY" } }
---
这行 frontmatter 告诉 OpenClaw:
- 只有在
PATH里能找到gemini这个二进制时,才加载这个 skill。 - 同时声明了
primaryEnv是GEMINI_API_KEY,方便openclaw.json里做apiKey绑定。
注意 requires.bins 是一个数组,写多个就表示”全部都要存在”;它只负责”检查”,不负责”安装”。这一点后面误区里还会强调。另外 primaryEnv 和 requires 是并列的两件事:前者是给配置绑定用的”主变量名提示”,后者是加载门槛,不要混为一谈。
2. Gating 字段全览
| 键 | 含义 |
|---|---|
requires.bins |
列出的二进制全部必须存在于 PATH |
requires.anyBins |
至少有一个列出的二进制存在于 PATH |
requires.env |
每个环境变量必须存在于进程或配置中 |
requires.config |
每个 openclaw.json 路径必须为 truthy |
os |
平台过滤:["darwin"] / ["linux"] / ["win32"] |
always |
只要 OS 兼容就加载,绕过 requires.* 检查 |
emoji |
macOS Skills UI 显示的表情 |
primaryEnv |
与 skills.entries.<name>.apiKey 关联的变量名 |
install |
给 macOS Skills UI 用的安装器规格 |
旧键
metadata.clawdbot在metadata.openclaw缺失时仍被接受。
这张表里最容易混淆的是 requires.bins 和 requires.anyBins 的”与/或”关系:前者是”全部都要”,后者是”至少一个”。比如你写的技能既支持 rg 也支持 grep,就别用 bins(那样会要求两个都有),而该用 anyBins: ["rg", "grep"],让装了任意一个的人都能用。
3. 常用配方
只在 macOS 加载:
---
name: mac-helper
description: macOS-specific helper.
metadata: { "openclaw": { "os": ["darwin"] } }
---
要求某个 CLI 存在:
---
name: docker-cleanup
description: Clean up Docker containers.
metadata: { "openclaw": { "requires": { "bins": ["docker"] } } }
---
二选一,兼容两个同类工具:
---
name: web-search
description: Search the web via available CLI.
metadata: { "openclaw": { "requires": { "anyBins": ["gemini", "ddgr"] } } }
---
要求环境变量存在:
---
name: internal-api
description: Call internal API.
metadata: { "openclaw": { "requires": { "env": ["INTERNAL_API_TOKEN"] } } }
---
强制加载,忽略依赖检查:
---
name: always-on
description: Always load on compatible OS.
metadata: { "openclaw": { "always": true, "os": ["linux", "darwin"] } }
---
关于 always: true,要记住它跳过的是 requires.* 的检查,却不跳过 os 的过滤。所以上面这个例子在 Windows 上依然不会加载——always 不等于”全平台”,它只是说”只要平台对,就别再卡我依赖了”。
四、可以复制的指令
---
name: python-format
description: Format Python files with black or ruff.
metadata: { "openclaw": { "requires": { "anyBins": ["black", "ruff"] } } }
---
---
name: company-deploy
description: Deploy to company server.
metadata: { "openclaw": { "requires": { "env": ["DEPLOY_TOKEN"], "bins": ["kubectl"] }, "primaryEnv": "DEPLOY_TOKEN" } }
---
// openclaw.json 中绑定 primaryEnv
{
skills: {
entries: {
"company-deploy": {
enabled: true,
apiKey: { source: "env", provider: "default", id: "DEPLOY_TOKEN" },
},
},
},
}
这一段给出了”声明依赖 + 绑定密钥”的最小闭环:metadata.openclaw.requires 负责加载门槛,primaryEnv 告诉框架主变量叫什么,openclaw.json 里的 apiKey 再把它真正注入。三者配合,技能既能在缺依赖时自动隐身,又能在环境齐全时顺畅拿到密钥。建议你每写一个依赖外部命令或密钥的技能,都先照这个模板把 gating 补齐,再写正文逻辑。
五、实战案例
你有一个技能叫 cloud-backup,功能是把本地笔记同步到某对象存储。它依赖:
rclone命令存在;RCLONE_CONFIG环境变量已配置;- 只在 macOS 和 Linux 上跑,因为 Windows 路径逻辑还没测试。
SKILL.md frontmatter:
---
name: cloud-backup
description: Sync notes to object storage via rclone.
metadata: {
"openclaw": {
"os": ["darwin", "linux"],
"requires": {
"bins": ["rclone"],
"env": ["RCLONE_CONFIG"]
},
"primaryEnv": "RCLONE_CONFIG"
}
}
---
openclaw.json:
{
skills: {
entries: {
"cloud-backup": {
enabled: true,
apiKey: { source: "env", provider: "default", id: "RCLONE_CONFIG" },
},
},
},
}
这样,Windows 用户不会加载这个 skill,没装 rclone 的用户也不会看到它,只有环境配齐的人才能用。出了问题也容易定位:先检查 rclone 在不在 PATH,再检查 RCLONE_CONFIG 有没有设。
补充一个排查顺序:当同事说”我怎么看不到 cloud-backup“时,别先怀疑 SKILL.md 写错。先问他是 Windows 还是 Mac/Linux(对应 os 过滤);再让他跑 which rclone(对应 bins);最后让他 echo $RCLONE_CONFIG(对应 env)。三步走完基本就能定位是哪一道门没过。把 gating 当成”自解释的安装说明书”,比在文档里写一大段”使用前请确认”要可靠得多。
六、常见误区
- 误区 1:
requires.bins会帮你安装二进制。不会。它只是检查,缺失就跳过加载。安装依赖要另外处理。 - 误区 2:
always: true可以覆盖所有平台。不能。always不覆盖os过滤,平台不匹配仍然不会加载。 - 误区 3:gating 是运行时权限控制。它只是加载期过滤,不是运行时授权。技能加载后依然可能做危险操作,要结合沙箱和白名单。
- 误区 4:
requires.config写的是 shell 路径。它写的是openclaw.json的 JSON 路径,比如agents.defaults.sandbox.docker.enabled。 - 误区 5:把密钥存在 requires.env 里就安全了。
requires.env只是检查存在性,密钥管理另有skills.entries.<key>.apiKey,且只注入宿主进程、不进沙箱。
七、进阶玩法
组合多个条件:
---
name: ai-image-gen
description: Generate images using local ComfyUI.
metadata: {
"openclaw": {
"requires": {
"bins": ["python3"],
"env": ["COMFYUI_URL"],
"config": ["agents.defaults.mediaModels.image"]
},
"primaryEnv": "COMFYUI_URL"
}
}
---
这里同时要求二进制、环境变量、配置文件三者都满足。
用 install 字段给 macOS Skills UI 提供安装器:
当你的 skill 在 macOS UI 里被发现但依赖缺失时,可以指定安装步骤,让用户一键补齐。
组合条件时有个实用心法:把”硬依赖”和”软依赖”分开想。硬依赖(缺了就完全跑不起来,比如核心 CLI)放进 requires.bins;软依赖(没有就降级功能,而不是不能用)则不该写进 gating,而应在技能正文里做分支判断。否则你会把一大批”其实勉强能用”的用户挡在门外。install 字段的意义也正在于此:它给 macOS UI 一个”缺口补丁”,让用户在看到技能的同时就能一键补齐依赖,而不是只能对着隐身的技能干瞪眼。
八、小结
Gating 是 skill 的”入场券”。通过 metadata.openclaw.requires 里的 bins、anyBins、env、config,再加上 os 和 always,你可以精确控制一个 skill 在什么环境下才加载。它不是运行时权限,只是加载期过滤,但已经能避免大量”环境不满足导致报错”的尴尬。用好 gating,你的 skill 在别人机器上才不会一装就炸。
一句话总结:bins/anyBins 管命令在不在,env/config 管配置齐不齐,os 管平台对不对,always 是”平台对就别卡依赖”的快捷通道。把这几样想清楚,再配合 primaryEnv 做密钥绑定,你写出的技能就能做到”该现身的现身、该隐身的隐身”,分发体验会好上不止一个档次。
