为什么大部分OpenClaw用户永远在用别人的技能,而你可以通过本文学会自己写
我接触OpenClaw快半年了,观察到一个有趣现象:90%的用户只会安装和使用别人写好的技能(Skill),遇到特殊需求就到处求人。剩下9%的人会改改现成技能的参数,只有不到1%的人真正从零写过自己的Skill。这个比例跟Python生态很像——大多数人用现成库,少数人写自己的工具包。
但OpenClaw的技能开发比你想的简单得多。我第一次写Skill是因为需要一个"每天自动备份特定文件夹到网盘"的功能,翻遍SkillHub都没找到合适的。花了2小时看完官方文档,又花了3小时写出第一个能用的版本,中间踩了十几个坑。这篇文章就是把那十几个坑全部列出来,让你少走弯路。
OpenClaw技能的本质:一个带SKILL.md的文件夹
很多人被"开发"这个词吓到了。其实OpenClaw的技能本质上就是一个文件夹 + 一个SKILL.md描述文件 + 若干脚本。跟我来拆解一个最基础的技能结构:
my-first-skill/ ← 技能文件夹(名字随便取)
├── SKILL.md ← 必须有的描述文件(OpenClaw靠这个识别技能)
├── scripts/
│ └── main.cjs ← 核心执行脚本(.cjs扩展名,CommonJS格式)
├── config.json ← 可选:配置文件
└── README.md ← 可选:给人看的使用说明就这么简单。不需要编译,不需要打包,不需要学新的DSL(领域特定语言)。skillhub install的时候,OpenClaw就是把这个文件夹复制到~/.qclaw/skills/目录下,然后读取SKILL.md里的元数据。
SKILL.md:OpenClaw识别技能的唯一入口
SKILL.md有严格的格式要求,这是我最开始踩的第一个坑。它必须有frontmatter(开头的---分隔符之间的部分),OpenClaw靠这个判断"这是不是一个合法技能"。一个最小化的SKILL.md是这样的:
---
name: backup-folder
description: "自动备份指定文件夹到网盘"
metadata:
openclaw:
emoji: "📦"
---
# 文件夹自动备份技能
## 功能说明
自动将指定文件夹压缩并上传到配置好的网盘。
## 使用方法
在OpenClaw中说:"帮我备份D:\\Projects文件夹"
## 依赖要求
- 需要安装7-Zip或WinRAR
- 需要配置网盘API Token
这里有三个关键点:
- name字段:只能包含小写字母、数字、连字符,不能含空格或中文。这是技能的唯一ID,skillhub install时用的就是它。
- description字段:OpenClaw的Agent会读这个,用来判断"用户的需求是否应该触发这个技能"。写得好不好直接影响触发准确率。我建议用"动词+名词"结构,比如"自动备份指定文件夹到网盘",而不是名词短语"文件夹备份工具"。
- metadata.openclaw.emoji:纯装饰,但用户选技能时会看到,建议选个有意义的emoji。
第一个实战:写一个"每日科技新闻摘要"技能
光讲理论太虚,跟我一步步写一个真正能用的技能。需求是:每天早上8点,自动搜索昨天的科技新闻,生成200字摘要,发送到我的邮箱。
第一步:创建技能文件夹和SKILL.md
// ~/.qclaw/skills/daily-tech-news/SKILL.md
---
name: daily-tech-news
description: "每日自动搜索并摘要科技新闻,支持邮件发送"
metadata:
openclaw:
emoji: "📰"
---
# 每日科技新闻摘要技能
## 触发方式
用户说:"订阅科技新闻" 或 "每天8点给我发科技新闻摘要"
## 工作流程
1. 调用online-search技能搜索"最新科技新闻"(freshness=24h)
2. 提取前5条新闻标题和摘要
3. 用大模型生成200字左右的综合摘要
4. 通过邮件发送(或输出到聊天窗口)
## 配置
在config.json中配置收件邮箱地址。
第二步:写核心执行脚本scripts/fetch-news.cjs
这是最关键的的部分。OpenClaw的技能脚本必须用.cjs扩展名(CommonJS格式),不能用.mjs(ES Module)。我第一次写的时候用了import语法,调试了半小时才发现问题。
// ~/.qclaw/skills/daily-tech-news/scripts/fetch-news.cjs
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
/**
* 调用online-search技能搜索最新科技新闻
* 返回搜索结果的message字段(已格式化的文本)
*/
function searchTechNews() {
const scriptPath = path.join(
process.env.APP_PATH || 'C:\\Program Files\\QClaw\\resources\\openclaw\\config\\skills',
'online-search',
'scripts',
'prosearch.cjs'
);
// 调用搜索脚本,freshness=24h表示搜索最近24小时
// 使用字符串拼接,避免使用模板字符串
const cmd = 'node "' + scriptPath + '" --keyword=最新科技新闻 --freshness=24h';
const result = execSync(cmd, { encoding: 'utf-8', timeout: 30000 });
return JSON.parse(result);
}
/**
* 主函数
*/
function main() {
try {
console.log('🔍 开始搜索最新科技新闻...');
const searchResult = searchTechNews();
if (!searchResult.success) {
console.error('❌ 搜索失败:', searchResult.message);
process.exit(1);
}
console.log('✅ 搜索成功,结果:');
console.log(searchResult.message); // 直接输出格式化的搜索结果
// 实际部署时,这里会调用邮件发送逻辑
// 参考email-skill或public-skill的写法
} catch (error) {
console.error('❌ 执行出错:', error.message);
process.exit(1);
}
}
main();
这段代码有几个值得注意的细节:
- 脚本路径问题:online-search技能的脚本路径是硬编码的(C:\\Program Files\\...),这是第二个坑。正确做法是用
process.env.APP_PATH动态获取,但Windows下这个环境变量不一定有。我最后的方案是在config.json里让用户手动配置路径。 - execSync的timeout:搜索脚本可能因为网络问题卡住,必须设timeout。我设的30秒,实际发现大部分情况下5秒内就能返回。
- 输出格式:脚本的输出会被Agent捕获,所以直接用console.log输出格式化的文本即可,不需要返回JSON(除非你要在脚本间传递结构化数据)。
- 避免模板字符串:在技能脚本中,尽量使用字符串拼接(
'string' + variable + 'string')而不是模板字符串(`string ${variable} string`),可以避免很多转义问题。在SKILL.md的代码示例中展示时,也建议用字符串拼接的写法。
第三步:配置Cron定时任务
技能写好了,怎么让它每天8点自动执行?答案是配合OpenClaw的Cron系统。在SKILL.md里加上这段:
## 定时任务配置
用户说"订阅科技新闻"时,Agent应该执行以下操作:
1. 调用 `openclaw cron add` 创建定时任务
2. 任务配置:
- 名称:每日科技新闻摘要
- 时间:每天 08:00(Asia/Shanghai时区)
- 执行命令:`node ~/.qclaw/skills/daily-tech-news/scripts/fetch-news.cjs`
- 输出渠道:邮件(从config.json读取邮箱地址)
3. 示例cron表达式:`0 8 * * *`
我实际测试时发现,技能本身不应该直接操作Cron,应该由Agent根据用户指令来创建Cron任务。技能的职责是"提供执行脚本",而不是"自己把自己注册成定时任务"。这个职责分离很重要,否则技能之间会互相冲突。
调试技能的5个实用技巧
写技能最痛苦的不是写代码,是调试。以下是我踩坑3周后总结的调试技巧:
技巧1:用openclaw skill test命令快速测试
# 测试技能是否被OpenClaw正确识别
openclaw skill list | findstr daily-tech-news
# 手动执行技能脚本,看输出是否正确
node ~/.qclaw/skills/daily-tech-news/scripts/fetch-news.cjs
# 查看OpenClaw的技能加载日志
openclaw gateway logs --tail 50
技巧2:在SKILL.md里写清楚的"触发词"
Agent靠SKILL.md的description字段判断"用户这句话应该触发哪个技能"。如果你发现技能总是不触发,90%是因为description写得太模糊。对比一下:
| 不好的description | 好的description |
|---|---|
| "新闻摘要工具" | "每日自动搜索并摘要科技新闻,支持邮件发送" |
| "备份工具" | "自动备份指定文件夹到网盘,支持7-Zip压缩" |
| "聊天记录导出" | "导出OpenClaw会话记录为Markdown或PDF格式" |
技巧3:用process.env读取配置,不要硬编码
我第一个技能把邮箱地址、API Key都硬编码在脚本里,结果换个电脑就跑不起来了。正确做法是在技能目录下创建config.json:
// ~/.qclaw/skills/daily-tech-news/config.json
{
"email": "your@email.com",
"news_sources": ["科技新闻", "AI新闻", "半导体新闻"],
"max_news_count": 5,
"summary_length": 200
}
然后在脚本里读取:
const config = JSON.parse(fs.readFileSync(path.join(__dirname, '../config.json'), 'utf-8'));
console.log('📧 收件邮箱:' + config.email);
技巧4:处理Windows路径的坑
OpenClaw同时支持Windows和macOS,但路径分隔符不一样。我建议在脚本里用path.join()而不是字符串拼接:
// ❌ 错误:Windows下会出错
const scriptPath = __dirname + '/../scripts/main.cjs';
// ✅ 正确:跨平台兼容
const scriptPath = path.join(__dirname, '..', 'scripts', 'main.cjs');
技巧5:输出要有"视觉层次"
Agent会把脚本的console.log输出展示给用户。如果你的输出是一大坨纯文本,用户会觉得你的技能很low。学会用emoji和分段:
// ❌ 不好的输出
console.log('开始搜索');
console.log('搜索完成,找到10条结果');
console.log('生成摘要...');
console.log('摘要:AI领域今日发生重大突破...');
// ✅ 好的输出
console.log('🔍 正在搜索最新科技新闻...');
console.log('✅ 搜索完成,找到 10 条结果\n');
console.log('📝 正在生成摘要...\n');
console.log('📰 今日科技新闻摘要:');
console.log('─'.repeat(50));
console.log('AI领域今日发生重大突破...');
console.log('─'.repeat(50));
发布和分享你的技能
技能写好了,怎么分享给社区?OpenClaw的技能分发通过SkillHub完成。打包命令:
# 在技能目录下执行
cd ~/.qclaw/skills/daily-tech-news
npm pack . # 生成 daily-tech-news-1.0.0.tgz
# 或者手动打包成zip
# 确保SKILL.md在压缩包的根目录,而不是在子文件夹里
上传到SkillHub后,别人就可以通过skillhub install daily-tech-news安装你的技能了。我建议在上架前检查以下几项:
- SKILL.md的description是否清晰?
- 是否提供了使用示例(最好带截图)?
- 是否列出了依赖要求(比如需要安装7-Zip)?
- 是否处理了常见错误(比如网络超时、文件路径不存在)?
进阶:技能之间的组合和编排
OpenClaw最强大的地方在于技能可以互相调用。比如我写了一个"科技新闻摘要"技能,又写了一个"微信消息推送"技能,然后在Cron任务里把两者串起来:
// 每天8点,先抓取新闻,再推送到微信
// Cron任务的执行脚本
const { execSync } = require('child_process');
// 第一步:抓取新闻
const news = execSync('node ~/.qclaw/skills/daily-tech-news/scripts/fetch-news.cjs', { encoding: 'utf-8' });
// 第二步:推送到微信(调用另一个技能)
// 注意:传递给命令行参数时,需要转义双引号
const escapedNews = news.replace(/"/g, '\\"');
const pushCmd = 'node ~/.qclaw/skills/wechat-pusher/scripts/push.cjs --message "' + escapedNews + '"';
execSync(pushCmd, { encoding: 'utf-8' });
console.log('✅ 新闻已推送到微信');
这种"技能编排"的思路,让你可以用简单的技能组合出复杂的工作流。这也是OpenClaw跟其他AI助手最大的区别:它不只是聊天,它是真正可编排的自动化平台。
写在最后:从用户到开发者的跨越
学会写OpenClaw技能,本质上是在学"怎么让AI帮你干活"。你不再是一个被动的工具使用者,而是一个主动的工作流设计者。我现在的日常是:早上让OpenClaw自动抓取行业新闻、生成摘要、推送到我的Notion;中午让OpenClaw自动整理上午的聊天记录、提取待办事项、更新到TAPD;晚上让OpenClaw自动备份当天的工作文件到网盘。
这些自动化流程,每一个都是一个小技能。它们单个看起来很简单,但组合起来,每天帮我节省至少2小时。这2小时,我可以用来学习新东西、陪伴家人、或者单纯发呆——反正比手动做那些重复劳动有意义得多。
如果你已经看到这里,说明你对OpenClaw技能开发是真的感兴趣。不妨现在就打开PowerShell,输入mkdir ~/.qclaw/skills/my-first-skill,然后按照本文的步骤,写出你的第一个技能。记住:完成比完美重要,先跑起来,再慢慢优化。
版权声明
本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论