1. 从零开发一个 OpenClaw Skill 到底要解决什么问题OpenClaw Skills 是 OpenClaw 这套 AI 助手框架里的“插件系统”你可以把它理解成给一个通用助手装上专用工具箱装上天气技能它就能查天气装上 GitHub 技能它就能管仓库。每个 Skill 是一个独立文件夹核心就三样东西——SKILL.md描述技能元信息index.js定义工具逻辑package.json管依赖。写完之后可以本地调试也可以发布到 ClawHub 让别人一键安装。这套东西适合谁适合已经用过 OpenClaw、想给它加自定义能力的开发者也适合想把内部 API、脚本、查询逻辑封装成对话工具的人。它不需要你从零写一个完整应用只要会写 JavaScript 函数、能看懂 JSON 结构就能上手。但真正卡住大多数人的不是“怎么写”而是“写完怎么跑通”。我见过太多情况SKILL.md写好了index.js也写好了openclaw gateway restart也执行了结果在对话里说“打个招呼”助手毫无反应。排查半天发现是description写得太模糊关键词匹配不上或者工具定义里parameters结构写错加载时静默失败。还有一个更隐蔽的坑Skill 里要调用外部模型或 API 时endpoint 和 Key 散落在各个技能里调试时改一处忘一处最后不知道哪个 Key 生效。这篇教程会把开发、调试、上架整条链路串起来并且把模型调用统一收敛到 TaoToken 的通道上让 Key 和 endpoint 只维护一份调试时不用来回翻配置文件。下面按“先跑通最小闭环再补细节”的顺序来。每一步都给可复制的代码和命令你跟着敲就能看到结果。2. TaoToken 前置准备统一 Key 与 endpoint 的接入方式在写 Skill 之前先把模型调用的通道准备好。OpenClaw 的 Skill 在execute里经常要调模型做总结、分类、改写如果每个技能各自配一套 Key调试会非常痛苦。TaoToken 提供的是 OpenAI 兼容的接口Base URL 固定为https://taotoken.net/api你只需要一个 Key所有技能共用。先拿到 Key。打开https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentskill_dev登录后在控制台创建 API Key复制出来形如sk-xxxxxxxx。这个 Key 就是后面所有配置里填的东西。接着确认你要用的模型 ID。在模型对话页https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentskill_dev里可以试跑页面上会显示当前可用的模型标识比如claude-sonnet-4-5、gpt-4o这类。记下你打算在 Skill 里调用的那个 Model ID后面写进配置。为什么要在 Skill 开发阶段就统一到 TaoToken因为 Skill 调试最烦的就是“请求发出去了但不知道哪一步断了”。统一通道之后你只要验证一次连通性后面所有技能复用同一套 Base URL Key Model ID出问题只在一个地方查。而且 TaoToken 的接口是标准 OpenAI 格式fetch直接调就行不需要额外 SDK。如果你打算长期做多个 Skill、甚至跑 Agent 类任务可以顺带看一下 Coding Plan 页面https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentskill_dev它面向的是持续编码和 Agent 场景配额和调用方式更适合高频调试。不过对于单个 Skill 的开发验证用 API Key 直接调就够了。这里要强调一点TaoToken 是合规的 API 服务通道不是让你绕过什么限制。你用它就是把模型调用集中管理Skill 代码里只认一个 endpoint换模型、换配额都在控制台操作代码不用动。准备好 Key 和 Model ID 之后先别急着写 Skill用一条 curl 验证通道是通的。这一步能省掉后面 80% 的“到底是 Skill 写错了还是 Key 不对”的扯皮。3. 可复制配置SKILL.md 模板与 index.js 骨架这一节给两份可以直接抄的东西一份SKILL.md模板一份index.js骨架骨架里已经把模型调用指向 TaoToken。先建目录mkdir -p ~/.openclaw/workspace/skills/summarize-skill cd ~/.openclaw/workspace/skills/summarize-skillSKILL.md的 frontmatter 决定技能能不能被正确加载和匹配name用短横线小写description要写清楚“这个技能能做什么、什么场景触发”因为 OpenClaw 是靠描述和工具名做关键词匹配的。模板如下--- name: summarize-skill description: 对长文本进行摘要总结当用户说“总结一下”“帮我摘要”“提炼要点”时触发 version: 1.0.0 author: Your Name --- # Summarize Skill 把用户提供的长文本压缩成要点列表。 ## 使用方式 在对话中说 - “总结一下这段内容……” - “帮我摘要这篇文章” - “提炼要点” ## 参数 - text需要摘要的原始文本必填 - max_points最多输出几条要点可选默认 5package.json声明依赖和入口{ name: summarize-skill, version: 1.0.0, main: index.js, type: module, dependencies: { openclaw/tool: ^1.0.0 } }index.js是核心。下面这份骨架把模型调用封装成一个函数endpoint 和 Key 从环境变量读避免硬编码。工具定义里parameters必须写清楚类型和是否必填否则加载时容易静默失败import { Tool } from openclaw/tool; const BASE_URL process.env.TAOTOKEN_BASE_URL || https://taotoken.net/api; const API_KEY process.env.TAOTOKEN_API_KEY; const MODEL_ID process.env.TAOTOKEN_MODEL_ID || claude-sonnet-4-5; async function callModel(prompt) { const resp await fetch(${BASE_URL}/v1/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${API_KEY}, }, body: JSON.stringify({ model: MODEL_ID, messages: [{ role: user, content: prompt }], temperature: 0.3, }), }); if (!resp.ok) { const errText await resp.text(); throw new Error(model call failed: ${resp.status} ${errText}); } const data await resp.json(); return data.choices[0].message.content; } export default { create: (options) { return new Tool({ name: summarize_text, description: 对长文本进行摘要总结输出要点列表, parameters: { text: { type: string, description: 需要摘要的原始文本, required: true, }, max_points: { type: number, description: 最多输出几条要点, required: false, }, }, execute: async (toolCallId, args) { try { const limit args.max_points || 5; const prompt 请把下面的内容总结成不超过 ${limit} 条要点每条一行不要额外解释\n\n${args.text}; const summary await callModel(prompt); return { result: summary }; } catch (error) { return { error: 摘要失败${error.message}, result: null }; } }, }); }, };装依赖npm install openclaw/tool然后把环境变量写进 shell 配置或者直接在启动 Gateway 时带上。用环境变量的好处是 Key 不进代码仓库调试时改一处全局生效export TAOTOKEN_BASE_URLhttps://taotoken.net/api export TAOTOKEN_API_KEYsk-你的Key export TAOTOKEN_MODEL_IDclaude-sonnet-4-5到这里Skill 的静态部分就齐了。注意index.js里parameters的required字段和SKILL.md里的描述要对应否则模型在决定调用哪个工具时会犹豫。下一节验证请求看它到底能不能跑起来。4. 验证请求与成功结果从 curl 到对话触发先单独验证 TaoToken 通道排除 Skill 本身的干扰。用 curl 打一次 chat completionscurl -s https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -d { model: $TAOTOKEN_MODEL_ID, messages: [{role: user, content: 只回复两个字通了}] }正常返回里会有choices[0].message.content内容是“通了”。如果这一步就报 401说明 Key 不对先回控制台确认 Key 有没有复制完整、有没有多余空格。如果报 model not found说明 Model ID 写错了回模型对话页核对。通道通了之后重启 Gateway 让新技能加载openclaw gateway restart openclaw gateway statusstatus里能看到 Gateway 是否在跑。接着确认技能被识别clawhub list | grep summarize-skill如果列表里没有说明SKILL.md的 frontmatter 格式有问题或者目录放错了位置。确认目录是~/.openclaw/workspace/skills/summarize-skill/且SKILL.md在根目录。然后直接在对话里触发。用 CLI 测试openclaw chat --message 总结一下OpenClaw 是一个 AI 助手框架支持通过 Skills 扩展能力。Skills 用 SKILL.md 描述用 index.js 定义工具逻辑。预期结果是助手调用summarize_text工具返回几条要点。如果你在群里测试就发一条包含“总结一下”的消息观察是否触发。成功时你会看到工具调用记录execute返回的result被拼进回复。如果模型调用失败execute里的 catch 会返回error字段对话里会显示“摘要失败model call failed: 401 ...”这就是通道问题回上一步查 Key。验证通过后可以故意传一个空text看错误处理是否生效确认parameters的required校验起作用。这一步做完开发闭环就跑通了。接下来是排错把最常见的几个报错对号入座。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth调试 Skill 时遇到的报错基本集中在四类下面按真实报错信息对照排查。401 Unauthorized。报错长这样model call failed: 401 {error:{message:invalid api key}}。原因通常是TAOTOKEN_API_KEY没导出、导出的是旧 Key、或者 Key 前后有空格。排查命令echo $TAOTOKEN_API_KEY | head -c 8看前几位是不是sk-。如果为空说明环境变量没生效检查是不是在另一个 shell 里 export 的Gateway 启动时有没有继承。注意环境变量要在启动 Gateway 的那个终端里 export或者写进~/.bashrc/~/.zshrc后重新 source。local proxy failed。报错类似local proxy failed: connect ECONNREFUSED 127.0.0.1:xxxx。这是本机网络层的问题不是 Skill 代码问题。先确认BASE_URL没被改成http://127.0.0.1:...这类本地地址。检查echo $TAOTOKEN_BASE_URL应该是https://taotoken.net/api。如果被其他工具的配置覆盖了在 Skill 里显式写死或重新 export。另外确认本机没有残留的代理环境变量干扰env | grep -i proxy如果有HTTP_PROXY/HTTPS_PROXY指向不可用的地址unset 掉再重启 Gateway。reading choices。报错Cannot read properties of undefined (reading choices)。这说明resp.json()返回的结构里没有choices通常是接口返回了错误对象但 HTTP 状态是 200或者你请求的路径不对。检查两点一是 URL 是不是${BASE_URL}/v1/chat/completions少写/v1会打到别的路由二是打印原始返回const data await resp.json(); console.log(raw response:, JSON.stringify(data));如果返回里有error字段按错误信息处理。这个报错在index.js里加一行日志就能定位别靠猜。OAuth / auth.json 相关。如果你在 Skill 里调用了需要 OAuth 的外部服务比如某些云平台报错会提示 token 过期或auth.json读取失败。OpenClaw 的凭证一般放在~/.openclaw/auth.json检查文件是否存在、权限是否正确ls -l ~/.openclaw/auth.json如果 Skill 需要读这个文件确保路径写对且 Gateway 运行用户有读权限。注意不要把生产库凭证直接塞进 Skill调试用测试凭证。另外提一个高频坑改了index.js之后没重启 Gateway对话里还是旧逻辑。每次改完代码都要openclaw gateway restart这是最容易忘的一步。还有SKILL.md的description写得太泛比如只写“一个技能”会导致关键词匹配不上工具永远不被触发。描述里要把触发词写进去像前面模板里的“总结一下”“帮我摘要”。排查顺序建议先 curl 验通道再 restart 验加载再对话验触发最后看日志。日志位置tail -f /tmp/openclaw/openclaw-*.log日志里能看到工具加载、匹配、执行的全过程比在对话里猜快得多。6. 上架 ClawHub 与统一 Key 的长期维护本地跑通之后上架 ClawHub 就是打包发布的事。先登录clawhub login按提示完成认证。然后在技能目录下发布cd ~/.openclaw/workspace/skills/summarize-skill clawhub publish ./发布前检查SKILL.md的version有没有递增README.md有没有写清楚使用方式和参数说明。ClawHub 上别人安装时看的就是这些信息。发布成功后可以验证clawhub inspect summarize-skill别人安装clawhub install summarize-skill上架之后Skill 里的模型调用仍然走 TaoToken 统一通道。这里有个维护上的建议不要把 Key 硬编码进index.js再发布因为发布出去的代码别人能看到。正确做法是让 Skill 从环境变量读 Key使用者在自己的环境里配置。你可以在README.md里写清楚需要设置哪些环境变量export TAOTOKEN_BASE_URLhttps://taotoken.net/api export TAOTOKEN_API_KEYsk-你的Key export TAOTOKEN_MODEL_IDclaude-sonnet-4-5这样 Skill 本身是干净的Key 由使用者自己管。如果你自己维护多个 Skill统一用同一套环境变量名换 Key 时只改一处。对于需要长期跑、调用量大的 Skill可以关注 Coding Plan 的配额方式它比按次调用更适合持续调试和 Agent 场景。接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentskill_dev里面有完整的接口说明和参数列表写复杂 Skill 时对着查。最后说一个实际经验Skill 的execute里做模型调用时给temperature设低一点0.2 到 0.4摘要、分类这类任务输出更稳定如果要做创意生成再调高。还有execute一定要包 try/catch 并返回error字段否则模型调用失败时对话里只会显示一个空白结果你根本不知道发生了什么。把错误信息透出来调试效率会高很多。整条链路走下来就是建目录写SKILL.md和index.js环境变量指向 TaoTokencurl 验通道restart 验加载对话验触发排错看日志最后clawhub publish上架。每一步都有可复制的命令照着做就能跑通。
阅读完成 · 觉得有帮助?