需要深入理解 OpenClaw 插件的能力注册模型、所有权边界和加载管道时使用此参考。运行时通过 openclaw plugins inspect <id> 查看插件形态和能力分解,用 openclaw doctor 检查兼容性信号。文档覆盖能力类型、插件形态分类、合约执行机制、四层加载流程及 Provider 运行时 Hook 顺序。

OpenClaw 插件内部架构:能力模型、所有权边界与加载流程

这是插件系统的深度架构参考。实践指南请见:插件安装与使用 · 插件开发入门 · Channel 插件 · Provider 插件

公共能力模型

每个原生 OpenClaw 插件针对一种或多种能力类型注册。下表列出所有内置能力类型及注册方法:

能力 注册方法 示例插件
文本推理 api.registerProvider(...) openai, anthropic
CLI 推理后端 api.registerCliBackend(...) openai, anthropic
向量嵌入 api.registerEmbeddingProvider(...) Provider 拥有的向量插件
语音合成 api.registerSpeechProvider(...) elevenlabs, microsoft
实时转录 api.registerRealtimeTranscriptionProvider(...) openai
实时语音 api.registerRealtimeVoiceProvider(...) openai
媒体理解 api.registerMediaUnderstandingProvider(...) openai, google
图像生成 api.registerImageGenerationProvider(...) openai, google, fal, minimax
音乐生成 api.registerMusicGenerationProvider(...) google, minimax
视频生成 api.registerVideoGenerationProvider(...) qwen
网页抓取 api.registerWebFetchProvider(...) firecrawl
网页搜索 api.registerWebSearchProvider(...) google
渠道/消息 api.registerChannel(...) msteams, matrix
网关发现 api.registerGatewayDiscoveryService(...) bonjour

::: info 注册零能力但提供 Hook、工具、发现服务或后台服务的插件属于旧版 Hook-only 插件。该模式仍然完全兼容。 :::

外部兼容性立场

插件情况 建议
已有的外部插件 保持 Hook 集成兼容,这是兼容性基线
新增内置/原生插件 优先使用显式能力注册,而非厂商特有侵入式或新的 Hook-only 设计
采纳能力注册的外部插件 允许,但除非文档标记为稳定,否则能力特定的辅助接口视为演进中

能力注册是未来方向。旧版 Hook 是过渡期最安全的不破坏已有外部插件的路径。导出的辅助子路径并不全部平等——优先使用窄文档化的合约,而非偶然导出的辅助函数。

插件形态

OpenClaw 根据实际注册行为(非静态元数据)将插件分为四种形态:

plain-capability

注册恰好一种能力类型(例如仅 Provider 的 mistral)。

hybrid-capability

注册多种能力类型(例如 openai 拥有文本推理、语音、媒体理解、图像生成)。

hook-only

只注册 Hook(类型化或自定义),无能力、工具、命令或服务。

non-capability

注册工具、命令、服务或路由,但无能力。

运行 openclaw plugins inspect <id> 查看插件形态和能力分解。详细参考 CLI 参考

旧版 Hook

before_agent_start Hook 仍作为 Hook-only 插件的兼容路径受支持。已有旧版插件仍依赖它。方向:保持其工作,标记为旧版;模型/Provider 覆写工作优先用 before_model_resolve;提示词修改工作优先用 before_prompt_build;仅在实际使用量下降且测试套件证明迁移安全后才移除。

兼容性信号

运行 openclaw doctoropenclaw plugins inspect <id> 可能看到以下标签:

信号 含义
config valid 配置解析正常,插件解析成功
compatibility advisory 插件使用了受支持但较旧的模式(例如 hook-only
legacy warning 插件使用了已废弃的 before_agent_start
hard error 配置无效或插件加载失败

hook-onlybefore_agent_start 目前都不会导致插件无法使用:hook-only 是建议性的,before_agent_start 仅触发警告。这些信号也会在 openclaw status --allopenclaw plugins doctor 中出现。

架构概览

OpenClaw 插件系统分四层:

  1. Manifest + 发现层:从配置路径、工作区根、全局扩展根和内置扩展中发现候选插件;先读取原生 openclaw.plugin.json 和支持的 bundle manifest。
  2. 启用 + 验证层:决定已发现插件的状态(启用/禁用/阻断/独占槽位如 memory)。
  3. 运行时加载层:原生插件通过 require 进程内加载,向中央注册表注册能力;兼容 bundle 标准化为注册记录,不导入运行时代码。
  4. 面向消费层:其余 OpenClaw 代码从注册表读取,暴露工具、渠道、Provider、Hook、HTTP 路由、CLI 命令和服务。

对于插件 CLI,根命令发现分为两个阶段:解析期元数据来自 registerCli(..., { descriptors: [...] });实际插件 CLI 模块可保持懒加载,在首次调用时注册。这样插件拥有的 CLI 代码保留在插件内,同时让 OpenClaw 在解析前保留根命令名。

关键设计边界:

  • Manifest/config 验证应仅从 manifest/schema 元数据工作,不执行插件代码。
  • 原生能力发现可能加载受信任的插件入口代码,以构建非激活的注册表快照。
  • 原生运行时行为来自插件模块的 register(api) 路径,此时 api.registrationMode === "full"

这种分离使 OpenClaw 能在完整运行时激活前验证 config、解释缺失/禁用的插件并构建 UI/schema 提示。

插件元数据快照与查找表

Gateway 启动时,为当前 config 快照构建一个 PluginMetadataSnapshot。该快照仅含元数据:已安装插件索引、manifest 注册表、manifest 诊断、所有者映射、插件 id 正规化器和 manifest 记录。它不保存已加载的插件模块、Provider SDK、包内容或运行时导出。

插件相关的 config 验证、启动自动启用和 Gateway 插件引导使用该快照,而非独立重建 manifest/index 元数据。PluginLookUpTable 从同一快照衍生,并添加了当前运行时 config 的启动插件计划。

启动后,Gateway 将当前元数据快照作为可替换的运行时产物。重复的运行时 Provider 发现可借用该快照,而非为每次 Provider 目录遍历重建已安装索引和 manifest 注册表。当 Gateway 关闭、config/插件清单变更、已安装索引写入时,快照被清除或替换;当无兼容的当前快照时,调用者回退到冷 manifest/index 路径。兼容性检查必须包含插件发现根目录如 plugins.load.paths 和默认智能体工作区,因为工作区插件在元数据范围内。

快照和查找表将重复的启动决策放在快速路径上:渠道所属、延迟渠道启动、启动插件 id、Provider 和 CLI 后端所属、setup provider、命令别名、模型目录 Provider 和 manifest 合约所属、插件 config schema 和渠道 config schema 验证、启动自动启用决策。

安全边界是快照替换,而非突变。当 config、插件清单、安装记录或持久化索引策略变更时重建快照。不要将其当作可变的全局注册表,也不要保留无限的历史快照。运行时插件加载与元数据快照分离,这样旧运行时状态不能隐藏在元数据缓存背后。

缓存规则记录在 插件架构内部资料:manifest 和发现元数据是新鲜的,除非调用者在当前流程中持有显式快照、查找表或 manifest 注册表。隐藏的元数据缓存和基于时钟 TTL 不属于插件加载。只有运行时加载器、模块和依赖工件缓存可在代码或已安装工件实际加载后持久存在。

部分冷路径调用者仍直接通过持久化的已安装插件索引重建 manifest 注册表,而非接收 Gateway 的 PluginLookUpTable。该路径现已改为按需重建注册表;当调用者已有当前查找表或显式 manifest 注册表时,优先通过运行时流程传递。

激活规划

激活规划是控制面的一部分。调用者可在加载更广泛的运行时注册表之前询问哪些插件与具体命令、Provider、渠道、路由、智能体 harness 或能力相关。

规划器保持当前 manifest 行为的兼容性:

  • activation.* 字段是显式的规划器提示。
  • providerschannelscommandAliasessetup.providerscontracts.tools 和 hooks 仍为 manifest 所属回退。
  • 仅 id 的规划器 API 仍可供已有调用者使用。
  • 规划 API 报告原因标签,以便诊断能区分显式提示与所属回退。

::: warning 不要将 activation 视为生命周期 Hook 或 register(...) 的替代品。它是用于缩小加载范围的元数据。当所属字段已描述关系时优先使用;仅当需要额外的规划器提示时才使用 activation。 :::

Channel 插件与共享消息工具

Channel 插件不需要为普通聊天动作注册单独的发送/编辑/反应工具。OpenClaw 在 core 中维护一个共享的 message 工具,Channel 插件拥有其背后的渠道特定发现和执行。

当前边界:

  • core 拥有:共享 message 工具宿主、提示词接线、会话/线程记录和执行调度。
  • Channel 插件拥有:范围内的动作发现、能力发现和渠道特定 schema 片段。
  • Channel 插件拥有:Provider 特定的会话会话语法(如会话 id 如何编码线程 id 或从父会话继承)。
  • Channel 插件拥有:通过其动作适配器执行最终动作。

对于 Channel 插件,SDK 接口是 ChannelMessageActionAdapter.describeMessageTool(...)。该统一发现调用让插件能同时返回可见动作、能力和 schema 贡献,避免这些部分发生漂移。

当渠道特定消息工具参数携带媒体源(本地路径或远程媒体 URL)时,插件也应从 describeMessageTool(...) 返回 mediaSourceParams。Core 使用该显式列表应用沙箱路径规范化和出站媒体访问提示,而无需硬编码插件拥有的参数名。优先使用动作范围映射,而非渠道级扁平列表,这样仅 profile 相关的媒体参数不会被在无关动作(如 send)上规范化。

Core 将运行时作用域传递给发现步骤。重要字段包括:

  • accountId
  • currentChannelId
  • currentThreadTs
  • currentMessageId
  • sessionKey
  • sessionId
  • agentId
  • 可信入站 requesterSenderId

这对上下文敏感的插件很重要:渠道可根据活跃账号、当前房间/线程/消息或可信请求者身份隐藏或暴露消息动作,而无需在 core 的 message 工具中硬编码渠道特定分支。

这也是为什么嵌入运行器的路由更改仍是插件工作:运行器负责将当前聊天/会话身份转发到插件发现边界,以便共享的 message 工具为当前轮次暴露正确的渠道拥有的接口。

对于渠道拥有的执行辅助方法,内置插件应将执行运行时保留在自己的扩展模块内。Core 不再在 src/agents/tools 下拥有 Discord、Slack、Telegram 或 WhatsApp 消息动作运行时。我们不发布独立的 plugin-sdk/*-action-runtime 子路径,内置插件应从其扩展模块直接导入自己的本地运行时代码。

同样的边界适用于 Provider 命名的 SDK 接缝:core 不应导入 Slack、Discord、Signal、WhatsApp 等扩展的渠道特定便利 barrel。如果 core 需要某种行为,要么消费内置插件的 api.ts / runtime-api.ts barrel,要么将需求提升为共享 SDK 中的窄泛能力。

内置插件遵循相同规则。内置插件的 runtime-api.ts 不应重新导出自己的品牌化 openclaw/plugin-sdk/<plugin-id> 外观。这些品牌化外观仍作为外部插件和旧版使用者的兼容性 shim,但内置插件应使用本地导出加窄泛型 SDK 子路径,例如 openclaw/plugin-sdk/channel-policyopenclaw/plugin-sdk/runtime-storeopenclaw/plugin-sdk/webhook-ingress。新代码不应添加插件特定 SDK 外观,除非因现有外部生态系统需要兼容性边界。

对于投票,有两种执行路径:

  • outbound.sendPoll 是适用于大多数渠道的通用投票基线。
  • actions.handleAction("poll") 是支持渠道特定投票语义或额外参数的优先路径。

Core 现在将通用投票解析推迟到插件投票分发拒绝该动作之后,这样插件拥有的投票处理器可先接受渠道特定投票字段,而不会被通用投票解析器阻塞。

详细启动序列见 插件架构内部资料

能力所有权模型

OpenClaw 将原生插件视为公司功能的所有权边界,而非不相关集成的大杂烩。

这意味着:

  • 公司插件通常应拥有该公司在 OpenClaw 中的所有面向接口。
  • 功能插件通常应拥有其所引入的完整功能接口。
  • 渠道应消费共享的 core 能力,而非临时重新实现 Provider 行为。
厂商多能力

openai 拥有文本推理、语音、实时语音、媒体理解和图像生成。google 拥有文本推理、媒体理解、图像生成和网页搜索。qwen 拥有文本推理、媒体理解和视频生成。

厂商单能力

elevenlabsmicrosoft 拥有语音;firecrawl 拥有网页抓取;minimax / mistral / moonshot / zai 拥有媒体理解后端。

功能插件

voice-call 拥有呼叫传输、工具、CLI、路由和 Twilio 媒体流桥接,但消费共享的语音、实时转录和实时语音能力,而非直接导入厂商插件。

最终状态是:

  • OpenAI 存在于一个插件中,即使它横跨文本模型、语音、图像和未来视频。
  • 另一个厂商可以为其自己的接口做同样的事。
  • 渠道不关心哪个厂商插件拥有 Provider;它们消费由 core 暴露的共享能力合约。

关键区分:

  • 插件 = 所有权边界
  • 能力 = 多个插件可实现或消费的 core 合约

因此,如果 OpenClaw 增加一个新领域如视频,第一个问题不是"哪个 Provider 应硬编码视频处理?"第一个问题是"core 的视频能力合约是什么?"合约存在后,厂商插件可注册实现,渠道/功能插件可消费。

如果能力还不存在,正确的做法通常是:

  1. 在 core 中定义缺失的能力。
  2. 通过插件 API/运行时以类型化方式暴露。
  3. 将渠道/功能与该能力接线。
  4. 让厂商插件注册实现。

这保持所有权显式,同时避免 core 行为依赖单一厂商或一次性插件特定代码路径。

能力分层

在决定代码归属时使用此心理模型:

Core 能力层

共享编排、策略、回退、config 合并规则、交付语义和类型合约。

厂商插件层

厂商特定 API、认证、模型目录、语音合成、图像生成、未来视频后端、用量端点。

渠道/功能插件层

Slack/Discord/voice-call 等集成,消费 core 能力并在表面上呈现。

例如 TTS 遵循此形状:core 拥有回复时 TTS 策略、回退顺序、偏好和渠道交付;openaielevenlabsmicrosoft 拥有合成实现;voice-call 消费电话 TTS 运行时辅助方法。

未来能力也应优先采用相同模式。

多能力公司插件示例

一个公司插件应对外界感觉统一。如果 OpenClaw 对模型、语音、实时转录、实时语音、媒体理解、图像生成、视频生成、网页抓取、网页搜索有共享合约,一个厂商可在同一处拥有其所有接口:

import type { OpenClawPluginDefinition } from "openclaw/plugin-sdk/plugin-entry";
import {
  describeImageWithModel,
  transcribeOpenAiCompatibleAudio,
} from "openclaw/plugin-sdk/media-understanding";

const plugin: OpenClawPluginDefinition = {
  id: "exampleai",
  name: "ExampleAI",
  register(api) {
    api.registerProvider({
      id: "exampleai",
      // auth/model catalog/runtime hooks
    });

    api.registerSpeechProvider({
      id: "exampleai",
      // vendor speech config — implement the SpeechProviderPlugin interface directly
    });

    api.registerMediaUnderstandingProvider({
      id: "exampleai",
      capabilities: ["image", "audio", "video"],
      async describeImage(req) {
        return describeImageWithModel({
          provider: "exampleai",
          model: req.model,
          input: req.input,
        });
      },
      async transcribeAudio(req) {
        return transcribeOpenAiCompatibleAudio({
          provider: "exampleai",
          model: req.model,
          input: req.input,
        });
      },
    });

    api.registerWebSearchProvider(
      createPluginBackedWebSearchProvider({
        id: "exampleai-search",
        // credential + fetch logic
      }),
    );
  },
};

export default plugin;

重要的是辅助方法名本身,而是结构:

  • 一个插件拥有厂商接口。
  • core 仍拥有能力合约。
  • 渠道和功能插件消费 api.runtime.* 辅助方法,而非厂商代码。
  • 合约测试可断言插件注册了其声称拥有的能力。

能力示例:视频理解

OpenClaw 已将图像/音频/视频理解视为一个共享能力。相同的所有权模型适用:

  1. Core 定义媒体理解合约。
  2. 厂商插件注册 describeImagetranscribeAudiodescribeVideo(如适用)。
  3. 渠道和功能插件消费共享 core 行为,而非直接接线到厂商代码。

这样可以避免将一个 Provider 的视频假设硬编码进 core。插件拥有厂商接口;core 拥有能力合约和回退行为。

视频生成已经使用相同序列:core 拥有类型化能力合约和运行时辅助方法,厂商插件注册 api.registerVideoGenerationProvider(...) 实现。

需要具体的发布清单?见 能力手册

合约与执行

插件 API 接口有意类型化并集中在 OpenClawPluginApi 中。该合约定义了受支持的注册点和插件可依赖的运行时辅助方法。

这意味着:

  • 插件作者有一个稳定的内部标准。
  • Core 可以拒绝重复所有权(如两个插件注册同一个 provider id)。
  • 启动时可为格式错误的注册产生可操作诊断。
  • 合约测试可强制执行内置插件所有权并防止无声漂移。

两层执行:

  • 运行时注册执行:插件注册表在加载时验证注册。重复 provider id、重复 speech provider id 和格式错误的注册会产生插件诊断,而非未定义行为。
  • 合约测试:内置插件在测试运行时捕获到合约注册表,以便 OpenClaw 显式断言所有权。目前用于 model provider、speech provider、web search provider 和内置注册所有权。

实际效果:OpenClaw 预先知道哪个插件拥有哪个接口。这让 core 和渠道能无缝组合,因为所有权是声明式、类型化且可测试的,而非隐式的。

什么是好合约

好合约 坏合约
类型化、小、能力特定、归 core 所有、可被多个插件重用、可被渠道/功能消费而不需厂商知识 隐藏于 core 中的厂商特定策略、绕过注册表的一次性插件逃生门、直接进入厂商实现的渠道代码、不属于 OpenClawPluginApiapi.runtime 的临时运行时对象

不确定时,提高抽象层次:先定义能力,然后让插件接入。

执行模型

原生 OpenClaw 插件进程内运行于 Gateway。它们不被沙箱化。已加载的原生插件与 core 代码具有相同的进程级信任边界。

::: warning 原生插件意味的后果:插件可注册工具、网络处理器、Hook 和服务;插件 bug 可导致 gateway 崩溃或不稳定;恶意原生插件等同于 OpenClaw 进程内的任意代码执行。 :::

兼容 bundle 默认更安全——OpenClaw 目前将其视为元数据/内容包(主要是内置 skill)。

使用白名单和显式安装/加载路径管理非内置插件。将工作区插件视为开发时代码,而非生产默认值。

对于内置工作区包名,将插件 id 锚定在 npm 名称中:默认 @openclaw/<id>,或使用批准的类型后缀如 -provider-plugin-speech-sandbox-media-understanding(当包有意暴露更窄的插件角色时)。

::: info 信任说明: plugins.allow 信任的是插件 id,而非来源。与内置插件同名的工作区插件会在该工作区插件被启用/加入白名单时有意覆盖内置副本。这是正常的,用于本地开发、补丁测试和热修复。内置插件信任从加载时的源快照解析(manifest 和磁盘上的代码),而非从安装元数据解析。损坏或替换的安装记录不能无声地扩大内置插件的信任范围超出实际来源所声称的。 :::

导出边界

OpenClaw 导出能力而非实现便利。

保持能力注册公开。修剪非合约辅助导出:

  • 内置插件特定的辅助子路径。
  • 不打算作为公开 API 的运行时管线子路径。
  • 厂商特定便利辅助方法。
  • 属于实现细节的 setup/onboarding 辅助方法。

已保留的内置插件辅助子路径已从生成的 SDK 导出映射中退役。将所有者特定辅助方法保留在所属插件包中;仅将可重用的宿主行为提升为泛型 SDK 合约,如 plugin-sdk/gateway-runtimeplugin-sdk/security-runtimeplugin-sdk/plugin-config-runtime

内部细节与参考

加载管道、注册表模型、Provider 运行时 Hook、Gateway HTTP 路由、消息工具 schema、渠道目标解析、Provider 目录、上下文引擎插件以及添加新能力的指南,见 插件架构内部资料

常见问题

开发自定义插件时,应该选择"原生插件"还是"bundle"模式?

需要注册工具、Provider、渠道或 HTTP 路由等运行时能力时,必须使用原生插件。Bundle 只是内容包(skill、hook-pack、MCP 配置),适合发布 Claude/Cursor/Codex 格式的内容,但无法运行任意 TypeScript 代码。

插件报 hard error 怎么定位问题?

运行 openclaw plugins inspect <id> 查看详细诊断,再查 openclaw logs --follow 中的加载错误。常见原因:manifest 格式不对(openclaw.plugin.json 缺失或字段错误)、入口文件路径错误、npm 依赖未安装。

两个插件都要注册同一个 provider id,会怎样?

注册表在加载时检测到重复 ID 会产生插件诊断(hard error 级别),后加载的插件注册失败。通过 openclaw plugins inspect 可查看冲突详情。