从零开始构建自定义 AI 技能 - OpenClaw Skill 开发教程

一、什么是 OpenClaw Skill?

OpenClaw Skill 是扩展 AI 助手能力的模块化组件。通过 Skill,你可以让 AI 执行特定任务,如:

  • 查询天气
  • 管理待办事项
  • 操作智能家居
  • 发布博客文章
  • 处理发票验证

核心优势:

  • 模块化设计,易于复用
  • 标准化接口,快速集成
  • 支持工具调用,连接外部服务
  • 内置记忆系统,保持上下文

二、Skill 项目结构

my-skill/
├── SKILL.md          # 技能描述和触发规则(必需)
├── index.js          # 技能主入口(可选)
├── handlers/         # 处理器目录
│   ├── weather.js    # 天气处理
│   └── todo.js       # 待办处理
├── tools/            # 自定义工具
│   └── api-client.js # API 客户端
├── templates/        # 响应模板
└── tests/            # 测试文件

SKILL.md 标准格式

# Skill Name

## Description
简短描述技能功能

## Triggers
- 当用户询问天气时
- 当用户说"明天会下雨吗"
- 当用户提到城市名 + 天气

## Tools Required
- web_search (可选)
- 自定义工具名

## Configuration
- API_KEY: 环境变量
- ENDPOINT: API 地址

三、开发第一个 Skill:待办事项管理器

3.1 创建项目结构

mkdir todo-skill
cd todo-skill
mkdir handlers tools tests

3.2 编写 SKILL.md

# Todo Skill

## Description
管理用户的待办事项,支持添加、删除、完成、列表查询

## Triggers
- 添加待办、新建任务
- 删除待办、完成任务
- 我的待办、任务列表
- 提醒我、记得做

## Tools Required
- file_write (写入本地文件)
- file_read (读取本地文件)

## Configuration
- TODO_FILE: 待办文件路径,默认 ~/todos.json

3.3 实现处理器 (handlers/todo.js)

// handlers/todo.js
const fs = require('fs').promises;
const path = require('path');

const TODO_FILE = process.env.TODO_FILE || path.join(
  require('os').homedir(), 
  'todos.json'
);

async function loadTodos() {
  try {
    const data = await fs.readFile(TODO_FILE, 'utf-8');
    return JSON.parse(data);
  } catch (e) {
    return { items: [], createdAt: new Date().toISOString() };
  }
}

async function saveTodos(todos) {
  await fs.writeFile(TODO_FILE, JSON.stringify(todos, null, 2));
}

async function addTodo(text, priority = 'normal') {
  const todos = await loadTodos();
  const item = {
    id: Date.now().toString(),
    text,
    priority,
    completed: false,
    createdAt: new Date().toISOString()
  };
  todos.items.push(item);
  await saveTodos(todos);
  return item;
}

async function listTodos(filter = 'all') {
  const todos = await loadTodos();
  switch (filter) {
    case 'active':
      return todos.items.filter(t => !t.completed);
    case 'completed':
      return todos.items.filter(t => t.completed);
    default:
      return todos.items;
  }
}

async function completeTodo(id) {
  const todos = await loadTodos();
  const item = todos.items.find(t => t.id === id);
  if (item) {
    item.completed = true;
    item.completedAt = new Date().toISOString();
    await saveTodos(todos);
  }
  return item;
}

module.exports = {
  addTodo,
  listTodos,
  completeTodo,
  loadTodos,
  saveTodos
};

3.4 在 SKILL.md 中定义处理逻辑

## Handlers

### add_todo
**Trigger:** 添加/新建 + 任务内容
**Action:** 调用 addTodo(text, priority)
**Response:** "✅ 已添加:{text}"

### list_todos
**Trigger:** 查看/列出 + 待办/任务
**Action:** 调用 listTodos(filter)
**Response:** 格式化列表

### complete_todo
**Trigger:** 完成/做完 + 任务标识
**Action:** 调用 completeTodo(id)
**Response:** "✅ 已完成:{text}"

四、工具调用与外部 API 集成

4.1 使用内置工具

OpenClaw 提供内置工具:

// 在技能中调用工具
const result = await tools.web_search({
  query: 'AI 编程助手对比',
  count: 5
});

4.2 创建自定义工具

// tools/weather-client.js
class WeatherClient {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = 'https://api.weather.com/v1';
  }

  async getCurrent(city) {
    const response = await fetch(
      `${this.baseUrl}/current?city=${city}&key=${this.apiKey}`
    );
    return response.json();
  }

  async getForecast(city, days = 3) {
    const response = await fetch(
      `${this.baseUrl}/forecast?city=${city}&days=${days}&key=${this.apiKey}`
    );
    return response.json();
  }
}

module.exports = WeatherClient;

五、状态管理与记忆

5.1 短期记忆(会话内)

// 在会话中保持状态
const sessionState = {
  currentCity: null,
  lastQuery: null,
  preferences: {}
};

async function handleWeatherQuery(city) {
  sessionState.currentCity = city;
  sessionState.lastQuery = new Date().toISOString();
  
  const weather = await weatherClient.getCurrent(city);
  return formatResponse(weather, sessionState.preferences);
}

5.2 长期记忆(跨会话)

使用 MEMORY.md 或记忆文件:

async function saveUserPreference(key, value) {
  const memory = await loadMemory();
  memory.preferences[key] = value;
  await saveMemory(memory);
}

async function getUserPreference(key, defaultValue) {
  const memory = await loadMemory();
  return memory.preferences[key] || defaultValue;
}

六、测试与调试

6.1 单元测试

// tests/todo.test.js
const { addTodo, listTodos, completeTodo } = require('../handlers/todo');

describe('Todo Skill', () => {
  test('should add todo', async () => {
    const todo = await addTodo('测试任务');
    expect(todo.text).toBe('测试任务');
    expect(todo.completed).toBe(false);
  });

  test('should list todos', async () => {
    const todos = await listTodos();
    expect(Array.isArray(todos)).toBe(true);
  });

  test('should complete todo', async () => {
    const todo = await addTodo('完成任务');
    const completed = await completeTodo(todo.id);
    expect(completed.completed).toBe(true);
  });
});

6.2 集成测试

# 测试技能触发
openclaw skill test todo-skill --trigger "添加一个待办:买牛奶"

七、发布与分享

7.1 本地安装

# 将技能复制到技能目录
cp -r todo-skill ~/.openclaw/skills/

# 重启 Gateway
openclaw gateway restart

7.2 使用 ClawHub 发布

# 安装 clawhub CLI
npm install -g clawhub

# 发布技能
cd todo-skill
clawhub publish --name todo-skill --version 1.0.0

八、最佳实践

8.1 错误处理

async function safeAddTodo(text) {
  try {
    return await addTodo(text);
  } catch (error) {
    console.error('添加待办失败:', error);
    return {
      error: true,
      message: '添加失败,请稍后重试',
      details: error.message
    };
  }
}

8.2 日志记录

const logger = {
  info: (msg, data) => {
    console.log(`[INFO] ${msg}`, JSON.stringify(data));
  },
  error: (msg, error) => {
    console.error(`[ERROR] ${msg}`, error);
  }
};

8.3 性能优化

  • 缓存频繁读取的数据
  • 批量处理多个操作
  • 异步执行非阻塞任务
  • 限制 API 调用频率

九、总结

Skill 开发流程:

  1. 定义技能功能和触发条件
  2. 设计项目结构
  3. 实现核心处理器
  4. 集成外部工具/API
  5. 添加状态管理
  6. 编写测试
  7. 发布与部署

核心要点:

  • SKILL.md 是技能的"身份证",清晰描述功能和触发规则
  • 处理器要单一职责,每个函数只做一件事
  • 错误处理要完善,给用户友好的反馈
  • 测试要覆盖主要场景,确保稳定性

系列导航:

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐