OpenClaw 插件体系拆解:能力注册、加载四层与所有权边界,附最小插件从写到装的完整路径

2026-08-17

OpenClaw 里很多东西都是插件:模型供应商、渠道、语音合成、图片生成、网页搜索都是。所以「插件体系」不是可选的扩展话题,而是理解这套系统绕不开的一层——装一个渠道、换一个模型厂商,走的都是同一套装载流程。

麻烦在于文档把这件事拆成了两套页面:一套讲内部机制(能力模型、注册表、加载管线),一套讲操作。只看前者会觉得抽象,只看后者会在出问题时不知道该查什么。这篇把两边并在一起:先说清插件怎么被组织和加载,再落到具体命令。

先给一句结论:插件是所有权边界,能力(capability)是核心定义的契约。插件不是一堆功能的杂货铺,它代表「某家厂商的全部对外表面」或「某个完整功能」;能力则是核心侧的类型化契约,多个插件可以实现它,也可以消费它。后面所有机制都围着这条线展开。

能力:插件对外注册的那一组入口

原生插件通过 api.registerXxx(...) 向核心注册能力。文档列出的能力类型与注册方法如下(示例插件也来自官方文档表格):

能力注册方法文档举的插件
文本推理api.registerProvider(...)anthropicopenai
CLI 推理后端api.registerCliBackend(...)anthropicopenai
向量嵌入api.registerEmbeddingProvider(...)厂商自有的向量插件
语音合成api.registerSpeechProvider(...)elevenlabsmicrosoft
实时转写api.registerRealtimeTranscriptionProvider(...)openai
实时语音api.registerRealtimeVoiceProvider(...)googleopenai
多模态理解api.registerMediaUnderstandingProvider(...)googleopenai
会议转写源api.registerTranscriptSourceProvider(...)discordgoogle-meetteams-meetingszoom-meetings
图片生成api.registerImageGenerationProvider(...)falgoogleopenai
音乐生成api.registerMusicGenerationProvider(...)falgoogleminimax
视频生成api.registerVideoGenerationProvider(...)falgoogleqwen
网页抓取api.registerWebFetchProvider(...)firecrawl
网页搜索api.registerWebSearchProvider(...)bravefirecrawlgoogle
渠道 / 消息api.registerChannel(...)matrixmsteams
网关发现api.registerGatewayDiscoveryService(...)bonjour

一个插件可以一个能力都不注册。只提供钩子、工具、发现服务或后台服务的插件属于遗留的 hook-only 形态,这种写法仍被完整支持。

OpenClaw 按插件实际的注册行为(不是清单里的静态声明)把它归成四种形状:只注册单一能力类型的 plain-capability(如 arceechutes)、注册多种能力类型的 hybrid-capability(如 openai 占着文本推理、语音、多模态理解和图片生成)、只注册钩子的 hook-only,以及注册了工具/命令/服务/路由但没有能力的 non-capability。查看某个插件属于哪种:

openclaw plugins inspect <id>

openclaw doctoropenclaw plugins inspect <id>openclaw status --allopenclaw plugins doctor 会给出四类兼容性信号:config validhook-only(提示级,能用但没迁到能力注册)、deprecated memory-embedding API(警告级,非内置插件还在用旧的 memory 专用嵌入接口而非 registerEmbeddingProvider)、hard error(配置无效或加载失败)。文档明确说:提示和警告类信号当下不会让你的插件失效

官方对外部插件的兼容立场很直白:已有外部插件继续用钩子集成,这是兼容基线;新的内置/原生插件优先用显式能力注册;外部插件也可以用能力注册,但除非文档标为稳定,能力相关的辅助接口要当成还在演进的看待

加载分四层,出问题要先分清卡在哪一层

文档把插件系统分成四层,顺序是固定的:

  1. 清单与发现——从配置路径、工作区根、全局插件根和内置插件里找出候选,先读原生的 openclaw.plugin.json 清单以及受支持的 bundle 清单。
  2. 启用与校验——核心决定这个插件是启用、禁用、被拦截,还是被选进某个独占槽位(比如 memory)。
  3. 运行时装载——原生插件在网关进程内加载并把能力注册进中心注册表。打包好的 JavaScript 走原生 require;第三方本地源码 TypeScript 走 Jiti 兜底路径。兼容型 bundle 会被规整成注册表记录,不导入运行时代码。
  4. 表面消费——OpenClaw 其余部分读注册表,暴露出工具、渠道、供应商配置、钩子、HTTP 路由、CLI 命令和服务。

这里有一条设计边界值得记住:清单/配置校验只靠清单与 schema 元数据完成,不执行插件代码;原生能力发现可以加载受信插件的入口代码,构建一份不激活的注册表快照;真正的运行时行为来自 register(api),此时 api.registrationMode === "full"。这样在完整运行时起来之前,就能校验配置、解释「某插件为什么没加载」。

另外还有一层「激活规划」:调用方可以先问「哪些插件跟这条命令、这个供应商、这个渠道、这个能力有关」,再决定要不要加载更大的运行时注册表。清单里的 activation.* 是给规划器的显式提示,而 providerschannelscommandAliasessetup.providerscontracts.tools 和钩子仍然是归属关系的兜底来源。文档特别警告:不要把 activation 当成生命周期钩子,也不要拿它替代 register(...),它只是用来收窄加载范围的元数据。

所有权边界:为什么一家厂商只该有一个插件

按官方文档的说法,一个原生插件应当是「一家公司」或「一个功能」的所有权边界:厂商插件拥有该厂商面向 OpenClaw 的全部表面;功能插件拥有它引入的完整功能表面;渠道则去消费核心的共享能力,而不是自己重新实现一遍供应商行为。

文档给的例子很能说明问题:google 一个插件里就占着文本推理、CLI 后端、嵌入、语音、实时语音、多模态理解、图片/音乐/视频生成和网页搜索;而 arceechutes 只做文本推理,microsoft 只做语音。功能插件 voice-call 拥有通话传输、工具、CLI、路由和 Twilio 媒体流桥接,但语音合成、实时转写、实时语音这些是消费共享能力,而不是直接 import 厂商插件。

契约约束也是硬的:插件 API 集中定义在 OpenClawPluginApi 里,核心据此拒绝重复归属(比如两个插件注册同一个 provider id),并用契约测试锁定内置插件的归属,目前覆盖模型供应商、语音供应商、网页搜索供应商和内置注册归属。这一节跟模型供应商接入与故障转移渠道接入的通用流程是同一套底座,可对照着看。

一条必须先看的安全前提:原生插件不沙箱

文档写得毫不含糊:原生 OpenClaw 插件与网关同进程运行,不做沙箱隔离,已加载的原生插件享有与核心代码同级的进程信任边界。三条直接后果:插件可以注册工具、网络处理器、钩子和服务;插件的 bug 可以让网关崩溃或不稳定;恶意的原生插件等价于在 OpenClaw 进程内任意执行代码。

相比之下兼容型 bundle 默认更安全,因为当前版本把它们当元数据/内容包处理,在现有发布里这主要指打包的技能(skills)。对非 bundle 插件,文档建议用允许清单和显式安装/加载路径来管,并且把工作区插件当成开发期代码,不要当生产默认值

还有一个容易踩的语义:plugins.allow 信任的是插件 id,不是来源。与内置插件同 id 的工作区插件,在被启用/加入允许清单时会有意遮蔽内置副本——本地开发、补丁测试、临时热修时这是正常且有用的。内置插件的信任从加载时磁盘上的清单与代码这一「源快照」解析,被篡改的安装记录不能悄悄扩大它的信任范围。想把这条跟工具策略、提权放在一起理解,可以看沙箱、工具策略与提权的边界

实际怎么装:Control UI 与 CLI 分工

Control UI 覆盖常见的发现、安装、启用、禁用流程,CLI 才有更新、卸载、高级配置和显式选源。

Control UI 里打开 Plugins,或者用相对于 Control UI 基路径的 /settings/plugins(基路径是 /openclaw 时就是 /openclaw/settings/plugins)。两个标签页:Installed 按类别(渠道、模型供应商、记忆、工具)列本地清单,每行的 菜单可启用/禁用,外部安装的插件还有 Remove;它同时列出配置里的 MCP 服务器,改的是 mcp.serversDiscover 是商店。自带插件不需要装包,而且内置插件不能移除,只能禁用

权限分两档:目录浏览与搜索要 operator.read;安装、启用、禁用、移除以及 MCP 服务器变更要 operator.admin。两条容易忽略的规则——管理员启用某个已安装插件时,会把它加进已有的严格 plugins.allow 清单;而 plugins.deny 里的显式条目是权威的,必须先删掉才能启用

重启规则分两种:安装或移除插件代码需要重启网关;仅仅改启用状态时,如果已安装插件和当前网关运行时都支持,可以不重启。OAuth 型的 MCP 连接器加完之后,还得从 CLI 跑一次 openclaw mcp login <name>

CLI 侧最常用的几组命令:

openclaw plugins list
openclaw plugins list --enabled
openclaw plugins list --json
openclaw plugins search "calendar"

openclaw plugins enable <plugin-id>
openclaw plugins disable <plugin-id>

openclaw plugins install clawhub:<package>
openclaw plugins install npm:@scope/openclaw-plugin@1.2.3
openclaw plugins install npm-pack:<path.tgz>
openclaw plugins install git:github.com/acme/openclaw-plugin@v1.0.0
openclaw plugins install --link ./my-plugin

排查时经常搞混的一点:plugins list冷清单检查,反映的是 OpenClaw 能从配置、清单和持久化注册表里发现什么,并不能证明运行中的网关真的导入了这个插件的运行时。要证明运行时确实注册上了工具、钩子、服务、网关方法、HTTP 路由和插件自带 CLI 命令,得用带 --runtime 的 inspect:

openclaw gateway restart
openclaw plugins inspect <plugin-id> --runtime --json

开了配置重载的托管网关,在安装、更新、卸载插件代码后会自动重启;非托管或关了重载的,得自己重启再看运行时表面。

选源方面,文档给的对照是这样的:

什么时候用
ClawHub想要 OpenClaw 原生的发现、扫描摘要、版本与安装提示
git要仓库里的某个分支、标签或提交
本地路径在同一台机器上开发测试插件
marketplace装 Claude 兼容的市场插件
npm pack用 npm 安装语义验证本地包产物
npmjs.com你本来就发 JavaScript 包,或者需要 npm dist-tag、私有 registry

裸包名在发布切换期仍从 npm 安装,除非名字命中内置或官方插件 id——那样会用本地/官方副本。想要确定性就显式加 clawhub:npm:git:npm-pack: 前缀。新的任意 npm、git、本地路径/归档或市场来源,在非交互安装里需要你审查并信任来源后加 --force。托管的本地路径安装必须是插件目录或归档;单个独立插件文件要放进 plugins.load.paths,而不是用 plugins install 装。

两个边角规则:新装的插件如果缺必要配置,OpenClaw 会记录安装但保持禁用,得先配好 plugins.entries.<id>.configenable;已有配置项无效时安装直接失败且不改写它。在 Nix 模式(OPENCLAW_NIX_MODE=1)下,插件的安装、更新、卸载、启用、禁用全部被禁用

写一个最小插件:包、清单、入口、验证

官方给的最短可用形态是「注册一个必备 agent 工具」的工具插件。环境要求是 Node 22.22.3+、Node 24.15+ 或 Node 25.9+,配 npmpnpm,用 TypeScript ESM 模块;仓库内的内置插件开发只支持 pnpm,因为 OpenClaw 是从 extensions/* 工作区包里发现它们的。

包元数据分两个文件。package.json 声明 OpenClaw 相关元数据:

{
  "name": "@myorg/openclaw-my-plugin",
  "version": "1.0.0",
  "type": "module",
  "openclaw": {
    "extensions": ["./index.ts"],
    "compat": {
      "pluginApi": ">=2026.3.24-beta.2",
      "minGatewayVersion": "2026.3.24-beta.2"
    }
  }
}

openclaw.plugin.json 是清单,每个插件都必须有,哪怕没有任何配置

{
  "id": "my-plugin",
  "name": "My Plugin",
  "description": "Adds a custom tool to OpenClaw",
  "contracts": {
    "tools": ["my_tool"]
  },
  "activation": {
    "onStartup": true
  },
  "configSchema": {
    "type": "object",
    "additionalProperties": false
  }
}

入口用 definePluginEntry 注册工具;渠道插件不一样,用 openclaw/plugin-sdk/core 里的 defineChannelPluginEntry

import { Type } from "typebox";
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";

export default definePluginEntry({
  id: "my-plugin",
  name: "My Plugin",
  description: "Adds a custom tool to OpenClaw",
  register(api) {
    api.registerTool({
      name: "my_tool",
      description: "Echo one input value",
      parameters: Type.Object({ input: Type.String() }),
      async execute(_id, params) {
        return {
          content: [{ type: "text", text: `Got: ${params.input}` }],
          details: { input: params.input },
        };
      },
    });
  },
});

三条硬规则不能漏。第一,运行时工具必须出现在清单的 contracts.tools,OpenClaw 才能在不加载插件运行时的前提下确定归属。第二,受宿主信任的表面同样要在清单里显式声明:api.registerAgentToolResultMiddleware(...) 对应 contracts.agentToolResultMiddlewareapi.registerTrustedToolPolicy(...) 对应 contracts.trustedToolPolicies。第三,已发布的外部插件运行时入口要指向构建后的 JavaScript,TypeScript 源码入口只用于本地开发。

工具还分必备和可选。必备工具在插件启用时始终可用;可选工具要用户显式 opt-in,OpenClaw 才会加载拥有它的插件运行时。可选工具在代码里传 { optional: true },清单里写 toolMetadata.<tool>.optional: true,两边要对齐;用户侧通过 tools.allow 放行:

{
  tools: { allow: ["workflow_tool"] }, // 或者写 ["my-plugin"] 放行该插件的所有工具
}

outputSchema 是可选的,描述供 Code Mode 与 Tool Search 使用的结构化 details 值。畸形注册会被跳过并记进插件诊断——缺少非空 nameexecute 不是函数、描述符没有 parameters 对象都算;工具名与核心工具冲突同样是跳过加报告。

验证顺序:先用 openclaw plugins inspect my-plugin --runtime --json 看运行时注册上没有;仓库内插件跑 pnpm test extensions/my-plugin/pnpm check;发包前再用 npm-pack: 走一遍真实安装形态:

npm pack --pack-destination /tmp
openclaw plugins install npm-pack:/tmp/<plugin-package>.tgz --force
openclaw plugins inspect my-plugin --runtime --json

为什么非要多这一步?npm-pack: 用的是 OpenClaw 托管的、每插件独立的 npm 工程,能抓出源码检出测试掩盖掉的依赖错误——运行时 import 的东西必须在 dependenciesoptionalDependencies 里,只留在 devDependencies 的不会被装进去。

这篇没覆盖的,和几处得先想清楚的

先说边界。这篇只吃了三份文档:插件内部机制、插件管理、第一个插件的教程。加载管线的完整启动时序、注册表模型、供应商运行时钩子、网关 HTTP 路由、消息工具 schema、上下文引擎插件、以及「怎么加一个新能力」,官方放在另一篇内部机制页里;清单的逐字段定义也在单独的清单页。

有三件事值得在动手前掂量。其一,原生插件不沙箱是设计取舍不是待修 bug,「装个插件试试」在生产网关上不是低风险动作。其二,写外部插件时,能力注册是官方指的方向,但文档自己也说能力相关的辅助接口在标为稳定前算演进中,钩子路径才是过渡期最不容易被改坏的选择。其三,一旦 OpenClaw 无法证明「只有一个包属主且子条目清单完整」,更新和卸载会失败关闭,不改包文件、不改配置、不改已安装索引;文档给的动作是跑 openclaw plugins registry --refresh、看 openclaw plugins doctor、用 openclaw doctor --fix 修可修的遗留索引状态,还不行就重装这个包再重试。

最后一句排查顺序:插件问题先分清是「没发现」「没启用」还是「没加载」。前两者看 plugins list 和配置就够,第三个只有 inspect --runtime 说了算。想把这层放回整体系统定位,配合架构总览那张分层图会快很多。

延伸阅读


本文依据 OpenClaw 官方仓库(github.com/openclaw/openclawdocs/ 下的官方文档整理,核对日 2026-08-17。 我们没有安装或运行过 OpenClaw,因此不涉及界面外观、操作手感与实测耗时的任何描述; 文中的默认值、命令与配置项均为文档口径,不构成对实际运行结果的保证。 该项目迭代很快,请以仓库最新内容为准。接入即时通讯平台前,请自行确认所在平台的规则与合规要求。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。