返回首页

🏷️ 标签(3)

🗂️ 分类(2)

第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. 实践 1 - 说明

  2. 实践 2 - 说明

  3. 实践 3 - 说明

常见问题

问题 1

症状:错误描述

解决方案

# 解决命令

下一步


📚 本章小结

✅ 完成了主要学习目标
✅ 掌握了核心概念
✅ 实践了示例代码
✅ 了解了最佳实践


返回目录 | 上一章 | 下一章