12.1 插件开发体系:自定义扩展的工程机制

扩展能力的工程化关键不是“能不能加功能”,而是“扩展是否可控、可审计、可回滚”。OpenClaw 以插件体系作为扩展主入口:插件可以引入新的工具、集成能力或运行时组件,并通过显式启用与白名单机制收敛风险。本节基于官方插件文档说明插件的核心结构、配置方式与安全边界,并给出一组可用于验收的命令。

12.1.1 插件是什么:长驻进程内的 TypeScript 模块

与仅提供文本提示思路的技能(Skills)截然不同,插件本质是运行在 Gateway 进程内的 TypeScript 扩展模块。它们通过 jiti 在运行时被动态编译加载,能直接挂载自定义的 Agent tools、注册全新的聊天频道、实现后台轮询服务,甚至接管系统的 RPC/HTTP handler。

官方文档对插件的定位、能力范围与加载方式给出了完整说明。其中最核心的安全与工程设计在于引入了 openclaw.plugin.json (Manifest) 文件。要求每个插件必须提供这份包含配置 JSON SchemauiHints 的清单声明,这么做的精妙之处是:系统在渲染 Control UI 表单或是进行首次配置拦截校验时,根本无需(也不会)执行第三方插件底层潜在的高风险 JS 代码

工程上建议把插件当作严肃的代码级系统依赖对待:

  1. 插件版本进入版本控制与发布流程。

  2. 插件的 openclaw.plugin.json 严格约束参数,不合规清单在 Gateway 启动期直接拒载。

  3. 插件的工具与副作用被工具策略与白名单彻底约束绑定。

12.1.2 配置与启用:entries、enabled、config 与 load.paths

插件的启用通过 plugins.entries 声明。插件 ID 使用与 npm 包名一致的短名(如 voice-call)或带作用域的包名(如 @yourorg/my-plugin)。每个条目包含唯一标识、启用开关与插件自定义配置。官方文档还提供了 allowdenyload.paths 等字段的完整写法:https://docs.openclaw.ai/tools/pluginarrow-up-right

下面示例展示了完整骨架:白名单准入 + 本地路径加载 + 启用插件并传入配置。

{
  plugins: {
    enabled: true,
    allow: ['my-plugin'],           // 白名单:仅允许列出的插件加载
    deny: ['untrusted-plugin'],     // 黑名单:deny 优先于 allow
    load: {
      paths: ['~/Projects/my-plugin'], // 本地开发中的插件目录
    },
    entries: {
      'my-plugin': {
        enabled: true,
        config: {
          // 插件自定义配置
        },
      },
    },
  },
}

为了降低供应链风险,建议配合白名单机制只允许经过审计的插件生效。

12.1.3 安全边界:插件白名单与工具策略协同

插件引入的是能力,能力的使用边界仍应由确定性的策略系统兜底。官方插件文档提供了 plugins.allowplugins.deny 的白名单机制;官方工具文档提供了工具的允许、拒绝与分层策略机制,并强调拒绝规则优先:

实践中建议采用“双层收敛”:

  1. 先用 plugins.allow 收敛可加载插件集合。

  2. 再用工具策略把插件提供的工具按风险分组,并在默认档案中拒绝高风险组。

这样可以把“扩展可用性”与“扩展可执行性”拆开管理,避免某个插件被安装后立即具备不受控副作用。

具体例子:Jira 插件的灰度上线流程

假设团队开发了一个 Jira 集成插件 jira-sync(npm 包名 @yourteam/jira-sync),能让智能体查询和创建工单。安全的上线路径如下:

  1. 阶段一:白名单准入 ——只在 plugins.allow 中添加该插件,但在 tools.deny 中拒绝其写入工具,先只允许查询功能:

  1. 阶段二:小范围放开写入 ——在内部 Telegram 群中,通过 toolsBySender 只允许管理员触发创建工单:

  1. 阶段三:全量放开 ——在内部验证 2 周无异常后,移除 tools.deny 中的 Jira 写入限制,但保留审计日志与幂等键。

12.1.4 Hook 架构:插件的核心运行机制

插件的真正力量不在于“注册工具”,而在于 Hook(钩子)系统——插件通过在执行管线的关键节点注册钩子函数来拦截、修改或观测系统行为。源码(plugins/hooks.tsplugins/hook-runner-global.ts)定义了 25 种钩子类型,覆盖了智能体的完整生命周期。

钩子分类总览

类别
钩子名称
执行模式
典型用途

智能体生命周期

before_model_resolve

修改型

覆盖模型/供应商选择

before_prompt_build

修改型

注入上下文与系统提示词

llm_input / llm_output

观测型

审计 LLM 精确输入输出

agent_end

观测型

分析已完成会话

before_compaction / after_compaction

修改型

干预消息压缩过程

消息处理

message_received

观测型(即发即忘)

消息到达后的旁路处理

message_sending

修改型

修改或取消待发消息

message_sent

观测型

消息发送后的确认

工具调用

before_tool_call

修改型

拦截或修改工具参数

after_tool_call

观测型

观测工具执行结果

tool_result_persist

同步型

修改持久化的工具结果

子智能体

subagent_spawning

顺序型

配置线程绑定

subagent_delivery_target

顺序型

解析路由目标

Gateway

gateway_start / gateway_stop

观测型

服务器生命周期

四种执行模式

  • 观测型(Void Hooks):并行执行,不能修改状态,用于审计与旁路处理(如 llm_inputmessage_sent)。

  • 修改型(Modifying Hooks):按优先级顺序执行(高优先级先执行),每个钩子的修改结果流入下一个钩子,最终合并应用(如 before_tool_callmessage_sending)。

  • 顺序型:与修改型类似按优先级链式执行,但专用于子智能体路由场景——如 subagent_spawning(配置线程绑定)和 subagent_delivery_target(解析路由目标),每个钩子可以补充或覆盖路由决策。

  • 同步型:在热路径上同步执行,系统会检测并拒绝意外的异步处理器。包括 tool_result_persist(链式修改持久化消息)和 before_message_write(JSONL 写入前的同步阻断)。

全局 Hook Runner:Gateway 启动时创建单例 Hook Runner,所有执行路径通过它统一调度。单个插件的钩子崩溃会被捕获并记录日志,不会中断系统运行。

插件注册钩子的方式

结果合并策略因钩子类型而异:before_model_resolve 采用首个非空结果获胜;before_prompt_build 拼接上下文与系统提示词;before_tool_call 合并参数与阻断状态;message_sending 首个取消操作获胜。

渠道插件是一种特殊插件类型,通过 api.registerChannel() 注册,可提供 15+ 种适配器接口(配置、网关、出站、消息处理等)。渠道插件深度集成子智能体路由——通过 subagent_spawning 钩子配置每会话的线程绑定,通过 subagent_delivery_target 钩子解析消息路由目标。

插件加载的完整生命周期

  1. 发现:从 bundled、global、workspace、config 四个源扫描候选插件。

  2. 校验:读取 openclaw.plugin.json 清单,验证配置是否符合 JSON Schema。

  3. 安全加载:通过 jiti(TypeScript/JS 运行时编译器)加载入口文件,执行边界检查。

  4. 注册:同步调用插件的 register(api) 函数,插件通过 API 注册工具、钩子、渠道、服务、HTTP 路由等。

  5. 激活:所有插件注册完成后,构建 PluginRegistry,初始化全局 Hook Runner。

  6. 运行:在每个钩子点,系统通过 Hook Runner 调度已注册的处理器。

12.1.5 验收与排障:用插件诊断与状态深查做成完整流程

插件相关问题不应靠“对话试出来”。官方 CLI 提供了插件列表与诊断命令,可用于上线前验收与线上排障:https://docs.openclaw.ai/cli/pluginsarrow-up-right

建议把以下命令固定为插件变更后的最小验收集:

当插件加载失败时,优先看 plugins doctorstatus --deep 输出;当插件加载成功但行为异常时,再结合结构化日志按 traceId 回放单次链路。

最后更新于