【Skill开发】OpenClaw插件开发入门------从零编写"文件统计与报表生成"Skill
一句话导读:如果把OpenClaw比作一个聪明的"大脑",那么Skill就是它的"手脚"------只有通过自定义Skill,你才能让AI真正按你的想法干活,本文将手把手带你从零开发一个实用的文件统计报表生成插件。
引言:Skill是OpenClaw的"手脚",自定义Skill实现个性化需求
在OpenClaw的生态中,有一个核心认知需要建立:模型负责"思考",Skill负责"执行"。官方预置的Skills(如文件管理、网页搜索、邮件发送)能满足通用需求,但面对你的个性化场景------比如统计项目目录下的文件类型分布、生成每日代码提交报表、对接公司内部的API------就显得力不从心。
这就是自定义Skill的价值所在。通过开发专属Skill,你可以:
- 突破边界:让AI直接操作你的本地文件、调用私有API、控制硬件设备
- 固化流程:将重复性工作封装成"一键执行",告别每次手动输入长提示词
- 提升精度:用代码逻辑替代模型猜测,确保任务执行100%符合预期
很多人觉得"开发Skill是高手的事",但实际上,只要你懂基础的JavaScript/TypeScript,遵循OpenClaw的标准化规范,完全可以在1小时内开发出第一个可运行的Skill。本文将从一个**"文件统计与报表生成"**的实战案例入手,带你完整走一遍Skill开发的四步流程,并深入讲解测试、调试和发布的全环节。
读完本文,你将不仅掌握自定义Skill的开发方法,更能理解:OpenClaw的"全能",本质是"可扩展"------而自定义Skills,就是你扩展它能力边界的最佳方式。
一、Skill核心规范:理解OpenClaw的插件化设计
在动手编码前,必须先搞清楚Skill的本质和规范。这能让你在开发时少走弯路,确保写出来的Skill能被OpenClaw内核正确识别和调度。
1.1 Skill的本质:标准化"执行单元"
OpenClaw的Skill不是黑盒插件,而是遵循固定规范的TypeScript/JavaScript模块。它的核心定位是:
- 不参与"意图解析":用户说的话由OpenClaw内核(或Agent层)转成结构化指令,Skill只负责"干活"
- 不管理权限:所有文件访问、网络请求的权限,都由OpenClaw内核统一校验
- 专注单一能力:一个Skill只做一件事(比如"统计目录文件""生成日报表"),简单、可复用、易维护
1.2 三文件结构:每个Skill的"标配"
所有OpenClaw Skill都遵循"三文件核心结构",这是能被内核识别、调度的基础:
Skill目录
元数据
执行逻辑
依赖管理
被内核调用
plugin.json
Skill核心
index.ts
package.json
OpenClaw网关
三个文件各司其职:
| 文件 | 作用 | 是否必选 |
|---|---|---|
plugin.json |
Skill的"身份证":定义名称、描述、输入参数、权限声明 | ✅ 必选 |
index.ts |
Skill的"手脚":实现具体的执行逻辑 | ✅ 必选 |
package.json |
Skill的"补给包":声明第三方依赖 | ❌ 可选(有依赖时需要) |
1.3 开发三大原则
- 标准化输入输出:参数和返回值必须符合规范,否则内核无法调度
- 最小权限原则:只申请完成任务必需的权限(如只读文件就不申请写入权限)
- 异常处理完备:必须捕获执行中的错误,返回清晰的错误信息,避免内核崩溃
1.4 Skill的加载路径与优先级
了解Skill的存放位置和加载顺序,有助于后续的测试和部署。OpenClaw按以下路径加载Skill,优先级从高到低:
低优先级
中优先级
高优先级
覆盖
覆盖
工作区Skill
~/.openclaw/workspace/skills/
托管Skill
~/.openclaw/skills/
内置Skill
随OpenClaw安装
| 加载位置 | 路径 | 说明 |
|---|---|---|
| 工作区Skill | ~/.openclaw/workspace/skills/ |
自定义开发的Skill,优先级最高,仅当前工作区生效 |
| 托管Skill | ~/.openclaw/skills/ |
从ClawHub安装的Skill,所有Agent共享 |
| 内置Skill | 随OpenClaw安装目录 | 系统自带基础Skill(如weather、session-logs) |
Windows系统路径示例:
- 工作区Skill:
C:\Users\你的用户名\.openclaw\workspace\skills\ - 托管Skill:
C:\Users\你的用户名\.openclaw\skills\
二、环境准备:5分钟搭好开发环境
自定义Skill开发不需要复杂环境,只需准备4样东西:
2.1 基础环境要求
| 组件 | 版本要求 | 作用 |
|---|---|---|
| Node.js | v18+(推荐v20) | OpenClaw核心依赖 |
| 包管理器 | npm/yarn/pnpm | 安装依赖 |
| 代码编辑器 | VS Code(推荐) | 编写代码 |
| OpenClaw | 已部署运行 | 测试Skill |
2.2 环境验证
打开终端,执行以下命令验证环境:
bash
# 检查Node.js版本
node -v # 输出 v18.x.x 或更高即可
# 检查npm版本
npm -v # 输出 9.x.x 或更高即可
# 确认OpenClaw已运行
openclaw gateway status # 显示"running"即为正常
2.3 初始化Skill目录
创建一个专属的Skill开发目录,后续所有代码都放在这里:
bash
# 创建目录
mkdir openclaw-custom-skills
cd openclaw-custom-skills
# 初始化第一个Skill项目
mkdir file-stat-skill
cd file-stat-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
tsconfig.json关键配置(可复制替换):
json
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"outDir": "./dist",
"rootDir": "./",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["**/*.ts"],
"exclude": ["node_modules", "dist"]
}
三、实战:文件统计Skill开发(四步走)
现在进入核心环节------开发一个"统计指定目录下文件类型和数量,生成Markdown报表"的Skill。这个案例覆盖"文件读取、数据处理、结果输出"三大核心能力,是自定义Skill的入门标配。
步骤1:创建Skill目录结构
按照规范,在刚才初始化的file-stat-skill目录下,我们需要创建核心文件:
file-stat-skill/
├── plugin.json # Skill元信息
├── index.ts # 核心执行逻辑
├── package.json # 依赖配置(已通过npm init创建)
└── tsconfig.json # TypeScript配置(已创建)
步骤2:编写plugin.json(Skill的"身份证")
plugin.json是OpenClaw识别Skill的关键文件,它告诉内核:这个Skill叫什么、能做什么、需要什么参数、申请什么权限。
创建plugin.json文件,写入以下内容:
json
{
"name": "file-stat-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": "要统计的目录绝对路径,如 D:/Documents 或 /home/user/docs"
},
{
"name": "outputPath",
"type": "string",
"required": false,
"default": "./file-report.md",
"description": "报表保存的路径(含文件名),默认生成在当前目录"
}
],
"permissions": [
"file.read",
"file.write"
]
}
]
}
关键字段解析:
| 字段 | 说明 |
|---|---|
name |
Skill的唯一标识符,建议用短横线命名 |
skills[].action |
动作名,内核调用时用,需唯一 |
parameters |
参数定义数组,遵循JSON Schema风格 |
permissions |
申请的权限清单,内核会据此进行权限校验 |
步骤3:编写index.ts(Skill的"手脚")
这是Skill的核心------实现具体的文件统计和报表生成逻辑。创建index.ts文件:
typescript
import fs from 'fs';
import path from 'path';
/**
* 统计目录下的文件类型和数量
* @param dirPath 目标目录路径
* @returns 文件统计数据(扩展名 -> 数量)
*/
function countFilesByType(dirPath: string): Record<string, number> {
const stats: Record<string, number> = {};
// 检查目录是否存在
if (!fs.existsSync(dirPath)) {
throw new Error(`目录不存在:${dirPath}`);
}
// 读取目录下所有文件(withFileTypes可区分文件和目录)
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('zh-CN', { timeZone: 'Asia/Shanghai' });
let markdown = `# 文件统计报表\n\n`;
markdown += `**统计目录**:\`${dirPath}\`\n`;
markdown += `**统计时间**:${now}\n\n`;
markdown += `| 文件类型 | 数量 |\n`;
markdown += `|----------|------|\n`;
// 按数量降序排序
const sortedEntries = Object.entries(stats).sort((a, b) => b[1] - a[1]);
for (const [ext, count] of sortedEntries) {
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 {
// 只处理我们定义的动作
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. 生成Markdown报表
const markdown = generateMarkdownReport(fileStats, dirPath);
// 3. 确定输出路径(处理相对路径)
const fullOutputPath = path.isAbsolute(outputPath)
? outputPath
: path.join(process.cwd(), outputPath);
// 确保输出目录存在
const outputDir = path.dirname(fullOutputPath);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
// 写入文件
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
};
}
}
代码要点解析:
- 异常处理全覆盖:用try-catch包裹所有逻辑,确保任何错误都能被捕获并返回
- 参数验证:检查目录是否存在,提供合理的错误信息
- 标准化返回格式 :统一使用
{ success, message, data }结构 - 路径处理:支持绝对路径和相对路径,自动创建输出目录
步骤4:编译TypeScript
由于OpenClaw直接加载JavaScript文件,我们需要将TypeScript编译成JS:
bash
# 在file-stat-skill目录下执行
npx tsc
编译后会在dist/目录生成index.js文件。后续OpenClaw加载的是这个JS文件。
步骤5:注册Skill到OpenClaw
将编译好的Skill部署到OpenClaw的工作区Skill目录:
bash
# 创建目标目录(如果不存在)
mkdir -p ~/.openclaw/workspace/skills/file-stat-skill
# 复制必要文件
cp plugin.json ~/.openclaw/workspace/skills/file-stat-skill/
cp dist/index.js ~/.openclaw/workspace/skills/file-stat-skill/index.js
# 如果有package.json也复制(但本示例无依赖)
注册Skill(让OpenClaw识别新技能):
bash
openclaw skill register file-stat-skill
# 或使用相对路径
openclaw skill register ~/.openclaw/workspace/skills/file-stat-skill
查看已安装Skill列表验证:
bash
openclaw skill list
# 应该能看到 file-stat-skill 在列表中
四、测试与调试:验证Skill是否按预期工作
Skill开发完成后,必须经过充分测试才能投入使用。以下是完整的测试与调试流程。
4.1 控制台调用测试
最简单的方式是通过OpenClaw命令行直接调用Skill:
bash
# 测试统计当前目录
openclaw run --skill file-stat-skill --action generate-file-report --params '{"dirPath": "./"}'
# 测试统计指定目录(Windows用户注意路径格式)
openclaw run --skill file-stat-skill --action generate-file-report --params '{"dirPath": "C:/Users/你的用户名/Desktop"}'
# 测试指定输出路径
openclaw run --skill file-stat-skill --action generate-file-report --params '{"dirPath": "./", "outputPath": "./my-report.md"}'
预期输出:
json
{
"success": true,
"message": "文件统计报表已生成",
"data": {
"stats": {
".js": 5,
".ts": 3,
".json": 2,
"无扩展名": 1
},
"reportPath": "/Users/yourname/my-report.md",
"totalFiles": 11
}
}
4.2 通过Web控制台测试
如果你更喜欢图形界面,可以通过OpenClaw Web控制台测试:
- 访问
http://localhost:18789(或你的服务器IP) - 在对话中输入:
帮我统计当前目录的文件,生成报表 - 观察AI是否能正确调用你的Skill
如果配置正确,AI应该能识别意图,调用file-stat-skill,并返回统计结果。
4.3 日志排查:当测试失败时
如果测试不成功,第一时间查看日志:
bash
# 查看OpenClaw网关日志
tail -f ~/.openclaw/logs/gateway.log
# 查看Skill专用日志
tail -f ~/.openclaw/logs/skill.log
常见错误及解决方法:
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
Skill not found |
Skill未注册或路径不对 | 检查openclaw skill list是否能看到Skill |
Permission denied |
权限不足 | 检查plugin.json中的permissions是否包含所需权限 |
Cannot find module |
依赖缺失 | 确保package.json中声明的依赖已安装 |
SyntaxError |
TypeScript编译错误 | 运行npx tsc --noEmit检查类型错误 |
4.4 调试技巧:打印日志辅助排查
在index.ts中添加调试日志(注意上线前移除或使用条件编译):
typescript
// 在关键位置添加
console.log(`[file-stat] 开始统计目录:${dirPath}`);
console.log(`[file-stat] 统计结果:`, fileStats);
然后通过网关日志查看输出:
bash
tail -f ~/.openclaw/logs/gateway.log | grep "\[file-stat\]"
五、发布到ClawHub:让更多人用上你的Skill
当你完成Skill开发并通过测试,可以考虑将其发布到OpenClaw官方市场ClawHub,与全球开发者分享。
5.1 发布前准备
完善plugin.json元数据
在plugin.json中添加更多信息,方便其他用户了解和使用:
json
{
"name": "file-stat-skill",
"version": "1.0.0",
"description": "统计指定目录的文件类型和数量,生成Markdown格式报表",
"author": "Your Name",
"homepage": "https://github.com/yourname/file-stat-skill",
"license": "MIT",
"keywords": ["file", "statistics", "report", "markdown"],
"skills": [
// ... 原有skills配置不变
]
}
编写使用文档
在Skill目录创建README.md,包含:
- Skill功能描述
- 安装方法
- 使用示例
- 参数说明
- 注意事项
版本规范
遵循语义化版本规范:主版本.次版本.修订号
- 主版本:不兼容的API变更
- 次版本:向下兼容的功能新增
- 修订号:向下兼容的问题修复
5.2 打包Skill
OpenClaw Skill的发布格式通常是一个压缩包:
bash
# 创建发布目录
mkdir file-stat-skill-release
cp plugin.json README.md dist/index.js file-stat-skill-release/
# 如果有依赖,需要包含package.json和node_modules?不,应该让用户自行安装依赖
# 打包
cd file-stat-skill-release
zip -r ../file-stat-skill-1.0.0.zip *
5.3 发布到ClawHub
有两种发布方式:
方式一:通过ClawHub CLI(推荐)
bash
# 安装ClawHub工具
npx clawhub@latest install
# 登录(需要先在clawhub.com注册账号)
npx clawhub login
# 发布Skill
npx clawhub publish ./file-stat-skill-1.0.0.zip
方式二:通过Web界面手动上传
- 访问 ClawHub官网
- 登录/注册账号
- 点击"Publish Skill"
- 填写元数据表单,上传打包文件
- 提交审核
5.4 发布后的维护
- 响应问题:关注ClawHub上的用户评论和Issue
- 版本更新:修复Bug或添加新功能后,更新版本号重新发布
- 文档维护:及时更新README和使用示例
六、进阶:让Skill支持图表生成
如果你想让"文件统计Skill"更进一步------生成可视化图表而非纯文本报表,可以结合图表库(如ECharts或Chart.js)实现。
6.1 添加图表依赖
在package.json中添加依赖:
json
{
"dependencies": {
"echarts": "^5.5.0"
}
}
安装并更新类型声明:
bash
npm install echarts @types/echarts
6.2 修改index.ts生成图表
typescript
import * as echarts from 'echarts';
import { createCanvas } from 'canvas'; // 需要安装canvas库
function generateChart(stats: Record<string, number>): Buffer {
// 创建画布
const canvas = createCanvas(800, 600);
const chart = echarts.init(canvas as any);
// 配置图表
const option = {
title: { text: '文件类型分布' },
tooltip: {},
xAxis: { data: Object.keys(stats) },
yAxis: {},
series: [{
name: '数量',
type: 'bar',
data: Object.values(stats)
}]
};
chart.setOption(option);
return canvas.toBuffer('image/png');
}
6.3 返回图表文件
修改返回结果,包含图表文件路径或Base64数据:
typescript
const chartBuffer = generateChart(fileStats);
const chartPath = path.join(outputDir, 'chart.png');
fs.writeFileSync(chartPath, chartBuffer);
return {
success: true,
message: '报表和图表已生成',
data: {
reportPath: fullOutputPath,
chartPath: chartPath,
stats: fileStats
}
};
七、面试考点:Skill开发的核心设计思路
在技术面试中,关于插件化架构和Skill开发,面试官通常会关注以下问题:
7.1 为什么采用"三文件结构"?
这种设计体现了关注点分离原则:
plugin.json:声明式配置,让内核无需执行代码就能了解Skill的能力index.ts:命令式实现,专注于具体逻辑package.json:依赖管理,保持环境隔离
7.2 如何保证Skill的安全性?
OpenClaw通过多层机制保障Skill执行安全:
- 权限声明:Skill必须声明所需权限,内核严格校验
- 沙箱隔离:可在独立容器或cgroup中执行Skill
- 资源限制:限制CPU、内存使用,防止恶意耗尽资源
- 代码审查:ClawHub会对发布的Skill进行审核
7.3 如何处理Skill升级的兼容性?
遵循向后兼容原则:
- 新增参数必须提供默认值
- 不改变现有参数的类型和含义
- 返回数据格式尽量保持稳定,如需变更则新增字段
- 主版本号变更意味着不兼容更新,需通知用户
7.4 如何调试复杂Skill?
分层调试策略:
- 单元测试:单独测试核心函数
- 集成测试:在OpenClaw环境中测试Skill调用
- 日志追踪 :利用
console.log和日志文件 - 断点调试:使用VS Code的Node.js调试功能
结语:从使用者到贡献者
通过本文,你已经完成了从零到一的Skill开发全流程------从理解核心规范,到动手编写"文件统计与报表生成"Skill,再到测试、调试和发布。这标志着你已经从OpenClaw的"使用者"进阶为"贡献者"。
开发一个Skill只是第一步。当你实现了一个具有通用价值的插件(比如对接某款流行的云存储服务,或者某个常用的开发工具API),不妨向ClawHub提交发布。开源的本质在于流动------通过编写Skill,你不仅让AI变得更强大,也在为整个AI Agent生态贡献属于你的那一砖一瓦。
接下来可以尝试的进阶方向:
- 开发对接私有API的企业Skill
- 实现多步骤复杂任务的Skill组合
- 探索基于WebAssembly的高性能Skill
快去动手写你的第一个Skill吧!如果在开发过程中遇到问题,欢迎在评论区留言交流。