为什么Skills是OpenClaw的灵魂
很多人装好OpenClaw后第一反应是"然后呢?"——框架本身只是一个骨架,真正让它干活的是Skills(技能)。Skills相当于给AI装上了工具箱,从简单的文件操作到复杂的多步骤自动化流程,都可以封装成技能让Agent随时调用。官方技能库里已经有PDF处理、Excel操作、邮件发送等常用技能,但真实业务场景往往需要定制开发。
我在开发第一个技能时踩了不少坑:文档写的技能结构是v1规范,实际运行时发现v2才是主流;JSON Schema验证失败但报错信息 cryptic 到让人怀疑人生;好不容易跑通了,结果Skill调用时Agent死活不认。本文把这些实战经验整理出来,帮你绕过那些官方文档没写的隐藏陷阱。
技能开发的核心架构:三层模型
理解Skills的架构是开发的第一步。一个完整的技能包含三个层次:
- SKILL.md:技能说明书,告诉Agent这个技能能做什么、什么时候触发、如何使用
- Scripts/Tools:实际执行的代码,可以是Python脚本、Node.js模块或CLI命令
- 配置文件:定义技能元数据、依赖环境、参数Schema等
这个三层模型的关键在于SKILL.md是给Agent看的,不是给用户看的。很多新手把SKILL.md写成用户手册,结果Agent完全无法理解技能的真实能力。正确的做法是用结构化语言描述:
## 触发条件
- 用户提到"PDF转换"或"PDF合并"
- 用户需要处理PDF文档但未明确工具
## 能力范围
- 合并多个PDF为一个文件
- 拆分PDF为单页
- 提取PDF中的文字和表格
- 不支持PDF编辑和加密破解
## 使用方式
调用 scripts/pdf_tools.py,参数通过命令行传入
Agent读取这段描述后,在合适的时机就会自动触发技能,而不是等着用户显式调用。
从零开发一个实战技能:网页内容监控
假设你想让OpenClaw定期监控某个网页的变化,比如商品价格、新闻更新或竞品动态。我们来开发一个"网页变化监控"技能。
第一步:创建技能目录结构
在OpenClaw的skills目录下创建新技能:
~/.qclaw/skills/web-monitor/
├── SKILL.md
├── scripts/
│ └── monitor.py
└── config.json
目录名建议用英文短横线分隔,避免空格和特殊字符。
第二步:编写SKILL.md
这是技能的灵魂文件,必须写得既能让Agent理解,又能覆盖边界情况:
# 网页变化监控技能
## 功能描述
监控指定网页的内容变化,当检测到更新时发送通知。
## 触发条件
- 用户说"监控这个网页"或"这个网站有变化通知我"
- 用户提到"价格监控"、"新闻监控"、"页面变化"等关键词
- 用户需要定时检查某网页但未明确工具
## 使用方式
```bash
python scripts/monitor.py --url="https://example.com" --interval=3600 --notify="email"
```
## 参数说明
- url: 监控目标地址(必填)
- interval: 检查间隔,单位秒,默认3600
- notify: 通知方式,支持email/wechat/webhook
## 限制说明
- 不支持需要登录的页面
- 不支持动态加载内容(需JS渲染)
- 单个技能实例最多监控10个URL
注意:示例代码块中的 ${变量} 必须转义为 \${变量},否则JavaScript解析时会报错。
第三步:实现核心脚本
monitor.py的核心逻辑是:抓取网页内容 -> 计算内容hash -> 与上次对比 -> 检测到变化则通知。
import hashlib
import json
import argparse
import requests
from pathlib import Path
def get_content_hash(url):
"""抓取网页并计算内容hash"""
resp = requests.get(url, timeout=30)
resp.raise_for_status()
# 移除动态元素(时间戳、随机ID等)
content = resp.text
for pattern in ['<time>', '<span class="timestamp">']:
content = content.replace(pattern, '')
return hashlib.md5(content.encode()).hexdigest()
def check_change(url, cache_file):
"""检查网页是否有变化"""
current_hash = get_content_hash(url)
if not cache_file.exists():
cache_file.write_text(current_hash)
return False, "首次监控,已记录基准内容"
last_hash = cache_file.read_text().strip()
if current_hash != last_hash:
cache_file.write_text(current_hash)
return True, "检测到内容变化"
return False, "内容无变化"
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--url", required=True)
parser.add_argument("--interval", type=int, default=3600)
parser.add_argument("--notify", default="email")
args = parser.parse_args()
cache_dir = Path.home() / ".web-monitor-cache"
cache_dir.mkdir(exist_ok=True)
cache_file = cache_dir / hashlib.md5(args.url.encode()).hexdigest()
changed, message = check_change(args.url, cache_file)
if changed:
print(f"[ALERT] {args.url} - {message}")
# 这里可以接入通知逻辑
脚本写得简单粗暴,但足够说明问题。实际生产中需要处理更多边界情况:网络超时、页面结构变化、反爬虫机制等。
第四步:配置技能元数据
config.json定义技能的元信息和依赖:
{
"name": "web-monitor",
"version": "1.0.0",
"description": "监控网页内容变化并发送通知",
"author": "your-name",
"dependencies": {
"python": ">=3.8",
"packages": ["requests"]
},
"triggers": ["监控网页", "网页变化", "价格监控"],
"category": "automation"
}
调试技能的实战技巧
技能开发最头疼的不是写代码,而是调试Agent为什么没有触发技能。这里有三个实战技巧:
技巧一:手动触发测试
不要一上来就让Agent自动调用,先用命令行手动测试脚本是否正常:
cd ~/.qclaw/skills/web-monitor
python scripts/monitor.py --url="https://example.com"
确保脚本能跑通,再考虑Agent集成。
技巧二:检查SKILL.md的触发条件
Agent判断是否触发技能主要靠SKILL.md的描述。如果你的触发条件写得模糊,比如只写"监控网页",用户说"帮我盯着这个网站"Agent可能就识别不到。建议在触发条件里多写几种表达方式:
## 触发条件
- 用户说"监控"、"盯着"、"盯着变化"
- 用户提到"这个网站"、"这个页面"
- 用户表达定时检查网页的意图
技巧三:查看Agent的决策日志
OpenClaw会记录Agent的决策过程,包括为什么选择或不选择某个技能。日志通常在 ~/.qclaw/logs/ 目录下。如果Agent没触发你的技能,去日志里搜技能名,看看Agent的推理过程。
与定时任务联动:实现自动化监控
监控类技能的真正价值在于自动化运行。OpenClaw的定时任务(cron)功能可以定期触发技能执行。配置方式:
openclaw cron add --name="价格监控" --schedule="0 */6 * * *" --skill="web-monitor" --params='{"url":"https://example.com/price", "notify":"email"}'
这个命令会创建一个每6小时执行一次的监控任务。当检测到变化时,技能会调用OpenClaw的通知接口发送提醒。
如果你想深入了解OpenClaw定时任务的配置,可以参考OpenClaw定时任务实战,里面有cron表达式的详细说明。
技能开发的三个常见坑
坑一:SKILL.md触发条件写得太具体
错误示例:
## 触发条件
- 用户说"请帮我监控这个网页"
正确示例:
## 触发条件
- 用户表达监控意图,关键词包括:监控、盯着、跟踪、追踪变化
- 用户提供了URL且上下文暗示需要定期检查
Agent的语义理解能力有限,触发条件要覆盖多种表达方式。
坑二:脚本输出格式不规范
Agent读取脚本的stdout输出作为结果。如果输出是乱码或非结构化文本,Agent很难解析。建议统一输出JSON:
print(json.dumps({
"success": True,
"changed": True,
"message": "检测到价格变化",
"url": args.url
}, ensure_ascii=False))
坑三:没有处理异常情况
脚本在Agent环境中运行,一旦抛出未捕获的异常,整个技能调用就会失败。必须对可能的错误做兜底处理:
try:
changed, message = check_change(args.url, cache_file)
except requests.Timeout:
print(json.dumps({"success": False, "error": "请求超时"}))
except requests.ConnectionError:
print(json.dumps({"success": False, "error": "网络连接失败"}))
except Exception as e:
print(json.dumps({"success": False, "error": str(e)}))
技能发布与共享
开发完成的技能可以发布到OpenClaw的技能市场,让其他用户也能使用。发布前需要:
- 完善SKILL.md的使用说明和示例
- 添加单元测试覆盖核心逻辑
- 在config.json中填写作者和许可证信息
- 创建README.md面向用户的使用文档
发布命令:
openclaw skills publish ~/.qclaw/skills/web-monitor
审核通过后,其他用户就可以通过 openclaw skills install web-monitor 直接安装你的技能。
技能开发的进阶方向
基础技能跑通后,可以考虑以下进阶方向:
- 多工具编排:一个技能内部调用多个工具,实现复杂工作流。比如监控到网页变化后,自动调用OCR提取关键信息,再调用大模型生成摘要报告
- 状态管理:技能维护自己的状态数据,支持跨调用持久化。比如记录历史监控结果,绘制变化趋势图
- 与MCP协议集成:通过MCP协议让技能与其他Agent或工具互联互通,打破信息孤岛
- 流式输出:长时间运行的任务实时返回进度,而不是等全部完成才输出
总结
OpenClaw的Skills体系本质上是一个让AI学会使用工具的框架。开发技能的核心不是写代码,而是理解Agent的决策机制,用结构化语言描述能力边界。掌握了这个方法论,你就能把任意业务场景封装成技能,让OpenClaw真正成为你的自动化助手。
关键要点回顾:
- SKILL.md是给Agent看的,不是给用户看的,描述要结构化且覆盖多种表达
- 调试时先手动测试脚本,再考虑Agent集成
- 脚本输出统一用JSON格式,便于Agent解析
- 必须处理所有异常情况,避免技能调用失败
- 与定时任务联动是实现自动化的关键
版权声明
本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论