资讯动态

OpenClaw 插件系统边界解析:manifest-first 架构、加载管线与契约执行

发布时间:2026/10/16 2:12:58 来源:尧图企业网站定制
人工智能大模型LLM 网关AI Agent即时通讯【免费下载链接】openclaw-zero-tokenOpenClaw: Use All Major AI Models NO API Token! Claude/ChatGPT/Gemini/DeepSeek/Doubao/Grok/Qwen/Manus/Kimi项目地址https://gitcode.com/gh_mirrors/op/openclaw-zero-token点击查看免费下载OpenClaw 将插件发现discovery、manifest 验证、插件加载、注册表组装registry assembly与契约执行contract enforcement整体收敛在src/plugins/边界目录内并以此为核心建立了「先元数据、后运行时」的插件架构。本文以 src/plugins/AGENTS.md 定义的边界规则为主线结合 docs/plugins/manifest.md、docs/tools/plugin.md 以及src/plugins/下的真实实现系统讲解 OpenClaw 插件系统的分层职责、manifest 契约、发现与加载管线、公开契约与边界约束帮助你理解如何编写、发布、调试插件以及核心维护者应当如何在不破坏契约的前提下演进插件系统。一、插件边界目录的职责划分src/plugins/是 OpenClaw 中插件能力的唯一归属方boundary owner它明确拥有以下五项职责职责说明插件发现discovery扫描配置路径、工作区、全局目录与内置bundled目录中的插件候选Manifest 验证manifest validation读取并校验openclaw.plugin.json不执行插件代码插件加载loading通过 jiti 运行时加载 TypeScript/JavaScript 插件模块注册表组装registry assembly将插件贡献的 tools、hooks、channels、providers、commands、services 等汇总为统一注册表契约执行contract enforcement保证内置插件与外部插件遵循同一套公开契约与测试该边界同时定义了它的公开契约Public Contracts即插件作者与核心维护者之间的事实约定文档面docs/plugins/architecture.md、docs/plugins/manifest.md、docs/plugins/sdk-overview.md、docs/plugins/sdk-entrypoints.md其中 manifest 规范与插件总览的当前版本见 docs/plugins/manifest.md 与 docs/tools/plugin.md定义面类型与契约集中在 src/plugins/types.ts、src/plugins/runtime/types.ts、src/plugins/contracts/registry.ts 与src/plugins/public-artifacts.ts。从源码结构看这套边界并非松散约定而是被 loader 的加载流程、manifest 的强制校验、以及contracts/下的契约测试如loader.contract.test.ts、registry.contract.test.ts、discovery.contract.test.ts具体落实为可执行的工程约束。二、manifest-first先校验元数据再执行插件代码OpenClaw 插件系统最核心的设计原则是manifest-firstmanifest 优先插件发现、配置校验与设置过程应当在插件运行时执行之前仅凭元数据即可完成。这一原则直接决定了插件的门槛形态每个插件必须在插件根目录plugin root下提供一份openclaw.plugin.json。缺失或无效的 manifest 会被视为插件错误并阻断配置校验——即使该插件在本地文件系统中加载也同样适用。这与「配置校验不执行插件代码」的安全模型互为表里manifest 只用于发现与校验插件模块仍由运行时另行加载。因此在配置阶段读取或写入配置时OpenClaw 永远不会因为插件本身的 bug 而崩溃这为 Doctor、配置修复等只读/离线流程提供了保障。2.1 manifest 的必需字段manifest.md 给出的最小可用 manifest 如下{ id: voice-call, configSchema: { type: object, additionalProperties: false, properties: {} } }必需键idstring插件的规范 idcanonical plugin idconfigSchemaobject插件配置的内联 JSON Schema。2.2 manifest 的可选字段字段类型说明kindstring插件种类如memory、context-enginechannelsarray该插件注册的 channel id 列表如[matrix]providersarray该插件注册的 provider id 列表skillsarray相对插件根目录的 skill 目录列表namestring插件显示名descriptionstring插件简介uiHintsobject配置字段的 label/placeholder/sensitive 标记供 UI 渲染versionstring插件版本仅信息性在源码实现 src/plugins/manifest.ts 中可发现 manifest 类型还支持更多进阶字段并会被严格归一化enabledByDefault是否默认启用legacyPluginIds应归一化到当前 id 的旧插件 idautoEnableWhenConfiguredProviders当配置中引用了这些 provider id 时自动启用该插件cliBackends插件拥有的 CLI 推理后端 id用于启动期廉价激活查找providerAuthEnvVarsprovider 相关的认证环境变量无需启动插件运行时即可查找providerAuthChoicesonboarding/auth-choice 路由元数据供配置校验、CLI 帮助与非运行时 auth-choice 路由使用contracts静态能力归属快照见下文「契约执行」一节channelConfigschannel 级配置 schema 与 UI 提示。2.3 JSON Schema 的强制要求每个插件都必须附带 JSON Schema即使它不接受任何配置空 schema 是合法的例如{ type: object, additionalProperties: false }Schema 在配置读写时校验而不是运行时——这意味着配置校验路径完全无插件代码参与。仓库中大量真实插件遵循这一形态。例如 bluebubbles/openclaw.plugin.json{ id: bluebubbles, channels: [bluebubbles], configSchema: { type: object, additionalProperties: false, properties: {} } }extensions/memory-core/openclaw.plugin.json 则展示了kind的用法{ id: memory-core, kind: memory, configSchema: { type: object, additionalProperties: false, properties: {} } }而 brave/openclaw.plugin.json 是进阶形态的范本它同时携带providerAuthEnvVars、uiHints与contracts并对webSearch.apiKey标记为sensitive与webSearch.mode枚举web/llm-context做了精细的 schema 约束{ id: brave, providerAuthEnvVars: { brave: [BRAVE_API_KEY] }, uiHints: { webSearch.apiKey: { label: Brave Search API Key, help: Brave Search API key (fallback: BRAVE_API_KEY env var)., sensitive: true, placeholder: BSA... }, webSearch.mode: { label: Brave Search Mode, help: Brave Search mode: web or llm-context. } }, contracts: { webSearchProviders: [brave] }, configSchema: { type: object, additionalProperties: false, properties: { webSearch: { type: object, additionalProperties: false, properties: { apiKey: { type: [string, object] }, mode: { type: string, enum: [web, llm-context] } } } } } }2.4 校验行为的严格规则manifest.md 明确了以下校验语义未知的channels.*键是错误除非该 channel id 已被某个插件 manifest 声明plugins.entries.id、plugins.allow、plugins.deny、plugins.slots.*必须引用可发现的插件 id未知 id 一律视为错误已安装但 manifest 或 schema 损坏/缺失的插件会令校验失败并由 Doctor 上报插件错误插件已配置但被禁用时配置会被保留并在 Doctor 与日志中给出警告而非错误。2.5 源码级的 manifest 加载校验在 src/plugins/manifest.ts 的loadPluginManifest中可以看到这些规则的具体落点通过resolvePluginManifestPath在插件根目录定位openclaw.plugin.json文件打开走openBoundaryFileSync边界检查rejectHardlinks默认拒绝硬链接路径逃逸会返回unsafe plugin manifest path错误JSON 解析失败返回failed to parse plugin manifestid缺失返回plugin manifest requires idconfigSchema非对象返回plugin manifest requires configSchema字符串列表字段channels、providers、skills、contracts 等统一经过normalizeStringList去空白过滤。三、插件发现管线四层扫描与优先级docs/tools/plugin.md 描述了插件的发现顺序src/plugins/discovery.ts则提供了实现支撑。OpenClaw 按以下顺序扫描候选配置路径Config pathsplugins.load.paths文件或目录工作区扩展Workspace extensionsworkspace/.openclaw/extensions/*.ts与workspace/.openclaw/extensions/*/index.ts全局扩展Global extensions~/.openclaw/extensions/*.ts与~/.openclaw/extensions/*/index.ts内置扩展Bundled extensions随 OpenClaw 发布的openclaw/extensions/*多数默认禁用。多个候选解析到同一插件 id 时按上述顺序靠前者胜出低优先级副本被忽略并被标记为overridden by origin plugin。在 src/plugins/loader.ts 中resolveCandidateDuplicateRank给出了精确的优先级config0 显式 install 的global1bundled2workspace3 其他4。3.1 发现阶段的缓存与安全防线缓存discovery.ts维护一个短窗口内存缓存默认 1000ms可用OPENCLAW_PLUGIN_DISCOVERY_CACHE_MS调整OPENCLAW_DISABLE_PLUGIN_DISCOVERY_CACHE1可整体关闭用于吸收启动/重载期的突发扫描路径安全候选路径在进入发现之前就经过安全校验以下情况会被拦截扩展入口解析后逃出插件根目录含符号链接/路径穿越插件根目录或源码路径是全局可写world-writable非内置插件的路径属主可疑POSIX 属主既不是当前 uid 也不是 root来源追踪provenance未命中 install/load-path 来源追踪的非内置插件会发出警告提示通过plugins.allow固定信任或通过plugins.installs记录安装来源。3.2 package pack 形式的扩展插件目录还可以携带package.json通过openclaw.extensions声明多个扩展入口{ name: my-pack, openclaw: { extensions: [./src/safety.ts, ./src/tools.ts] } }每个入口都会成为独立插件若列出多个扩展插件 id 变为name/fileBase。安全护栏每个openclaw.extensions入口在符号链接解析后必须留在插件目录内越界即被拒绝。四、加载器与注册表组装从候选到可运行插件src/plugins/loader.ts是整条管线的编排者loadOpenClawPlugins它将发现结果、manifest 注册表与插件运行时组装为最终的PluginRegistry。关键环节包括1. 启用状态解析。每个候选经resolveEffectiveEnableState综合enabledByDefault、plugins.allow、plugins.deny、plugins.entries.id.enabled与候选来源计算启用与否。未启用的插件仍会以disabled状态进入注册表并携带禁用原因保证配置诊断可见。2. 模块加载。通过 jiti 在运行时加载 TS/JS 入口index.ts/index.js/index.mjs/index.cjs均为默认候选。加载器针对源码图与构建产物分别决定是否使用原生加载tryNative并对每个插件根目录做边界文件打开校验非 bundled 插件拒绝硬链接。3. 导出形态归一。resolvePluginModuleExport兼容两种导出函数形式(api) { ... }对象形式{ id, name, configSchema, register(api) { ... } }也接受activate作为register别名。若导出对象声明了id且与 manifest id 不一致加载器会报plugin id mismatch错误若 manifest 的kind与导出的kind不一致则给出警告。4. 配置校验前置。模块加载后、register调用前先以 manifest 中的configSchema校验plugins.entries.id.configvalidateJsonSchemaValue含applyDefaults校验失败即pushPluginLoadError(invalid config: ...)插件不会进入激活状态。5. 延迟运行时lazy runtime。加载器通过 Proxy 延迟实例化插件运行时只有真正访问运行时能力如version、config、channel、events等时才创建避免启动路径上急切加载所有 channel/runtime 依赖树。6. 注册表缓存。以workspace/global/stock 根、插件配置、installs、loadPaths、作用域等为键缓存 registry上限 128 条并保持 LRU 刷新频繁复用的 registry 免于逐出。4.1 插件的可注册能力完成注册的插件可以向api注册对应 docs/tools/plugin.md 的清单Gateway RPC 方法api.registerGatewayMethod命名pluginId.actionGateway HTTP 路由api.registerHttpRoute必须显式声明auth: gateway | pluginmatch支持exact/prefixAgent 工具api.registerToolCLI 命令api.registerCli背景服务api.registerServiceContext engineapi.registerContextEngine自动回复命令api.registerCommand无需调用 AI agent 即可执行命令处理先于内置命令与 agentProvider 认证流程api.registerProvider含 OAuth/API key/device code 等方法消息 channelapi.registerChannel({ plugin })运行时钩子api.registerHook与类型化的api.on。五、契约执行bundled registry 上的能力归属校验「契约执行」是src/plugins/边界的一大特色。插件 manifest 可以通过contracts字段声明其静态能力归属type PluginManifestContracts { speechProviders?: string[]; mediaUnderstandingProviders?: string[]; imageGenerationProviders?: string[]; webSearchProviders?: string[]; tools?: string[]; };这些声明与 src/plugins/contracts/registry.ts 中的契约测试联动。契约 registry 基于BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS与BUNDLED_*_PLUGIN_IDS等生成式元数据验证插件声明的 provider id、tool 名与 CLI backend 是否与运行时注册一致。配套的契约测试包括loader.contract.test.ts、registry.contract.test.ts、discovery.contract.test.ts、shape.contract.test.ts等覆盖发现、加载、注册表与能力形状。边界规则特别强调契约加载与契约测试必须停留在专用 bundled registry 路径上不得让契约验证依赖通过无关生产解析流程激活 providers。这样契约测试始终可复现、可离线运行不会因某个 provider 的认证流程失败而阻塞。六、边界规则逐条拆解src/plugins/AGENTS.md 给出的六条边界规则是核心维护者与插件作者都必须遵守的行为约束6.1 保持 manifest-first 行为发现、配置校验与设置应在插件运行时执行之前仅凭元数据完成。这要求任何新能力在引入时都要考虑能否先在 manifest 层面表达能否不执行插件代码就完成验证从 loader.ts 的流程可以看到配置校验validatePluginConfig严格发生在register(api)之前正是这一原则的实现证据。6.2 不创建私有后门Loader 行为必须与文档化的 Plugin SDK 与 manifest 契约保持一致不得为 bundled 插件创建外部插件无法使用的私有后门。换言之内置插件与第三方插件走同一套api表面、同一套 manifest 校验、同一套边界文件检查内置插件仅豁免硬链接拒绝这一项安全差异。6.3 影响插件作者时更新公开契约如果 loader 或 registry 的改动会影响插件作者正确做法是同步更新公开 SDK、文档与契约测试而不是依赖偶然的内部实现细节。这也解释了为什么src/plugins/边界文档明确列出「公开契约」清单——它们是演进时的护栏。6.4 不将 plugin-owned 规范化为 core-owned禁止通过在不相关的核心路径中散落直接读取plugins.entries.id.config来把「插件拥有的」配置变成「核心拥有的」。正确姿势是通用辅助函数generic helpers、插件运行时钩子、manifest 元数据以及显式的 auto-enable 接线例如 manifest 中的autoEnableWhenConfiguredProviders。这保证了插件配置的读写面收敛、可审计核心代码不会悄悄依赖某个插件的私有字段。6.5 保持契约通用并尊重禁用与 SecretRef当插件拥有的工具或 provider fallback 需要核心参与时契约必须保持通用并且尊重插件禁用状态与 SecretRef 语义插件被禁用后其工具/回退不应生效配置中的 SecretRef而非明文密钥在核心路径上同样要被正确解析。这与provider-auth-ref.ts、provider-auth-storage.ts等认证抽象的设计目标一致——核心只消费通用凭据语义不关心具体 provider 的存储细节。6.6 契约测试留在 bundled registry 路径契约加载与契约测试固定在专用 bundled registry 路径src/plugins/contracts/registry.ts上不得让契约验证依赖激活 providers 的无关生产解析流程。该约束保证了测试确定性也防止契约校验被外部环境网络、认证状态干扰。七、插件配置与 CLI 实操7.1 配置示例{ plugins: { enabled: true, allow: [voice-call], deny: [untrusted-plugin], load: { paths: [~/Projects/oss/voice-call-extension] }, entries: { voice-call: { enabled: true, config: { provider: twilio } }, }, }, }字段语义enabled总开关默认trueallow白名单可选deny黑名单可选deny 优先load.paths额外插件文件/目录slots独占槽位选择器如memory、contextEngineentries.id单插件开关与配置。配置变更需要重启 Gateway才能生效。7.2 独占槽位slots某些插件类别是独占的同一时刻只激活一个{ plugins: { slots: { memory: memory-core, // 或 none 禁用 memory 插件 contextEngine: legacy, // 或某个插件 id如 lossless-claw }, }, }memory激活的 memory 插件none表示禁用所有 memory 插件默认槽位插件是memory-corecontextEngine激活的 context engine 插件legacy为内置默认。若多个插件声明kind: memory或kind: context-engine只有被槽位选中的插件会被加载其余以诊断信息标记为禁用。memory 插件还会在 loader 中走「快速路径」当槽位策略保证其被禁用时直接跳过导入重型插件模块。7.3 CLI 命令openclaw plugins list openclaw plugins info id openclaw plugins install path # 复制本地文件/目录到 ~/.openclaw/extensions/id openclaw plugins install ./extensions/voice-call openclaw plugins install ./plugin.tgz openclaw plugins install ./plugin.zip openclaw plugins install -l ./extensions/voice-call # 开发用软链不复制 openclaw plugins install openclaw/voice-call # 从 npm 安装 openclaw plugins install openclaw/voice-call --pin # 记录精确 nameversion openclaw plugins update id openclaw plugins update --all openclaw plugins enable id openclaw plugins disable id openclaw plugins doctor要点npm spec仅支持 registry 包包名 可选精确版本或 dist-tagGit/URL/文件 spec 与 semver 区间会被拒绝裸 spec 与latest停留在稳定轨道若 npm 解析出预发布版本OpenClaw 会停下并要求显式选择beta/rc或精确预发布版本plugins install以npm install --ignore-scripts安装依赖不执行生命周期脚本因此插件依赖树应保持「纯 JS/TS」避免需要postinstall构建的包plugins update只对记录在plugins.installs下的 npm 安装生效完整性元数据变化时会警告并要求确认可用全局--yes跳过原生模块依赖需要文档化构建步骤与包管理器白名单要求例如 pnpm 的allow-build-scripts与pnpm rebuild package。八、插件工具与运行时钩子的边界示例8.1 注册 Agent 工具agent-tools.md 给出了两种形态。基础工具import { Type } from sinclair/typebox; export default function (api) { api.registerTool({ name: my_tool, description: Do a thing, parameters: Type.Object({ input: Type.String(), }), async execute(_id, params) { return { content: [{ type: text, text: params.input }] }; }, }); }可选工具opt-in永远不会被自动启用必须加入 allowlistexport default function (api) { api.registerTool( { name: workflow_tool, description: Run a local workflow, parameters: { type: object, properties: { pipeline: { type: string } }, required: [pipeline], }, async execute(_id, params) { return { content: [{ type: text, text: params.pipeline }] }; }, }, { optional: true }, ); }在agents.list[].tools.allow或全局tools.allow中启用{ agents: { list: [ { id: main, tools: { allow: [ workflow_tool, // 具体工具名 workflow, // 插件 id启用该插件全部工具 group:plugins, // 所有插件工具 ], }, }, ], }, }规则提示工具名不得与核心工具重名冲突工具会被跳过插件 id 用于 allowlist 时也不得与核心工具名冲突会触发副作用或依赖额外二进制/凭据的工具优先标记为optional: true。8.2 注册运行时钩子export default function register(api) { api.on( before_prompt_build, (event, ctx) { return { prependSystemContext: Follow company style guide. }; }, { priority: 10 }, ); }提示词构建相关的关键钩子before_model_resolve会话加载前可确定性覆盖modelOverride/providerOverride、before_prompt_build会话加载后塑造提示词输入、before_agent_start兼容旧钩子。核心还允许运维按插件关闭提示词篡改类钩子plugins.entries.id.hooks.allowPromptInjection: false。九、安全模型与发布注意插件与 Gateway同进程运行属于可信代码只安装信任的插件优先使用plugins.allow白名单plugins.allow为空且存在可发现的非内置插件时启动会打印包含插件 id 与来源的警告未带安装/加载路径来源追踪的已加载非内置插件会触发警告用于固定信任plugins.allow或安装追踪plugins.installs发布到 npm 的插件包package.json必须包含openclaw.extensions入口入口可为.js或.tsjiti 运行时加载 TSscoped 包在plugins.entries.*中会归一化为unscopedid独立发布的插件应自带 CIlint/build/test并保证openclaw.extensions指向构建产物入口如dist/index.js。十、总结OpenClaw 的插件边界不是一张功能清单而是一套可执行的架构契约manifest-first 保证配置校验与插件代码解耦统一的发现/加载管线保证内置与外部插件行为一致bundled registry 契约测试保证能力归属可验证六条边界规则约束核心演进不侵蚀插件所有权。无论是编写一个openclaw.plugin.json的最小插件还是为 OpenClaw 增加新的插件能力以上约定都值得作为第一份参考资料——它们同时定义了「插件如何工作」与「插件系统如何被安全地扩展」。赞分享人工智能大模型LLM 网关AI Agent即时通讯【免费下载链接】openclaw-zero-tokenOpenClaw: Use All Major AI Models NO API Token! Claude/ChatGPT/Gemini/DeepSeek/Doubao/Grok/Qwen/Manus/Kimi项目地址https://gitcode.com/gh_mirrors/op/openclaw-zero-token点击查看免费下载相关推荐OpenClaw 插件系统边界设计plugins/ 目录的 Manifest-First 加载与契约强制执行机制OpenClaw 插件系统边界设计plugins/ 目录的 Manifest First 加载与契约强制执行机制 本篇以 plugins/CLAUDE.md人工智能大模型LLM 网关AI Agent即时通讯大麦自动抢票ticket-purchase快速指南4个环节完成环境安装与首次运行大麦自动抢票ticket purchase快速指南4个环节完成环境安装与首次运行 晚上8点手指悬在空格键上页面一刷新状态栏变成已售罄。ticketRPAOpenClaw 插件系统边界剖析从 Manifest 校验到契约强制的一体化设计OpenClaw 插件系统边界剖析从 Manifest 校验到契约强制的一体化设计 导读 OpenClaw 的插件体系不是简单的脚本目录 注册函数人工智能大模型LLM 网关AI Agent即时通讯上一篇Bootstrap-rtl的未来为什么它仍然是Bootstrap 3.x项目的首选RTL解决方案下一篇VRCT跨语言交流解决方案打破VRChat虚拟世界的语言隔阂创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑