Appearance
本页是 OpenClaw 插件系统的深度架构参考。覆盖能力注册模型(文本推理、语音、媒体理解、图像/视频生成等)、插件所有权边界、合约执行机制、加载管道(发现→启用→加载→注册→消费)、以及 Provider 运行时 Hook 完整顺序表。
OpenClaw 插件内部架构
这是深度架构参考。实践指南请见:插件安装与使用 · 插件开发入门 · Channel 插件 · Provider 插件
公共能力模型
每个原生 OpenClaw 插件针对一种或多种能力类型注册:
| 能力 | 注册方法 | 示例插件 |
|---|---|---|
| 文本推理 | api.registerProvider(...) | openai, anthropic |
| 语音 | 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 |
插件形态
OpenClaw 根据实际注册行为(非静态元数据)将插件分为四种形态:
- plain-capability — 注册恰好一种能力类型(如仅 Provider 的
mistral) - hybrid-capability — 注册多种能力类型(如
openai拥有文本推理、语音、媒体理解和图像生成) - hook-only — 只注册 Hook,无能力/工具/命令/服务
- non-capability — 注册工具、命令、服务或路由,但无能力
运行 openclaw plugins inspect <id> 查看插件形态和能力分解。
兼容性信号
运行 openclaw doctor 或 openclaw plugins inspect <id> 可能看到:
| 信号 | 含义 |
|---|---|
| config valid | 配置解析正常,插件解析成功 |
| compatibility advisory | 插件使用了受支持但较旧的模式(如 hook-only) |
| legacy warning | 插件使用了已废弃的 before_agent_start |
| hard error | 配置无效或插件加载失败 |
架构概览
OpenClaw 插件系统分四层:
- Manifest + 发现层 — 从配置路径、工作区根、全局扩展根和内置扩展中发现候选插件;先读取原生
openclaw.plugin.json和支持的 bundle manifest - 启用 + 验证层 — 决定已发现插件的状态(启用/禁用/阻断/独占槽位如 memory)
- 运行时加载层 — 原生插件通过 jiti 进程内加载,向中央注册表注册能力;兼容 bundle 标准化为注册记录,不导入运行时代码
- 面向消费层 — 其余 OpenClaw 代码从注册表读取,暴露工具、渠道、Provider、Hook、HTTP 路由、CLI 命令和服务
Channel 插件与共享消息工具
Channel 插件无需为普通聊天动作注册单独的发送/编辑/反应工具。OpenClaw 在 core 中维护一个共享的 message 工具,Channel 插件拥有其背后的渠道特定发现和执行。
核心边界:
- core 拥有:共享
message工具宿主、提示词接线、会话/线程管理、执行调度 - Channel 插件拥有:范围内的动作发现、能力发现和渠道特定 schema 片段
能力所有权模型
OpenClaw 将原生插件视为公司或功能的所有权边界,而非不相关集成的大杂烩。
核心区分:
- plugin = 所有权边界
- capability = 可由多个插件实现或消费的 core 合约
推荐的分层模型:
- core 能力层:共享编排、策略、回退、config 合并规则、交付语义和类型合约
- 厂商插件层:厂商特定 API、认证、模型目录、语音合成、图像生成等
- 渠道/功能插件层:消费 core 能力并在特定平台上呈现的集成
合约与执行
插件 API 面向有意设计为类型化、集中化。这意味着:
- 插件作者有统一的内部标准
- core 可以拒绝重复所有权(如两个插件注册同一 provider id)
- 启动时可给出可操作的诊断信息
- 合约测试可以明确执行已绑定插件的所有权
两层执行机制:
- 运行时注册执行 — 注册表在插件加载时验证注册,重复 ID、格式错误的注册产生插件诊断而非未定义行为
- 合约测试 — 绑定插件在测试运行时被捕获到合约注册表,断言所有权
执行模型
原生 OpenClaw 插件进程内运行于网关。它们不被沙箱化。已加载的原生插件与 core 代码具有相同的进程级信任边界。
含义:
- 原生插件可注册工具、网络处理器、Hook 和服务
- 原生插件 bug 可能导致网关崩溃或不稳定
- 恶意原生插件等同于 OpenClaw 进程内的任意代码执行
兼容 bundle 默认更安全——OpenClaw 目前将其视为元数据/内容包。
使用白名单和明确的安装/加载路径管理非内置插件。将工作区插件视为开发时代码,而非生产默认值。
加载管道
启动时,OpenClaw 大致执行:
- 发现候选插件根目录
- 读取原生或兼容 bundle 的 manifest 和包元数据
- 拒绝不安全候选
- 规范化插件配置(
plugins.enabled、allow、deny、entries、slots、load.paths) - 决定每个候选的启用状态
- 通过 jiti 加载已启用的原生模块
- 调用原生
register(api)并将注册收集到插件注册表 - 向命令/运行时面暴露注册表
安全门控在运行时执行之前发生。当入口逃逸插件根目录、路径全局可写或路径所有权可疑时,候选被阻断。
activate是register的旧版别名。加载器解析def.register ?? def.activate并在同一时间点调用。所有内置插件使用register,新插件请用register。
注册表模型
已加载插件向中央插件注册表注册,注册表跟踪:
- 插件记录(标识、来源、状态、诊断)
- 工具
- 旧版/类型化 Hook
- 渠道
- Provider
- 网关 RPC 处理器
- HTTP 路由
- CLI 注册器
- 后台服务
- 插件拥有的命令
Core 功能从注册表读取,而非直接与插件模块通信。这保持了加载的单向性:插件模块 → 注册表注册;core 运行时 → 注册表消费。
Provider 运行时 Hook
Provider 插件有两层:
- manifest 元数据:
providerAuthEnvVars(cheap 的 env 认证查询)和providerAuthChoices(cheap 的引导/认证选择标签) - 运行时 Hook:完整列表见 Plugin Internals 原文
Hook 执行顺序(简表):
| # | Hook | 用途 |
|---|---|---|
| 1 | catalog | 发布 Provider 配置到 models.providers |
| 2 | applyConfigDefaults | 应用 Provider 拥有的全局配置默认值 |
| 3 | normalizeModelId | 规范化旧版或预览模型 ID 别名 |
| 4-6 | normalizeTransport/Config/... | 规范化传输和配置 |
| 7-9 | resolveConfigApiKey/SyntheticAuth/... | 解析认证凭证 |
| 10-13 | resolveDynamicModel/... | 解析动态模型 ID |
| 14-18 | capabilities/normalizeToolSchemas/... | 工具 schema 和能力元数据 |
| 19-20 | createStreamFn/wrapStreamFn | 自定义或包装流式传输 |
| 36 | prepareRuntimeAuth | 推理前的 token 交换 |
| 37-38 | resolveUsageAuth/fetchUsageSnapshot | 用量/配额端点 |
| 40-42 | buildReplayPolicy/sanitizeReplayHistory/... | 回放策略和历史清理 |
运行时辅助 API
Provider 插件可通过 api.runtime 访问 core 辅助方法:
TTS(文字转语音):
ts
const clip = await api.runtime.tts.textToSpeech({ text: "Hello", cfg: api.config });媒体理解:
ts
const image = await api.runtime.mediaUnderstanding.describeImageFile({
filePath: "/tmp/photo.jpg",
cfg: api.config,
agentDir: "/tmp/agent",
});音频转录:
ts
const { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({
filePath: "/tmp/audio.ogg",
cfg: api.config,
});子 Agent 运行:
ts
const result = await api.runtime.subagent.run({
sessionKey: "agent:main:subagent:search-helper",
message: "Expand this query.",
provider: "openai",
model: "gpt-4.1-mini",
deliver: false,
});图像生成:
ts
const result = await api.runtime.imageGeneration.generate({
config: api.config,
args: { prompt: "A friendly lobster mascot", size: "1024x1024" },
});网关 HTTP 路由
ts
api.registerHttpRoute({
path: "/acme/webhook",
auth: "plugin", // "gateway" 或 "plugin"
match: "exact",
handler: async (_req, res) => {
res.statusCode = 200;
res.end("ok");
return true;
},
});注意:
auth字段必填- Exact
path + match冲突被拒绝(除非replaceExisting: true) - 一个插件不能替换另一个插件的路由
auth: "plugin"路由不自动获得 operator 运行时 scope
Plugin SDK 导入路径
使用 SDK 子路径而非整体 openclaw/plugin-sdk 导入:
openclaw/plugin-sdk/plugin-entry— 插件注册原语openclaw/plugin-sdk/core— 通用共享插件合约openclaw/plugin-sdk/config-schema— rootopenclaw.jsonZod schemaopenclaw/plugin-sdk/channel-setup、channel-pairing、channel-contract、channel-inbound等 — 渠道共享工具
兼容性提示:避免使用根 openclaw/plugin-sdk barrel;优先使用窄型稳定原语。
常见问题
Q: 开发自定义插件时,应该选择"原生插件"还是"bundle"模式?
A: 需要注册工具、Provider、渠道或 HTTP 路由等运行时能力时,必须使用原生插件。Bundle 只是内容包(skill、hook-pack、MCP 配置),适合发布 Claude/Cursor/Codex 格式的内容,但无法运行任意 TypeScript 代码。
Q: 插件报 hard error 怎么定位问题?
A: 运行 openclaw plugins inspect <id> 查看详细诊断,再查 openclaw logs --follow 中的加载错误。常见原因:manifest 格式不对(openclaw.plugin.json 缺失或字段错误)、入口文件路径错误、npm 依赖未安装。
Q: 两个插件都要注册同一个 provider id,会怎样?
A: 注册表在加载时检测到重复 ID 会产生插件诊断(hard error 级别),后加载的插件注册失败。通过 openclaw plugins inspect 可查看冲突详情。