🛠️ Agent Skills开发完全指南:打造你的OpenClaw超能力

世界上有一种能力,叫Skills。它就像AI的手,让它从只会聊天的"话痨"变成能干活儿的"打工人"。4分17秒前,我决定写一个Skill...

Agent Skills是OpenClaw的核心扩展机制。本文将手把手教你如何从零开发一个Skill,并发布到ClawHub供全球开发者使用。

🎯 什么是Agent Skills?

Skills是OpenClaw的能力模块,每个Skill教AI如何做一件特定的事:

你可以把Skills理解为"超能力卡片",装上它,AI就多了一项技能。

📁 Skill目录结构

一个标准的Skill包含以下文件:

my-skill/
├── SKILL.md          # 技能描述和使用说明
├── index.js          # 主逻辑(可选)
├── utils.js          # 工具函数(可选)
├── package.json      # Node.js依赖(可选)
└── examples/         # 示例代码(可选)

📝 SKILL.md 编写规范

SKILL.md是Skill的核心文件,采用YAML Frontmatter格式:

---
name: weather-check
version: 1.0.0
description: 查询全球城市天气信息
author: miaoquai
tags:
  - weather
  - api
  - utility
requires:
  - node >= 14
permissions:
  - network
  - env:WEATHER_API_KEY
---

# Weather Check Skill

查询指定城市的实时天气和未来7天预报。

## 使用方式

```
查询北京天气
北京今天天气怎么样
明天上海会下雨吗
```

## 环境变量

- `WEATHER_API_KEY`: 天气API密钥(从 weatherapi.com 获取)

## 返回格式

```json
{
  "city": "北京",
  "temperature": 25,
  "condition": "晴天",
  "humidity": "45%"
}
```
💡 SKILL.md结构说明

💻 编写Skill逻辑

如果需要自定义逻辑,创建 index.js

// index.js
const axios = require('axios');

module.exports = {
  // Skill名称
  name: 'weather-check',
  
  // Skill描述
  description: '查询全球城市天气',
  
  // 工具函数列表
  tools: [
    {
      name: 'get_weather',
      description: '获取指定城市的天气信息',
      parameters: {
        type: 'object',
        properties: {
          city: {
            type: 'string',
            description: '城市名称(如:北京、London)'
          },
          days: {
            type: 'integer',
            description: '预报天数(1-7)',
            default: 1
          }
        },
        required: ['city']
      },
      handler: async ({ city, days = 1 }) => {
        const apiKey = process.env.WEATHER_API_KEY;
        const url = `https://api.weatherapi.com/v1/forecast.json?key=${apiKey}&q=${city}&days=${days}`;
        
        try {
          const response = await axios.get(url);
          return {
            success: true,
            data: response.data
          };
        } catch (error) {
          return {
            success: false,
            error: error.message
          };
        }
      }
    }
  ]
};

🔧 本地测试Skill

开发完成后,在本地测试:

# 1. 将Skill复制到OpenClaw技能目录
cp -r my-skill ~/.openclaw/skills/

# 2. 重启OpenClaw加载新Skill
openclaw restart

# 3. 测试Skill功能
openclaw test my-skill "查询北京天气"

# 4. 查看调试日志
openclaw logs --skill my-skill

📤 发布到ClawHub

准备工作

  1. 注册 ClawHub 账号
  2. 安装 clawhub CLI:
    npm install -g @openclaw/clawhub
  3. 登录ClawHub:
    clawhub login

发布流程

# 1. 初始化Skill项目
clawhub init my-skill

# 2. 编写和测试Skill(见上文)

# 3. 打包Skill
clawhub pack

# 4. 发布到ClawHub
clawhub publish

# 5. 查看已发布的Skill
clawhub list
⚠️ 发布前检查清单

🎨 高级技巧

1. 参数验证

const Joi = require('joi');

const schema = Joi.object({
  city: Joi.string().min(2).max(50).required(),
  days: Joi.number().integer().min(1).max(7).default(1)
});

// 在handler中验证
const { error, value } = schema.validate(params);
if (error) {
  throw new Error(`参数错误: ${error.message}`);
}

2. 缓存机制

const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 300 }); // 5分钟缓存

async function getWeather(city) {
  const cacheKey = `weather:${city}`;
  let result = cache.get(cacheKey);
  
  if (!result) {
    result = await fetchWeatherFromAPI(city);
    cache.set(cacheKey, result);
  }
  
  return result;
}

3. 错误处理

async function handler(params) {
  try {
    const result = await doSomething(params);
    return { success: true, data: result };
  } catch (error) {
    console.error('Skill执行错误:', error);
    return { 
      success: false, 
      error: '操作失败,请稍后重试',
      details: error.message 
    };
  }
}

🚀 最佳实践

命名规范

文档规范

安全规范

📚 实战案例:GitHub PR分析Skill

下面是一个完整的实战案例,展示如何创建一个有用的Skill:

// github-pr-analyzer/index.js
const { Octokit } = require('@octokit/rest');

module.exports = {
  name: 'github-pr-analyzer',
  description: '分析GitHub Pull Request的代码变更和质量',
  
  tools: [
    {
      name: 'analyze_pr',
      description: '分析指定PR的代码变更',
      parameters: {
        type: 'object',
        properties: {
          owner: { type: 'string', description: '仓库所有者' },
          repo: { type: 'string', description: '仓库名' },
          pr_number: { type: 'integer', description: 'PR编号' }
        },
        required: ['owner', 'repo', 'pr_number']
      },
      handler: async ({ owner, repo, pr_number }) => {
        const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
        
        try {
          // 获取PR详情
          const { data: pr } = await octokit.pulls.get({
            owner, repo, pull_number: pr_number
          });
          
          // 获取文件变更
          const { data: files } = await octokit.pulls.listFiles({
            owner, repo, pull_number: pr_number
          });
          
          // 分析结果
          const analysis = {
            title: pr.title,
            author: pr.user.login,
            additions: files.reduce((sum, f) => sum + f.additions, 0),
            deletions: files.reduce((sum, f) => sum + f.deletions, 0),
            files_changed: files.length,
            summary: pr.body?.substring(0, 200) || '无描述'
          };
          
          return { success: true, data: analysis };
        } catch (error) {
          return { success: false, error: error.message };
        }
      }
    }
  ]
};

🔗 相关资源