news 2026/9/13 17:19:32

OpenClaw 插件 SDK 边界指南:从契约、入口到演进规范

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenClaw 插件 SDK 边界指南:从契约、入口到演进规范

OpenClaw 插件 SDK 边界指南:从契约、入口到演进规范

【免费下载链接】openclawThe AI that really does things. Any OS. Any Platform. The lobster way. 🦞项目地址: https://gitcode.com/GitHub_Trending/cl/openclaw

OpenClaw 的插件 SDK(Plugin SDK)是插件与核心(core)之间唯一的公共契约层,承载着内置插件与第三方插件的全部能力注册与运行时交互。本文基于仓库中 src/plugin-sdk/CLAUDE.md 这一边界契约文档展开,结合 docs/plugins 下的 SDK 文档与 src/plugin-sdk 的源码实现,系统讲解边界规则的由来、版本化能力契约、验证手段以及如何安全地扩展 SDK 表面。读完本文,你将掌握 OpenClaw 插件 SDK 的边界设计原则、入口辅助函数(definePluginEntry/defineChannelPluginEntry/defineSingleProviderPluginEntry)的注册语义,以及为仓库新增公共子路径时必须对齐的文件清单。

一、什么是 Plugin SDK 边界

src/plugin-sdk/目录在仓库中承担一个特殊角色:它是插件与核心之间的公共契约(public contract)。任何对这里的改动都可能同时影响内置插件(bundled plugins)和第三方插件(third-party plugins),因此仓库用一份专门的 CLAUDE.md 规定改动前的边界规则、必需能力、验证步骤与扩展流程。

边界契约的核心立场可以概括为一句话:宿主(host,即 OpenClaw 核心)加载插件,插件不应穿透 SDK 去抓取任意的宿主内部实现。SDK 应当是一层受控的、窄而文档化的接缝,而不是一个把所有内部工具都倒出来的"便利桶"。

二、边界的事实来源(Source Of Truth)

要修改或扩展 SDK,必须清楚哪些文件是权威事实来源。根据 src/plugin-sdk/CLAUDE.md,SDK 边界的权威定义分散在两类文件中:

文档侧(docs)

  • docs/plugins/sdk-overview.md —— 导入映射、注册 API 参考与 SDK 架构
  • docs/plugins/sdk-entrypoints.md —— 入口辅助函数与注册模式
  • docs/plugins/sdk-runtime.md ——api.runtime运行时助手
  • docs/plugins/sdk-migration.md —— 从已废弃表面迁移
  • docs/plugins/architecture.md —— 插件内部架构与能力模型

定义文件(definition files)

  • package.json—— 包导出(exports)映射
  • scripts/lib/plugin-sdk-entrypoints.json —— 全部 SDK 子路径清单(共 350+ 个入口)
  • scripts/lib/plugin-sdk-entries.mts —— 从清单派生出 public / private / production / deprecated 等入口集合
  • src/plugin-sdk/api-baseline.ts —— API 基线(baseline)渲染,用于契约漂移报告
  • src/plugin-sdk/plugin-entry.ts —— 非渠道插件的规范入口
  • src/plugin-sdk/core.ts —— 渠道插件入口与聊天渠道组合器
  • src/plugin-sdk/provider-entry.ts —— 单 Provider 插件入口

可以看到,一套 SDK 公共表面同时由文档、入口清单、包导出和基线校验四类文件锁定,任何一边单独改动都会造成漂移。这正是后文"扩展边界"要强调多文件对齐的原因。

三、边界规则详解

src/plugin-sdk/CLAUDE.md 用十余条规则定义了 SDK 表面应有的形状。下面按主题分组解读,并给出源码层面的印证。

3.1 窄入口优先,拒绝便利桶

  • 宿主加载插件,插件不得反向抓取宿主内部。SDK 应提供"小的、带版本号的宿主/内核接缝 + 窄而文档化的入口点",而不是宽泛的 barrel 再导出。
  • 不要从src/channels/**src/agents/**src/plugins/**或其他内部模块暴露实现便利,除非是有意将其提升为受支持的公共契约。

这条规则在 src/plugin-sdk/plugin-sdk-entries 相关脚本 中有具体落地:入口被分为publicPluginSdkEntrypoints(公开、带类型与文档)与privateLocalOnlyPluginSdkEntrypoints(仅本地使用、不进公共类型面),从机制上防止内部模块被误导出。

3.2 公共入口在模块加载时必须廉价

Keep public SDK entrypoints cheap at module load.

如果某个助手只会在异步路径(如 send、monitor、probe、directory-live、login、setup)上用到,就应该放在窄的*.runtime子路径里,而不是通过一个宽泛的 SDK barrel 再导出——因为热渠道入口(hot channel entrypoints)在启动时会 import 这些 barrel,过重的加载会影响启动成本。

仓库里大量*-runtime.ts文件正是这一原则的产物:例如 src/plugin-sdk/blob-runtime.ts、src/plugin-sdk/fetch-runtime.ts、src/plugin-sdk/reply-runtime.ts 等。它们只承载运行时实现,注册入口保持轻量。在 src/plugin-sdk/provider-entry.ts 中可以看到具体手法——createLazyRuntimeModule/createLazyRuntimeMethod把 live catalog 等重模块延迟到运行时钩子触发时才加载:

// src/plugin-sdk/provider-entry.ts const liveCatalogRuntime = createLazyRuntimeModule( () => import("./provider-catalog-live-runtime.js"), ); const buildOpenAICompatibleProviderCatalog = createLazyRuntimeMethod( liveCatalogRuntime, (runtime) => runtime.buildOpenAICompatibleProviderCatalog, );

3.3 保持 SDK 门面无环

  • 不要添加把轻量契约文件重新路由回更重的 policy/runtime 模块的反向再导出(back-edge re-exports)。
  • 不要在塑造 SDK 接缝时混用同一运行时表面的静态与动态导入:如果某个表面必须保持懒加载,就把急切侧放在轻量契约文件上,把延迟侧放在专门的 runtime 子路径上。
  • 当核心或测试需要内置插件助手时,优先使用插件包自身的api.tsruntime-api.ts,加上通用的 SDK 能力;不要为了核心感知某个内置渠道的私有助手,而新增一个以 provider 命名的src/plugin-sdk/<id>.ts接缝。

3.4 Provider 工作优先家族级接缝

  • 共享助手应该描述可复用行为,例如重放策略(replay policy)、工具 schema 兼容(tool-schema compat)、载荷归一化(payload normalization)、流包装组合(stream-wrapper composition)、传输装饰(transport decoration)。
  • 避免新增只包装某一家 provider 本地实现的 SDK 导出,除非已经存在第二个消费者。
  • 当 options 编码的是稳定契约时,优先命名助手而非原始 options 对象。文档中的例子是:导出 "OpenAI 风格的 Anthropic 工具载荷兼容" 助手,而不是让每个插件都传同样的 mode 标志。
  • 保持传输/运行时策略与插件面向的助手对齐:如果同一行为既出现在插件注册路径又出现在核心运行时路径,就暴露一个共享助手,避免两条路径各自漂移。
  • SDK 子路径应帮助调用方一次解决一个能力或运行时需求,不要长出要求宽泛运行时注册表访问的新表面作为默认路径。
  • 如果某个提议的 SDK 导出主要是为了让 setup/config/control-plane 代码执行插件运行时,这通常是边界异味(boundary smell)——应优先采用元数据或描述符驱动的 control-plane 接缝。

四、版本化必需能力(Versioned Required Capabilities)

边界文档用"Always / Never / Ask first"三档约束能力契约的演进,这是 SDK 安全性的核心部分:

等级规则含义
Always已发布的 Plugin SDK 参数契约一旦获得必需的宿主权限,就必须引入需要该权限的版本化类型旧类型在文档化的弃用窗口内保持源码兼容,并在同一改动中迁移所有内置/内部调用方
Always宿主能力必须保持通用且闭包绑定(closure-bound)每个暴露的 tool、preparer、callback、审批操作与 native-action 表面都要绑定;所有权或能力关闭后(包括 await 策略工作期间的关闭),保留的副本必须失效
Never不要把旧的可选性当作无能力的运行时路径禁止在插件内部重建宿主权限,也禁止给通用契约附加 provider 专属权限
Never不要手工编辑生成的 SDK 基线、声明、哈希或预算必须通过规范流程重新生成
Ask first缩短兼容窗口、让已发布类型源码不兼容、或扩大某项能力的信任/权限/持久化边界必须先征得 SDK 与安全负责人的认可

最后一条"Ask first"尤其值得注意:能力的信任、权限或持久化边界(trust, authority, persistence boundary)一旦扩大,等于改变了整个插件生态的安全假设,属于需要审批的变更,而不是顺手就能做的清理。

五、验证(Verification)

改动 SDK 后,仓库要求按影响范围执行验证:

  1. 涉及懒加载、热渠道入口或内置插件导入拓扑的改动,运行pnpm build
  2. 可能改变内置渠道启动成本的改动,还要对受影响的插件运行隔离入口点分析器:
OPENCLAW_LOCAL_CHECK=0 node --import tsx scripts/profile-extension-memory.mts --extension <id> --skip-combined --concurrency 1

这条命令来自 scripts/profile-extension-memory.mts,用于度量单个扩展入口的内存/加载画像,是"入口必须廉价"这条规则的量化保障。

六、如何扩展边界(Expanding The Boundary)

6.1 不因便利而扩张

  • SDK 表面已经太大:不要为了便利添加兼容 barrel、别名或 fallback 导出。旧入口点应当被替换而不是叠加。
  • 公共第三方 API 是唯一的兼容例外:文档化/版本化破坏性变更,先迁移全部内置/内部插件,再激进地弃用未使用的导出。

6.2 新增或修改公共子路径时的对齐清单

当添加或修改一个公共子路径时,必须保持以下四处对齐:

  • docs/plugins 下的 SDK 文档;
  • scripts/lib/plugin-sdk-entrypoints.json;
  • scripts/lib/plugin-sdk-entries.mts;
  • package.json的 exports 字段;
  • API diff 与导出检查(src/plugin-sdk/api-diff.ts)。

入口清单与导出映射的联动关系可以在 scripts/lib/plugin-sdk-entries.mts 中看到:buildPluginSdkPackageExports()遍历全部入口,公开入口生成./plugin-sdk/<entry>的 types + default 双导出,打包私有运行时入口只生成 default 导出,其余一律不出现在包导出中。

6.3 跨包边界的判断顺序

如果内置渠道/助手的某个需求要跨包边界,先问:这个需求是否真正通用?

  • 是 → 添加一个窄的通用子路径;
  • 否 → 通过插件本地的api.ts/runtime-api.ts保持插件本地化。

6.4 扩展 Provider 接缝时的测试锁定

扩展 provider 面向的接缝时,必须新增或更新匹配的窄测试来锁定契约:

  • Plugin SDK 的 diff/export 检查(针对公共子路径);
  • 针对被集中化的行为,编写最直接的 provider/plugin 测试。

6.5 破坏性变更的版本纪律

破坏性的删除或重命名属于 major 版本工作,而不是顺手清理(drive-by cleanup)。这要求任何移除导出都必须走完整的弃用窗口、内部迁移与版本号提升流程。

七、从源码看边界的落地:三类入口辅助函数

SDK 边界最终通过一组"入口辅助函数"暴露给插件作者。它们把"插件应该怎么声明自己"规范化,避免每个插件各自发明注册形状。

7.1definePluginEntry—— 非渠道插件入口

定义于 src/plugin-sdk/plugin-entry.ts,适用于 provider、tool、command、service、memory、context-engine 插件。其选项结构为:

选项类型说明
idstring插件 id
namestring显示名称
descriptionstring描述
kindOpenClawPluginDefinition["kind"]已弃用:专属插件类型请改在openclaw.plugin.json的 manifestkind中声明,运行时kind仅作为旧插件兼容回退
configSchemaOpenClawPluginConfigSchema \| (() => ...)配置 schema,支持懒工厂;默认emptyPluginConfigSchema
reloadOpenClawPluginDefinition["reload"]重载注册
nodeHostCommandsOpenClawPluginDefinition["nodeHostCommands"]节点宿主命令
securityAuditCollectors...安全审计收集器
register(api: OpenClawPluginApi) => void必填,核心注册回调

实现上,configSchema通过createCachedLazyValueGetter懒求值,其余字段只在提供时才透传——这保证了入口对象本身的加载是廉价的。

7.2defineChannelPluginEntry—— 渠道插件入口

定义于 src/plugin-sdk/core.ts,除注册渠道能力外,还按api.registrationMode分派不同的注册行为,这是理解边界的关键。从 core.ts 的实现 可以看到实际分派逻辑:

  • cli-metadata模式:只调用registerCliMetadata?.(api),用于 CLI 解析期元数据(对应 docs/plugins/architecture.md 中"parse-time metadata 来自registerCli(..., { descriptors }),真实 CLI 模块保持懒加载"的设计);
  • tool-discovery模式:调用registerFull+registerCapabilities
  • 其他非 full 模式:先api.registerChannel({ plugin })setRuntime,若为discovery模式再补registerCliMetadata+registerCapabilities
  • full模式:registerCliMetadata+registerFull+registerCapabilities全部执行。

7.3defineSingleProviderPluginEntry—— 单 Provider 插件入口

定义于 src/plugin-sdk/provider-entry.ts,面向运行时恰好导出一个主模型 provider 的插件。它替你规范化了:

  • API-key 认证:从provider.auth或 manifest 的providerAuthChoices派生认证方法(createProviderApiKeyAuthMethod),并生成 setup wizard 元数据;
  • 模型目录:支持buildProvider静态目录、liveModelDiscovery(从 OpenAI 兼容的 model-list 端点实时发现)、或完全自定义的run目录实现;
  • 统一文本目录投影:通过projectProviderCatalogResultToUnifiedTextRows把目录结果投影为统一模型目录条目,并自动注册api.registerModelCatalogProvider

在注册时,如果 provider 既没有run目录、也没有buildProvider、manifest 中也没有对应modelCatalog.providers.<id>,会直接抛出Missing modelCatalog.providers.${providerId}错误——入口辅助函数在边界上做静态完整性校验,而不是把错误留到运行时。

7.4 注册模式与插件形态

根据 docs/plugins/sdk-overview.md,api.registrationMode还包含轻量的"setup-runtime"(setup 流程,runtime 可用);而 docs/plugins/architecture.md 强调api.registrationMode === "full"才是插件模块的完整运行时注册路径。

每个已加载插件都会按实际注册行为归类为一种形态,可用openclaw plugins inspect <id>查看:

形态说明示例
plain-capability只注册一种能力类型provider-only 的arceechutes
hybrid-capability注册多种能力类型openai同时拥有文本推理、语音、媒体理解、图像生成
hook-only只注册 hooks,无能力/工具/命令/服务受支持的路径,但尚未迁移到能力注册
non-capability有工具/命令/服务/路由,但没有能力

形态分类的意义在于:它是openclaw doctoropenclaw plugins inspect <id>openclaw status --allopenclaw plugins doctor输出的兼容性信号(config valid/hook-only提示 / 弃用的 memory-embedding API 警告 / 硬错误)的基础。

八、能力模型:插件是所有权边界,能力是核心契约

docs/plugins/architecture.md 给出了边界之上的能力模型,这是理解"该把代码放哪"的思维框架:

  • 插件(plugin)= 所有权边界:一个厂商插件通常应拥有该厂商在 OpenClaw 上的全部表面(文本、语音、图像、视频、web 搜索等),而不是一堆互不相关的集成。
  • 能力(capability)= 核心契约:多个插件可以实现或消费它。例如 TTS 由elevenlabsgooglemicrosoftopenai各自实现合成,voice-call消费共享的运行时助手,而核心只拥有回复期 TTS 策略、回退顺序与渠道投递。

能力注册方法覆盖推理、CLI 后端、嵌入、语音、实时转录、实时语音、媒体理解、字幕源、图像/音乐/视频生成、web fetch、web search、渠道、网关发现、迁移等十余类(完整表格见 docs/plugins/architecture.md 的 "Public capability model" 一节)。新增领域时的正确顺序是:先在核心定义能力契约 → 通过 SDK 类型化暴露 → 接线渠道/特性消费者 → 让厂商插件注册实现

九、API 稳定性与兼容策略

docs/plugins/sdk-overview.md 明确:所有 OpenClaw 插件 API 均为实验性(experimental),包括每个openclaw/plugin-sdk/*子路径、注册与运行时 API、渠道与 provider 契约、hooks 以及原生 Control UI API。它们可能在版本间变化。

因此插件作者需要:

  • 固定开发与部署所用的 OpenClaw 版本,并对声明的每个宿主版本进行测试;
  • 从测试过的版本出发设置包兼容范围,不要假设能跑的构建一定兼容未来版本;
  • 实验性状态并不免除文档化的迁移路径——docs/plugins/sdk-migration.md 中的迁移路径依然适用。

SDK 表面由 src/plugin-sdk/api-baseline.ts 渲染成 API 基线:它用 TypeScript 编译程序扫描每个公开入口的导出符号、声明文本与闭包哈希,供契约漂移报告使用——这正是"Never: hand-edit generated SDK baselines"的机器侧保障。

十、实践清单:为 OpenClaw 扩展或维护插件 SDK

综合全文,参与 SDK 相关工作时应按以下清单自查:

  1. 改动是否影响内置或第三方插件?是 → 遵循 src/plugin-sdk/CLAUDE.md 的边界规则。
  2. 新增导出是否真正通用?否 → 留在插件本地(api.ts/runtime-api.ts);是 → 添加窄子路径,不做 barrel。
  3. 模块加载是否廉价?只走异步路径的助手 → 放*.runtime子路径;重模块 →createLazyRuntimeModule/ 动态import()
  4. 门面是否有环?禁止轻量契约文件回路由到重模块的反向再导出。
  5. 能力是否闭包绑定?每个 tool/preparer/callback/审批/native-action 表面必须随 owner 或能力关闭而失效。
  6. 需要新权限?发布过的参数契约必须版本化,并在同一改动迁移全部内部调用方。
  7. 改动了公共子路径?对齐 docs/plugins 文档、scripts/lib/plugin-sdk-entrypoints.json、scripts/lib/plugin-sdk-entries.mts、package.jsonexports、API diff 检查。
  8. 运行验证。涉懒加载/热入口 →pnpm build;涉启动成本 → 运行 scripts/profile-extension-memory.mts 的隔离入口分析器。
  9. 是破坏性变更?这是 major 版本工作,走弃用窗口 + 内部迁移,而不是顺手删除。
  10. 生成了基线/声明/哈希?重新生成,绝不手工编辑。

这套规则最终要回答的问题只有一个:哪些表面是插件可以依赖的契约,哪些只是实现细节。边界清晰,核心与插件才能长期并行演进——这正是 OpenClaw 插件体系在拥有 350+ SDK 子路径和数十个内置插件的情况下仍能保持契约一致性的根本原因。

【免费下载链接】openclawThe AI that really does things. Any OS. Any Platform. The lobster way. 🦞项目地址: https://gitcode.com/GitHub_Trending/cl/openclaw

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 17:14:50

车规级CAN容错设计:超时、丢包、抖动的量化建模与工程实践

1. 项目概述&#xff1a;这不是Bug&#xff0c;是车规级系统在“呼吸” 你有没有遇到过这样的场景&#xff1a;整车下线测试时&#xff0c;CAN总线上某条报文偶尔超时10ms&#xff0c;诊断仪却显示“无故障码”&#xff1b;台架标定过程中&#xff0c;CANape读取MF4日志发现某E…

作者头像 李华
网站建设 2026/9/13 17:11:41

3条命令跑通COLMAP MVS稠密重建,附参数速查与空洞修复指南

3条命令跑通COLMAP MVS稠密重建&#xff0c;附参数速查与空洞修复指南 【免费下载链接】colmap COLMAP - Structure-from-Motion and Multi-View Stereo 项目地址: https://gitcode.com/GitHub_Trending/co/colmap 你拍了80张雕塑的照片&#xff0c;想把它们变成能3D打印…

作者头像 李华
网站建设 2026/9/13 17:10:35

2026嵌入式工程师的四大硬核能力:C语言、单片机、RTOS、Linux

1. “实话难听”不是态度问题&#xff0c;是嵌入式工程师的生存阈值“实话难听”这四个字&#xff0c;放在2026年谈嵌入式入行&#xff0c;已经不是一句情绪化吐槽&#xff0c;而是一道硬性准入门槛的刻度线。我带过37个应届生做STM32项目&#xff0c;其中21个在第三周主动退出…

作者头像 李华