OpenClaw 插件开发从入门到精通
发布日期: 2026-03-18
分类: 开发进阶
标签: OpenClaw, 插件,开发,扩展,API
为什么需要开发插件?
OpenClaw 内置功能强大,但遇到以下场景时需要自定义插件:
- 集成内部系统 - CRM、ERP、OA 等
- 特殊业务逻辑 - 公司特有的工作流程
- 性能优化 - 缓存、批处理等
- 数据导出 - 定制化报表
- 第三方服务 - 未在官方支持列表中的服务
插件架构
插件类型
| 类型 | 用途 | 示例 |
|---|---|---|
| Tool 插件 | 扩展工具 | 自定义 API 调用 |
| Skill 插件 | 封装工作流 | 自动化任务 |
| Channel 插件 | 消息渠道 | 企业微信、钉钉 |
| Model 插件 | AI 模型 | 本地部署模型 |
插件生命周期
加载 → 初始化 → 执行 → 清理
↓ ↓ ↓ ↓
验证配置 建立连接 处理请求 释放资源开发环境搭建
步骤 1:创建插件目录
bash
mkdir -p ~/.openclaw/plugins/my-custom-plugin
cd ~/.openclaw/plugins/my-custom-plugin
# 初始化项目
npm init -y
npm install openclaw-sdk步骤 2:目录结构
my-custom-plugin/
├── package.json
├── index.js # 插件入口
├── src/
│ ├── tools/ # 工具插件
│ │ └── custom-api.js
│ ├── skills/ # 技能插件
│ │ └── workflow.js
│ └── utils/ # 工具函数
│ └── helpers.js
├── config/
│ └── default.json # 默认配置
└── README.md实战 1:开发 Tool 插件
场景:集成公司内部 API
javascript
// src/tools/custom-api.js
const axios = require('axios');
class CustomAPITool {
constructor(config) {
this.baseUrl = config.baseUrl;
this.apiKey = config.apiKey;
this.timeout = config.timeout || 5000;
}
// 工具定义
static get definition() {
return {
name: 'custom_api',
description: '调用公司内部 API 系统',
parameters: {
type: 'object',
properties: {
endpoint: {
type: 'string',
description: 'API 端点路径'
},
method: {
type: 'string',
enum: ['GET', 'POST', 'PUT', 'DELETE'],
description: 'HTTP 方法'
},
data: {
type: 'object',
description: '请求数据'
}
},
required: ['endpoint', 'method']
}
};
}
// 执行方法
async execute(params) {
const { endpoint, method, data } = params;
try {
const response = await axios({
method,
url: `${this.baseUrl}${endpoint}`,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
data,
timeout: this.timeout
});
return {
success: true,
data: response.data,
status: response.status
};
} catch (error) {
return {
success: false,
error: error.message,
status: error.response?.status
};
}
}
}
module.exports = CustomAPITool;注册插件
javascript
// index.js
const CustomAPITool = require('./src/tools/custom-api');
module.exports = {
name: 'my-custom-plugin',
version: '1.0.0',
description: '公司内部 API 集成插件',
// 初始化
async init(config) {
console.log('🔌 初始化自定义插件...');
// 注册工具
this.registerTool('custom_api', new CustomAPITool(config));
console.log('✅ 插件初始化完成');
},
// 清理
async cleanup() {
console.log('🧹 清理插件资源...');
}
};配置插件
json
{
"plugins": {
"entries": {
"my-custom-plugin": {
"path": "~/.openclaw/plugins/my-custom-plugin",
"config": {
"baseUrl": "https://api.company.com",
"apiKey": "your-api-key",
"timeout": 5000
}
}
}
}
}使用插件
javascript
// 在技能中调用
{
"name": "query-user-info",
"actions": [
{
"tool": "custom_api",
"params": {
"endpoint": "/users/123",
"method": "GET"
}
}
]
}实战 2:开发 Skill 插件
场景:自动化审批流程
javascript
// src/skills/approval-workflow.js
class ApprovalWorkflow {
constructor(config) {
this.db = config.database;
this.notifyChannel = config.notifyChannel;
}
static get definition() {
return {
name: 'approval_workflow',
description: '自动化审批工作流',
triggers: ['审批', 'approve', '申请'],
parameters: {
type: 'object',
properties: {
type: {
type: 'string',
enum: ['leave', 'expense', 'purchase'],
description: '审批类型'
},
amount: {
type: 'number',
description: '金额(如适用)'
},
reason: {
type: 'string',
description: '申请原因'
}
},
required: ['type', 'reason']
}
};
}
async execute(params) {
const { type, amount, reason } = params;
// 1. 创建审批单
const approvalId = await this.createApproval({
type,
amount,
reason,
status: 'pending',
createdAt: new Date()
});
// 2. 确定审批人
const approvers = await this.determineApprovers(type, amount);
// 3. 发送审批通知
await this.sendNotification(approvers, {
approvalId,
type,
amount,
reason
});
// 4. 等待审批结果
const result = await this.waitForApproval(approvalId);
// 5. 执行后续操作
if (result.approved) {
await this.executeApproval(approvalId);
return {
status: 'approved',
message: '✅ 审批通过'
};
} else {
return {
status: 'rejected',
message: '❌ 审批拒绝:' + result.reason
};
}
}
async createApproval(data) {
// 实现创建逻辑
return 'APR-' + Date.now();
}
async determineApprovers(type, amount) {
// 根据类型和金额确定审批人
if (amount > 10000) {
return ['manager', 'director', 'cfo'];
} else if (amount > 5000) {
return ['manager', 'director'];
} else {
return ['manager'];
}
}
async sendNotification(approvers, data) {
// 发送飞书/企业微信通知
const message = `
📋 新的审批申请
类型:${data.type}
金额:${data.amount}元
原因:${data.reason}
请点击链接审批:
http://oa.company.com/approval/${data.approvalId}
`;
for (const approver of approvers) {
await this.sendMessage(approver, message);
}
}
async waitForApproval(approvalId) {
// 轮询审批结果
return new Promise((resolve) => {
const checkInterval = setInterval(async () => {
const status = await this.getApprovalStatus(approvalId);
if (status !== 'pending') {
clearInterval(checkInterval);
resolve(status);
}
}, 5000);
// 超时处理
setTimeout(() => {
clearInterval(checkInterval);
resolve({ approved: false, reason: '超时未审批' });
}, 86400000); // 24 小时超时
});
}
}
module.exports = ApprovalWorkflow;实战 3:开发 Channel 插件
场景:集成企业微信
javascript
// src/channels/wechat-work.js
const axios = require('axios');
class WeChatWorkChannel {
constructor(config) {
this.corpId = config.corpId;
this.agentId = config.agentId;
this.secret = config.secret;
this.accessToken = null;
this.tokenExpiry = null;
}
static get definition() {
return {
name: 'wechat_work',
description: '企业微信消息渠道'
};
}
async init() {
await this.refreshToken();
// 定期刷新 Token
setInterval(() => this.refreshToken(), 7200000); // 2 小时
}
async refreshToken() {
const response = await axios.get(
`https://qyapi.weixin.qq.com/cgi-bin/gettoken`,
{
params: {
corpid: this.corpId,
corpsecret: this.secret
}
}
);
this.accessToken = response.data.access_token;
this.tokenExpiry = Date.now() + 7200000;
}
async sendMessage(userId, message) {
await axios.post(
`https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${this.accessToken}`,
{
touser: userId,
msgtype: 'text',
agentid: this.agentId,
text: {
content: message
}
}
);
}
async sendMarkdownMessage(userId, content) {
await axios.post(
`https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${this.accessToken}`,
{
touser: userId,
msgtype: 'markdown',
agentid: this.agentId,
markdown: {
content
}
}
);
}
async sendCardMessage(userId, card) {
await axios.post(
`https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${this.accessToken}`,
{
touser: userId,
msgtype: 'template_card',
agentid: this.agentId,
template_card: card
}
);
}
}
module.exports = WeChatWorkChannel;调试技巧
1. 本地测试
javascript
// test.js
const CustomAPITool = require('./src/tools/custom-api');
async function test() {
const tool = new CustomAPITool({
baseUrl: 'https://api.test.company.com',
apiKey: 'test-key'
});
const result = await tool.execute({
endpoint: '/users/123',
method: 'GET'
});
console.log('测试结果:', result);
}
test();2. 日志记录
javascript
const debug = require('debug')('my-custom-plugin');
class MyTool {
async execute(params) {
debug('执行参数:', params);
try {
const result = await this.doSomething(params);
debug('执行结果:', result);
return result;
} catch (error) {
debug('执行错误:', error);
throw error;
}
}
}3. 热重载
javascript
// 开发模式下支持热重载
if (process.env.NODE_ENV === 'development') {
require('fs').watch('./src', (eventType, filename) => {
console.log(`🔄 检测到文件变化:${filename}`);
// 重新加载插件
this.reload();
});
}发布插件
1. 打包
bash
npm pack
# 生成 my-custom-plugin-1.0.0.tgz2. 发布到 npm
bash
npm publish3. 安装插件
bash
# 从 npm 安装
npm install -g @your-org/openclaw-custom-plugin
# 从本地安装
npm install -g ./my-custom-plugin-1.0.0.tgz最佳实践
✅ 应该做的
- 错误处理 - 捕获异常,返回友好错误信息
- 日志记录 - 记录关键操作,便于排查问题
- 配置验证 - 启动时验证配置完整性
- 资源清理 - 释放连接、定时器等资源
- 文档完善 - README、API 文档、示例代码
❌ 不应该做的
- 硬编码凭证 - 使用环境变量或配置文件
- 阻塞操作 - 使用异步,避免阻塞主线程
- 无限重试 - 设置重试次数上限
- 忽略超时 - 所有网络请求设置超时
- 泄露敏感信息 - 日志中脱敏
插件示例库
官方插件
@openclaw/feishu- 飞书集成@openclaw/wechat- 微信集成@openclaw/email- 邮件服务@openclaw/database- 数据库连接
社区插件
@community/jira- Jira 集成@community/github- GitHub 集成@community/slack- Slack 集成@community/notion- Notion 集成
总结
插件开发核心要点:
- ✅ 理解架构 - Tool、Skill、Channel 三种类型
- ✅ 规范开发 - 遵循接口定义和生命周期
- ✅ 完善测试 - 单元测试 + 集成测试
- ✅ 文档齐全 - README、示例、API 文档
- ✅ 持续维护 - 修复 bug、添加功能
下一步:
- 查看官方 SDK 文档
- 参考示例插件代码
- 加入开发者社区
相关文档: