OpenClaw 技能开发指南:从入门到精通
OpenClaw 技能开发指南:从入门到精通
技能是 OpenClaw 的核心能力扩展机制。通过开发自定义技能,你可以让 AI 助手执行各种特定的任务。本文将从基础概念到高级技巧,全面讲解 OpenClaw 技能开发。
技能开发基础
什么是技能?
技能是可插拔的功能模块,定义了: - 触发条件:什么时候执行 - 输入参数:需要什么数据 - 处理逻辑:如何处理数据 - 输出格式:返回什么结果
技能生命周期
初始化 → 触发 → 执行 → 返回结果 → 清理
创建第一个技能
步骤 1:创建技能目录
mkdir -p skills/hello
cd skills/hello
步骤 2:编写技能配置
创建 skill.yaml:
name: "hello"
version: "1.0.0"
description: "简单的打招呼技能"
# 触发关键词
triggers:
- "你好"
- "hello"
- "打招呼"
# 无需参数
parameters: []
# 作者信息
author:
name: "Your Name"
email: "you@example.com"
步骤 3:编写技能逻辑
创建 index.js:
module.exports = {
// 技能名称
name: 'hello',
// 技能描述
description: '打招呼技能',
// 执行函数(必需)
async execute(params, context) {
const username = context.user?.name || '朋友';
const hour = new Date().getHours();
let greeting;
if (hour < 12) {
greeting = '早上好';
} else if (hour < 18) {
greeting = '下午好';
} else {
greeting = '晚上好';
}
return {
type: 'text',
content: `${greeting},${username}!很高兴见到你!`
};
}
};
步骤 4:注册技能
在 config/skills.yaml 中:
skills:
- path: "./skills/hello"
enabled: true
步骤 5:测试技能
# 重启服务
npm run dev
# 在 Telegram/Discord 中发送 "你好"
# 应该收到问候消息
技能配置详解
触发器配置
triggers:
# 关键词触发
- "关键词1"
- "关键词2"
# 正则触发
patterns:
- "^查天气\\s+(.+)$"
- "^天气\\s+(.+)$"
# 命令触发
commands:
- name: "hello"
description: "打招呼"
# 事件触发
events:
- "message.receive"
- "user.join"
参数配置
parameters:
# 字符串参数
- name: "city"
type: "string"
required: true
description: "城市名称"
default: "北京"
# 数字参数
- name: "count"
type: "number"
required: false
default: 10
min: 1
max: 100
# 布尔参数
- name: "detailed"
type: "boolean"
required: false
default: false
# 枚举参数
- name: "unit"
type: "string"
required: false
default: "celsius"
options:
- value: "celsius"
label: "摄氏度"
- value: "fahrenheit"
label: "华氏度"
# 数组参数
- name: "tags"
type: "array"
required: false
items:
type: "string"
高级技能开发
1. 调用外部 API
const axios = require('axios');
module.exports = {
name: 'weather',
async execute(params, context) {
const { city } = params;
try {
// 调用天气 API
const response = await axios.get('https://api.weather.example.com/v1/current', {
params: {
city,
apiKey: process.env.WEATHER_API_KEY
},
timeout: 10000 // 10秒超时
});
const { condition, temperature, humidity } = response.data;
return {
type: 'text',
content: `【${city}天气】\n天气:${condition}\n温度:${temperature}°C\n湿度:${humidity}%`
};
} catch (error) {
// 错误处理
context.logger.error('Weather API error', error);
return {
type: 'text',
content: `抱歉,无法获取 ${city} 的天气信息。请稍后重试。`
};
}
}
};
2. 使用记忆功能
module.exports = {
name: 'preference',
async execute(params, context) {
const { action, key, value } = params;
const userId = context.user?.id;
switch (action) {
case 'set':
// 存储偏好
await context.memory.set(`pref:${userId}:${key}`, value);
return {
type: 'text',
content: `已记住您的偏好:${key} = ${value}`
};
case 'get':
// 读取偏好
const savedValue = await context.memory.get(`pref:${userId}:${key}`);
return {
type: 'text',
content: savedValue
? `您的 ${key} 偏好是:${savedValue}`
: `您还没有设置 ${key} 偏好`
};
case 'list':
// 列出所有偏好
const prefs = await context.memory.keys(`pref:${userId}:*`);
const prefList = await Promise.all(
prefs.map(async (k) => {
const v = await context.memory.get(k);
return `${k.split(':').pop()}: ${v}`;
})
);
return {
type: 'text',
content: `您的偏好设置:\n${prefList.join('\n')}`
};
}
}
};
3. 调用其他技能
module.exports = {
name: 'research',
async execute(params, context) {
const { topic, language = 'zh' } = params;
// 步骤 1:搜索
const searchResult = await context.callSkill('search', {
query: topic,
limit: 5
});
// 步骤 2:摘要
const summaryResult = await context.callSkill('summarize', {
content: searchResult.content,
maxLength: 500
});
// 步骤 3:翻译(如果需要)
if (language !== 'en') {
const translateResult = await context.callSkill('translate', {
text: summaryResult.content,
to: language
});
return translateResult;
}
return summaryResult;
}
};
4. 异步长时间任务
module.exports = {
name: 'long-task',
async execute(params, context) {
const { taskId } = params;
// 立即返回,任务在后台执行
setImmediate(async () => {
try {
// 执行耗时任务
for (let i = 0; i < 10; i++) {
await new Promise(r => setTimeout(r, 1000));
// 发送进度通知
await context.notifier.notify({
userId: context.user.id,
content: `任务进度:${(i + 1) * 10}%`
});
}
// 任务完成通知
await context.notifier.notify({
userId: context.user.id,
content: '✅ 任务完成!'
});
} catch (error) {
// 错误通知
await context.notifier.notify({
userId: context.user.id,
content: `❌ 任务失败:${error.message}`
});
}
});
return {
type: 'text',
content: `任务 ${taskId} 已启动,我会通知你进度。`
};
}
};
5. 处理文件
const fs = require('fs').promises;
const path = require('path');
module.exports = {
name: 'file-handler',
async execute(params, context) {
const { action, filename, content } = params;
const dataDir = './data/skill-data';
switch (action) {
case 'read':
const filePath = path.join(dataDir, filename);
const fileContent = await fs.readFile(filePath, 'utf-8');
return {
type: 'text',
content: fileContent
};
case 'write':
await fs.mkdir(dataDir, { recursive: true });
await fs.writeFile(path.join(dataDir, filename), content);
return {
type: 'text',
content: `文件 ${filename} 已保存`
};
case 'list':
const files = await fs.readdir(dataDir);
return {
type: 'text',
content: `文件列表:\n${files.join('\n')}`
};
}
}
};
技能测试
单元测试
创建 skills/hello/index.test.js:
const { execute } = require('./index');
describe('hello skill', () => {
test('should return greeting', async () => {
const context = {
user: { name: 'Test' }
};
const result = await execute({}, context);
expect(result.type).toBe('text');
expect(result.content).toContain('Test');
});
});
运行测试:
npm test skills/hello/index.test.js
集成测试
describe('hello skill integration', () => {
test('should respond to trigger', async () => {
// 模拟触发
const message = {
content: '你好',
user: { id: 'test-user' }
};
const result = await skillManager.trigger(message);
expect(result).toBeDefined();
expect(result.type).toBe('text');
});
});
技能发布
打包技能
// package.json
{
"name": "openclaw-skill-hello",
"version": "1.0.0",
"main": "index.js",
"keywords": ["openclaw", "skill", "hello"],
"openclaw": {
"skill": {
"name": "hello",
"description": "打招呼技能",
"author": "Your Name"
}
}
}
发布到 NPM
npm login
npm publish
使用发布的技能
# config/skills.yaml
skills:
- package: "openclaw-skill-hello"
version: "^1.0.0"
enabled: true
最佳实践
1. 错误处理
async execute(params, context) {
try {
// 业务逻辑
} catch (error) {
// 记录日志
context.logger.error('Skill execution failed', {
skill: 'my-skill',
error: error.message,
params
});
// 返回友好错误
return {
type: 'text',
content: '抱歉,处理您的请求时出现问题。请稍后重试。'
};
}
}
2. 参数验证
async execute(params, context) {
// 验证必填参数
if (!params.required) {
return {
type: 'text',
content: '请提供必要的参数'
};
}
// 验证参数类型
if (typeof params.count !== 'number') {
return {
type: 'text',
content: 'count 必须是数字'
};
}
// 验证参数范围
if (params.count < 1 || params.count > 100) {
return {
type: 'text',
content: 'count 必须在 1-100 之间'
};
}
// 继续处理...
}
3. 日志记录
async execute(params, context) {
const startTime = Date.now();
context.logger.info('Skill started', {
skill: 'my-skill',
userId: context.user?.id,
params
});
// 业务逻辑...
context.logger.info('Skill completed', {
skill: 'my-skill',
duration: Date.now() - startTime
});
}
4. 性能优化
async execute(params, context) {
// 使用缓存
const cacheKey = `skill:${params.query}`;
const cached = await context.cache.get(cacheKey);
if (cached) {
return cached;
}
// 计算结果
const result = await expensiveOperation(params);
// 缓存结果
await context.cache.set(cacheKey, result, 3600); // 1小时
return result;
}
调试技巧
1. 开启调试日志
logging:
level: "debug"
2. 使用调试器
node --inspect dist/index.js
然后在 Chrome DevTools 中连接调试。
3. 输出调试信息
async execute(params, context) {
context.logger.debug('Debug info', {
params,
user: context.user,
timestamp: Date.now()
});
// ...
}
总结
技能开发是 OpenClaw 的核心能力,通过本文你应该掌握了:
- ✅ 技能的基本概念
- ✅ 创建和注册技能
- ✅ 参数配置和验证
- ✅ 高级功能(API、记忆、异步)
- ✅ 测试和发布技能
- ✅ 最佳实践
开始开发你自己的技能吧!
相关阅读: - OpenClaw 技能系统 - OpenClaw 自动化工作流 - OpenClaw 最佳实践