自定义技能
创建你自己的 OpenClaw 技能,扩展 AI 助手的能力。
技能结构
一个完整的技能包含以下文件:
my-skill/
├── skill.json # 技能配置文件
├── index.ts # 主入口文件
├── prompts/ # 提示词模板
│ └── main.md
├── functions/ # 辅助函数
│ └── helper.ts
└── tests/ # 测试文件
└── index.test.ts创建第一个技能
1. 创建技能目录
bash
mkdir -p ~/.openclaw/skills/my-skill
cd ~/.openclaw/skills/my-skill2. 创建配置文件
skill.json:
json
{
"name": "my-skill",
"version": "1.0.0",
"description": "我的第一个自定义技能",
"author": "your-name",
"main": "index.ts",
"permissions": [
"filesystem",
"network"
],
"config": {
"apiKey": {
"type": "string",
"required": true,
"description": "API 密钥"
}
},
"triggers": [
{
"type": "command",
"pattern": "myskill"
}
]
}3. 创建入口文件
index.ts:
typescript
import { Skill, Context, Response } from 'openclaw'
interface MySkillConfig {
apiKey: string
}
export default class MySkill implements Skill {
name = 'my-skill'
description = '我的第一个技能'
private config: MySkillConfig
async initialize(config: MySkillConfig) {
this.config = config
console.log('技能初始化完成')
}
async execute(context: Context): Promise<Response> {
const { message, user } = context
// 解析用户意图
const intent = await this.parseIntent(message.content)
// 执行相应操作
const result = await this.performAction(intent)
return {
success: true,
message: result,
metadata: {
skill: this.name,
executedAt: new Date().toISOString()
}
}
}
private async parseIntent(content: string) {
// 解析用户意图
if (content.includes('查询')) {
return { action: 'query', params: {} }
}
return { action: 'default', params: {} }
}
private async performAction(intent: any) {
// 执行具体操作
switch (intent.action) {
case 'query':
return '查询结果:...'
default:
return '默认响应'
}
}
}4. 创建提示词模板
prompts/main.md:
markdown
# 技能说明
你是一个专业的助手,帮助用户完成以下任务:
1. 任务一描述
2. 任务二描述
## 用户请求
{{user_message}}
## 上下文
{{context}}
## 请根据以上信息执行相应操作5. 安装依赖
bash
npm init -y
npm install typescript @types/node6. 注册技能
bash
openclaw skills register ./my-skill技能 API
Context 对象
typescript
interface Context {
// 用户消息
message: {
id: string
content: string
attachments?: Attachment[]
}
// 用户信息
user: {
id: string
name: string
preferences?: Record<string, any>
}
// 会话信息
session: {
id: string
history: Message[]
metadata: Record<string, any>
}
// 配置
config: Record<string, any>
// 工具函数
tools: {
memory: MemoryTools
filesystem: FilesystemTools
network: NetworkTools
browser: BrowserTools
}
}Response 对象
typescript
interface Response {
success: boolean
message: string | ResponseMessage
actions?: Action[]
metadata?: Record<string, any>
}
interface ResponseMessage {
text: string
rich?: {
type: 'markdown' | 'html' | 'json'
content: string
}
attachments?: Attachment[]
}使用工具
typescript
// 文件系统操作
const content = await context.tools.filesystem.readFile('/path/to/file')
await context.tools.filesystem.writeFile('/path/to/file', 'content')
// 网络请求
const response = await context.tools.network.fetch('https://api.example.com')
// 浏览器控制
await context.tools.browser.open('https://example.com')
const screenshot = await context.tools.browser.screenshot()
// 记忆操作
await context.tools.memory.store('key', { data: 'value' })
const memory = await context.tools.memory.retrieve('key')技能配置
配置 Schema
json
{
"config": {
"apiKey": {
"type": "string",
"required": true,
"description": "API 密钥",
"default": "",
"validation": {
"pattern": "^sk-",
"message": "API Key 必须以 sk- 开头"
}
},
"maxRetries": {
"type": "number",
"required": false,
"description": "最大重试次数",
"default": 3
},
"features": {
"type": "object",
"properties": {
"enableCache": {
"type": "boolean",
"default": true
}
}
}
}
}用户配置
用户可以在 ~/.openclaw/config.json 中配置技能:
json
{
"skills": {
"my-skill": {
"apiKey": "your-api-key",
"maxRetries": 5,
"features": {
"enableCache": true
}
}
}
}触发器
技能可以通过多种方式触发:
命令触发
json
{
"triggers": [
{
"type": "command",
"pattern": "myskill"
}
]
}用户发送 /myskill 即可触发。
关键词触发
json
{
"triggers": [
{
"type": "keyword",
"patterns": ["查询天气", "天气预报"]
}
]
}定时触发
json
{
"triggers": [
{
"type": "schedule",
"cron": "0 9 * * *"
}
]
}事件触发
json
{
"triggers": [
{
"type": "event",
"event": "file.created",
"filter": {
"path": "/Documents/*.md"
}
}
]
}技能权限
声明技能所需的权限:
json
{
"permissions": [
{
"type": "filesystem",
"paths": ["~/Documents"],
"operations": ["read", "write"]
},
{
"type": "network",
"domains": ["api.example.com"],
"methods": ["GET", "POST"]
},
{
"type": "command",
"allow": ["git", "npm"],
"deny": ["rm -rf"]
}
]
}测试技能
单元测试
tests/index.test.ts:
typescript
import MySkill from '../index'
import { createMockContext } from 'openclaw-testing'
describe('MySkill', () => {
let skill: MySkill
beforeEach(() => {
skill = new MySkill()
})
test('should execute successfully', async () => {
const context = createMockContext({
message: { content: '测试消息' }
})
const response = await skill.execute(context)
expect(response.success).toBe(true)
expect(response.message).toBeDefined()
})
})运行测试:
bash
openclaw skills test my-skill调试模式
bash
openclaw skills debug my-skill发布技能
发布到 ClawHub
bash
# 登录
openclaw login
# 发布
openclaw skills publish my-skill版本管理
bash
# 更新版本
npm version patch # 1.0.0 -> 1.0.1
# 发布新版本
openclaw skills publish my-skill --update最佳实践
- 单一职责:每个技能专注一个任务
- 错误处理:优雅处理各种异常
- 用户反馈:提供清晰的操作反馈
- 日志记录:记录关键操作日志
- 性能优化:避免阻塞操作
- 安全考虑:不暴露敏感信息
示例:天气查询技能
typescript
import { Skill, Context, Response } from 'openclaw'
export default class WeatherSkill implements Skill {
name = 'weather'
description = '查询天气信息'
async execute(context: Context): Promise<Response> {
const city = this.extractCity(context.message.content)
if (!city) {
return {
success: false,
message: '请告诉我你想查询哪个城市的天气?'
}
}
const weather = await this.fetchWeather(city)
return {
success: true,
message: `${city}今天${weather.condition},气温${weather.temp}°C`
}
}
private extractCity(content: string): string | null {
const match = content.match(/(.+)天气/)
return match ? match[1] : null
}
private async fetchWeather(city: string) {
const response = await fetch(`https://api.weather.com/${city}`)
return response.json()
}
}