第10章 技能系统全解
第10章 技能系统全解
深入理解 Skill 系统,学习如何编写自定义 Skill。
🎯 本章目标
-
目标 1
-
目标 2
-
目标 3
-
目标 4
内置 Skill 列表
1. web_search - 网页搜索
功能:使用搜索引擎搜索网页内容。
安装:
openclaw skills install web_search
配置:
{
"skills": {
"web_search": {
"engine": "google",
"maxResults": 5,
"language": "zh-CN"
}
}
}
使用示例:
openclaw agent --message "搜索 OpenClaw 最新版本" --skills web_search
2. web_fetch - 网页抓取
功能:抓取并解析网页内容。
安装:
openclaw skills install web_fetch
使用示例:
openclaw agent --message "抓取 https://example.com 的内容" --skills web_fetch
3. browser - 浏览器自动化
功能:使用 Puppeteer 进行浏览器自动化操作。
安装:
openclaw skills install browser
配置:
{
"skills": {
"browser": {
"headless": true,
"timeout": 30000,
"viewport": {
"width": 1920,
"height": 1080
}
}
}
}
4. code_interpreter - 代码执行
功能:在沙箱中执行代码。
支持语言:
-
Python
-
JavaScript/TypeScript
-
Bash
安装:
openclaw skills install code_interpreter
安全配置:
{
"skills": {
"code_interpreter": {
"sandbox": "docker",
"timeout": 30000,
"maxMemory": "512m",
"allowedPackages": ["numpy", "pandas"]
}
}
}
5. file_operations - 文件操作
功能:读写文件、目录操作。
安装:
openclaw skills install file_operations
权限配置:
{
"skills": {
"file_operations": {
"allowedPaths": ["/home/user/documents"],
"maxFileSize": "10m",
"allowedExtensions": [".txt", ".md", ".json"]
}
}
}
编写第一个自定义 Skill
创建 Skill 项目
# 创建目录
mkdir my-skill
cd my-skill
# 初始化项目
npm init -y
# 安装依赖
npm install @openclaw/skill-sdk
Skill 基本结构
import { Skill, SkillContext, SkillResult } from '@openclaw/skill-sdk';
export class MySkill extends Skill {
// Skill 元数据
static metadata = {
name: 'my_skill',
version: '1.0.0',
description: '我的第一个自定义 Skill',
author: 'Your Name',
tags: ['custom', 'example']
};
// 参数定义
static parameters = {
input: {
type: 'string',
required: true,
description: '输入参数'
},
option: {
type: 'string',
required: false,
default: 'default',
description: '可选参数'
}
};
// 执行方法
async execute(context: SkillContext): Promise<SkillResult> {
const { input, option } = context.parameters;
try {
// 你的逻辑
const result = await this.processInput(input, option);
return {
success: true,
data: result,
message: '执行成功'
};
} catch (error) {
return {
success: false,
error: error.message,
message: '执行失败'
};
}
}
// 辅助方法
private async processInput(input: string, option: string): Promise<any> {
// 实现你的逻辑
return { processed: input, option };
}
// 生命周期钩子
async onInit(): Promise<void> {
console.log('Skill 初始化');
}
async onDestroy(): Promise<void> {
console.log('Skill 销毁');
}
}
// 导出 Skill
export default MySkill;
完整示例:天气查询 Skill
import { Skill, SkillContext, SkillResult } from '@openclaw/skill-sdk';
import axios from 'axios';
export class WeatherSkill extends Skill {
static metadata = {
name: 'weather',
version: '1.0.0',
description: '查询天气信息',
author: 'OpenClaw Team'
};
static parameters = {
city: {
type: 'string',
required: true,
description: '城市名称'
},
days: {
type: 'number',
required: false,
default: 1,
description: '查询天数'
}
};
private apiKey: string;
private baseURL: string = 'https://api.weatherapi.com/v1';
async onInit(): Promise<void> {
this.apiKey = this.config.get('apiKey');
if (!this.apiKey) {
throw new Error('Weather API Key not configured');
}
}
async execute(context: SkillContext): Promise<SkillResult> {
const { city, days } = context.parameters;
try {
const response = await axios.get(`${this.baseURL}/forecast.json`, {
params: {
key: this.apiKey,
q: city,
days: days,
lang: 'zh'
}
});
const weather = this.formatWeather(response.data);
return {
success: true,
data: weather,
message: `${city} 的天气信息`
};
} catch (error) {
return {
success: false,
error: error.message,
message: '查询天气失败'
};
}
}
private formatWeather(data: any): string {
const current = data.current;
const location = data.location;
return `
📍 ${location.name}, ${location.country}
🌡️ 温度:${current.temp_c}°C
💧 湿度:${current.humidity}%
🌬️ 风速:${current.wind_kph} km/h
☁️ 天气:${current.condition.text}
`.trim();
}
}
export default WeatherSkill;
测试 Skill
// test/weather.test.ts
import { WeatherSkill } from '../src/weather';
describe('WeatherSkill', () => {
let skill: WeatherSkill;
beforeEach(() => {
skill = new WeatherSkill({
apiKey: 'test_key'
});
});
it('should query weather', async () => {
const result = await skill.execute({
parameters: {
city: '北京',
days: 1
}
});
expect(result.success).toBe(true);
expect(result.data).toContain('北京');
});
});
发布 Skill
# 构建
npm run build
# 发布到 npm
npm publish
# 或发布到 OpenClaw Skill 市场(规划中)
openclaw skills publish
使用自定义 Skill
# 从 npm 安装
openclaw skills install my-skill
# 从本地安装
openclaw skills install ./my-skill
# 使用
openclaw agent --message "查询北京天气" --skills weather
Skill 生命周期
生命周期钩子
export class MySkill extends Skill {
// 1. 构造函数
constructor(config: SkillConfig) {
super(config);
}
// 2. 初始化
async onInit(): Promise<void> {
// 加载配置、连接数据库等
}
// 3. 执行前
async beforeExecute(context: SkillContext): Promise<void> {
// 参数验证、权限检查等
}
// 4. 执行
async execute(context: SkillContext): Promise<SkillResult> {
// 主要逻辑
}
// 5. 执行后
async afterExecute(result: SkillResult): Promise<void> {
// 日志记录、清理资源等
}
// 6. 错误处理
async onError(error: Error): Promise<void> {
// 错误处理逻辑
}
// 7. 销毁
async onDestroy(): Promise<void> {
// 关闭连接、释放资源等
}
}
上下文对象
interface SkillContext {
// 参数
parameters: Record<string, any>;
// 用户信息
user: {
id: string;
name: string;
channel: string;
};
// 会话信息
session: {
id: string;
history: Message[];
};
// Agent 信息
agent: {
name: string;
model: string;
};
// 工具方法
utils: {
logger: Logger;
cache: Cache;
http: HttpClient;
};
}
异步 Skill 与流式输出
异步 Skill
export class AsyncSkill extends Skill {
async execute(context: SkillContext): Promise<SkillResult> {
// 返回任务 ID
const taskId = await this.startAsyncTask(context.parameters);
return {
success: true,
data: { taskId },
message: '任务已启动',
async: true
};
}
// 查询任务状态
async getTaskStatus(taskId: string): Promise<TaskStatus> {
// 实现逻辑
}
}
流式输出
export class StreamSkill extends Skill {
async *executeStream(context: SkillContext): AsyncGenerator<string> {
const data = await this.fetchData();
for (const chunk of data) {
// 处理数据块
const processed = await this.process(chunk);
// 流式输出
yield processed;
}
}
}
// 使用
for await (const chunk of skill.executeStream(context)) {
console.log(chunk);
}
Skill 最佳实践
1. 错误处理
async execute(context: SkillContext): Promise<SkillResult> {
try {
// 参数验证
this.validateParameters(context.parameters);
// 执行逻辑
const result = await this.doWork(context);
return {
success: true,
data: result
};
} catch (error) {
// 记录错误
this.logger.error('Skill execution failed', error);
// 返回友好的错误信息
return {
success: false,
error: this.formatError(error),
message: '执行失败,请稍后重试'
};
}
}
2. 缓存策略
async execute(context: SkillContext): Promise<SkillResult> {
const cacheKey = this.getCacheKey(context.parameters);
// 检查缓存
const cached = await this.cache.get(cacheKey);
if (cached) {
return {
success: true,
data: cached,
cached: true
};
}
// 执行并缓存
const result = await this.doWork(context);
await this.cache.set(cacheKey, result, { ttl: 3600 });
return {
success: true,
data: result
};
}
3. 超时控制
async execute(context: SkillContext): Promise<SkillResult> {
const timeout = this.config.get('timeout', 30000);
try {
const result = await Promise.race([
this.doWork(context),
this.timeout(timeout)
]);
return {
success: true,
data: result
};
} catch (error) {
if (error.name === 'TimeoutError') {
return {
success: false,
error: 'Execution timeout',
message: '执行超时'
};
}
throw error;
}
}
private timeout(ms: number): Promise<never> {
return new Promise((_, reject) => {
setTimeout(() => {
const error = new Error('Timeout');
error.name = 'TimeoutError';
reject(error);
}, ms);
});
}
4. 资源管理
export class DatabaseSkill extends Skill {
private connection: Connection;
async onInit(): Promise<void> {
// 建立连接
this.connection = await createConnection(this.config.get('database'));
}
async execute(context: SkillContext): Promise<SkillResult> {
// 使用连接
const result = await this.connection.query(context.parameters.sql);
return {
success: true,
data: result
};
}
async onDestroy(): Promise<void> {
// 关闭连接
if (this.connection) {
await this.connection.close();
}
}
}
最佳实践
-
实践 1 - 说明
-
实践 2 - 说明
-
实践 3 - 说明
常见问题
问题 1
症状:错误描述
解决方案:
# 解决命令
下一步
📚 本章小结
✅ 完成了主要学习目标
✅ 掌握了核心概念
✅ 实践了示例代码
✅ 了解了最佳实践