)
1. 为什么 Skills 分层架构值得先搞懂再动手OpenClaw 的 Skills 系统本质上是一套「技能加载与覆盖」机制它决定了你写的技能到底会不会被加载、被谁覆盖、在什么条件下生效。很多人第一次接触 OpenClaw 时习惯性地把技能文件往一个目录里一丢然后发现改了没反应、同名技能打架、环境变量读不到最后怀疑是平台 bug。实际上绝大多数「技能不生效」的问题都源于没搞清楚分层架构和优先级规则。我先把结论摆出来OpenClaw 的 Skills 采用四层结构从高到低分别是工作区层、用户自定义层、系统内置层以及通过skills.load.extraDirs配置的额外目录。同名技能遵循「就近原则」工作区技能拥有绝对优先权。这意味着你在项目里放一个同名技能就能完全覆盖系统内置版本而不用去改动全局安装目录。这套设计解决了一个很现实的问题不同项目可能需要同一个技能的不同行为。比如你在 A 项目里需要一个只读的数据库查询技能在 B 项目里需要同一个技能带写入能力。如果只有全局一层你只能反复改配置有了工作区分层每个项目自带一份技能定义互不干扰。对于需要在多工具间统一管理 API Key 的开发者来说分层架构还带来一个额外好处你可以把「Key 从哪来」这件事收敛到配置层而不是散落在每个技能脚本里。技能本身只声明它需要哪些环境变量真正的值由 OpenClaw 的配置系统注入。这样换 Key、换供应商、切换测试环境时你只需要改一处配置。接下来我会从目录结构讲起然后给出可复制的统一 Key 配置片段再带你跑通一个自定义技能的完整验证流程最后把常见的报错逐个拆开。整个过程你都可以跟着操作不需要提前理解全部源码。2. OpenClaw Skills 四层目录与加载优先级实战2.1 四层目录各自负责什么先看目录这是理解一切的基础。OpenClaw 的技能分布在四个位置每个位置承担不同职责。系统内置层位于/usr/lib/node_modules/openclaw/skills/这是官方随包发布的技能稳定性最高经过充分测试。典型代表包括healthcheck系统健康检查、mcporterMCP 工具管理、weather天气查询、video-frames视频帧提取。这一层你不应该直接修改因为升级 OpenClaw 时会被覆盖。用户自定义层位于~/.openclaw/skills/这是你安装第三方技能或修改现有技能的地方。它的优先级高于系统内置层适合放那些你希望在所有项目里都能用的个性化技能。工作区层位于workspace/skills/也就是你当前项目根目录下的skills/文件夹。这一层优先级最高可以完全覆盖其他层的同名技能。项目专用的定制化技能放这里最合适。扩展插件层位于~/.openclaw/extensions/plugin/skills/比如~/.openclaw/extensions/qqbot/skills/。这是为特定插件或渠道定制的技能跟随插件生命周期。2.2 加载优先级与同名覆盖规则加载顺序从高到低是这样的workspace/skills/ (最高优先级) ↓ ~/.openclaw/skills/ ↓ bundled/skills/ (最低优先级) ↓ skills.load.extraDirs 配置的额外目录当同名技能存在于多个层级时系统遵循「就近原则」工作区技能绝对优先用户自定义技能次之系统内置技能作为基础保障。这个规则带来的直接好处是灵活性和隔离性——你可以在不改动全局的前提下为单个项目定制技能行为。这里有个容易踩的坑很多人以为在~/.openclaw/skills/里改了技能项目里就会生效。但如果项目工作区里存在同名技能你的修改会被工作区版本覆盖。排查时第一件事就是确认同名技能到底有几个副本。2.3 技能门控机制为什么你的技能没被加载OpenClaw 在加载技能时会做条件检查这叫门控Gating。检查项包括四类二进制依赖所需命令行工具是否存在、环境变量必要的环境配置是否齐全、系统平台是否限制特定操作系统、配置开关用户是否启用。这意味着一个技能即使文件放对了位置也可能因为缺少依赖而不被加载。比如video-frames依赖ffmpeg如果系统里没有ffmpeg这个技能就不会激活。排查时可以用which ffmpeg确认二进制是否存在。技能目录里必须包含SKILL.md它的 YAML frontmatter 定义了元数据和依赖声明。一个标准的SKILL.md头部长这样--- name: skill-name description: 技能功能描述 metadata: { openclaw: { requires: { bins: [required-binary], env: [REQUIRED_ENV_VAR] } } } ---requires.bins声明需要的二进制requires.env声明需要的环境变量。只有这些条件都满足技能才会被加载。这也是为什么统一 Key 管理要从配置层入手——技能只声明「我需要TAOTOKEN_API_KEY」具体值由配置注入。2.4 渐进式披露三级加载如何省 tokenOpenClaw 采用三级加载系统来优化上下文使用。Level 1 是元数据层始终加载只包含技能名称、描述、触发条件和依赖要求大约 100 词占用最少 token。Level 2 是主体层技能触发时才加载SKILL.md的主要内容建议控制在 5000 词以内。Level 3 是资源层脚本、参考文档、资源文件按需加载不占用主上下文窗口可执行脚本甚至无需加载到上下文中。这个设计对写技能的人有直接影响你应该把「什么时候用这个技能」放在元数据里把「怎么用」放在主体里把「详细参考资料」拆到references/目录。这样技能在未被触发时几乎不消耗上下文。3. TaoToken 统一 Key 接入的可复制配置3.1 为什么要在 Skills 层做统一 KeySkills 系统里每个技能都可能需要调用外部模型或 API。如果每个技能各自维护一份 Key你会面临三个问题Key 散落难以轮换、不同技能可能指向不同供应商、测试环境和生产环境容易混。把 Key 收敛到 OpenClaw 的配置层技能只声明环境变量需求是更干净的做法。TaoToken 提供统一的 API 入口Base URL 是https://taotoken.net/api你可以在一个地方管理多个模型的访问。下面给出可直接复制的配置片段。3.2 主配置文件片段OpenClaw 的主配置在~/.openclaw/openclaw.json。技能相关的配置放在skills.entries下每个技能可以单独设置enabled、apiKey和env{ skills: { entries: { coding-agent: { enabled: true, apiKey: sk-your-taotoken-key, env: { TAOTOKEN_BASE_URL: https://taotoken.net/api, TAOTOKEN_MODEL: claude-sonnet-4-5 } }, weather: { enabled: true, env: { WEATHER_API_TIMEOUT: 5000 } } } } }这里的关键点是apiKey字段由 OpenClaw 注入到技能运行环境技能脚本通过读取环境变量拿到它。env字段可以补充额外的环境变量比如 Base URL 和默认模型 ID。如果你希望全局统一而不是每个技能单独配可以在配置顶层加一个共享环境块让所有技能继承{ env: { TAOTOKEN_API_KEY: sk-your-taotoken-key, TAOTOKEN_BASE_URL: https://taotoken.net/api }, skills: { entries: { coding-agent: { enabled: true } } } }这样coding-agent技能在SKILL.md里声明requires.env: [TAOTOKEN_API_KEY]就能自动拿到值。3.3 技能侧的环境变量声明对应的SKILL.md头部要声明依赖否则门控会拦截--- name: coding-agent description: 编程助手支持多模型代码生成与审查 metadata: { openclaw: { requires: { bins: [node], env: [TAOTOKEN_API_KEY, TAOTOKEN_BASE_URL] } } } ---注意bins和env都要写全。如果只声明了env但系统里没有node技能依然不会加载。3.4 三件套对照表无论你用的是 Claude Code、Cline MCP 还是 Codex 的auth.json接入时都要对齐三件套Base URL、Key、Model ID。下表是统一对照配置项值说明Base URLhttps://taotoken.net/api统一 API 入口不加 UTMAPI Keysk-...在控制台创建写入配置或环境变量Model ID如claude-sonnet-4-5按实际可用模型填写如果你用的是 Codex 的auth.json结构类似{ baseURL: https://taotoken.net/api, apiKey: sk-your-taotoken-key, model: claude-sonnet-4-5 }Cline MCP 的配置则写在 MCP server 定义里Base URL 和 Key 通过环境变量传入。核心原则不变三件套对齐技能只声明需求值由配置注入。4. 自定义 Skill 验证请求与成功结果4.1 用 skill-creator 初始化技能OpenClaw 提供了skill-creator工具来初始化技能骨架。命令如下scripts/init_skill.py my-skill --path skills/public --resources scripts,references执行后会生成这样的目录结构my-skill/ ├── SKILL.md (必需) │ ├── YAML frontmatter 元数据 │ └── Markdown 使用说明 ├── scripts/ (可选) │ └── 可执行脚本 ├── references/ (可选) │ └── 参考文档 └── assets/ (可选) └── 输出资源4.2 写一个最小可验证技能为了验证统一 Key 是否真的注入成功我们写一个最小技能它只做一件事读取环境变量并调用一次模型接口打印返回结果。SKILL.md内容--- name: key-check description: 验证 TaoToken 统一 Key 是否注入成功 metadata: { openclaw: { requires: { bins: [node], env: [TAOTOKEN_API_KEY, TAOTOKEN_BASE_URL] } } } --- # key-check ## 快速开始 运行 scripts/check.js 验证 Key 注入。 ## 说明 该技能读取 TAOTOKEN_API_KEY 和 TAOTOKEN_BASE_URL 向统一入口发送一次最小请求打印状态码和响应片段。scripts/check.js内容const baseUrl process.env.TAOTOKEN_BASE_URL; const apiKey process.env.TAOTOKEN_API_KEY; if (!baseUrl || !apiKey) { console.error(缺少环境变量TAOTOKEN_BASE_URL 或 TAOTOKEN_API_KEY); process.exit(1); } async function main() { const res await fetch(${baseUrl}/v1/messages, { method: POST, headers: { Content-Type: application/json, x-api-key: apiKey, anthropic-version: 2023-06-01 }, body: JSON.stringify({ model: process.env.TAOTOKEN_MODEL || claude-sonnet-4-5, max_tokens: 32, messages: [{ role: user, content: 回复 OK 两个字母 }] }) }); console.log(HTTP 状态码:, res.status); const text await res.text(); console.log(响应片段:, text.slice(0, 200)); } main().catch((err) { console.error(请求失败:, err.message); process.exit(1); });4.3 运行验证把技能放到工作区目录workspace/skills/key-check/然后在 OpenClaw 会话里触发它或者直接手动运行脚本验证环境变量cd workspace/skills/key-check node scripts/check.js如果配置正确你会看到类似输出HTTP 状态码: 200 响应片段: {id:msg_...,type:message,role:assistant,content:[{type:text,text:OK}]}状态码 200 且响应里包含模型返回内容说明三件事都对了Key 注入成功、Base URL 正确、模型 ID 可用。如果状态码是 401说明 Key 有问题如果是 404多半是 Base URL 或路径写错。4.4 验证技能被正确加载除了手动跑脚本还要确认 OpenClaw 真的加载了这个技能。可以在会话里查看已加载技能列表或者检查日志里是否有门控通过记录。如果技能没出现在列表里回到第 2.3 节检查requires.bins和requires.env是否都满足。5. 常见报错逐条排查5.1 401 Unauthorized这是最常见的报错含义是 Key 无效或未正确传递。排查顺序先确认TAOTOKEN_API_KEY环境变量在技能运行环境里确实存在可以在脚本开头打印process.env.TAOTOKEN_API_KEY ? 已设置 : 未设置。如果显示未设置说明配置没注入检查openclaw.json里的apiKey字段拼写以及技能SKILL.md是否声明了requires.env。如果环境变量存在但仍 401检查 Key 是否过期或被撤销去控制台重新生成一个。还要注意请求头字段名Anthropic 风格用x-api-keyOpenAI 风格用Authorization: Bearer用错字段名也会 401。5.2 local proxy failed这个报错通常出现在网络层含义是本地代理转发失败。先确认 Base URL 写的是https://taotoken.net/api没有多余斜杠或路径。然后检查本机网络是否能正常访问该地址可以用curl -I https://taotoken.net/api测试连通性。如果 curl 能通但技能里报错多半是技能运行环境的网络隔离策略导致检查沙箱配置是否允许出站请求。5.3 reading choices 相关报错这类报错通常出现在解析响应时含义是响应结构不符合预期。常见原因是模型返回了错误信息而不是正常内容但脚本直接按成功结构解析。排查时先把原始响应完整打印出来看res.status和响应体。如果响应体里是错误对象先解决错误本身如果响应体正常但字段名对不上检查你解析的字段路径是否和实际返回一致。5.4 OAuth 相关报错如果你用的是需要 OAuth 的接入方式报错通常和 token 刷新有关。检查auth.json或对应配置文件里的 token 是否过期以及刷新逻辑是否正常。对于统一 Key 接入一般不需要 OAuth直接用 API Key 即可遇到 OAuth 报错先确认自己是不是用错了接入方式。5.5 技能不加载但无报错这种情况最隐蔽。技能文件放对了但会话里就是没有。按这个顺序查第一确认SKILL.md存在且 frontmatter 格式正确YAML 对缩进敏感第二确认requires.bins里的二进制都存在用which逐个验证第三确认requires.env里的变量都已注入第四确认同名技能没有在更高优先级层被覆盖第五确认skills.entries里该技能enabled为true。排查时可以在配置里临时打开调试日志观察加载阶段的门控检查结果通常能直接看到是哪个条件没通过。6. 把统一 Key 接入沉淀成长期习惯走到这里你已经完成了从架构理解到实际接入的完整链路。我想强调一个容易被忽略的点统一 Key 的价值不在于「少写几行配置」而在于它把「凭证管理」和「技能逻辑」解耦了。技能只声明它需要什么值从哪来由配置层决定。这样你换供应商、轮换 Key、切换环境时改动范围被限制在配置层技能代码一行不用动。如果你打算长期在 OpenClaw 里做编码类或 Agent 类任务建议把常用技能的 Key 配置统一收敛到openclaw.json的顶层env块技能侧只声明requires.env。这样新增技能时你只需要在SKILL.md里加一行环境变量声明不用重复配置 Key。对于需要频繁调用模型的场景可以进一步了解 Coding Plan 这类长期方案把调用额度和 Key 管理放在一起规划。验证模型可用性时模型对话入口适合快速试接入和排障阶段API Keys 页面和接入文档是主要参考。最后留一个实用技巧每次新增或修改技能后先手动跑一遍技能脚本确认环境变量和请求都正常再回到 OpenClaw 会话里触发。这样能把「技能逻辑问题」和「加载配置问题」分开定位排查效率会高很多。