从零造 “手脚”:OpenClaw 自定义 Skills 开发实战 —— 让 AI 按你的想法干活
OpenClaw 最迷人的地方,从来不是官方预置的几十种基础能力,而是“你能为它定制专属技能”。官方 Skills 能满足文件整理、浏览器操控、邮件发送等通用需求,但面对你的个性化场景 —— 比如对接公司内部 CRM 系统、自动生成行业专属报表、同步个人理财数据,就显得力不从心。而自定义 Skills,正是让 OpenClaw 从 “通用 AI 助手” 变成 “专属数字员工” 的核心钥匙。
前言
OpenClaw 最迷人的地方,从来不是官方预置的几十种基础能力,而是“你能为它定制专属技能”。
官方 Skills 能满足文件整理、浏览器操控、邮件发送等通用需求,但面对你的个性化场景 —— 比如对接公司内部 CRM 系统、自动生成行业专属报表、同步个人理财数据,就显得力不从心。而自定义 Skills,正是让 OpenClaw 从 “通用 AI 助手” 变成 “专属数字员工” 的核心钥匙。
很多人觉得 “开发 Skill 是高手的事”,但实际门槛远低于想象:只要你懂基础的 JavaScript/TypeScript,遵循 OpenClaw 的标准化规范,就能在 1 小时内开发出第一个可运行的自定义 Skill。
本文是纯实战导向的开发指南,不堆砌复杂原理,只讲 “怎么写、怎么测、怎么用”:从 Skill 核心结构拆解,到两个典型案例(基础文件处理 + 第三方 API 对接)的完整开发流程,再到调试、部署、优化的全环节,全程附带可直接复制的代码,新手也能跟着落地。
读完这篇文章,你不仅能掌握自定义 Skill 的开发方法,更能理解:OpenClaw 的 “全能”,本质是 “可扩展”—— 而自定义 Skills,就是你扩展它能力边界的最佳方式。
一、先搞懂:OpenClaw 自定义 Skill 到底是什么?
在动手前,先明确 3 个核心认知,避免走弯路:
1.1 自定义 Skill 的本质
OpenClaw 的 Skill 不是 “黑盒插件”,而是遵循固定规范的 TypeScript/JavaScript 模块—— 核心作用是 “接收 OpenClaw 内核的标准化指令,执行具体操作,返回标准化结果”。
它的定位是:
- 不参与 “意图解析”:用户说的话由 OpenClaw 内核转成结构化指令,Skill 只负责 “干活”;
- 不管理权限:所有文件访问、网络请求的权限,都由 OpenClaw 内核统一校验;
- 专注单一能力:一个 Skill 只做一件事(比如 “查询快递”“生成周报”),简单、可复用、易维护。
1.2 自定义 Skill 的核心结构
所有 OpenClaw Skill 都遵循 “3 文件核心结构”,这是能被内核识别、调度的基础:
plaintext
my-custom-skill/
├── plugin.json # Skill 元信息(名称、权限、接口声明)
├── index.ts/js # 核心执行逻辑(接收参数、处理任务、返回结果)
└── package.json # 依赖配置(可选,有第三方依赖时需要)
其中:
plugin.json是 “身份证”:告诉 OpenClaw 这个 Skill 叫什么、能做什么、需要什么权限;index.ts/js是 “干活的手”:实现具体的执行逻辑;package.json是 “补给包”:声明 Skill 依赖的第三方库(比如请求 API 的 axios)。
1.3 开发核心原则
- 标准化输入输出:参数和返回值必须符合 OpenClaw 规范,否则内核无法调度;
- 最小权限原则:只申请完成任务必需的权限(比如读取文件就不申请写入权限);
- 异常处理完备:必须捕获执行中的错误,返回清晰的错误信息,避免内核崩溃;
- 无状态设计:Skill 不保存用户数据,所有上下文由 OpenClaw 内核传递。
二、环境准备:5 分钟搭好开发环境
自定义 Skill 开发不需要复杂环境,只需准备 4 样东西:
2.1 基础环境
- Node.js:v18+(OpenClaw 核心依赖,推荐 v18/v20 稳定版);
- 包管理器:npm/yarn/pnpm(Node.js 自带 npm,无需额外安装);
- 代码编辑器:VS Code(推荐,搭配 TypeScript 插件);
- 已部署的 OpenClaw:本地已运行 OpenClaw 网关(参考官方文档部署即可)。
2.2 环境验证
打开终端,执行以下命令,验证环境是否正常:
bash
运行
# 检查 Node.js 版本
node -v # 输出 v18.x.x 或更高即可
# 检查 npm 版本
npm -v # 输出 9.x.x 或更高即可
# 确认 OpenClaw 已运行
# 若 OpenClaw 以默认端口启动,访问 http://localhost:3000 能看到状态页即正常
2.3 初始化 Skill 目录
创建一个专属的 Skill 开发目录,后续所有代码都放在这里:
bash
运行
# 创建目录
mkdir openclaw-custom-skills
cd openclaw-custom-skills
# 初始化第一个 Skill 项目
mkdir file-report-skill
cd file-report-skill
# 初始化 package.json(一路回车即可)
npm init -y
# 安装必要依赖(TypeScript + 类型声明)
npm install typescript @types/node --save-dev
# 生成 tsconfig.json(TypeScript 配置)
npx tsc --init --target ES2020 --module CommonJS --outDir dist
三、实战 1:开发基础 Skill—— 文件统计报表生成
先从最简单的场景入手:开发一个 “统计指定目录下文件类型和数量,生成 Markdown 报表” 的 Skill。这个案例覆盖 “文件读取、数据处理、结果输出” 核心能力,是自定义 Skill 的入门标配。
3.1 步骤 1:编写 plugin.json(Skill 元信息)
在 file-report-skill 目录下创建 plugin.json,这是 OpenClaw 识别 Skill 的关键:
json
{
"name": "file-report-skill",
"version": "1.0.0",
"description": "统计指定目录的文件类型和数量,生成Markdown格式报表",
"author": "Your Name",
"skills": [
{
"action": "generate-file-report", // Skill 唯一动作名(内核调用时用)
"description": "统计目录文件并生成Markdown报表",
"parameters": [ // 接收的参数列表
{
"name": "dirPath", // 参数名:目标目录路径
"type": "string",
"required": true,
"description": "要统计的目录绝对路径,如 D:/Documents 或 /home/user/docs"
},
{
"name": "outputPath", // 参数名:报表输出路径
"type": "string",
"required": false,
"default": "./file-report.md", // 默认值
"description": "报表保存的路径(含文件名)"
}
],
"permissions": [ // 申请的权限
"file.read", // 读取文件权限
"file.write" // 写入文件权限
]
}
]
}
3.2 步骤 2:编写核心执行逻辑(index.ts)
创建 index.ts,实现文件统计和报表生成的核心逻辑:
typescript
运行
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
// 解决 ESModule __dirname 问题(若用 CommonJS 可省略)
const __dirname = path.dirname(fileURLToPath(import.meta.url));
/**
* 统计目录下的文件类型和数量
* @param dirPath 目标目录路径
* @returns 文件统计数据
*/
function countFilesByType(dirPath: string): Record<string, number> {
const stats: Record<string, number> = {};
// 检查目录是否存在
if (!fs.existsSync(dirPath)) {
throw new Error(`目录不存在:${dirPath}`);
}
// 读取目录下所有文件
const files = fs.readdirSync(dirPath, { withFileTypes: true });
for (const file of files) {
// 跳过目录
if (file.isDirectory()) continue;
// 获取文件扩展名
const ext = path.extname(file.name).toLowerCase() || '无扩展名';
// 统计数量
stats[ext] = (stats[ext] || 0) + 1;
}
return stats;
}
/**
* 生成 Markdown 格式报表
* @param stats 文件统计数据
* @param dirPath 目标目录
* @returns Markdown 字符串
*/
function generateMarkdownReport(stats: Record<string, number>, dirPath: string): string {
const now = new Date().toLocaleString();
let markdown = `# 文件统计报表\n`;
markdown += `**统计目录**:${dirPath}\n`;
markdown += `**统计时间**:${now}\n\n`;
markdown += `| 文件类型 | 数量 |\n`;
markdown += `|----------|------|\n`;
// 遍历统计数据生成表格
Object.entries(stats).forEach(([ext, count]) => {
markdown += `| ${ext} | ${count} |\n`;
});
// 计算总文件数
const total = Object.values(stats).reduce((sum, val) => sum + val, 0);
markdown += `\n**总文件数**:${total}\n`;
return markdown;
}
/**
* Skill 核心执行函数(必须导出 default 函数)
* @param action 调用的动作名(对应 plugin.json 中的 action)
* @param params 传递的参数
* @returns 标准化执行结果
*/
export default async function run(action: string, params: any) {
try {
// 只处理 generate-file-report 动作
if (action !== 'generate-file-report') {
return {
success: false,
message: `不支持的动作:${action}`,
data: null
};
}
// 提取参数(带默认值)
const { dirPath, outputPath = './file-report.md' } = params;
// 1. 统计文件
const fileStats = countFilesByType(dirPath);
// 2. 生成报表
const markdown = generateMarkdownReport(fileStats, dirPath);
// 3. 写入文件(处理相对路径)
const fullOutputPath = path.isAbsolute(outputPath)
? outputPath
: path.join(__dirname, outputPath);
fs.writeFileSync(fullOutputPath, markdown, 'utf8');
// 返回成功结果
return {
success: true,
message: `文件统计报表已生成`,
data: {
stats: fileStats,
reportPath: fullOutputPath,
totalFiles: Object.values(fileStats).reduce((sum, val) => sum + val, 0)
}
};
} catch (error) {
// 捕获所有错误,返回标准化结果
return {
success: false,
message: `执行失败:${(error as Error).message}`,
data: null
};
}
}
3.3 步骤 3:编译 TypeScript(可选)
若使用 TypeScript 开发,需要编译为 JavaScript:
bash
运行
# 执行编译(根据 tsconfig.json 输出到 dist 目录)
npx tsc
# 编译后会生成 dist/index.js,后续部署用这个文件
3.4 步骤 4:部署 Skill 到 OpenClaw
将开发好的 Skill 接入 OpenClaw 内核,才能被调用:
- 找到 OpenClaw 的 Skills 目录:OpenClaw 安装目录下的
skills/文件夹(若没有,手动创建)。 - 复制 Skill 目录:将
file-report-skill整个目录复制到skills/下。 - 重启 OpenClaw:停止 OpenClaw 网关,重新执行
npm start,内核会自动扫描并加载新 Skill。
3.5 步骤 5:测试 Skill
通过 OpenClaw 的交互入口(如 Telegram、WebUI)发送指令:
plaintext
执行 generate-file-report,参数:{"dirPath":"D:/桌面","outputPath":"D:/桌面/文件报表.md"}
若配置正确,OpenClaw 会:
- 统计 D 盘桌面的文件类型和数量;
- 在桌面生成
文件报表.md; - 返回执行结果:“文件统计报表已生成”,并附带统计数据。
四、实战 2:进阶 Skill—— 对接第三方 API(快递查询)
基础案例掌握后,我们开发一个对接外部 API 的 Skill,覆盖 “网络请求、参数校验、第三方服务集成” 核心场景 —— 以 “快递查询” 为例。
4.1 步骤 1:准备工作
- 申请快递 API 密钥:去「快递 100」或「聚合数据」申请免费的快递查询 API,获取 API Key。
- 安装依赖:在 Skill 目录下安装 axios(用于网络请求):
bash
运行
npm install axios npm install @types/axios --save-dev
4.2 步骤 2:编写 plugin.json
json
{
"name": "express-query-skill",
"version": "1.0.0",
"description": "通过快递100 API 查询快递物流信息",
"author": "Your Name",
"skills": [
{
"action": "query-express",
"description": "查询快递物流信息",
"parameters": [
{
"name": "expressNo",
"type": "string",
"required": true,
"description": "快递单号"
},
{
"name": "companyCode",
"type": "string",
"required": false,
"default": "",
"description": "快递公司编码(如 sf=顺丰,yt=圆通,空则自动识别)"
},
{
"name": "apiKey",
"type": "string",
"required": true,
"description": "快递100 API Key"
}
],
"permissions": [
"network.request" // 网络请求权限
]
}
]
}
4.3 步骤 3:编写核心逻辑(index.ts)
typescript
运行
import axios from 'axios';
/**
* 调用快递100 API 查询物流
* @param expressNo 快递单号
* @param companyCode 快递公司编码
* @param apiKey API密钥
* @returns 物流信息
*/
async function queryExpress(expressNo: string, companyCode: string, apiKey: string) {
// 快递100 API 地址(参考官方文档)
const url = 'https://www.kuaidi100.com/api';
const params = {
type: companyCode,
postid: expressNo,
id: 1,
valicode: '',
temp: Math.random(),
key: apiKey
};
const response = await axios.get(url, { params });
// 检查 API 返回状态
if (response.data.status !== '200') {
throw new Error(`API 返回错误:${response.data.message || '未知错误'}`);
}
return response.data;
}
/**
* 格式化物流信息为易读文本
* @param expressData API 返回的物流数据
* @returns 格式化文本
*/
function formatExpressData(expressData: any): string {
let result = `📦 快递查询结果\n`;
result += `单号:${expressData.postid}\n`;
result += `快递公司:${expressData.kuaidiName || '未知'}\n`;
result += `状态:${expressData.state === '0' ? '运输中' : expressData.state === '1' ? '已签收' : '异常'}\n\n`;
// 遍历物流轨迹
if (expressData.data && expressData.data.length > 0) {
result += `🚚 物流轨迹:\n`;
expressData.data.forEach((item: any, index: number) => {
result += `${index + 1}. ${item.time} - ${item.context}\n`;
});
} else {
result += `暂无物流信息\n`;
}
return result;
}
/**
* Skill 核心执行函数
*/
export default async function run(action: string, params: any) {
try {
if (action !== 'query-express') {
return {
success: false,
message: `不支持的动作:${action}`,
data: null
};
}
// 提取并校验参数
const { expressNo, companyCode = '', apiKey } = params;
if (!expressNo) throw new Error('快递单号不能为空');
if (!apiKey) throw new Error('API Key 不能为空');
// 1. 调用快递API
const expressData = await queryExpress(expressNo, companyCode, apiKey);
// 2. 格式化结果
const formattedResult = formatExpressData(expressData);
return {
success: true,
message: formattedResult,
data: expressData // 返回原始数据,方便后续扩展
};
} catch (error) {
return {
success: false,
message: `快递查询失败:${(error as Error).message}`,
data: null
};
}
}
4.4 步骤 4:部署与测试
- 编译 TypeScript(若使用);
- 将
express-query-skill复制到 OpenClaw 的skills/目录; - 重启 OpenClaw;
- 发送测试指令:
plaintext
成功后,OpenClaw 会返回格式化的快递物流信息。执行 query-express,参数:{"expressNo":"7894561230123","companyCode":"sf","apiKey":"你的API密钥"}
五、自定义 Skill 调试与避坑指南
开发过程中,难免遇到各种问题,这里总结高频坑点和调试方法:
5.1 调试技巧
- 日志打印:在关键逻辑处添加
console.log(),OpenClaw 启动日志会输出这些信息; - 本地测试:先写测试脚本,单独调用 Skill 的
run函数,验证逻辑是否正常:typescript
运行
// test.js import run from './index.js'; // 模拟 OpenClaw 调用 run('generate-file-report', { dirPath: 'D:/桌面' }) .then(result => console.log(result)) .catch(err => console.error(err)); - 查看 OpenClaw 日志:启动 OpenClaw 时,终端会输出 Skill 加载和调用的日志,能快速定位 “加载失败”“参数错误” 等问题。
5.2 高频坑点
- 路径问题:Skill 运行目录可能不是自身目录,务必使用绝对路径或
__dirname处理; - 权限问题:忘记在
plugin.json中声明权限,导致 OpenClaw 内核拦截操作; - 参数格式:传递的参数类型不匹配(比如传数字而非字符串),需在代码中做类型校验;
- 异步处理:网络请求、文件操作未用
async/await,导致返回结果为空; - 错误捕获:未捕获所有异常,导致 Skill 崩溃,进而影响 OpenClaw 内核。
六、进阶优化:让自定义 Skill 更专业
基础 Skill 开发完成后,可做这些优化,提升稳定性和复用性:
6.1 配置分离
将 API Key、默认路径等敏感 / 可变配置,抽离到单独的 config.json 文件,避免硬编码:
json
// config.json
{
"expressApiUrl": "https://www.kuaidi100.com/api",
"defaultReportPath": "./file-report.md"
}
6.2 权限细化
遵循 “最小权限”,比如只读文件的 Skill 只申请 file.read,不申请 file.write;
6.3 版本管理
在 plugin.json 中维护版本号,后续迭代时,OpenClaw 能识别 Skill 版本;
6.4 多动作支持
一个 Skill 可声明多个动作,比如快递 Skill 可同时支持 “查询物流” 和 “订阅物流提醒”。
总结:自定义 Skills,让 OpenClaw 真正属于你
OpenClaw 的魅力,在于它不是一个 “成品工具”,而是一个 “能力平台”—— 官方提供骨架,你用自定义 Skills 填充血肉。
本文通过两个实战案例,拆解了自定义 Skill 的完整开发流程:
- 遵循
plugin.json规范,声明 Skill 元信息和权限; - 编写核心执行逻辑,保证输入输出标准化;
- 部署到 OpenClaw 目录,重启加载;
- 测试、调试、优化,确保稳定运行。
核心要点回顾:
- 结构标准化:3 文件核心结构是被 OpenClaw 识别的基础;
- 逻辑单一化:一个 Skill 专注一件事,易维护、易复用;
- 结果标准化:无论成功失败,都返回
success/message/data结构; - 权限最小化:只申请必需的权限,保证安全。
当你掌握了自定义 Skill 开发,就可以:
- 对接企业内部系统,实现办公自动化;
- 集成个人常用服务,打造专属助理;
- 开发行业专属能力,解决垂直场景问题。
这正是 OpenClaw “本地优先、开放扩展” 理念的最佳体现 —— 你的 AI,理应由你定义它的能力。
更多推荐



所有评论(0)