第16课:OpenClaw|开发你的第一个自定义Skill

32 阅读22分钟

正文.png

前15节课,你已经深入掌握了OpenClaw从部署、Workspace配置到企业级集成的方方面面。现在,你可能正面临这样一个渴望——让OpenClaw不再只是使用别人写好的技能,而是按照你自己独特的工作流程来行动。比如对接公司内部的CRM系统、从特定API拉取行业报表、自动同步个人理财数据、或者生成完全贴合你业务逻辑的周报。

官方Skills能满足文件整理、浏览器操控、邮件发送等通用需求,但面对这些个性化场景,就显得力不从心。而自定义Skills,正是让OpenClaw从“通用AI助手”变成“专属数字员工”的核心钥匙

很多人觉得“开发Skill是高手的事”,但实际门槛远低于想象:只要你懂基础的JavaScript/TypeScript,遵循OpenClaw的标准化规范,就能在1小时内开发出第一个可运行的自定义Skill。OpenClaw v2026.4.20+版本以来,技能开发体系已经高度规范化——不仅有声明式Skill(仅需SKILL.md,零代码开发),还有原生代码Skill(TypeScript/JavaScript),你完全可以根据自己的需求和能力水平选择最适合的开发路径。

本节课,我们将完整走通自定义Skill的开发全链路:从环境准备和项目结构说起,到TypeScript编写技能的核心模板,再到ISkill接口的详解、参数校验、本地测试与调试方法,最后以一个货币汇率转换技能将全部概念串联。读完这节课,你将具备独立开发、测试、调试OpenClaw自定义技能的全套能力。

16.1 技能开发环境准备与项目结构

一句话概括:自定义Skill的开发环境极度轻量——Node.js 18+(推荐22+)加任意代码编辑器即可,采用“3文件核心结构”(plugin.json/SKILL.md + index.ts + 可选package.json),OpenClaw内核自动识别技能目录并完成加载与调度。

开始编写代码之前,先理解OpenClaw Skill的本质到底是什么。

理解Skill的本质

OpenClaw的Skill不是“黑盒插件”,而是遵循固定规范的TypeScript/JavaScript模块——核心作用是“接收OpenClaw内核的标准化指令,执行具体操作,返回标准化结果”。它的定位是:

  • 不参与意图解析:用户说的话由OpenClaw内核转成结构化指令,Skill只负责“干活”;
  • 不管理权限:所有文件访问、网络请求的权限,都由OpenClaw内核统一校验;
  • 专注单一能力:一个Skill只做一件事(比如“查询实时汇率”“生成每周报告”),简单、可复用、易维护。

开发准备清单

只需要以下四个要素即可开始:

要素最低要求说明
Node.jsv18+(推荐v22+)OpenClaw运行核心依赖
包管理器npm/yarn/pnpm用于安装依赖和管理项目
代码编辑器VS Code(推荐)TypeScript语法支持和调试体验
OpenClaw环境已部署运行的OpenClaw实例用于技能加载和测试

避坑指南:建议使用与OpenClaw核心版本匹配的Node版本(官方推荐22+)。Node版本过低可能导致某些技能依赖的API不可用。

两种Skill开发模式

OpenClaw v2026.4.20+版本支持两种Skill开发模式,你可以根据功能复杂度和编码能力灵活选择:

类型开发难度功能上限适用场景
声明式Skill极低(无需代码)工具组合、提示词封装、简单流程自动化
原生代码Skill中等(需TS/JS基础)API集成、数据库操作、复杂逻辑编排

此外,还有一种MCP兼容Skill,可将第三方MCP Server暴露的工具直接包装成OpenClaw Skill,适用于飞书、企业微信对接、云服务操作等跨生态复用场景。

标准项目结构

一个完整的原生代码Skill项目遵循以下目录结构:

my-custom-skill/
├── SKILL.md          # 技能元数据和AI调用说明(永远必需)
├── manifest.json     # 权限声明和配置定义(v2026.4+推荐)
├── package.json      # Node.js依赖配置
├── index.ts          # 核心逻辑入口(TypeScript)
├── src/
│   ├── tools.ts      # 工具定义
│   └── types.ts      # 类型定义
├── tests/
│   ├── unit.test.ts  # 单元测试
│   └── integration.test.ts # 集成测试
├── assets/           # 静态资源
└── README.md         # 用户文档

对于最简单的声明式Skill,唯一必需文件只有SKILL.md。这极大降低了入门门槛——你可以从纯描述式技能开始,随着需求增长逐渐添加代码逻辑,形成渐进式的技能开发路径。

16.2 TypeScript编写技能的基本模板

一句话概括:使用官方推荐的脚手架工具生成TypeScript技能项目是最佳实践——一条命令便可快速构建一个完整的技能模板,包含SKILL.md、TypeScript严格模式配置、TDD工具链和ClawHub发布结构,一次性省去手动配置的繁琐工序。

推荐方式:使用脚手架工具

对于第一次开发自定义Skill,强烈建议使用openclaw-skill-boilerplate脚手架工具。这个由社区开发者构建的CLI工具填补了OpenClaw官方TypeScript模板的空白,让你告别手动复制SKILL.md结构、反复配置tsconfig的重复劳动——原本需要30+分钟的初始化准备工作,压缩到30秒内即可完成。

# 生成技能项目
npx openclaw-skill-boilerplate my-awesome-skill

# 进入目录
cd my-awesome-skill

# 安装依赖
npm install

# 编译TypeScript
npm run build

执行上述命令后,脚手架会自动生成一个包含以下元素的完整技能项目:

  • ✅ 格式正确的SKILL.md(OpenClaw专用frontmatter)
  • ✅ TypeScript严格模式配置(tsconfig.json
  • src/tools.ts + types.ts工具定义模式
  • {{SKILL_NAME}}占位符在全部模板中的自动替换
  • ✅ GitHub Actions CI流水线(build + typecheck)
  • ✅ ClawHub发布就绪的项目结构

生成的项目目录结构如下:

my-awesome-skill/
├── SKILL.md          # Skill入口文件(OpenClaw首先读取)
├── README.md         # 用户文档
├── package.json      # npm包配置
├── tsconfig.json     # TypeScript严格模式配置
├── src/
│   ├── index.ts      # 核心逻辑与导出
│   ├── tools.ts      # 工具定义
│   └── types.ts      # 共享类型定义
├── scripts/
│   └── scaffold.ts   # CLI脚手架脚本
├── templates/
│   └── skill/        # 模板文件目录
├── examples/
│   └── hello-world/  # 最小可工作示例
└── .github/
    └── workflows/
        └── ci.yml    # CI流水线

备选方式:手动创建技能

若你希望完全控制每个细节,或者仅需要一个最简结构的声明式技能(仅包含SKILL.md),可以使用以下手动方式:

Step 1:创建技能目录

Skills位于工作区根目录下的skills/文件夹中:

mkdir -p ~/.openclaw/workspace/skills/hello-world
cd ~/.openclaw/workspace/skills/hello-world

Step 2:编写SKILL.md(最简版)

---
name: hello-world
description: A simple skill that says hello.
---

# Hello World Skill

When the user asks for a greeting, use the `echo` tool to say "Hello from your custom skill!".

Step 3:加载并测试

# 重启Gateway或者开始新会话
openclaw gateway restart
# 或在聊天中发送 /new 命令

然后发送消息请求问候语,验证技能是否被调用。

Step 4:验证技能已加载

openclaw skills list

如果输出中包含hello-world且状态为enabled,说明技能已被正确识别。

16.3 ISkill接口详解与生命周期钩子

一句话概括:OpenClaw的Skills模块采用声明式注册机制,每个原生代码Skill需实现SkillDefinition接口——它定义了唯一标识(id)、输入参数schema、执行函数execute,以及可选的初始化init和清理cleanup等生命周期钩子。

SkillDefinition接口定义

一个符合OpenClaw规范的自定义Skill,其核心是导出一个符合SkillDefinition接口的对象。最新版本的Skill SDK提供了强类型的TypeScript接口,让你在编写过程中即可获得完整的类型检查和IDE智能提示。

import { SkillDefinition } from '@openclaw/skills-core';

const mySkill: SkillDefinition = {
  id: "my-custom-skill",
  name: "我的自定义技能",
  description: "一句话描述技能功能",
  version: "1.0.0",
  
  // 输入参数schema定义
  parameters: {
    type: "object",
    properties: {
      query: { type: "string", description: "用户查询内容" },
      limit: { type: "number", default: 10 }
    },
    required: ["query"]
  },
  
  // 核心执行函数
  execute: async (args, context) => {
    // 业务逻辑实现
    return { success: true, data: result };
  },
  
  // 可选:技能初始化钩子(加载时执行一次)
  init: async (context) => {
    console.log("技能已加载");
  },
  
  // 可选:技能清理钩子(卸载时执行)
  cleanup: async (context) => {
    console.log("技能已卸载");
  }
};

export default mySkill;

Skill插件的注册过程:需在Agent主入口文件中显式导入并注册该插件——Skills需通过Agent启动时显式加载,未注册的插件不会被网关路由识别。

// 在Agent配置对象的skills数组中插入该插件引用
import mySkill from '../skills/my-custom-skill';

const agent = createAgent({
  skills: [mySkill, /* 其他技能 */]
});

核心组件详解

OpenClaw的Skills模块采用分层设计,各核心组件的职能划分非常清晰:

  • Loader(加载器) :负责技能发现、加载和缓存,按照优先级搜索多个路径(workspace → user global → bundled)
  • Registry(注册表) :持有活跃的技能实例及其注册的能力(providers、tools),管理技能元数据和运行状态
  • Manifest Registry:读取静态元数据(来自plugin.json/manifest.json),无需加载技能代码即可获取信息
  • Runtime:提供执行上下文的API——包括参数获取、日志记录和Memory读写接口

Skill插件运行在隔离沙箱中,仅能通过预设的context对象访问Gateway转发的请求与Memory读写接口。这种隔离设计保证了即使技能包含错误或恶意逻辑,也不会直接损坏主系统。

生命周期流程图

flowchart TD
    A[Gateway启动] --> B[扫描技能路径]
    B --> C[发现skill目录]
    C --> D[加载SKILL.md元数据]
    D --> E[调用init钩子]
    E --> F[技能注册到Registry]
    F --> G[等待用户触发]
    G --> H{用户请求匹配?}
    H -->|是| I[参数校验]
    I --> J[调用execute]
    J --> K[返回执行结果]
    K --> G
    H -->|否| G
    F --> L[Gateway关闭]
    L --> M[调用cleanup钩子]
    M --> N[技能卸载]

无代码版本:更简单的Skill定义方式

对于不愿编写TypeScript代码的用户,OpenClaw也支持纯声明式Skill——只需一个SKILL.md即可让AI根据步骤指南完成工作,无需编写execute函数。AI会像阅读说明书一样读取SKILL.md中的指南,然后自主决定和组合Tool来完成你描述的任务。

16.4 技能的输入参数定义与类型校验

一句话概括:OpenClaw推荐使用ArkType进行参数schema定义和运行时校验——它比传统的JSON Schema更简洁、类型安全,且完全兼容TypeScript的类型推断。

参数Schema定义(ArkType推荐)

在OpenClaw v2026.3.31及以上版本中,官方推荐使用ArkType库进行输入参数的schema定义和运行时类型校验。与JSON Schema相比,ArkType的语法更接近TypeScript本身,让你在IDE中就能获得完整的类型检查和智能提示。

import { type } from '@arktype/arktype';

// 定义参数校验规则
const inputSchema = type({
  from: 'string',
  to: 'string',
  amount: 'number'
});

这种方式比手写JSON Schema直观得多,且ArkType自动完成类型推断,无需额外编写TypeScript类型声明。

自定义插件中的参数处理

在自定义插件(plugin)的更高级模式中,代码结构略有不同。你可以在Skill Definition内部包含一个tools数组,其中每个工具都需要显式声明parameters.zip参数schema:

export const myTool: SkillTool = {
  name: "currency-convert",
  description: "Convert amount between currencies",
  parameters: {
    type: "object",
    properties: {
      from: { type: "string", description: "源货币代码,如USD" },
      to: { type: "string", description: "目标货币代码,如CNY" },
      amount: { type: "number", description: "转换金额" }
    },
    required: ["from", "to", "amount"]
  },
  execute: async (args) => {
    const from = args.from as string;
    const to = args.to as string;
    const amount = args.amount as number;
    
    // 业务逻辑
    const result = await convertCurrency(from, to, amount);
    
    return { success: true, data: result };
  }
};

使用ArkType也可实现同样的schema定义,殊途同归。

参数校验最佳实践

在实际开发中,应始终遵循以下原则:

  1. 所有输入必须校验:即使你认为调用方只会传正确参数,也不能相信外部输入。
  2. 提供有意义的错误信息:当校验失败时,返回明确的错误提示而非通用的“参数错误”。
  3. 为参数设置合理默认值:例如limit默认10、timeout默认30秒,这样即使调用方漏传,技能仍能正常工作。
  4. 记录参数内容(脱敏后)到日志:便于排查问题,但注意避坑——切勿将API Key、密码等敏感信息记录到日志中。
  5. 使用TypeScript类型增强IDE体验:与ArkType配合,让所有参数在代码中有完整的类型提示。

16.5 技能的本地测试与调试方法

一句话概括:OpenClaw提供了从命令行基础调用到Gateway会话模拟的多级测试工具链,并支持DEV模式下的热重载,让你能够在真实环境中反复验证技能行为,而不必频繁重启服务。

完整的测试验证流程可以分为五个标准化步骤,我在其中加入了亲身实践得出的优化技巧。

第一层:命令行直接测试(最快反馈)

在技能源码仓库的根目录下直接运行测试命令,可在不依赖Gateway的情况下先验证核心逻辑:

# 假设技能包提供了测试脚本
pnpm --filter @my/skills run skill pipeline

对于使用脚手架生成的项目,通常在examples/hello-world目录下有最小可工作示例,你可以从中复制测试模式。

第二层:AI指令交互触发(端到端测试)

在集成环境中通过自然语言触发技能,验证从用户消息到技能执行的完整链路。先在聊天工具或WebUI中发送测试消息,观察Agent是否能够正确识别并调用你的技能:

帮我用currency-convert技能把100美元转成人民币

如果技能已被正确加载并注册到系统的user-invocable列表中,AI会自动将你自然语言的请求解析为技能调用。

第三层:开发者模式热重载(节省调试时间)

OpenClaw v2026.3.31及以上版本支持Skills模块的运行时热重载。启动时附加环境变量开启DEV模式:

OPENCLAW_DEV_MODE=true npm run start

在这种模式下,修改任何已注册技能文件并保存后,控制台会输出[HOTRELOAD] skill-name reloaded提示,技能逻辑立即生效。这是迭代调试时最节省时间的方式——无需反复重启Gateway。

第四层:调试日志输出策略

使用this.logger(在BaseSkill类中)或通过context.logger输出结构化日志,而不是随意的console.log:

context.logger.info("技能执行开始", { from, to, amount });
try {
  const result = await doSomething();
  context.logger.debug("API响应", { status: result.status });
  return { success: true, data: result };
} catch (error) {
  context.logger.error("技能执行失败", { error: error.message });
  return { success: false, error: error.message };
}

登录OpenClaw日志查看方式:

openclaw logs --follow | grep "my-skill"

第五层:异常路径与边界条件测试

测试用例必须覆盖以下异常场景:

测试类型示例预期行为
参数缺失调用时不传amount返回明确错误,明确提示缺少哪个参数
参数类型错误传字符串而非数字类型转换或报错
网络超时API响应缓慢按指数退避重试,最终返回超时错误
API凭证失效API Key过期捕获401/403,提示用户更新Key
限流触发短时间内大量调用队列等待或返回限流错误
数据格式异常API返回非预期JSON解析失败时优雅降级

常用CLI调试命令速查

# 列出所有已安装技能
openclaw skills list

# 查看指定技能的详细信息(含SKILL.md内容)
openclaw skills info my-skill

# 校验技能语法和格式
openclaw skill validate my-skill

# 强制重载技能(修改SKILL.md后)
openclaw skills reload

# 在命令行中直接调用技能测试(需Gateway运行)
openclaw skills run my-skill --params '{"from":"USD","to":"CNY","amount":100}'

16.6 实战:开发一个货币汇率转换技能

一句话概括:本实战将从头开发一个完整的货币汇率转换技能——它同时包含SKILL.md(声明式指导)和TypeScript代码(执行逻辑),让你在一个示例中体验声明式Skill和原生代码Skill两种模式的完整开发流程。

本实战将开发一个能够根据实时汇率完成货币转换的智能技能。它支持USD、EUR、CNY、JPY、GBP等主要货币的相互转换,当汇率API不可用时自动回退到缓存汇率,并在每次调用时记录转换日志。

第一步:脚手架生成项目

npx openclaw-skill-boilerplate currency-converter
cd currency-converter
npm install

第二步:编写SKILL.md(文档驱动)

编辑SKILL.md

---
name: currency-converter
description: 当用户询问货币汇率、货币转换、某币种对另一币种的兑换时使用。调用实时汇率API进行精准转换。
author: your-name
version: 1.0.0
license: MIT
user-invocable: true
metadata:
  openclaw:
    emoji: "💱"
    requires:
      bins: ["curl"]
      env: ["EXCHANGE_API_KEY"]
---

# 货币汇率转换技能

## 触发条件
当用户消息包含以下意图时调用此技能:
- “把X美元换成人民币”
- “100欧元等于多少日元”
- “查询USD/CNY汇率”

## 执行步骤
1. 从用户输入中识别源货币代码、目标货币代码和金额
2. 若用户只问汇率未提供金额,默认视为转换1单位
3. 调用汇率API获取实时数据
4. 执行金额转换计算
5. 格式化返回结果,保留两位小数

## 可用工具
本技能使用内置的 `fetch` 工具调用汇率API

## 输出格式
"{amount} {from} = {converted} {to} (汇率: {rate},数据时间: {timestamp})"

第三步:实现TypeScript核心逻辑(编辑src/tools.ts

import { SkillTool } from '@openclaw/skills-core';

// 汇率API配置(示例免费API,生产环境请替换为正式服务)
const EXCHANGE_API_URL = 'https://api.exchangerate-api.com/v4/latest';

// 汇率缓存(简单实现,生产环境建议使用Redis或更完善的方案)
let rateCache: { rates: Record<string, number>; timestamp: number } | null = null;
const CACHE_TTL = 3600000; // 1小时缓存

async function fetchExchangeRates(base: string) {
  // 检查缓存是否有效
  if (rateCache && (Date.now() - rateCache.timestamp) < CACHE_TTL) {
    return rateCache.rates;
  }
  
  try {
    const response = await fetch(`${EXCHANGE_API_URL}/${base}`);
    
    if (!response.ok) {
      throw new Error(`API请求失败: ${response.status}`);
    }
    
    const data = await response.json();
    
    // 更新缓存
    rateCache = {
      rates: data.rates,
      timestamp: Date.now()
    };
    
    return data.rates;
  } catch (error) {
    // ⚠️【重要】网络错误时尝试返回上次缓存的汇率(即使已过期)
    if (rateCache) {
      console.warn(`API调用失败,使用缓存汇率: ${error.message}`);
      return rateCache.rates;
    }
    throw new Error(`无法获取汇率数据: ${error.message}`);
  }
}

export const convertCurrencyTool: SkillTool = {
  name: "convert",
  description: "Convert amount between currencies",
  parameters: {
    type: "object",
    properties: {
      from: { 
        type: "string", 
        description: "Source currency code (e.g., USD, EUR, CNY)",
        enum: ["USD", "EUR", "CNY", "JPY", "GBP", "AUD", "CAD", "CHF"]
      },
      to: { 
        type: "string", 
        description: "Target currency code",
        enum: ["USD", "EUR", "CNY", "JPY", "GBP", "AUD", "CAD", "CHF"]
      },
      amount: { 
        type: "number", 
        description: "Amount to convert",
        minimum: 0,
        default: 1
      }
    },
    required: ["from", "to"]
  },
  execute: async (args) => {
    const from = args.from as string;
    const to = args.to as string;
    const amount = (args.amount as number) || 1;
    
    // 参数校验
    if (amount <= 0) {
      return {
        success: false,
        error: "金额必须大于0"
      };
    }
    
    if (from === to) {
      return {
        success: true,
        data: {
          original: amount,
          converted: amount,
          rate: 1,
          from,
          to,
          timestamp: new Date().toISOString()
        },
        summary: `${amount} ${from} = ${amount} ${to}`
      };
    }
    
    try {
      const rates = await fetchExchangeRates(from);
      const rate = rates[to];
      
      if (!rate) {
        return {
          success: false,
          error: `不支持的货币类型: ${to}`
        };
      }
      
      const converted = amount * rate;
      
      return {
        success: true,
        data: {
          original: amount,
          converted: parseFloat(converted.toFixed(2)),
          rate: parseFloat(rate.toFixed(4)),
          from,
          to,
          timestamp: new Date().toISOString()
        },
        summary: `${amount} ${from} = ${converted.toFixed(2)} ${to} (汇率: ${rate.toFixed(4)})`
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }
};

第四步:注册工具(编辑src/index.ts

import { SkillDefinition } from '@openclaw/skills-core';
import { convertCurrencyTool } from './tools.js';

const currencyConverterSkill: SkillDefinition = {
  id: "currency-converter",
  name: "货币汇率转换",
  description: "根据实时汇率完成货币金额转换",
  version: "1.0.0",
  
  // 注册此技能提供的工具
  tools: [convertCurrencyTool],
  
  execute: async (args, context) => {
    // 直接透传给工具执行
    return convertCurrencyTool.execute(args);
  }
};

export default currencyConverterSkill;

第五步:编译与测试

# 编译TypeScript
npm run build

# 在OpenClaw命令行中测试
openclaw skills run currency-converter --params '{"from":"USD","to":"CNY","amount":100}'

# 在聊天环境中测试自然语言触发
# "帮我查询100美元等于多少人民币"

测试结果预期

{
  "success": true,
  "data": {
    "original": 100,
    "converted": 720.50,
    "rate": 7.2050,
    "from": "USD",
    "to": "CNY",
    "timestamp": "2026-05-05T12:34:56.789Z"
  },
  "summary": "100 USD = 720.50 CNY (汇率: 7.2050)"
}

16.7 技能开发的常见错误与调试技巧

错误类型速查表

错误现象可能原因解决方案
技能不加载SKILL.md YAML格式错误检查frontmatter分隔符---是否首尾对齐,必需字段namedescription是否存在
openclaw skills list找不到目录未放在正确位置Skill目录必须位于~/.openclaw/workspace/skills/~/.openclaw/skills/
技能无法触发触发描述不匹配用户话术检查SKILL.md的description字段是否足够明确地关联了用户意图
工具调用失败缺少必需的API Key/环境变量检查manifest.json中的required环境变量是否已在环境中配置好,或在技能配置中添加env字段
执行结果不符合预期参数类型不匹配(string vs number)使用ArkType或显式类型转换确保参数类型正确;启用了ArkType的技能,校验失败会清晰报错提示“字段x应为number类型”
权限被拒绝技能未声明所需权限manifest.jsonpermissions数组中添加所需权限(如network:readfs:read
返回数据格式错误返回值不符合规范化结构确保execute返回的是标准的{ success, data, summary }对象结构
编译错误TypeScript版本/配置问题确认tsconfig.json的target设置为ES2022及以上,module为NodeNext
热重载不生效DEV模式未开启启动时添加OPENCLAW_DEV_MODE=true环境变量

调试专用工具(闪电排查)

如果技能无法加载或频繁崩溃,善用这条快速诊断命令链,能在一个会话里扫清90%的配置问题:

openclaw doctor                # 诊断OpenClaw整体健康
openclaw skills check my-skill # 仅校验某个技能的SKILL.md格式
openclaw skills info my-skill  # 显示技能的元数据和触发条件

发布到ClawHub

技能完成开发和测试后,可通过ClawHub分享给社区:

# 登录ClawHub
clawhub login

# 发布技能
clawhub publish

发布前确保:

  • SKILL.md格式正确且通过openclaw validate验证
  • 版本号遵循语义化版本(如1.0.0)
  • metadata.openclaw.security扫描显示为低风险或无恶意标记
  • 已删除任何潜在的硬编码密钥和Token

16.8 本节小结

本节课我们完整走过了自定义Skill的开发全链路,核心知识点可总结为:

  • Skill的本质:不是传统意义的“插件”,而是遵循标准化规范的TypeScript/JavaScript模块——接收内核的标准化指令,执行业务逻辑,返回标准化结果,不参与意图解析,由内核统一进行权限管理。

  • 两种开发模式:声明式Skill(仅需SKILL.md,零代码开发)适合简单的工具组合和提示词封装;原生代码Skill(TypeScript)适合API集成、数据库操作和复杂业务逻辑。

  • 核心接口:SkillDefinition包含id、name、description、parameters(ArkType schema)和execute函数,以及可选的init和cleanup生命周期钩子。

  • 开发脚手架:使用openclaw-skill-boilerplate可30秒内生成完整项目结构,包含SKILL.md、TypeScript严格模式、工具定义模式和ClawHub发布就绪配置。

  • 参数校验:推荐使用ArkType进行schema定义——语法简洁,完全兼容TypeScript的类型推断,比手写JSON Schema更高效。

  • 测试调试:支持热重载DEV模式(OPENCLAW_DEV_MODE=true),修改技能文件后立即生效无需重启Gateway;多级测试工具链从命令行基础调用延伸到Gateway会话模拟。

  • 实战项目:完整实现了货币汇率转换技能——同时包含SKILL.md和TypeScript代码,涵盖API调用、缓存设计、错误处理和标准化的参数验证,展示了生产级技能的核心设计模式。

16.9 课后习题

1. Skill vs Plugin 概念辨析

根据DeepWiki中关于扩展类型的分类:Skills是通过Markdown文件定义Agent能力(位于ClawHub或workspace/skills),Plugins是通过TypeScript模块注册运行时能力(如模型提供商、工具、通道)。请用你自己的话解释Plugin和Skill的核心区别,并分别列举它们各自解决什么类型的问题。

2. 本地环境搭建与首个技能验证

根据16.2节的最小化Skill模板(仅需SKILL.md),在~/.openclaw/workspace/skills/下创建hello-world技能。测试发送“问候一下”或直接通过命令行工具观察系统如何加载该技能。记录openclaw skills list的输出状态。

3. Schema参数扩展

在16.6节的汇率转换技能基础上,扩展参数校验功能:增加date可选字段,允许用户查询历史汇率的某一天数据。若用户不提供date字段则默认获取今天汇率;若提供则从缓存API(如exchangerate.host)获取指定日期的汇率。实现时需处理日期格式校验和数据时效性判断。

4. 自定义日志记录调试

在开发中的技能内使用context.logger记录三阶段信息:技能执行开始(含入参)、汇率的API调用完成(含响应状态和耗时)、技能成功输出。运行时通过openclaw logs --follow | grep "your-skill-id"抓取这些日志,并用grep或awk筛选出执行耗时统计。

5. 异常路径与边界测试

为汇率转换技能设计一套单元测试用例,覆盖以下异常情形:API超时(使用mock延迟响应)、返回非200状态码、返回的JSON中不包含目标货币字段、amount金额为负数或零、用户传入不支持的货币代码。每个异常情形都要有对应的错误返回,且错误信息能清晰指示问题所在。

进阶思考题(选做)

如何将本节开发的汇率转换技能打包并通过ClawHub发布分享?发布前需要补充哪些元数据和安全清单?参考7.2节和7.5节的发布流程,设计一份检查清单,包含:SKILL.md元数据完整性、API Key的外部注入配置(而非硬编码!)、权限声明最小化(是否只需network:read?是否需要fs:write?申请过多则过重)、经过clawhub publish之前务必运行的security scan标记等。清单完成后再规划一个包括上述步骤在内的端到端验证脚本。

🔗《30节课精通 OpenClaw》系列课程导航

去订阅

第一部分(第1-5课) :基础认知与入门部署——解决“这是什么、怎么搭建”的问题;

第二部分(第6-10课):核心原理深度剖析——解决“底层怎么工作”的问题;

第三部分(第11-15课) :应用场景与平台集成——解决“能用来做什么”的问题;

第四部分(第16-21课) :技能开发与定制扩展——解决“如何自己扩能力”的问题;

第五部分(第22-26课):高级特性与性能优化——解决“怎么用得更好”的问题;

第六部分(第27-30课) :安全、运维与生态进阶——解决“如何安全可靠地规模化”的问题;