第21课:OpenClaw|Plugin插件系统开发
本文系统阐述了OpenClaw平台的扩展架构体系,重点解析了Skill、Hook与Plugin三者的本质区别与适用场景。文章通过分层架构图展示了策略层、拦截层与系统层的功能边界,提供了开发决策速查表帮助开发者正确选择扩展方式。深入讲解了Gateway插件的发现加载机制、能力注册模型和钩子体系,并演示了认证插件的开发流程。全文采用技术原理与实践指导相结合的方式,帮助开发者掌握从业务逻辑封装到系统级扩

文章目录
前20节课,你已经掌握了OpenClaw从零部署到模型定制的完整能力链。自定义Skills写了,MCP Server接了,Provider配了——但还有一个更高阶的领域等待你去开拓:直接扩展Gateway的核心能力。
你可能遇到过这样的需求:想给所有飞书消息加上专属路由规则、想在调用前做统一的Token限流审计、想批量拦截越权的敏感工具调用——这些需求靠Skill和MCP根本无解,因为它们运行在Agent层,而以上需求发生在消息进入Gateway管道的一刹那。
这正是Plugin的价值所在。Plugin是真正的“系统级手术刀”——Skill只教AI“怎么做”,而Plugin能修改“系统的运行本身”;Hook在生命周期缝隙中插入逻辑;Plugin则是把这些能力打包成完整的扩展容器,可以注册HTTP路由、修改认证策略、添加全局过滤器,甚至彻底重写消息流转的逻辑。
本节课将彻底厘清Skill、Hook、Plugin三者的边界与分工,然后深入Gateway插件架构的注册与发现机制、开发周期与钩子体系。我们将从零上手一个通用认证插件、一个消息预处理插件和一个日志监控插件,最后以一个生产级的请求限流插件贯穿所有知识——你可以将这个插件直接部署到企业环境,为你的多用户Agent保驾护航。
21.1 Plugin与Skill的本质区别
一句话概括:OpenClaw的扩展体系采用三层分层架构——Skill(策略层)负责定义“AI如何用能力”,Hook(拦截层)负责在运行缝隙中插入逻辑,Plugin(系统层)负责将一切能力注册到内核——三者在开发选择上遵循“只教AI做事用Skill、只改运行时流程用Hook、要加新功能/新服务用Plugin”的刚性原则。
在开始开发之前,先彻底理清一个在社区里争论最多、也让最多开发者困惑的概念:Skill、Hook和Plugin到底是什么关系?
三层架构的清晰划分
OpenClaw的扩展体系从上层到下层依次为:Skill(策略层)→ Hook(拦截层)→ Plugin(系统层),三者各司其职、互不越界,共同构成完整的扩展生态。
| 层次 | 本质 | 核心动作 | 适用场景 |
|---|---|---|---|
| Skill(策略层) | 说明书,不是代码 | 教AI如何组合使用Tool | 组织现有工具、设定任务执行逻辑 |
| Hook(拦截层) | 生命周期监听点 | 在特定时机插入代码 | 拦截/修改prompt、补充上下文、执行前后校验 |
| Plugin(系统层) | 扩展容器 | 向内核注册新能力 | 新增渠道、模型提供商、HTTP路由、全局服务 |
Skill不是代码实现,而是告诉AI如何使用能力的规则集合,相当于AI的操作手册与工作流定义。Skill的核心作用是定义能力边界、使用条件、调用步骤和优先级规则。它不修改运行流程,不注册到系统内核,只是Agent在对话中按需读取的说明书。
Hook是嵌入Agent和Gateway生命周期的逻辑切点,在特定时机插入自定义代码,只改流程不增能力。Hook的核心作用是拦截/修改prompt、监听系统事件、调整运行参数。它本质是流程控制层,基于事件触发。
Plugin是真正把能力注入OpenClaw内核的系统模块,负责注册新功能、服务、接口,是所有扩展的载体。Plugin是唯一能真正扩展系统表面的模块,可以承载Skill、Hook、Tool、Service等所有扩展类型,注册tool、HTTP路由、CLI命令、后台服务等。
Skill vs Plugin:一个几乎所有人都会弄混的核心区别
社区最常被问到的问题:“Skill和Plugin有什么区别?”
Skills are capabilities the AI agent can invoke during a conversation. The LLM decides when to call a skill based on the user’s intent.Plugins are lifecycle hooks that run automatically at defined points in the message processing pipeline——they intercept, modify, or augment messages and responses without the LLM explicitly choosing to invoke them.
一个最简洁的记忆口诀:Skill = AI主动调用的工具;Plugin = 系统自动执行的钩子。Skills触发由LLM意图决定,Plugins触发由生命周期事件决定。Skills面向功能复用,Plugins面向系统集成。
openclaw skills list列出的是AI可以调用的“工具”,openclaw plugins list列出的是系统层面的“服务”。前者侧重可复用、面向任务的逻辑封装;后者更底层,支持自定义API集成、外部服务调用及运行时行为扩展,赋予开发者对执行流程的精细控制。
开发决策速查表
| 你的需求 | 应该用 | 理由 |
|---|---|---|
| 让AI学会一个新业务流程 | Skill | AI主动调用,遵循说明书 |
| 在模型调用前注入额外提示词 | Hook | 流程拦截点 |
| 给OpenClaw加一个全新渠道(如飞书插件) | Plugin | 系统级扩展 |
| 注册一个Agent可调用的新Tool | Plugin内注册Tool | 能力注入内核 |
| 监听用户输入做敏感词过滤 | Hook(message:received) | 消息生命周期拦截 |
| 为所有渠道统一记录调用日志 | Plugin注册全局hook | 持久化+日志服务 |
21.2 Gateway插件的架构与钩子机制
一句话概括:Gateway作为OpenClaw的中央控制平面,采用“声明式发现 + 运行时注册”的两阶段插件模型——PluginRegistry负责管理插件生命周期,api.registerXxx方法将能力注入内核,多种钩子在不同阶段介入处理流程。
在开始第一个插件开发之前,先站在架构层面理解Gateway与插件系统是如何协同的。
Gateway插件的发现与加载
OpenClaw按以下优先级顺序扫描插件,高优先级覆盖低优先级:
- 配置路径:
plugins.load.paths显式指定的文件或目录 - 工作区扩展:
<workspace>/.openclaw/extensions/ - 全局扩展:
~/.openclaw/extensions/ - 内置插件:随OpenClaw发布的
extensions/目录
同ID插件,高优先级路径覆盖低优先级路径。例如工作区中的voice-call会覆盖内置版本,便于本地开发调试。
安全检查方面,非内置插件必须通过以下审查,否则被拒绝加载:插件入口不得通过符号链接逃逸出插件根目录、插件目录不得为全局可写、POSIX系统下目录所有者必须是当前用户或root。
能力注册(Capability Registration)
Plugin通过register(api)函数向核心注册能力。OpenClaw的能力模型是原生插件模型,每个原生插件都会针对一种或多种能力类型进行注册。
核心能力类型及注册方法主要包括:
| 能力类型 | 注册方法 |
|---|---|
| 文本推理/LLM | api.registerProvider(...) |
| 渠道/消息通道 | api.registerChannel(...) |
| Agent工具 | api.registerTool(...) |
| HTTP路由 | api.registerHttpRoute(...) |
| CLI命令 | api.registerCli(...) |
| 后台服务 | api.registerService(...) |
| 语音/实时转录 | api.registerSpeechProvider(...) |
| 图像/视频生成 | api.registerImageGenerationProvider(...) |
插件钩子系统
OpenClaw的钩子系统清晰分为两大类——内部钩子和插件钩子。
内部钩子(Internal Hooks) 运行在Gateway内部的事件驱动脚本,响应各类命令执行和系统生命周期事件。核心事件包括command:new、command:reset、command:stop、session:compact:before/after、message:received、gateway:startup/shutdown等。
插件钩子(Plugin Hooks) 在Agent运行的关键阶段介入处理。插件钩子的关键生命周期包括:
| 钩子 | 触发时机 |
|---|---|
bootstrap |
注入工作区引导文件之前 |
ingest |
上下文构建初期 |
assemble |
上下文组装阶段 |
compact |
上下文压缩时 |
afterTurn |
Agent回合结束后 |
prepareSubagentSpawn |
子Agent创建前 |
onSubagentEnded |
子Agent结束后 |
OpenClaw调用钩子遵循固定的执行顺序,如需在Agent启动前覆盖安全策略,使用before_agent_start钩子;如需修改模型解析前的请求,使用before_model_resolve;如需在提示词生成前修改内容,使用before_prompt_build。
上下文引擎插件化
2026年3月,OpenClaw新增了ContextEngine插件插槽,提供了完整的生命周期钩子,插件可以在上下文生成、压缩、拼接以及子Agent生命周期管理的各个阶段介入,实现完全不同的上下文策略。社区代表性插件lossless-claw在OOLONG benchmark测试中得分74.8,击败了Claude Code的70.3分。
21.3 开发自定义认证插件
一句话概括:认证插件通过注册自定义Provider认证流程,让OpenClaw支持OAuth 2.0、Device授权、API Key校验等多种策略——可接入企业内部SSO系统,也可为渠道层添加访问白名单。
认证插件最常见的使用场景是:为企业内部部署的OpenClaw添加统一的SSO认证、为已存在的渠道(如飞书、钉钉)增加额外的API Key校验层、或集成OAuth 2.0全流程。
最小认证插件骨架
创建一个认证插件,需要包含manifest、入口文件和认证逻辑。在插件目录创建package.json:
{
"name": "@myorg/openclaw-auth-guard",
"version": "1.0.0",
"type": "module",
"main": "dist/index.js",
"openclaw": {
"extensions": ["./index.ts"],
"compat": {
"pluginApi": ">=2026.3.24-beta.2"
}
}
}
入口文件index.ts:
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { Type } from "@sinclair/typebox";
interface AuthContext {
userId: string;
channel: string;
sessionKey: string;
}
export default definePluginEntry({
id: "auth-guard",
name: "Auth Guard Plugin",
description: "Adds API key validation for inbound messages",
register(api) {
// 注册认证钩子:在消息流入agent之前拦截
api.registerHook("message:received", async (event) => {
const { from, channelId } = event.context;
const apiKey = event.context.metadata?.apiKey;
// 校验API Key
if (!apiKey || !isValidApiKey(apiKey, from)) {
event.messages.push({
type: "error",
content: "Invalid or missing API key. Please provide a valid API key."
});
return; // 阻止消息继续流入agent
}
// 注入认证通过的上下文
event.context.auth = { userId: extractUserId(apiKey), channelId };
});
// 注册CLI命令:管理API Key
api.registerCli({
name: "apikey",
description: "Manage API keys",
async execute(args, context) {
if (args[0] === "generate") {
const key = generateApiKey();
await saveApiKey(key);
return `Generated new API key: ${key}`;
}
if (args[0] === "revoke") {
await revokeApiKey(args[1]);
return `Revoked API key: ${args[1]}`;
}
return "Usage: apikey generate|revoke <key>";
}
});
}
});
// 示例:简单内存存储(生产环境应使用数据库)
const apiKeyStore = new Map<string, string>();
function isValidApiKey(key: string, userId?: string): boolean {
const storedUserId = apiKeyStore.get(key);
return !!storedUserId && (userId ? storedUserId === userId : true);
}
部署与验证
# 方式一:本地开发链接(推荐)
ln -s $(pwd) ~/.openclaw/extensions/auth-guard
# 方式二:全局安装npm包(生产)
npm pack
openclaw plugins install $(pwd)/openclaw-auth-guard-1.0.0.tgz
# 启用插件
openclaw plugins enable auth-guard
# 验证加载状态
openclaw plugins list
# 重启Gateway
openclaw gateway restart
进阶场景:集成OAuth 2.0
对于需要对接企业内部SSO的场景,OpenClaw支持Device授权码流程。registerProvider注册时可指定auth数组,包含createProviderOAuthAuthMethod或createProviderApiKeyAuthMethod等认证方法。Device授权码跨平台,适合命令行环境的多账号登录。创建认证方法后,用户可通过/login命令触发浏览器授权,实现标准的OAuth 2.0 PKCE流程。
21.4 开发自定义消息预处理/后处理插件
一句话概括:消息预处理插件在Message Pipeline中抢占先机——在消息进入会话队列之前添加黑/白名单过滤、关键词脱敏或格式标准化,将Agent从不必要的噪音中解放出来。
消息预处理/后处理插件拦截入站或出站消息,实现统一过滤、格式化、敏感词替换等全局处理。
预处理插件:拦截敏感词
// index.ts - message-filter插件
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
export default definePluginEntry({
id: "message-filter",
name: "Message Filter Plugin",
description: "Filters inbound messages in real time",
register(api) {
// 入站消息拦截(预处理)
api.registerHook("message:received", (event) => {
const content = event.context.content || "";
const fromUser = event.context.from;
const bannedPatterns = [
{ pattern: /rm -rf \/\s*$/i, severity: "block", reason: "Dangerous command" },
{ pattern: /sudo\s+shutdown/i, severity: "block", reason: "System command" },
{ pattern: /password=.*/i, severity: "redact", reason: "Credential leak" }
];
for (const bp of bannedPatterns) {
if (bp.pattern.test(content)) {
if (bp.severity === "block") {
event.messages.push({
type: "warning",
content: `Message blocked: ${bp.reason}`
});
event.context.handled = true; // 静默丢弃,不进入Agent
break;
} else if (bp.severity === "redact") {
event.context.content = content.replace(bp.pattern, "[REDACTED]");
}
}
}
// 记录预处理审计日志
api.logger.info("Message preprocessed", {
from: fromUser,
originalLength: content.length,
filtered: event.context.handled
});
});
// 出站消息后处理(在channel发出前)
api.registerHook("message:sent", (event) => {
const response = event.context.content;
// 在响应中追加统一签名
if (response && !response.includes("[Powered by OpenClaw]")) {
event.context.content = response + "\n\n> *[Powered by OpenClaw]*";
}
});
}
});
插件配置(openclaw.json)
{
"plugins": {
"entries": {
"message-filter": {
"enabled": true,
"config": {
"blockLevel": "strict",
"bannedList": ["rm -rf", "sudoshutdown", "drop database"],
"logToFile": "/var/log/message-filter.log"
}
}
}
}
}
配置中的blockLevel可作为参数传递给插件的register函数,实现运行时可配置的策略。
21.5 开发日志与监控插件
一句话概括:日志监控插件在Gateway的出口处为所有操作建立审计档案——自动捕获工具调用、记录模型Token消耗、汇集多用户的API请求日志,为企业级部署提供可追溯的运行证据。
企业级部署的刚需是“发生了什么、谁调用了什么、花了多少Token”——日志与监控插件全面覆盖。
监控插件核心实现
// index.ts - audit插件
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
export default definePluginEntry({
id: "audit-monitor",
name: "Audit and Monitor Plugin",
description: "Logs all agent interactions for audit compliance",
register(api) {
// 1. 记录Agent调用开始(before_agent_start钩子)
api.registerHook("before_agent_start", async (event) => {
const { sessionKey, messages, modelId, userId } = event.context;
const startTime = Date.now();
const auditRecord = {
sessionKey,
userId,
modelId,
messageCount: messages?.length || 0,
timestamp: new Date().toISOString(),
status: "started"
};
// 写入审计存储(数据库/日志文件)
await persistAuditLog(auditRecord);
// 在上下文中附加auditId,供后续钩子关联
event.context.auditId = auditRecord.id;
});
// 2. 记录工具调用(hook工具执行前/后)
api.registerHook("before_tool_call", (event) => {
const { tool, params, auditId } = event.context;
api.logger.info("Tool execution started", {
auditId,
tool: tool.name,
params: sanitizeSensitiveParams(params)
});
});
// 3. 记录Agent调用完成
api.registerHook("after_agent_turn", (event) => {
const { auditId, usage, duration, status } = event.context;
api.logger.info("Agent turn completed", {
auditId,
tokens: usage?.totalTokens,
duration,
status,
timestamp: new Date().toISOString()
});
});
// 4. 注册HTTP接口:暴露监控指标
api.registerHttpRoute({
method: "GET",
path: "/api/audit/recent",
async handler(req, res) {
const limit = parseInt(req.query.limit as string) || 100;
const logs = await getRecentAuditLogs(limit);
res.json({ success: true, data: logs });
}
});
}
});
function sanitizeSensitiveParams(params: any): any {
const sensitiveKeys = ["password", "apiKey", "token", "secret"];
if (!params) return params;
const sanitized = { ...params };
for (const key of sensitiveKeys) {
if (sanitized[key]) sanitized[key] = "***REDACTED***";
}
return sanitized;
}
存量遥测与合规审计
监控可以输出到~/.openclaw/logs/audit.ndjson,然后转发到ELK做集中分析。openclaw security audit命令扫描安全基线时可以集成插件暴露的审计数据。对于企业合规需求,配置日志轮转、加密存储和留存S3冷存等策略是生产级的必需模块。
21.6 插件的注册、加载与生命周期管理
一句话概括:插件采用“声明式发现 + 运行时注册”的两阶段模型——从清单文件预扫描元数据到实际加载插件代码、注册能力、调用生命周期钩子,形成一条完整严明的四步启动闭环。
两阶段模型
声明式发现(Declarative Discovery)+运行时注册(Runtime Registration)的两阶段模型确保了配置错误可在启动前被检测,控制台可安全展示插件元数据,插件行为与核心解耦便于维护。
完整加载流程:四步闭环
-
清单预扫描(Manifest Scan) :Gateway扫描配置路径中的
package.json,检查openclaw.extensions字段指向插件入口。验证版本兼容性:compat.pluginApi和compat.minGatewayVersion确保插件与核心API匹配。 -
加载模块(Load Module) :Gateway以动态导入方式加载插件入口文件,执行
definePluginEntry导出的默认函数。所有注册逻辑必须在register(api)回调内完成。 -
能力注册(Capability Registration) :插件调用
api.registerXxx方法,将渠道、Provider、Tool、Hook、HTTP路由、CLI命令、后台服务等能力注入内核的对应Registry中。OpenClaw根据注册行为将插件分类为:plain-capability(恰好一种能力类型)、hybrid-capability(多种能力类型)、hook-only(仅注册钩子)、non-capability(仅注册工具/命令/服务)。 -
生命周期钩子调用:插件注册的钩子根据触发条件被调用,可在Gateway启动、消息收发、Agent运行等阶段介入。
插件启用与配置
{
"plugins": {
"enabled": true,
"allow": ["auth-guard", "audit-monitor"],
"deny": ["malicious-plugin"],
"load": { "paths": ["~/Projects/my-plugin"] },
"slots": { "memory": "memory-lancedb", "contextEngine": "lossless-claw" },
"entries": {
"auth-guard": { "enabled": true },
"audit-monitor": { "enabled": true, "config": { "logLevel": "info" } }
}
}
}
allow和deny:显式控制允许加载的插件ID白名单与黑名单slots:指定使用哪个插件填补系统能力插槽(如memory和contextEngine)entries.<pluginId>:插件专属配置,options以插件定义的配置Schema传递
修改插件配置后必须重启Gateway才能生效。可使用openclaw plugins inspect <id>查看插件形态和拆分情况。
21.7 实战:开发一个请求限流插件
前面的课程让你掌握了Plugin从架构到开发的完整知识框架。现在,我们用请求限流插件这个企业级实战案例,展示一个完整Plugin从创建到部署的全链路开发过程。
Step 1:创建项目与依赖管理
mkdir openclaw-ratelimit && cd openclaw-ratelimit
npm init -y
npm install @sinclair/typebox
package.json:
{
"name": "@myorg/openclaw-ratelimit",
"version": "1.0.0",
"type": "module",
"main": "dist/index.js",
"openclaw": {
"extensions": ["./index.ts"],
"compat": { "pluginApi": ">=2026.3.24-beta.2" },
"build": { "openclawVersion": "2026.3.24-beta.2", "pluginSdkVersion": "2026.3.24-beta.2" }
}
}
Step 2:实现限流逻辑
index.ts:
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { Type } from "@sinclair/typebox";
// 简化的令牌桶限流器
class RateLimiter {
private tokens: number;
private lastRefill: number;
private readonly capacity: number;
private readonly refillRate: number;
constructor(capacity: number, refillRate: number) {
this.capacity = capacity;
this.refillRate = refillRate;
this.tokens = capacity;
this.lastRefill = Date.now();
}
private refill() {
const now = Date.now();
const delta = (now - this.lastRefill) / 1000;
this.tokens = Math.min(this.capacity, this.tokens + delta * this.refillRate);
this.lastRefill = now;
}
allow(): boolean {
this.refill();
if (this.tokens >= 1) {
this.tokens -= 1;
return true;
}
return false;
}
}
type RateLimitConfig = {
limits: {
perUser?: { capacity: number; refillRate: number };
perChannel?: { capacity: number; refillRate: number };
global?: { capacity: number; refillRate: number };
};
};
export default definePluginEntry({
id: "rate-limit",
name: "Rate Limit Plugin",
description: "Prevents API abuse by limiting request rates per user/channel/global",
register(api) {
const config = api.getPluginConfig<RateLimitConfig>() || {
limits: {
perUser: { capacity: 10, refillRate: 1 },
perChannel: { capacity: 20, refillRate: 2 },
global: { capacity: 50, refillRate: 5 }
}
};
const userLimiters = new Map<string, RateLimiter>();
const channelLimiters = new Map<string, RateLimiter>();
let globalLimiter: RateLimiter | null = null;
if (config.limits.global) {
globalLimiter = new RateLimiter(
config.limits.global.capacity,
config.limits.global.refillRate
);
}
// 注册message:received钩子进行限流
api.registerHook("message:received", (event) => {
const { from, channelId } = event.context;
// 全局限流检查
if (globalLimiter && !globalLimiter.allow()) {
event.messages.push({
type: "error",
content: "Service is under high load. Please try again later."
});
event.context.handled = true;
return;
}
// 渠道级别限流检查
if (channelId && config.limits.perChannel) {
let channelLimiter = channelLimiters.get(channelId);
if (!channelLimiter) {
channelLimiter = new RateLimiter(
config.limits.perChannel.capacity,
config.limits.perChannel.refillRate
);
channelLimiters.set(channelId, channelLimiter);
}
if (!channelLimiter.allow()) {
event.messages.push({
type: "error",
content: `Rate limit exceeded for channel ${channelId}. Please wait and try again.`
});
event.context.handled = true;
return;
}
}
// 用户级别限流检查(基于senderId)
if (config.limits.perUser && from) {
let userLimiter = userLimiters.get(from);
if (!userLimiter) {
userLimiter = new RateLimiter(
config.limits.perUser.capacity,
config.limits.perUser.refillRate
);
userLimiters.set(from, userLimiter);
}
if (!userLimiter.allow()) {
event.messages.push({
type: "error",
content: `You've exceeded your request limit (${config.limits.perUser.capacity} per interval). Please wait.`
});
event.context.handled = true;
return;
}
}
});
// 注册CLI命令——查看限流状态
api.registerCli({
name: "ratelimit",
description: "View rate limit status",
async execute(args) {
if (args[0] === "stats") {
return {
message: "Rate limit stats:\n" +
`- Active users: ${userLimiters.size}\n` +
`- Active channels: ${channelLimiters.size}\n` +
`- Global: ${globalLimiter ? "enabled" : "disabled"}`
};
}
if (args[0] === "reset") {
userLimiters.clear();
channelLimiters.clear();
if (globalLimiter) globalLimiter = new RateLimiter(
config.limits.global?.capacity || 50,
config.limits.global?.refillRate || 5
);
return "All rate limiters have been reset.";
}
return "Usage: ratelimit stats | reset";
}
});
// 注册HTTP路由——暴露限流指标
api.registerHttpRoute({
method: "GET",
path: "/api/ratelimit/metrics",
async handler(req, res) {
res.json({
activeUsers: userLimiters.size,
activeChannels: channelLimiters.size,
config: config.limits
});
}
});
}
});
Step 3:配置插件(openclaw.json)
{
"plugins": {
"entries": {
"rate-limit": {
"enabled": true,
"config": {
"limits": {
"perUser": { "capacity": 10, "refillRate": 1 },
"perChannel": { "capacity": 20, "refillRate": 2 },
"global": { "capacity": 50, "refillRate": 5 }
}
}
}
}
}
}
Step 4:部署与验证
# 方式一:本地开发链接
cd openclaw-ratelimit && npm run build
ln -s $(pwd) ~/.openclaw/extensions/rate-limit
# 方式二:npm包安装
openclaw plugins install clawhub:@myorg/openclaw-ratelimit
# 启用并重启
openclaw plugins enable rate-limit
openclaw gateway restart
# 验证加载
openclaw plugins list | grep rate-limit
# 测试限流效果
openclaw ratelimit stats
Step 5:发布到ClawHub
clawhub login
clawhub package publish myorg/openclaw-ratelimit --dry-run
clawhub package publish myorg/openclaw-ratelimit
21.8 本节小结
本节课是Plugin插件开发的完整实战指南,核心知识点浓缩如下:
-
三层架构:Skill教AI做事、Hook在运行缝隙中插入逻辑、Plugin将一切能力注册到内核——“只教怎么做用Skill、只改运行时流程用Hook、要加新功能用Plugin”
-
Gateway插件模型:采用“声明式发现+运行时注册”两阶段模型,通过
PluginRegistry管理生命周期,api.registerXxx方法注册渠道、Provider、Tool、HTTP路由、CLI命令、日志钩子等多种能力 -
自定义认证插件:通过
registerHook("message:received")拦截入站请求,实现API Key校验、白名单管理、OAuth 2.0对接等认证策略 -
消息预处理/后处理:在Message Pipeline中添加敏感词过滤、脱敏、多格式归一化和签名,从源头阻断不安全请求
-
日志与监控:通过
before_agent_start、after_agent_turn和工具调用钩子,记录会话元数据、Token消耗和审计轨迹 -
请求限流插件(实战) :令牌桶算法实现三级限流(用户级、渠道级、全局级),搭配HTTP指标暴露和CLI命令管理,形成完整的可部署企业级扩展
将整个专栏串联起来看,从第16课自定义Skill开始,到第17课状态管理、第19课MCP生态接入、第20课Provider扩展,再到这节课Plugin系统开发——你已经完整覆盖了OpenClaw扩展生态的每一块拼图。
当你从现在往后看,你的OpenClaw已经不再是一个固化的工具,而是一个“可编程的数字员工”——你可以任意为其增加渠道、改写消息、注入日志,甚至在未来复用新的上下文管理策略。下一节课,我们将进入Gateway的高可用篇章,将目前开发的每一款插件安全、稳定、高吞吐地部署到生产环境中,真正打造7×24小时不掉线的企业级Agent。
21.9 课后习题
1. Skill vs Plugin区分实践
查看你当前OpenClaw环境中的技能和插件列表:openclaw skills list与openclaw plugins list。选择一个技能和一个插件,说明该Skill在对话中如何被AI调用,该Plugin在消息处理生命周期的哪个节点自动执行。参照21.1节的对比表格写出答案。
2. 认证网关模拟开发
参照21.3节的代码框架,开发一个“部门访问控制”插件:要求读取请求负载中的X-Department Header,若值为Admin或IT则放行,其他部门直接返回禁止消息。你能在message:received钩子中检测该Header吗?还是需要在前置的Web Server层解析?
3. 限流插件压测
将限流插件的用户级限制配置为capacity: 2, refillRate: 0.1,然后使用curl或openclaw agent命令连续发送5条消息。观察日志中触发限流拒绝的次数,记录被拒绝消息的响应格式。的响应格式。思考的响应格式。思考思考如何在如何在限如何在限流拒绝限流拒绝时增加流拒绝时增加Ret时增加Retry-ARetry-Afterry-After响应头fter响应头。
**4.响应头。
**4.。
4. 日志 日志 日志插件增强
为21.5节的插件增强**
为21.5节的aud插件增强**
为21.5节的audaudit插件it插件增加会话it插件增加会话输出功能增加会话输出功能:在输出功能:在after:在afterafter_agent_turn_agent_turn钩_agent_turn钩钩子中将最终的子中将最终的Token消耗子中将最终的Token消耗Token消耗写入写入session写入session.msession.metadata.metadata.estimatedetadata.estimated.estimatedCost。Cost。再添加Cost。再添加再添加一个HTTP一个HTTP endpoint /api/一个HTTP endpoint /api/ endpoint /api/auditaudit/costaudit/cost-sum/cost-summary?-summary?hoursmary?hours=24,hours=24,=24,返回最近返回最近24小时的累计返回最近24小时的累计24小时的累计Token量和Token量和预估成本。Token量和预估成本。能通过预估成本。能通过能通过``openclaw securityopenclaw securityopenclaw security audit audit命令触发插件 audit命令触发插件内部的成本命令触发插件内部的成本内部的成本审计。
**5审计。
**5. Plugin审计。
5. Plugin. Plugin发布演练
将发布演练**
将开发的发布演练**
将开发的开发的限流插件打包限流插件打包整理后限流插件打包整理后整理后,使用,使用claw,使用clawhub packageclawhub package publishhub package publish命令发布 publish命令发布到Cl命令发布到Cl到ClawHub(或模拟发布)。awHub(或模拟发布)。awHub(或模拟发布)。在发布前确保满足以下检查在发布前确保满足以下检查在发布前确保满足以下检查项:项:项:清单文件com清单文件compat.pl清单文件compat.plpat.pluginApiuginApi版本范围与uginApi版本范围与版本范围与当前Open当前OpenClaw匹配、当前OpenClaw匹配、Claw匹配、已在已在Sandbox环境已在Sandbox环境Sandbox环境验证限验证限流功能不干扰验证限流功能不干扰正常流功能不干扰正常正常消息、消息、配置Schema对消息、配置Schema对配置Schema对configconfig.limitsconfig.limits.limits做做做参数合法性校验(参数合法性校验(参数合法性校验(capccapcacity不可为0capcacity不可为0)、发布acity不可为0)、发布)、发布时提供时提供README.md时提供README.md``README.md说明文档。
🔗《30节课精通 OpenClaw》系列课程导航
第一部分(第1-5课):基础认知与入门部署——解决“这是什么、怎么搭建”的问题。
第二部分(第6-10课):核心原理深度剖析——解决“底层怎么工作”的问题。
第三部分(第11-15课) :应用场景与平台集成——解决“能用来做什么”的问题。
第四部分(第16-21课) :技能开发与定制扩展——解决“如何自己扩能力”的问题。
第五部分(第22-26课):高级特性与性能优化——解决“怎么用得更好”的问题。
第六部分(第27-30课) :安全、运维与生态进阶——解决“如何安全可靠地规模化”的问题。
🌟 感谢您耐心阅读到这里!
💡 如果本文对您有所启发欢迎:
👍 点赞📌 收藏 📤 分享给更多需要的伙伴。
🗣️ 期待在评论区看到您的想法, 共同进步。
🔔 关注我,持续获取更多干货内容~
🤗 我们下篇文章见~
更多推荐




所有评论(0)