OpenClaw 插件开发:创建自定义插件

OpenClaw 的插件系统让你可以扩展其功能,添加自定义工具、命令和渠道。本文将详细介绍如何开发 OpenClaw 插件。

插件概述

OpenClaw 插件是独立的 npm 包,可以:

  • 注册工具:添加 AI Agent 可以调用的新工具
  • 注册命令:添加自定义 CLI 命令
  • 注册渠道:添加新的聊天平台支持
  • 注册技能:添加自定义技能和提示词

插件开发环境

前置要求

  • Node.js:22+
  • TypeScript:推荐使用 TypeScript
  • pnpm:推荐的包管理器

安装开发工具

# 安装 OpenClaw SDK
$ npm install @openclaw/sdk

# 或使用 pnpm
$ pnpm add @openclaw/sdk

创建插件项目

初始化项目

# 创建项目目录
$ mkdir my-openclaw-plugin
$ cd my-openclaw-plugin

# 初始化项目
$ pnpm init

# 安装依赖
$ pnpm add @openclaw/sdk

配置 TypeScript

创建 tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "node",
    "declaration": true,
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

插件基础结构

创建 src/index.ts

import { Plugin, PluginAPI } from '@openclaw/sdk';

export class MyPlugin implements Plugin {
  name = 'my-plugin';
  version = '1.0.0';
  description = 'My custom OpenClaw plugin';

  async onLoad(api: PluginAPI): Promise {
    // 插件加载时执行
    console.log(`${this.name} v${this.version} loaded`);

    // 注册工具
    api.registerTool({
      name: 'my-tool',
      description: 'My custom tool',
      execute: async (params: any) => {
        return { success: true, result: 'Hello from my tool!' };
      }
    });

    // 注册命令
    api.registerCommand({
      name: 'hello',
      description: 'Say hello',
      handler: async () => {
        console.log('Hello from my plugin!');
      }
    });
  }

  async onUnload(): Promise {
    // 插件卸载时执行
    console.log(`${this.name} unloaded`);
  }
}

// 导出插件
export default new MyPlugin();

开发工具

注册工具

工具是 AI Agent 可以调用的函数:

api.registerTool({
  name: 'calculator',
  description: 'Perform mathematical calculations',
  parameters: {
    type: 'object',
    properties: {
      expression: {
        type: 'string',
        description: 'Mathematical expression to evaluate'
      }
    },
    required: ['expression']
  },
  execute: async (params: { expression: string }) => {
    try {
      // 简单示例:使用 eval (生产环境应使用更安全的解析器)
      const result = eval(params.expression);
      return { success: true, result: String(result) };
    } catch (error) {
      return { success: false, error: (error as Error).message };
    }
  }
});

注册命令

命令是可以通过 CLI 执行的命令:

api.registerCommand({
  name: 'status',
  description: 'Show plugin status',
  handler: async (args: string[]) => {
    console.log('Plugin Status: Running');
    console.log(`Version: ${this.version}`);
  }
});

注册渠道

添加新的聊天平台支持:

api.registerChannel({
  name: 'custom-platform',
  description: 'Custom chat platform',
  connect: async (config: any) => {
    // 连接到平台
    console.log('Connected to custom platform');
  },
  disconnect: async () => {
    // 断开连接
    console.log('Disconnected from custom platform');
  },
  sendMessage: async (channelId: string, message: string) => {
    // 发送消息
    console.log(`Sending to ${channelId}: ${message}`);
  }
});

示例插件

天气插件

创建一个获取天气信息的插件:

import { Plugin, PluginAPI } from '@openclaw/sdk';

export class WeatherPlugin implements Plugin {
  name = 'weather-plugin';
  version = '1.0.0';

  async onLoad(api: PluginAPI): Promise {
    api.registerTool({
      name: 'get_weather',
      description: 'Get weather information for a city',
      parameters: {
        type: 'object',
        properties: {
          city: {
            type: 'string',
            description: 'City name'
          }
        },
        required: ['city']
      },
      execute: async (params: { city: string }) => {
        // 调用天气 API(示例)
        const weather = await this.fetchWeather(params.city);
        return { success: true, result: weather };
      }
    });
  }

  async onUnload(): Promise {
    console.log('Weather plugin unloaded');
  }

  private async fetchWeather(city: string): Promise {
    // 这里应该调用真实的天气 API
    // 示例返回
    return `Weather in ${city}: 25°C, Sunny`;
  }
}

export default new WeatherPlugin();

时间插件

创建一个获取时间的插件:

import { Plugin, PluginAPI } from '@openclaw/sdk';

export class TimePlugin implements Plugin {
  name = 'time-plugin';
  version = '1.0.0';

  async onLoad(api: PluginAPI): Promise {
    api.registerTool({
      name: 'get_time',
      description: 'Get current time in a timezone',
      parameters: {
        type: 'object',
        properties: {
          timezone: {
            type: 'string',
            description: 'Timezone (e.g., Asia/Shanghai)',
            default: 'UTC'
          }
        }
      },
      execute: async (params: { timezone: string }) => {
        const time = new Date().toLocaleString('zh-CN', {
          timeZone: params.timezone
        });
        return { success: true, result: time };
      }
    });
  }

  async onUnload(): Promise {
    console.log('Time plugin unloaded');
  }
}

export default new TimePlugin();

构建和发布

构建插件

# 编译 TypeScript
$ pnpm build

更新 package.json

{
  "name": "my-openclaw-plugin",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch"
  }
}

本地安装

# 在插件目录安装
$ pnpm link

# 在 OpenClaw 目录链接
$ pnpm link my-openclaw-plugin

发布到 npm

# 发布
$ pnpm publish

安装和使用插件

从 npm 安装

$ pnpm add -g my-openclaw-plugin

配置插件

openclaw.json 中配置:

{
  "plugins": {
    "my-plugin": {
      "enabled": true,
      "config": {
        "apiKey": "your-api-key"
      }
    }
  }
}

测试插件

单元测试

使用 Jest 进行测试:

$ pnpm add -D jest @types/jest

创建测试文件 src/__tests__/index.test.ts

import { PluginAPI } from '@openclaw/sdk';
import { MyPlugin } from '../index';

describe('MyPlugin', () => {
  let plugin: MyPlugin;
  let mockApi: jest.Mocked;

  beforeEach(() => {
    plugin = new MyPlugin();
    mockApi = {
      registerTool: jest.fn(),
      registerCommand: jest.fn(),
      registerChannel: jest.fn()
    } as any;
  });

  it('should load successfully', async () => {
    await plugin.onLoad(mockApi);
    expect(mockApi.registerTool).toHaveBeenCalled();
  });

  it('should register tool', async () => {
    await plugin.onLoad(mockApi);
    expect(mockApi.registerTool).toHaveBeenCalledWith(
      expect.objectContaining({
        name: 'my-tool'
      })
    );
  });
});

最佳实践

错误处理

execute: async (params) => {
  try {
    // 工具逻辑
    return { success: true, result: data };
  } catch (error) {
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error'
    };
  }
}

类型安全

interface ToolParams {
  city: string;
  units?: 'metric' | 'imperial';
}

execute: async (params: ToolParams) => {
  const city = params.city;
  const units = params.units || 'metric';
  // ...
}

日志记录

api.logger.info('Tool executed', { tool: 'my-tool' });
api.logger.error('Tool failed', { error: error.message });

总结

OpenClaw 插件系统提供了强大的扩展能力。通过创建自定义插件,你可以添加任何你需要的功能,让 AI 助手更符合你的需求。

相关资源

发表回复

后才能评论