ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

OpenClaw技能开发:用plugin.json与index.ts搭一个可复用的自定义技能骨架

OpenClaw技能开发:用plugin.json与index.ts搭一个可复用的自定义技能骨架 1. 为什么我要把重复操作做成 OpenClaw 自定义技能如果你已经在用 OpenClaw 处理日常任务大概率会遇到一个瓶颈每次都要用自然语言把同一套操作重新描述一遍。比如“帮我统计一下这个目录下各类文件的数量生成一份 Markdown 报表”第一次说还挺新鲜说到第五次就开始烦了。OpenClaw 自定义技能就是解决这个问题的——它让你把一套固定的输入、处理、输出逻辑封装成一个可复用的模块之后只需要一句话触发甚至可以让 Agent 在编排流程里自动调用。OpenClaw 技能开发的核心其实就两个文件plugin.json负责声明“这个技能叫什么、能执行什么动作、需要什么参数和权限”index.ts负责实现“具体怎么干”。前者是身份证加说明书后者是手脚加操作手册。把这两个文件写对再放到正确的目录下OpenClaw 内核就能加载并调用它。这篇文章面向的是需要把重复操作沉淀为可复用能力的开发者。我会给出plugin.json与index.ts的可复制骨架说明 TaoToken 统一 Key/API 通道在技能里的配置位置然后完整走一遍本地加载技能、验证触发与返回的流程。目标是一次跑通从技能注册到调用链路让你拿到骨架就能改出自己的技能。2. TaoToken 前置统一 Key 与 API 通道的配置位置在写技能逻辑之前先把模型调用的通道配好。OpenClaw 技能本身不绑定某个模型供应商但如果你希望技能内部调用大模型能力比如让技能对统计结果做一段自然语言总结就需要一个统一的 API 入口。我用的是 TaoToken 的统一 Key 通道好处是技能代码里只认一个 base URL 和一个 Key换模型不用改业务逻辑。TaoToken 的 API 地址是https://taotoken.net/api官网是https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content。你需要在控制台创建一个 API Key然后把它放到环境变量里而不是硬编码在index.ts中。技能代码通过process.env.TAOTOKEN_API_KEY读取这样本地调试和部署到其他环境时只需要改环境变量。具体操作路径先打开控制台创建 Key地址是https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite。创建完成后在 API Keys 页面复制 Key地址是https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite。如果你对模型对话能力还不熟悉可以先在模型对话页面试一下通道是否通地址是https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite。配置环境变量的方式在 macOS/Linux 下可以写进~/.zshrc或~/.bashrcexport TAOTOKEN_API_KEY你的Key export TAOTOKEN_BASE_URLhttps://taotoken.net/apiWindows PowerShell 下用$env:TAOTOKEN_API_KEY你的Key $env:TAOTOKEN_BASE_URLhttps://taotoken.net/api这样技能代码里就可以统一读取这两个变量。注意 API 地址不要加 UTM 参数保持https://taotoken.net/api干净即可。3. 可复制骨架plugin.json 与 index.ts 怎么写3.1 目录结构先定好一个标准的 OpenClaw 自定义技能文件夹我建议这样组织file-report-skill/ ├── plugin.json ├── index.ts ├── package.json └── tsconfig.jsonplugin.json是必须的index.ts是核心逻辑package.json用来声明依赖tsconfig.json保证 TypeScript 编译配置正确。如果你只用 Node.js 原生模块package.json里可以不装第三方依赖但保留它方便后续扩展。3.2 plugin.json 骨架这个文件告诉 OpenClaw 内核技能叫什么、能执行哪个 action、需要哪些参数、申请什么权限。下面是一个可直接复制的骨架我以“文件统计报表”为例{ name: file-report-skill, version: 1.0.0, description: 统计目录文件类型并生成 Markdown 报表, author: your-name, skills: [ { action: generate-file-report, description: 统计指定目录下各类文件数量并输出 Markdown 报表, parameters: [ { name: dirPath, type: string, required: true, description: 要统计的目录绝对路径 }, { name: outputPath, type: string, required: false, default: ./file-report.md, description: 报表保存路径 } ], permissions: [file.read, file.write] } ] }几个关键点action是内核调用时传入的动作名必须和index.ts里判断的字符串一致parameters里required为 true 的参数如果缺失内核会在调用前拦截permissions遵循最小权限原则只读就不写file.write。3.3 index.ts 骨架index.ts必须导出一个默认的异步函数接收action和params返回标准化结果。下面这个骨架包含了参数解析、核心处理、结果输出和异常处理import fs from fs; import path from path; interface SkillResult { success: boolean; message: string; data?: any; } function countFilesByType(dirPath: string): Recordstring, number { const stats: Recordstring, number {}; const entries fs.readdirSync(dirPath, { withFileTypes: true }); for (const entry of entries) { if (entry.isFile()) { const ext path.extname(entry.name).toLowerCase() || no-ext; stats[ext] (stats[ext] || 0) 1; } } return stats; } function generateMarkdownReport(stats: Recordstring, number, dirPath: string): string { const lines: string[] []; lines.push(# 文件统计报表); lines.push(); lines.push(统计目录\${dirPath}\); lines.push(); lines.push(| 文件类型 | 数量 |); lines.push(| --- | --- |); for (const [ext, count] of Object.entries(stats)) { lines.push(| ${ext} | ${count} |); } lines.push(); lines.push(总计${Object.values(stats).reduce((a, b) a b, 0)} 个文件); return lines.join(\n); } export default async function run(action: string, params: any): PromiseSkillResult { try { if (action ! generate-file-report) { return { success: false, message: 不支持的动作${action} }; } const { dirPath, outputPath ./file-report.md } params; if (!dirPath || typeof dirPath ! string) { return { success: false, message: 参数 dirPath 必须是非空字符串 }; } if (!fs.existsSync(dirPath)) { return { success: false, message: 目录不存在${dirPath} }; } const fileStats countFilesByType(dirPath); const markdown generateMarkdownReport(fileStats, dirPath); fs.writeFileSync(outputPath, markdown, utf8); return { success: true, message: 报表已生成至 ${outputPath}, data: fileStats }; } catch (error: any) { return { success: false, message: error.message || 未知错误 }; } }这个骨架里run函数先校验 action再校验参数然后执行统计和报表生成最后返回统一结构。任何异常都被try...catch捕获不会让内核崩溃。3.4 如果技能内部要调用模型假设你想在报表生成后让模型对统计结果做一段自然语言总结可以在index.ts里加一个调用函数。这里用 TaoToken 的统一通道async function summarizeWithModel(stats: Recordstring, number): Promisestring { const apiKey process.env.TAOTOKEN_API_KEY; const baseUrl process.env.TAOTOKEN_BASE_URL || https://taotoken.net/api; if (!apiKey) { return 未配置 TAOTOKEN_API_KEY跳过模型总结; } const prompt 请用一段话总结以下文件统计结果${JSON.stringify(stats)}; const response await fetch(${baseUrl}/v1/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${apiKey} }, body: JSON.stringify({ model: gpt-4o-mini, messages: [{ role: user, content: prompt }] }) }); const data await response.json(); return data.choices?.[0]?.message?.content || 模型未返回内容; }注意 base URL 用https://taotoken.net/api不要加 UTM 参数。模型名称根据你实际使用的通道支持的模型来填。如果你需要长期做编码类技能可以考虑 Coding Plan地址是https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite。4. 本地加载技能并验证触发与返回4.1 安装依赖与编译进入技能目录先初始化package.json和tsconfig.jsoncd file-report-skill npm init -y npm install typescript ts-node types/node --save-dev npx tsc --inittsconfig.json里确保target和module设置合理比如{ compilerOptions: { target: ES2020, module: CommonJS, outDir: ./dist, rootDir: ./, strict: true, esModuleInterop: true, skipLibCheck: true }, include: [index.ts] }然后编译npx tsc编译成功后会在dist/下生成index.js。OpenClaw 加载技能时如果配置指向index.ts需要确保运行环境支持 TypeScript更稳妥的方式是编译后指向dist/index.js。4.2 把技能放到 OpenClaw 技能目录OpenClaw 加载自定义技能通常有两种方式一种是放到全局技能目录另一种是在项目配置里指定技能路径。我建议先在项目级配置里指定方便调试。假设你的 OpenClaw 项目根目录下有一个skills/文件夹把整个file-report-skill复制进去cp -r file-report-skill /path/to/your-openclaw-project/skills/然后在 OpenClaw 的配置文件里注册这个技能。不同版本的配置字段可能略有差异核心是告诉内核技能目录和入口文件。一个常见的配置片段如下{ skills: [ { name: file-report-skill, path: ./skills/file-report-skill, entry: dist/index.js } ] }保存后重启 OpenClaw 服务让内核重新扫描技能目录。4.3 验证触发重启后在 OpenClaw 的对话界面里输入触发语句比如请调用 file-report-skill 的 generate-file-report 动作统计 /Users/me/projects 目录输出到 ./report.md如果内核正确加载了技能它会解析出 action 和参数然后调用index.ts里的run函数。你可以在技能代码里加一行console.log来确认调用是否发生console.log([file-report-skill] 收到调用, action, params);4.4 验证返回调用成功后检查./report.md是否生成内容应该是一个 Markdown 表格列出各类文件的数量。同时OpenClaw 对话界面应该返回类似{ success: true, message: 报表已生成至 ./report.md, data: { .ts: 12, .json: 3, .md: 5 } }如果返回success: false先看message里的错误信息再对照下一节的排查清单。5. 本篇常见错排查5.1 技能加载后不触发最常见的原因是plugin.json里的action和index.ts里判断的字符串不一致。比如plugin.json写的是generate-file-report但index.ts里判断的是generateFileReport内核传过来的 action 匹配不上直接返回“不支持的动作”。排查方法在run函数开头打印action对比两个文件。另一个原因是入口文件路径不对。如果配置里写entry: index.ts但运行环境不支持直接执行 TypeScript就会加载失败。建议编译后指向dist/index.js或者用ts-node注册。5.2 参数传递为空OpenClaw 内核在调用技能前会根据plugin.json的parameters做校验。如果required: true的参数没传内核可能直接拦截也可能传空值进来。你需要在index.ts里做二次校验比如if (!dirPath)就返回明确错误。不要假设内核一定帮你校验完整。5.3 权限不足导致文件读写失败plugin.json里声明了file.read和file.write但实际运行环境的文件系统权限可能不够。比如技能试图写入/root/下的文件但进程没有写权限就会抛异常。排查方法先用一个你有权限的目录测试比如./output/确认逻辑通了再换目标路径。5.4 模型调用返回 401 或 404如果技能内部调用了 TaoToken 通道返回 401 通常是 Key 没配或配错。检查process.env.TAOTOKEN_API_KEY是否在当前 shell 会话里生效可以用echo $TAOTOKEN_API_KEY确认。返回 404 通常是 base URL 写错了确保是https://taotoken.net/api不要多加/v1之外的路径也不要在 API 地址后面加 UTM 参数。5.5 编译报错找不到模块index.ts里import fs from fs如果报错检查tsconfig.json里是否设置了esModuleInterop: true。如果用了第三方库比如axios确保npm install已经执行并且package.json的dependencies里有记录。OpenClaw 加载技能时如果依赖没装运行时会报Cannot find module。5.6 技能返回了但界面没显示有些 OpenClaw 版本要求技能返回结构里必须包含success字段否则界面可能不渲染结果。确保你的run函数在所有分支都返回{ success: boolean, message: string }。如果返回了data确认它是可序列化的对象不要返回undefined或循环引用。6. 把技能接入 TaoToken 通道的完整动作如果你希望技能不仅能做本地文件处理还能调用模型能力接入 TaoToken 统一通道的完整动作是这样的先在控制台创建 Key地址是https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite然后在 API Keys 页面复制 Key地址是https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite接着把 Key 写入环境变量TAOTOKEN_API_KEYbase URL 设为https://taotoken.net/api最后在index.ts里用fetch或 SDK 调用/v1/chat/completions。如果你在接入过程中遇到报错可以先对照接入文档排查地址是https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite。如果你更习惯用 Claude Code 这类编码工具来辅助开发技能可以参考 ClaudeCodeAnthropic 的配置说明地址是https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaude-code-anthropicutm_campaignrewrite。长期做编码类技能或 Agent 编排的话Coding Plan 会更省心地址是https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite。技能骨架跑通之后你可以把countFilesByType换成任何自己的业务逻辑比如对接内部 CRM、生成周报、批量重命名文件。核心模式不变plugin.json声明能力index.ts实现逻辑TaoToken 提供统一的模型通道。先把一个最小技能跑通再逐步加参数、加权限、加模型调用这样每一步都有可验证的结果。
返回列表