Appearance
OpenClaw 本地技能封装开发指南
🎯 技能开发概述
什么是 OpenClaw 技能?
OpenClaw 技能是可复用的功能模块,封装了特定领域的能力,可以被不同的 Agent 调用。技能通过标准化的目录结构和配置文件实现插拔式集成。
技能核心特性
- 模块化设计:每个技能独立封装,互不干扰
- 标准接口:统一的调用方式和参数规范
- 配置驱动:通过配置文件控制行为
- 易于分发:支持本地和远程安装
📁 技能目录结构
标准目录结构
skill-name/
├── SKILL.md # 技能说明文档(必需)
├── scripts/ # 执行脚本目录
│ ├── main.sh # 主执行脚本
│ ├── config.json # 配置文件
│ └── utils/ # 工具函数目录
├── references/ # 参考文档
│ ├── api_reference.md # API 参考
│ ├── examples.md # 使用示例
│ └── troubleshooting.md # 故障排除
├── test/ # 测试脚本
│ └── test_main.sh # 测试脚本
└── assets/ # 静态资源(可选)
├── images/
└── templates/SKILL.md 文档规范
markdown
# 技能名称
**简短描述** - 一句话说明技能功能
## 📋 功能特性
### 核心功能
- 功能点1
- 功能点2
### 技术优势
- 优势1
- 优势2
## 🏗️ 架构设计
### 组件结构目录结构说明
### 数据流程
1. 步骤1
2. 步骤2
## 🛠️ 安装与配置
### 前置要求
```bash
# 依赖安装命令安装步骤
- 步骤1
- 步骤2
🚀 使用指南
基础使用
bash
# 示例命令高级配置
json
{
"配置项": "说明"
}⚙️ 配置参考
配置文件详解
- 配置项1: 说明
- 配置项2: 说明
🔧 维护与更新
更新日志
- 版本1.0.0: 初始版本
已知问题
- 问题1: 解决方案
## 🛠️ 技能开发实战
### 1. 创建技能模板
```bash
# 创建技能目录结构
mkdir -p my-skill/{scripts,references,test,assets}
cd my-skill
# 创建SKILL.md
cat > SKILL.md << 'EOF'
# My Skill
**简短描述** - 一句话说明技能功能
## 📋 功能特性
...
EOF
# 创建主脚本
cat > scripts/main.sh << 'EOF'
#!/bin/bash
# 主执行脚本
set -e
# 加载配置
CONFIG_FILE="$(dirname "$0")/config.json"
if [ -f "$CONFIG_FILE" ]; then
source <(jq -r 'to_entries|map("export \(.key)=\(.value|tostring)")|.[]' "$CONFIG_FILE")
fi
# 主逻辑
main() {
echo "技能执行开始"
# 业务逻辑
echo "技能执行完成"
}
main "$@"
EOF
# 创建配置文件
cat > scripts/config.json << 'EOF'
{
"enabled": true,
"timeout": 30,
"log_level": "info"
}
EOF2. 技能参数设计
bash
# 支持命令行参数
#!/bin/bash
# 默认参数
DEFAULT_TIMEOUT=30
DEFAULT_OUTPUT="result.json"
# 解析参数
while [[ $# -gt 0 ]]; do
case $1 in
--timeout)
TIMEOUT="$2"
shift 2
;;
--output)
OUTPUT="$2"
shift 2
;;
--help)
show_help
exit 0
;;
*)
echo "未知参数: $1"
exit 1
;;
esac
done3. 错误处理与日志
bash
#!/bin/bash
# 日志函数
log_info() {
echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') - $*"
}
log_error() {
echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') - $*" >&2
}
# 错误处理
trap 'cleanup_on_error' ERR
cleanup_on_error() {
log_error "脚本执行失败"
# 清理资源
exit 1
}
# 重试机制
retry_command() {
local max_attempts=3
local attempt=1
while [ $attempt -le $max_attempts ]; do
if "$@"; then
return 0
fi
log_error "尝试 $attempt/$max_attempts 失败"
((attempt++))
sleep 2
done
log_error "所有重试均失败"
return 1
}🔌 技能集成模式
1. 直接调用模式
bash
# 在Agent中直接调用技能脚本
#!/bin/bash
SKILL_DIR="$HOME/.openclaw/skills/my-skill"
# 检查技能是否存在
if [ -f "$SKILL_DIR/scripts/main.sh" ]; then
# 执行技能
"$SKILL_DIR/scripts/main.sh" --param1 value1 --param2 value2
else
echo "技能未安装: my-skill"
exit 1
fi2. 配置驱动模式
json
{
"skills": {
"my-skill": {
"enabled": true,
"schedule": "0 9 * * *",
"params": {
"timeout": 60,
"output_format": "markdown"
}
}
}
}3. 事件驱动模式
bash
#!/bin/bash
# 监听事件并触发技能
EVENT_TYPE="$1"
EVENT_DATA="$2"
case "$EVENT_TYPE" in
"message_received")
# 消息接收事件
if echo "$EVENT_DATA" | grep -q "关键词"; then
"$SKILL_DIR/scripts/main.sh" --trigger "message"
fi
;;
"schedule_trigger")
# 定时触发事件
"$SKILL_DIR/scripts/main.sh" --trigger "schedule"
;;
*)
echo "未知事件类型: $EVENT_TYPE"
;;
esac📊 技能配置管理
配置文件设计
json
{
"skill": {
"name": "my-skill",
"version": "1.0.0",
"description": "技能描述",
"author": "作者",
"license": "MIT"
},
"runtime": {
"timeout": 30,
"memory_limit": "256M",
"retry_count": 3
},
"parameters": {
"required": ["api_key", "endpoint"],
"optional": {
"format": "json",
"verbose": false
}
},
"triggers": {
"schedule": "0 9 * * *",
"events": ["message_received", "file_uploaded"]
},
"output": {
"format": "markdown",
"destination": "stdout"
}
}环境变量配置
bash
# 技能环境变量
export MY_SKILL_API_KEY="your-api-key"
export MY_SKILL_ENDPOINT="https://api.example.com"
export MY_SKILL_DEBUG="false"
# 在脚本中使用
API_KEY="${MY_SKILL_API_KEY:-default-key}"
ENDPOINT="${MY_SKILL_ENDPOINT:-https://default.endpoint}"🧪 技能测试
单元测试脚本
bash
#!/bin/bash
# test/test_main.sh
set -e
# 测试环境设置
TEST_DIR="$(dirname "$0")/.."
cd "$TEST_DIR"
# 测试函数
test_basic_function() {
echo "测试基本功能..."
# 执行技能
result=$(scripts/main.sh --test)
# 验证结果
if echo "$result" | grep -q "成功"; then
echo "✅ 测试通过"
else
echo "❌ 测试失败"
return 1
fi
}
test_error_handling() {
echo "测试错误处理..."
# 测试错误情况
set +e
result=$(scripts/main.sh --invalid-param 2>&1)
set -e
if echo "$result" | grep -q "错误"; then
echo "✅ 错误处理正常"
else
echo "❌ 错误处理异常"
return 1
fi
}
# 运行所有测试
run_tests() {
local tests_passed=0
local tests_failed=0
for test_func in test_basic_function test_error_handling; do
if $test_func; then
((tests_passed++))
else
((tests_failed++))
fi
done
echo "测试完成: $tests_passed 通过, $tests_failed 失败"
return $tests_failed
}
run_tests集成测试
bash
#!/bin/bash
# test/integration_test.sh
# 模拟Agent调用
AGENT_CALL="openclaw exec --command 'bash scripts/main.sh'"
# 测试不同参数组合
test_cases=(
"--help"
"--version"
"--input test.json --output result.json"
"--timeout 10 --verbose"
)
for test_case in "${test_cases[@]}"; do
echo "测试用例: $test_case"
eval "$AGENT_CALL $test_case"
echo "---"
done📦 技能打包与分发
打包脚本
bash
#!/bin/bash
# package-skill.sh
SKILL_NAME="my-skill"
VERSION="1.0.0"
PACKAGE_DIR="dist/$SKILL_NAME-$VERSION"
# 创建打包目录
mkdir -p "$PACKAGE_DIR"
# 复制必要文件
cp -r scripts references test SKILL.md "$PACKAGE_DIR/"
# 移除开发文件
rm -f "$PACKAGE_DIR/scripts/config.local.json"
rm -rf "$PACKAGE_DIR/test/tmp/"
# 创建安装脚本
cat > "$PACKAGE_DIR/install.sh" << 'EOF'
#!/bin/bash
set -e
echo "安装 $SKILL_NAME v$VERSION"
# 检查目标目录
TARGET_DIR="$HOME/.openclaw/skills/$SKILL_NAME"
if [ -d "$TARGET_DIR" ]; then
echo "警告: 技能已存在,将进行备份"
BACKUP_DIR="$TARGET_DIR.backup.$(date +%s)"
mv "$TARGET_DIR" "$BACKUP_DIR"
echo "已备份到: $BACKUP_DIR"
fi
# 复制文件
cp -r . "$TARGET_DIR"
chmod +x "$TARGET_DIR/scripts/"*.sh
echo "✅ 安装完成"
EOF
chmod +x "$PACKAGE_DIR/install.sh"
# 创建压缩包
tar -czf "dist/$SKILL_NAME-$VERSION.tar.gz" -C dist "$SKILL_NAME-$VERSION"
echo "打包完成: dist/$SKILL_NAME-$VERSION.tar.gz"分发清单
yaml
# skill-manifest.yaml
name: my-skill
version: 1.0.0
description: 技能描述
author: 作者
license: MIT
homepage: https://github.com/username/my-skill
repository: https://github.com/username/my-skill.git
dependencies:
- name: jq
type: system
check: "which jq"
- name: curl
type: system
check: "which curl"
files:
- SKILL.md
- scripts/
- references/
- test/
install: ./install.sh
uninstall: ./uninstall.sh🔄 技能更新机制
版本管理
bash
#!/bin/bash
# scripts/update.sh
CURRENT_VERSION="1.0.0"
LATEST_VERSION=$(curl -s https://api.github.com/repos/username/my-skill/releases/latest | jq -r '.tag_name')
if [ "$CURRENT_VERSION" != "$LATEST_VERSION" ]; then
echo "发现新版本: $LATEST_VERSION"
echo "当前版本: $CURRENT_VERSION"
# 下载新版本
wget "https://github.com/username/my-skill/releases/download/$LATEST_VERSION/$SKILL_NAME-$LATEST_VERSION.tar.gz"
# 安装新版本
tar -xzf "$SKILL_NAME-$LATEST_VERSION.tar.gz"
cd "$SKILL_NAME-$LATEST_VERSION"
./install.sh
echo "✅ 更新完成"
else
echo "已是最新版本"
fi配置迁移
bash
#!/bin/bash
# scripts/migrate-config.sh
# 备份旧配置
if [ -f "config.json" ]; then
cp config.json "config.json.backup.$(date +%s)"
fi
# 迁移配置
if [ -f "config.old.json" ]; then
jq '.new_field = .old_field // "default"' config.old.json > config.json
echo "配置迁移完成"
fi📈 性能优化
缓存机制
bash
#!/bin/bash
CACHE_DIR="/tmp/$SKILL_NAME-cache"
CACHE_TTL=3600 # 1小时
# 获取缓存
get_cache() {
local key="$1"
local cache_file="$CACHE_DIR/$key"
if [ -f "$cache_file" ]; then
local age=$(($(date +%s) - $(stat -f %m "$cache_file")))
if [ $age -lt $CACHE_TTL ]; then
cat "$cache_file"
return 0
fi
fi
return 1
}
# 设置缓存
set_cache() {
local key="$1"
local value="$2"
local cache_file="$CACHE_DIR/$key"
mkdir -p "$CACHE_DIR"
echo "$value" > "$cache_file"
}并发控制
bash
#!/bin/bash
LOCK_FILE="/tmp/$SKILL_NAME.lock"
# 获取锁
acquire_lock() {
exec 200>"$LOCK_FILE"
flock -n 200 || {
echo "另一个实例正在运行"
exit 1
}
echo $$ >&200
}
# 释放锁
release_lock() {
flock -u 200
rm -f "$LOCK_FILE"
}
# 使用锁
acquire_lock
trap release_lock EXIT
# 业务逻辑
main_function🛡️ 安全最佳实践
输入验证
bash
#!/bin/bash
# 验证参数
validate_input() {
local input="$1"
# 检查空值
if [ -z "$input" ]; then
echo "错误: 输入不能为空"
return 1
fi
# 检查特殊字符
if echo "$input" | grep -q '[<>|&;`$]'; then
echo "错误: 输入包含非法字符"
return 1
fi
# 检查长度
if [ ${#input} -gt 1000 ]; then
echo "错误: 输入过长"
return 1
fi
return 0
}权限控制
bash
#!/bin/bash
# 检查执行权限
check_permissions() {
# 不能以root运行
if [ "$(id -u)" -eq 0 ]; then
echo "错误: 不应以root权限运行"
exit 1
fi
# 检查文件权限
for file in scripts/*.sh; do
if [ ! -x "$file" ]; then
chmod +x "$file"
fi
done
}📚 参考案例:github-trending 技能
结构分析
github-trending/
├── SKILL.md # 完整文档
├── scripts/
│ ├── github_trending.sh # 主脚本:数据获取与处理
│ ├── format_message.py # 辅助脚本:消息格式化
│ └── config.json # 配置文件
├── references/
│ ├── api_reference.md # GitHub API参考
│ └── examples.md # 使用示例
└── test/
└── test_github_trending.sh # 测试脚本关键设计模式
- 模块化设计:主脚本负责业务逻辑,辅助脚本负责格式化
- 配置驱动:通过config.json控制行为
- 错误处理:完善的错误处理和重试机制
- 测试覆盖:包含完整的测试脚本
可复用组件
- 定时任务集成:支持OpenClaw cron调度
- 消息格式化:生成飞书卡片格式
- 缓存机制:避免重复