OpenClaw 技能系统完全指南:从零开始创建自定义技能
摘要:本文详细讲解 OpenClaw 技能系统的架构设计、开发流程和最佳实践。包含技能创建、测试、调试、部署的完整教程,以及 10+ 个实用技能示例,所有代码均可直接使用。
封面图:
作者:江神
邮箱: jiangyayun72@gmail.com
发布时间:2026-03-04
阅读时间:25 分钟
一、为什么需要技能系统?
在 《30 天实战》 中,我记录了搭建 AI 助手"戴蒙"的过程。但原生的 LLM 能力有限,无法:
- 访问外部 API - 天气、股票、新闻等实时数据
- 执行代码 - Python、Node.js 脚本运行
- 操作文件 - 读写、转换、分析文件
- 控制设备 - 发送消息、管理日程、控制 IoT
技能系统的作用:扩展 AI 能力边界,让 Agent 能够与外部世界交互。
我的技能架构:
用户请求 → Agent 识别意图 → 选择技能 → 执行技能 → 返回结果
↓
技能注册表
↓
├─ web_search (网络搜索)
├─ image_generate (图片生成)
├─ voice_transcribe (语音转写)
├─ code_runner (代码执行)
├─ file_converter (文件转换)
└─ ... (更多技能)
二、技能系统架构
2.1 技能目录结构
/root/.openclaw/skills/
├── web_search/
│ ├── SKILL.md # 技能描述和触发条件
│ ├── skill.py # 技能实现
│ └── README.md # 使用说明
├── image-generate/
│ ├── SKILL.md
│ ├── image_generate.py
│ └── README.md
├── voice-transcribe/
│ └── ...
└── ...
2.2 技能元数据(SKILL.md)
每个技能必须包含 SKILL.md 文件,定义技能的触发条件和行为:
---
name: web_search
description: 使用 Brave Search API 搜索互联网
trigger: 用户提到"搜索"、"查找"、"调研"等关键词
---
# Web Search Skill
## 触发条件
- 用户请求搜索互联网
- 用户提到"搜索 XXX"、"查找 XXX"
- 用户需要最新信息
## 执行流程
1. 提取搜索关键词
2. 调用 Brave Search API
3. 解析搜索结果
4. 返回摘要和链接
## 配置
- API Key: BRAVE_API_KEY
- 搜索结果数量:10
2.3 技能执行机制
技能执行分为 4 个阶段:
1. 触发检测 → 2. 参数提取 → 3. 技能执行 → 4. 结果返回
示例流程:
用户:"搜索一下 2026 年 AI Agent 框架对比"
↓
Agent: 检测到"搜索"关键词 → 触发 web_search 技能
↓
参数提取:query="2026 年 AI Agent 框架对比"
↓
执行技能:调用 Brave Search API
↓
返回结果:搜索摘要 + 链接列表
三、创建第一个技能
3.1 示例:天气查询技能
步骤 1:创建目录
mkdir -p /root/.openclaw/skills/weather-query
步骤 2:创建 SKILL.md
cat > /root/.openclaw/skills/weather-query/SKILL.md << 'EOF'
---
name: weather-query
description: 查询指定城市的天气信息
trigger: 用户提到"天气"、"气温"、"天气预报"等
---
# Weather Query Skill
## 触发条件
- 用户询问天气情况
- 用户提到城市名 + 天气
- 用户需要天气预报
## 执行流程
1. 提取城市名称
2. 调用天气 API
3. 解析天气数据
4. 返回天气信息
## 配置
- API: Open-Meteo (免费,无需 API Key)
- 数据:温度、降水、风速、湿度
EOF
步骤 3:创建技能实现
cat > /root/.openclaw/skills/weather-query/skill.py << 'EOF'
#!/usr/bin/env python3
"""
天气查询技能
使用 Open-Meteo API(免费,无需 API Key)
"""
import requests
from typing import Dict, Optional
# 城市坐标映射(简化版)
CITY_COORDS = {
"北京": {"lat": 39.9042, "lon": 116.4074},
"上海": {"lat": 31.2304, "lon": 121.4737},
"深圳": {"lat": 22.5431, "lon": 114.0579},
"广州": {"lat": 23.1291, "lon": 113.2644},
}
def get_weather(city: str) -> Dict:
"""获取指定城市的天气信息"""
if city not in CITY_COORDS:
return {"error": f"不支持的城市:{city}"}
coords = CITY_COORDS[city]
url = "https://api.open-meteo.com/v1/forecast"
params = {
"latitude": coords["lat"],
"longitude": coords["lon"],
"current_weather": True
}
response = requests.get(url, params=params)
data = response.json()
current = data.get("current_weather", {})
return {
"city": city,
"temperature": current.get("temperature", "N/A"),
"windspeed": current.get("windspeed", "N/A"),
"time": current.get("time", "N/A"),
}
def execute(query: str) -> str:
"""技能入口"""
for city in CITY_COORDS.keys():
if city in query:
weather = get_weather(city)
return f"🌤 {weather['city']}天气\n温度:{weather['temperature']}°C"
return "请提供城市名称"
if __name__ == "__main__":
print(execute("北京天气怎么样?"))
EOF
步骤 4:测试技能
cd /root/.openclaw/skills/weather-query
python3 skill.py
输出:
🌤 北京天气
温度:15°C
四、高级技能开发
4.1 使用外部 API
示例:网络搜索技能
#!/usr/bin/env python3
"""网络搜索技能 - 使用 Brave Search API"""
import os
import requests
from typing import List, Dict
class WebSearchSkill:
def __init__(self):
self.api_key = os.getenv("BRAVE_API_KEY")
self.base_url = "https://api.search.brave.com/res/v1/web/search"
def search(self, query: str, count: int = 10) -> List[Dict]:
"""执行网络搜索"""
headers = {
"Accept": "application/json",
"X-Subscription-Token": self.api_key
}
params = {"q": query, "count": count}
response = requests.get(self.base_url, headers=headers, params=params)
data = response.json()
results = []
for result in data.get("web", {}).get("results", []):
results.append({
"title": result.get("title"),
"url": result.get("url"),
"description": result.get("description")
})
return results
def execute(self, query: str) -> str:
"""技能入口"""
results = self.search(query)
output = ["🔍 搜索结果:\n"]
for i, result in enumerate(results, 1):
output.append(f"{i}. **{result['title']}**")
output.append(f" {result['description']}")
output.append(f" 🔗 {result['url']}\n")
return "\n".join(output)
# 使用示例
if __name__ == "__main__":
skill = WebSearchSkill()
print(skill.execute("2026 AI Agent 框架对比"))
4.2 文件操作技能
示例:Markdown 转 HTML 技能
#!/usr/bin/env python3
"""Markdown 转 HTML 技能"""
import markdown
from pathlib import Path
class MarkdownToHTMLSkill:
def convert(self, md_file: str, output_file: str = None) -> str:
"""转换 Markdown 为 HTML"""
md_path = Path(md_file)
if not md_path.exists():
return f"❌ 文件不存在:{md_file}"
with open(md_path, 'r', encoding='utf-8') as f:
md_content = f.read()
html_content = markdown.markdown(
md_content,
extensions=['extra', 'codehilite', 'toc']
)
if output_file:
with open(output_file, 'w', encoding='utf-8') as f:
f.write(html_content)
return f"✅ 已转换:{md_file} → {output_file}"
return html_content
def execute(self, args: str) -> str:
"""技能入口"""
parts = args.split()
if len(parts) < 1:
return "用法:markdown-to-html <input.md> [output.html]"
return self.convert(parts[0], parts[1] if len(parts) > 1 else None)
# 使用示例
if __name__ == "__main__":
skill = MarkdownToHTMLSkill()
print(skill.execute("README.md output.html"))
4.3 代码执行技能
示例:Python 代码执行技能
#!/usr/bin/env python3
"""Python 代码执行技能"""
import subprocess
import tempfile
import os
class PythonCodeExecutor:
def __init__(self, timeout: int = 30):
self.timeout = timeout
def execute_code(self, code: str) -> dict:
"""执行 Python 代码"""
with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
f.write(code)
temp_file = f.name
try:
result = subprocess.run(
['python3', temp_file],
capture_output=True,
text=True,
timeout=self.timeout
)
return {
'success': result.returncode == 0,
'stdout': result.stdout,
'stderr': result.stderr,
}
finally:
os.unlink(temp_file)
def execute(self, code: str) -> str:
"""技能入口"""
result = self.execute_code(code)
if result['success']:
return f"✅ 执行成功\n\n输出:\n```\n{result['stdout']}\n```"
else:
return f"❌ 执行失败\n\n错误:\n```\n{result['stderr']}\n```"
# 使用示例
if __name__ == "__main__":
executor = PythonCodeExecutor()
test_code = """
print("Hello, World!")
for i in range(5):
print(f"Count: {i}")
"""
print(executor.execute(test_code))
五、技能注册与调用
5.1 技能注册
在 openclaw.json 中注册技能:
{
"skills": {
"enabled": [
"web_search",
"weather-query",
"image-generate",
"voice-transcribe",
"code-runner"
],
"config": {
"web_search": {
"api_key": "YOUR_BRAVE_API_KEY",
"max_results": 10
},
"weather-query": {
"default_city": "北京"
}
}
}
}
5.2 技能调用方式
方式 1:自动触发 - 基于用户意图自动选择技能
方式 2:显式调用 - 用户明确指定技能
方式 3:链式调用 - 多个技能组合使用
六、最佳实践
6.1 技能设计原则
- 单一职责 - 每个技能只做一件事
- 明确接口 - 输入输出清晰定义
- 错误处理 - 优雅处理异常情况
- 日志记录 - 详细记录执行过程
- 性能优化 - 避免阻塞和超时
6.2 安全注意事项
- API Key 管理 - 使用环境变量,不硬编码
- 输入验证 - 验证所有用户输入
- 权限控制 - 限制技能访问范围
- 超时设置 - 防止无限循环
- 资源限制 - 限制内存和 CPU 使用
6.3 性能优化
- 缓存结果 - 避免重复调用
- 异步执行 - 非阻塞 I/O
- 批量处理 - 减少 API 调用次数
- 连接池 - 复用数据库连接
- 懒加载 - 按需加载资源
七、总结
7.1 核心要点
- 技能架构 - SKILL.md + 技能实现 + 测试
- 开发流程 - 创建→测试→注册→调用
- 最佳实践 - 单一职责、错误处理、日志记录
- 安全注意 - API Key 管理、输入验证、权限控制
7.2 后续优化方向
- [ ] 创建更多实用技能
- [ ] 建立技能商店
- [ ] 实现技能链式调用
- [ ] 添加技能性能监控
欢迎交流讨论。如果你在技能开发过程中遇到问题,或者有相关问题,欢迎在评论区留言或私信我。