From 1fe4e0807097598b9fabe2275f69703313d7dab0 Mon Sep 17 00:00:00 2001 From: "openclaw-docs-i18n[bot]" Date: Fri, 24 Apr 2026 03:09:13 +0000 Subject: [PATCH] chore(i18n): refresh zh-CN translations --- docs/zh-CN/automation/hooks.md | 154 +-- docs/zh-CN/cli/hooks.md | 144 +- docs/zh-CN/concepts/agent-loop.md | 178 +-- docs/zh-CN/plugins/architecture-internals.md | 875 +++++++++++++ docs/zh-CN/plugins/architecture.md | 1229 +++--------------- docs/zh-CN/plugins/building-plugins.md | 177 ++- docs/zh-CN/plugins/manifest.md | 440 ++++--- docs/zh-CN/plugins/message-presentation.md | 154 +-- docs/zh-CN/plugins/sdk-channel-plugins.md | 309 ++--- docs/zh-CN/plugins/sdk-provider-plugins.md | 264 ++-- 10 files changed, 1925 insertions(+), 1999 deletions(-) create mode 100644 docs/zh-CN/plugins/architecture-internals.md diff --git a/docs/zh-CN/automation/hooks.md b/docs/zh-CN/automation/hooks.md index c89ce368f..01a9dc575 100644 --- a/docs/zh-CN/automation/hooks.md +++ b/docs/zh-CN/automation/hooks.md @@ -1,60 +1,60 @@ --- read_when: - - 你希望为 /new、/reset、/stop 和智能体生命周期事件提供事件驱动自动化 - - 你希望构建、安装或调试 hooks + - 你希望为 `/new`、`/reset`、`/stop` 和智能体生命周期事件提供事件驱动自动化。 + - 你希望构建、安装或调试 hooks。 summary: Hooks:用于命令和生命周期事件的事件驱动自动化 title: Hooks x-i18n: - generated_at: "2026-04-23T20:40:40Z" + generated_at: "2026-04-24T03:06:51Z" model: gpt-5.4 provider: openai - source_hash: 93d26dfe9e04d3e3de070223f4e186a31bd3afb854f1b0e5f5bff893a69538d6 + source_hash: 9e24d5a95748151059e34f8c9ff9910dbcd7a32e7cadb44d1fa25352ef3a09a6 source_path: automation/hooks.md workflow: 15 --- -Hooks 是在 Gateway 网关内部发生某些事件时运行的小型脚本。它们可以从目录中被发现,并可通过 `openclaw hooks` 进行检查。只有在你启用 hooks,或至少配置了一个 hook 条目、hook pack、旧版处理器或额外 hook 目录后,Gateway 网关才会加载内部 hooks。 +Hooks 是在 Gateway 网关内部发生某些事件时运行的小型脚本。它们可以从目录中被发现,并可使用 `openclaw hooks` 进行检查。只有在你启用 hooks,或至少配置了一个 hook 条目、hook pack、旧版处理器或额外 hook 目录后,Gateway 网关才会加载内部 hooks。 OpenClaw 中有两种 hooks: -- **内部 hooks**(本页):当智能体事件触发时在 Gateway 网关内运行,例如 `/new`、`/reset`、`/stop` 或生命周期事件。 -- **Webhooks**:外部 HTTP 端点,允许其他系统在 OpenClaw 中触发工作流。参见 [Webhooks](/zh-CN/automation/cron-jobs#webhooks)。 +- **内部 hooks**(本页):当智能体事件触发时在 Gateway 网关内部运行,例如 `/new`、`/reset`、`/stop` 或生命周期事件。 +- **Webhooks**:外部 HTTP 端点,允许其他系统在 OpenClaw 中触发任务。参见 [Webhooks](/zh-CN/automation/cron-jobs#webhooks)。 -Hooks 也可以打包在插件中。`openclaw hooks list` 会同时显示独立 hooks 和由插件管理的 hooks。 +Hooks 也可以打包在插件中。`openclaw hooks list` 会显示独立 hooks 和由插件管理的 hooks。 ## 快速开始 ```bash -# 列出可用 hooks +# List available hooks openclaw hooks list -# 启用一个 hook +# Enable a hook openclaw hooks enable session-memory -# 检查 hook 状态 +# Check hook status openclaw hooks check -# 获取详细信息 +# Get detailed information openclaw hooks info session-memory ``` ## 事件类型 -| 事件 | 触发时机 | +| Event | 触发时机 | | ------------------------ | ------------------------------------------------ | -| `command:new` | 发出 `/new` 命令时 | -| `command:reset` | 发出 `/reset` 命令时 | -| `command:stop` | 发出 `/stop` 命令时 | -| `command` | 任意命令事件(通用监听器) | -| `session:compact:before` | 在压缩开始总结历史之前 | -| `session:compact:after` | 压缩完成之后 | -| `session:patch` | 会话属性被修改时 | -| `agent:bootstrap` | 注入工作区 bootstrap 文件之前 | -| `gateway:startup` | 渠道启动并加载 hooks 之后 | -| `message:received` | 来自任意渠道的入站消息 | -| `message:transcribed` | 音频转录完成之后 | -| `message:preprocessed` | 所有媒体和链接理解完成之后 | -| `message:sent` | 出站消息已发送 | +| `command:new` | 发出 `/new` 命令时 | +| `command:reset` | 发出 `/reset` 命令时 | +| `command:stop` | 发出 `/stop` 命令时 | +| `command` | 任意命令事件(通用监听器) | +| `session:compact:before` | 压缩总结历史记录之前 | +| `session:compact:after` | 压缩完成之后 | +| `session:patch` | 修改会话属性时 | +| `agent:bootstrap` | 注入工作区 bootstrap 文件之前 | +| `gateway:startup` | 渠道启动并加载 hooks 后 | +| `message:received` | 来自任意渠道的入站消息 | +| `message:transcribed` | 音频转录完成后 | +| `message:preprocessed` | 所有媒体和链接理解完成后 | +| `message:sent` | 出站消息已送达 | ## 编写 hooks @@ -64,38 +64,38 @@ openclaw hooks info session-memory ```text my-hook/ -├── HOOK.md # 元数据 + 文档 -└── handler.ts # 处理器实现 +├── HOOK.md # Metadata + documentation +└── handler.ts # Handler implementation ``` -### `HOOK.md` 格式 +### HOOK.md 格式 ```markdown --- name: my-hook -description: "此 hook 功能的简短描述" +description: "Short description of what this hook does" metadata: { "openclaw": { "emoji": "🔗", "events": ["command:new"], "requires": { "bins": ["node"] } } } --- # My Hook -详细文档写在这里。 +Detailed documentation goes here. ``` -**元数据字段**(`metadata.openclaw`): +**Metadata 字段**(`metadata.openclaw`): -| 字段 | 描述 | +| Field | 说明 | | ---------- | ---------------------------------------------------- | -| `emoji` | CLI 中显示的 emoji | -| `events` | 要监听的事件数组 | -| `export` | 要使用的命名导出(默认为 `"default"`) | -| `os` | 所需平台(例如 `["darwin", "linux"]`) | +| `emoji` | 用于 CLI 显示的表情符号 | +| `events` | 要监听的事件数组 | +| `export` | 要使用的具名导出(默认为 `"default"`) | +| `os` | 所需平台(例如 `["darwin", "linux"]`) | | `requires` | 必需的 `bins`、`anyBins`、`env` 或 `config` 路径 | -| `always` | 跳过可用性检查(布尔值) | -| `install` | 安装方式 | +| `always` | 绕过资格检查(布尔值) | +| `install` | 安装方式 | -### 处理器实现 +### Handler 实现 ```typescript const handler = async (event) => { @@ -129,41 +129,41 @@ export default handler; **Bootstrap 事件**(`agent:bootstrap`):`context.bootstrapFiles`(可变数组)、`context.agentId`。 -**会话 patch 事件**(`session:patch`):`context.sessionEntry`、`context.patch`(仅包含已更改字段)、`context.cfg`。只有特权客户端才能触发 patch 事件。 +**会话 patch 事件**(`session:patch`):`context.sessionEntry`、`context.patch`(仅包含变更字段)、`context.cfg`。只有特权客户端可以触发 patch 事件。 -**压缩事件**:`session:compact:before` 包含 `messageCount`、`tokenCount`。`session:compact:after` 额外包含 `compactedCount`、`summaryLength`、`tokensBefore`、`tokensAfter`。 +**压缩事件**:`session:compact:before` 包含 `messageCount`、`tokenCount`。`session:compact:after` 还会增加 `compactedCount`、`summaryLength`、`tokensBefore`、`tokensAfter`。 -## Hook 发现机制 +## Hook 发现 -Hooks 会从以下目录中按覆盖优先级由低到高进行发现: +Hooks 会按以下目录进行发现,覆盖优先级依次递增: 1. **内置 hooks**:随 OpenClaw 一起提供 2. **插件 hooks**:打包在已安装插件中的 hooks -3. **托管 hooks**:`~/.openclaw/hooks/`(用户安装,跨工作区共享)。来自 `hooks.internal.load.extraDirs` 的额外目录与此优先级相同。 -4. **工作区 hooks**:`/hooks/`(每个智能体独有,默认禁用,需显式启用) +3. **托管 hooks**:`~/.openclaw/hooks/`(用户安装,在各工作区间共享)。来自 `hooks.internal.load.extraDirs` 的额外目录也具有相同优先级。 +4. **工作区 hooks**:`/hooks/`(按智能体划分,默认禁用,直到显式启用) 工作区 hooks 可以添加新的 hook 名称,但不能覆盖同名的内置、托管或插件提供的 hooks。 -在配置内部 hooks 之前,Gateway 网关会在启动时跳过内部 hook 发现。你可以通过 `openclaw hooks enable ` 启用内置或托管 hook,安装 hook pack,或设置 `hooks.internal.enabled=true` 来选择启用。当你启用一个命名 hook 时,Gateway 网关只会加载该 hook 的处理器;`hooks.internal.enabled=true`、额外 hook 目录和旧版处理器则会启用广泛发现。 +在配置内部 hooks 之前,Gateway 网关会在启动时跳过内部 hook 发现。你可以通过 `openclaw hooks enable ` 启用某个内置或托管 hook,安装 hook pack,或设置 `hooks.internal.enabled=true` 来选择启用。当你启用一个具名 hook 时,Gateway 网关只会加载该 hook 的 handler;而 `hooks.internal.enabled=true`、额外 hook 目录和旧版处理器会启用广泛发现。 ### Hook packs -Hook pack 是通过 `package.json` 中的 `openclaw.hooks` 导出 hooks 的 npm 包。安装方式: +Hook packs 是通过 `package.json` 中的 `openclaw.hooks` 导出 hooks 的 npm 包。安装方式: ```bash openclaw plugins install ``` -npm 规格仅支持 registry 项(包名 + 可选精确版本或 dist-tag)。Git/URL/file 规格和 semver 范围会被拒绝。 +Npm 规格仅支持 registry 形式(包名 + 可选的精确版本或 dist-tag)。Git/URL/文件规格和 semver 范围都不受支持。 ## 内置 hooks -| Hook | 事件 | 功能说明 | +| Hook | Events | 作用 | | --------------------- | ------------------------------ | ----------------------------------------------------- | -| session-memory | `command:new`, `command:reset` | 将会话上下文保存到 `/memory/` | -| bootstrap-extra-files | `agent:bootstrap` | 从 glob 模式注入额外的 bootstrap 文件 | -| command-logger | `command` | 将所有命令记录到 `~/.openclaw/logs/commands.log` | -| boot-md | `gateway:startup` | 在 Gateway 网关启动时运行 `BOOT.md` | +| session-memory | `command:new`, `command:reset` | 将会话上下文保存到 `/memory/` | +| bootstrap-extra-files | `agent:bootstrap` | 从 glob 模式注入额外的 bootstrap 文件 | +| command-logger | `command` | 将所有命令记录到 `~/.openclaw/logs/commands.log` | +| boot-md | `gateway:startup` | 在 gateway 启动时运行 `BOOT.md` | 启用任意内置 hook: @@ -173,13 +173,13 @@ openclaw hooks enable -### `session-memory` 详情 +### session-memory 详情 提取最近 15 条用户/助手消息,通过 LLM 生成描述性文件名 slug,并保存到 `/memory/YYYY-MM-DD-slug.md`。要求已配置 `workspace.dir`。 -### `bootstrap-extra-files` 配置 +### bootstrap-extra-files 配置 ```json { @@ -200,21 +200,21 @@ openclaw hooks enable -### `command-logger` 详情 +### command-logger 详情 将每个斜杠命令记录到 `~/.openclaw/logs/commands.log`。 -### `boot-md` 详情 +### boot-md 详情 -在 Gateway 网关启动时,从当前工作区运行 `BOOT.md`。 +在 gateway 启动时,从当前活动工作区运行 `BOOT.md`。 ## 插件 hooks -插件可以通过插件 SDK 注册 hooks,以实现更深层集成:拦截工具调用、修改提示词、控制消息流等。插件 SDK 暴露了 28 个 hooks,覆盖模型解析、智能体生命周期、消息流、工具执行、子智能体协调和 Gateway 网关生命周期。 +插件可以通过插件 SDK 注册 hooks,以实现更深度的集成:拦截工具调用、修改提示词、控制消息流等。插件 SDK 暴露了 28 个 hooks,覆盖模型解析、智能体生命周期、消息流、工具执行、子智能体协调以及 gateway 生命周期。 -完整的插件 hook 参考,包括 `before_tool_call`、`before_agent_reply`、`before_install` 以及所有其他插件 hooks,请参见 [Plugin Architecture](/zh-CN/plugins/architecture#provider-runtime-hooks)。 +有关完整的插件 hook 参考,包括 `before_tool_call`、`before_agent_reply`、`before_install` 以及所有其他插件 hooks,请参见 [Plugin Architecture](/zh-CN/plugins/architecture-internals#provider-runtime-hooks)。 ## 配置 @@ -264,31 +264,31 @@ openclaw hooks enable ``` -旧版 `hooks.internal.handlers` 数组配置格式仍受支持以保持向后兼容,但新 hooks 应使用基于发现的系统。 +旧版的 `hooks.internal.handlers` 数组配置格式仍然受支持,以保持向后兼容,但新的 hooks 应使用基于发现的系统。 ## CLI 参考 ```bash -# 列出所有 hooks(可添加 --eligible、--verbose 或 --json) +# List all hooks (add --eligible, --verbose, or --json) openclaw hooks list -# 显示某个 hook 的详细信息 +# Show detailed info about a hook openclaw hooks info -# 显示可用性摘要 +# Show eligibility summary openclaw hooks check -# 启用/禁用 +# Enable/disable openclaw hooks enable openclaw hooks disable ``` ## 最佳实践 -- **保持处理器快速执行。** Hooks 会在命令处理期间运行。对于较重的任务,使用 `void processInBackground(event)` 以 fire-and-forget 方式处理。 -- **优雅地处理错误。** 用 try/catch 包裹有风险的操作;不要抛出异常,以便其他处理器继续运行。 -- **尽早过滤事件。** 如果事件类型/动作无关,立即返回。 +- **让 handlers 保持快速。** Hooks 会在命令处理期间运行。对于较重的任务,使用 `void processInBackground(event)` 以即发即弃方式处理。 +- **优雅地处理错误。** 用 try/catch 包装有风险的操作;不要抛出异常,以便其他 handlers 可以继续运行。 +- **尽早过滤事件。** 如果事件类型/操作无关,立即返回。 - **使用具体事件键。** 优先使用 `"events": ["command:new"]`,而不是 `"events": ["command"]`,以减少开销。 ## 故障排除 @@ -296,31 +296,31 @@ openclaw hooks disable ### Hook 未被发现 ```bash -# 验证目录结构 +# Verify directory structure ls -la ~/.openclaw/hooks/my-hook/ -# 应显示:HOOK.md, handler.ts +# Should show: HOOK.md, handler.ts -# 列出所有已发现的 hooks +# List all discovered hooks openclaw hooks list ``` -### Hook 不可用 +### Hook 不符合资格 ```bash openclaw hooks info my-hook ``` -检查是否缺少二进制文件(PATH)、环境变量、配置值或 OS 兼容性。 +检查是否缺少二进制文件(PATH)、环境变量、配置值或操作系统兼容性。 ### Hook 未执行 1. 确认 hook 已启用:`openclaw hooks list` -2. 重启你的 Gateway 网关进程以重新加载 hooks。 -3. 检查 Gateway 网关日志:`./scripts/clawlog.sh | grep hook` +2. 重启你的 gateway 进程,以便重新加载 hooks。 +3. 检查 gateway 日志:`./scripts/clawlog.sh | grep hook` ## 相关内容 - [CLI 参考:hooks](/zh-CN/cli/hooks) - [Webhooks](/zh-CN/automation/cron-jobs#webhooks) -- [Plugin Architecture](/zh-CN/plugins/architecture#provider-runtime-hooks) — 完整插件 hook 参考 +- [Plugin Architecture](/zh-CN/plugins/architecture-internals#provider-runtime-hooks) — 完整插件 hook 参考 - [配置](/zh-CN/gateway/configuration-reference#hooks) diff --git a/docs/zh-CN/cli/hooks.md b/docs/zh-CN/cli/hooks.md index 2514eb9ab..98dfaf2bc 100644 --- a/docs/zh-CN/cli/hooks.md +++ b/docs/zh-CN/cli/hooks.md @@ -1,28 +1,28 @@ --- read_when: - 你想管理智能体钩子 - - 你想检查钩子的可用性或启用工作区钩子 + - 你想检查钩子的可用性,或启用工作区钩子 summary: '`openclaw hooks` 的 CLI 参考(智能体钩子)' title: 钩子 x-i18n: - generated_at: "2026-04-23T20:43:53Z" + generated_at: "2026-04-24T03:06:48Z" model: gpt-5.4 provider: openai - source_hash: 5846aaf8c9b966b92575a4c0c5268195ce4f8b29feb0846bfb121ed3bb8f69ef + source_hash: 6df7ddd75441983f7cd03b4408f91cb1a883243b9f19d2528f05dfc51b2891e8 source_path: cli/hooks.md workflow: 15 --- # `openclaw hooks` -管理智能体钩子(针对 `/new`、`/reset` 和 Gateway 网关启动等命令的事件驱动自动化)。 +管理智能体钩子(用于 `/new`、`/reset` 和 Gateway 网关启动等命令的事件驱动自动化)。 在不带子命令的情况下运行 `openclaw hooks`,等同于 `openclaw hooks list`。 相关内容: -- Hooks:[钩子](/zh-CN/automation/hooks) -- 插件钩子:[插件钩子](/zh-CN/plugins/architecture#provider-runtime-hooks) +- 钩子: [Hooks](/zh-CN/automation/hooks) +- 插件钩子: [Plugin hooks](/zh-CN/plugins/architecture-internals#provider-runtime-hooks) ## 列出所有钩子 @@ -35,20 +35,20 @@ openclaw hooks list **选项:** -- `--eligible`:仅显示可用钩子(满足要求) -- `--json`:以 JSON 输出 +- `--eligible`:仅显示符合条件的钩子(已满足要求) +- `--json`:以 JSON 格式输出 - `-v, --verbose`:显示详细信息,包括缺失的要求 -**输出示例:** +**示例输出:** -```text +``` Hooks (4/4 ready) Ready: - 🚀 boot-md ✓ - 在 Gateway 网关启动时运行 BOOT.md - 📎 bootstrap-extra-files ✓ - 在智能体引导期间注入额外的工作区引导文件 - 📝 command-logger ✓ - 将所有命令事件记录到集中式审计文件 - 💾 session-memory ✓ - 在发出 /new 或 /reset 命令时将会话上下文保存到 memory + 🚀 boot-md ✓ - Run BOOT.md on gateway startup + 📎 bootstrap-extra-files ✓ - Inject extra workspace bootstrap files during agent bootstrap + 📝 command-logger ✓ - Log all command events to a centralized audit file + 💾 session-memory ✓ - Save session context to memory when /new or /reset command is issued ``` **示例(详细模式):** @@ -57,7 +57,7 @@ Ready: openclaw hooks list --verbose ``` -显示不可用钩子缺失的要求。 +显示不符合条件的钩子所缺失的要求。 **示例(JSON):** @@ -65,7 +65,7 @@ openclaw hooks list --verbose openclaw hooks list --json ``` -返回结构化 JSON,供程序使用。 +返回结构化 JSON,供程序化使用。 ## 获取钩子信息 @@ -77,11 +77,11 @@ openclaw hooks info **参数:** -- ``:钩子名称或钩子键(例如 `session-memory`) +- ``:钩子名称或钩子键名(例如 `session-memory`) **选项:** -- `--json`:以 JSON 输出 +- `--json`:以 JSON 格式输出 **示例:** @@ -91,42 +91,42 @@ openclaw hooks info session-memory **输出:** -```text -💾 session-memory ✓ 已就绪 +``` +💾 session-memory ✓ Ready -在发出 /new 或 /reset 命令时将会话上下文保存到 memory +Save session context to memory when /new or /reset command is issued -详情: +Details: Source: openclaw-bundled Path: /path/to/openclaw/hooks/bundled/session-memory/HOOK.md Handler: /path/to/openclaw/hooks/bundled/session-memory/handler.ts Homepage: https://docs.openclaw.ai/automation/hooks#session-memory Events: command:new, command:reset -要求: +Requirements: Config: ✓ workspace.dir ``` -## 检查钩子可用性 +## 检查钩子资格状态 ```bash openclaw hooks check ``` -显示钩子可用状态摘要(有多少已就绪,多少未就绪)。 +显示钩子资格状态摘要(有多少已就绪,多少未就绪)。 **选项:** -- `--json`:以 JSON 输出 +- `--json`:以 JSON 格式输出 -**输出示例:** +**示例输出:** -```text -Hooks 状态 +``` +Hooks Status -钩子总数:4 -已就绪:4 -未就绪:0 +Total hooks: 4 +Ready: 4 +Not ready: 0 ``` ## 启用钩子 @@ -135,9 +135,9 @@ Hooks 状态 openclaw hooks enable ``` -通过将指定钩子添加到你的配置中来启用它(默认是 `~/.openclaw/openclaw.json`)。 +通过将特定钩子添加到你的配置中来启用它(默认是 `~/.openclaw/openclaw.json`)。 -**注意:** 工作区钩子默认禁用,必须在这里或配置中启用。由插件管理的钩子会在 `openclaw hooks list` 中显示为 `plugin:`,无法在这里启用/禁用。请改为启用/禁用对应插件。 +**注意:** 工作区钩子默认处于禁用状态,必须在这里或在配置中启用。由插件管理的钩子会在 `openclaw hooks list` 中显示为 `plugin:`,不能在这里启用或禁用。请改为启用或禁用相应插件。 **参数:** @@ -151,22 +151,21 @@ openclaw hooks enable session-memory **输出:** -```text -✓ 已启用钩子:💾 session-memory +``` +✓ Enabled hook: 💾 session-memory ``` -**它会执行:** +**它会执行以下操作:** -- 检查钩子是否存在且可用 +- 检查钩子是否存在且符合条件 - 在你的配置中更新 `hooks.internal.entries..enabled = true` - 将配置保存到磁盘 -如果钩子来自 `/hooks/`,则在 -Gateway 网关加载它之前,必须完成这一步显式启用。 +如果该钩子来自 `/hooks/`,则必须先执行此选择启用步骤,Gateway 网关才会加载它。 **启用后:** -- 重启 Gateway 网关以重新加载钩子(在 macOS 上重启菜单栏应用,或在开发环境中重启你的 gateway 进程)。 +- 重启 Gateway 网关以重新加载钩子(在 macOS 上重启菜单栏应用,或在开发环境中重启你的 Gateway 网关进程)。 ## 禁用钩子 @@ -174,7 +173,7 @@ Gateway 网关加载它之前,必须完成这一步显式启用。 openclaw hooks disable ``` -通过更新配置来禁用特定钩子。 +通过更新你的配置来禁用特定钩子。 **参数:** @@ -188,8 +187,8 @@ openclaw hooks disable command-logger **输出:** -```text -⏸ 已禁用钩子:📝 command-logger +``` +⏸ Disabled hook: 📝 command-logger ``` **禁用后:** @@ -199,41 +198,36 @@ openclaw hooks disable command-logger ## 说明 - `openclaw hooks list --json`、`info --json` 和 `check --json` 会将结构化 JSON 直接写入 stdout。 -- 插件管理的钩子无法在这里启用或禁用;请改为启用或禁用对应的所属插件。 +- 由插件管理的钩子不能在这里启用或禁用;请改为启用或禁用其所属插件。 ## 安装钩子包 ```bash -openclaw plugins install # 先查 ClawHub,再查 npm +openclaw plugins install # 先查找 ClawHub,再查找 npm openclaw plugins install --pin # 固定版本 openclaw plugins install # 本地路径 ``` 通过统一的插件安装器安装钩子包。 -`openclaw hooks install` 仍可作为兼容别名使用,但它会打印一条 -弃用警告,并转发到 `openclaw plugins install`。 +`openclaw hooks install` 仍可作为兼容别名使用,但会打印弃用警告,并转发到 `openclaw plugins install`。 -npm 规范是**仅限 registry** 的(包名 + 可选的**精确版本**或 -**dist-tag**)。不接受 Git/URL/file 规范和 semver 范围。 -出于安全考虑,依赖安装使用 `--ignore-scripts` 运行。 +Npm 规格仅支持 **registry-only**(包名 + 可选的**精确版本**或 **dist-tag**)。Git/URL/file 规格和 semver 范围不被支持。为安全起见,依赖安装会使用 `--ignore-scripts` 运行。 -裸规范和 `@latest` 会保持在稳定轨道上。如果 npm 将其中任一项解析为预发布版本, -OpenClaw 会停止并要求你显式选择加入,例如使用 -`@beta`/`@rc` 等预发布标签,或使用精确的预发布版本。 +裸规格和 `@latest` 会保持在稳定通道上。如果 npm 将这两者中的任意一种解析为预发布版本,OpenClaw 会停止并要求你显式选择加入,例如使用 `@beta`/`@rc` 这样的预发布标签,或使用精确的预发布版本。 -**它会执行:** +**它会执行以下操作:** - 将钩子包复制到 `~/.openclaw/hooks/` - 在 `hooks.internal.entries.*` 中启用已安装的钩子 -- 在 `hooks.internal.installs` 下记录安装信息 +- 在 `hooks.internal.installs` 下记录该安装 **选项:** - `-l, --link`:链接本地目录而不是复制(将其添加到 `hooks.internal.load.extraDirs`) -- `--pin`:将 npm 安装以解析后的精确 `name@version` 形式记录到 `hooks.internal.installs` +- `--pin`:将 npm 安装记录为 `hooks.internal.installs` 中精确解析后的 `name@version` -**支持的压缩包格式:** `.zip`、`.tgz`、`.tar.gz`、`.tar` +**支持的归档格式:** `.zip`、`.tgz`、`.tar.gz`、`.tar` **示例:** @@ -241,7 +235,7 @@ OpenClaw 会停止并要求你显式选择加入,例如使用 # 本地目录 openclaw plugins install ./my-hook-pack -# 本地压缩包 +# 本地归档文件 openclaw plugins install ./my-hook-pack.zip # NPM 包 @@ -251,8 +245,7 @@ openclaw plugins install @openclaw/my-hook-pack openclaw plugins install -l ./my-hook-pack ``` -链接的钩子包会被视为来自运维人员配置目录的托管钩子, -而不是工作区钩子。 +已链接的钩子包会被视为来自运维人员配置目录的托管钩子,而不是工作区钩子。 ## 更新钩子包 @@ -263,23 +256,20 @@ openclaw plugins update --all 通过统一的插件更新器更新已跟踪的基于 npm 的钩子包。 -`openclaw hooks update` 仍可作为兼容别名使用,但它会打印一条 -弃用警告,并转发到 `openclaw plugins update`。 +`openclaw hooks update` 仍可作为兼容别名使用,但会打印弃用警告,并转发到 `openclaw plugins update`。 **选项:** - `--all`:更新所有已跟踪的钩子包 -- `--dry-run`:显示将会发生的更改,但不写入 +- `--dry-run`:仅显示将发生的变化,不写入内容 -当已存储完整性哈希且拉取到的工件哈希发生变化时, -OpenClaw 会打印警告,并在继续前请求确认。在 CI/非交互运行中, -可使用全局 `--yes` 跳过提示。 +当存在已存储的完整性哈希,且获取到的构件哈希发生变化时,OpenClaw 会打印警告并在继续前请求确认。在 CI 或非交互式运行中,可使用全局 `--yes` 跳过提示。 ## 内置钩子 ### session-memory -在你发出 `/new` 或 `/reset` 时,将会话上下文保存到 memory。 +当你发出 `/new` 或 `/reset` 时,将会话上下文保存到 memory 中。 **启用:** @@ -289,7 +279,7 @@ openclaw hooks enable session-memory **输出:** `~/.openclaw/workspace/memory/YYYY-MM-DD-slug.md` -**参见:** [session-memory 文档](/zh-CN/automation/hooks#session-memory) +**参见:** [session-memory documentation](/zh-CN/automation/hooks#session-memory) ### bootstrap-extra-files @@ -301,11 +291,11 @@ openclaw hooks enable session-memory openclaw hooks enable bootstrap-extra-files ``` -**参见:** [bootstrap-extra-files 文档](/zh-CN/automation/hooks#bootstrap-extra-files) +**参见:** [bootstrap-extra-files documentation](/zh-CN/automation/hooks#bootstrap-extra-files) ### command-logger -将所有命令事件记录到集中式审计文件。 +将所有命令事件记录到集中式审计文件中。 **启用:** @@ -321,25 +311,25 @@ openclaw hooks enable command-logger # 最近的命令 tail -n 20 ~/.openclaw/logs/commands.log -# 美化输出 +# 美化打印 cat ~/.openclaw/logs/commands.log | jq . -# 按操作过滤 +# 按操作筛选 grep '"action":"new"' ~/.openclaw/logs/commands.log | jq . ``` -**参见:** [command-logger 文档](/zh-CN/automation/hooks#command-logger) +**参见:** [command-logger documentation](/zh-CN/automation/hooks#command-logger) ### boot-md 在 Gateway 网关启动时运行 `BOOT.md`(在渠道启动之后)。 -**事件**:`gateway:startup` +**事件**: `gateway:startup` -**启用**: +**启用:** ```bash openclaw hooks enable boot-md ``` -**参见:** [boot-md 文档](/zh-CN/automation/hooks#boot-md) +**参见:** [boot-md documentation](/zh-CN/automation/hooks#boot-md) diff --git a/docs/zh-CN/concepts/agent-loop.md b/docs/zh-CN/concepts/agent-loop.md index 33efd3508..320671f31 100644 --- a/docs/zh-CN/concepts/agent-loop.md +++ b/docs/zh-CN/concepts/agent-loop.md @@ -1,165 +1,165 @@ --- read_when: - - 你需要对智能体循环或生命周期事件进行精确讲解 - - 你正在更改会话排队、transcript 写入或会话写锁行为 -summary: 智能体循环生命周期、流和等待语义 + - 你需要一份关于智能体循环或生命周期事件的精确演练说明 + - 你正在更改会话排队、转录写入,或会话写锁行为 +summary: 智能体循环生命周期、流,以及等待语义 title: 智能体循环 x-i18n: - generated_at: "2026-04-23T20:45:32Z" + generated_at: "2026-04-24T03:06:51Z" model: gpt-5.4 provider: openai - source_hash: 849a0737185cb9e26cc4815ac05fee28d2495676579832eeb09d79459d989152 + source_hash: 1c124684b8f252440f096f5075a2a5b99071d89f9b1d2a6970b178a0bc15ea69 source_path: concepts/agent-loop.md workflow: 15 --- # 智能体循环(OpenClaw) -智能体循环是一次智能体完整且“真实”的运行:输入接收 → 上下文组装 → 模型推理 → -工具执行 → 流式回复 → 持久化。它是将一条消息转化为动作和最终回复的权威路径,同时保持会话状态一致。 +智能体循环是智能体一次完整且“真实”的运行过程:输入接收 → 上下文组装 → 模型推理 → +工具执行 → 流式回复 → 持久化。它是将一条消息转换为操作和最终回复的权威路径,同时保持会话状态一致。 -在 OpenClaw 中,每个循环都是每个会话的一次单独串行运行;当模型思考、调用工具并流式输出时,它会发出生命周期和流事件。本文档解释这个真实循环是如何端到端连接起来的。 +在 OpenClaw 中,一个循环是每个会话的一次单独、串行化运行;当模型思考、调用工具并流式输出内容时,它会发出生命周期和流事件。本文档解释了这一真实循环是如何端到端连接起来的。 ## 入口点 -- Gateway RPC:`agent` 和 `agent.wait`。 +- Gateway 网关 RPC:`agent` 和 `agent.wait`。 - CLI:`agent` 命令。 -## 工作方式(高层概览) +## 工作原理(高层概览) -1. `agent` RPC 验证参数,解析会话(`sessionKey`/`sessionId`),持久化会话元数据,并立即返回 `{ runId, acceptedAt }`。 +1. `agent` RPC 验证参数,解析会话(sessionKey/sessionId),持久化会话元数据,并立即返回 `{ runId, acceptedAt }`。 2. `agentCommand` 运行智能体: - - 解析模型 + thinking/verbose/trace 默认值 - - 加载技能快照 + - 解析模型以及 thinking/verbose/trace 默认值 + - 加载 Skills 快照 - 调用 `runEmbeddedPiAgent`(pi-agent-core 运行时) - - 如果嵌入式循环未发出**生命周期结束/错误**事件,则发出该事件作为补偿 + - 如果内嵌循环没有发出 **lifecycle end/error**,则发出 **lifecycle end/error** 3. `runEmbeddedPiAgent`: - - 通过每会话队列 + 全局队列对运行进行串行化 - - 解析模型 + 认证配置,并构建 pi 会话 - - 订阅 pi 事件并流式传输助手/工具增量 - - 强制执行超时 → 超时后中止运行 - - 返回 payload 和使用量元数据 + - 通过每会话队列和全局队列对运行进行串行化 + - 解析模型和 auth profile,并构建 pi 会话 + - 订阅 pi 事件并流式传输 assistant/tool 增量 + - 强制执行超时;超过时中止运行 + - 返回负载和 usage 元数据 4. `subscribeEmbeddedPiSession` 将 pi-agent-core 事件桥接到 OpenClaw `agent` 流: - 工具事件 => `stream: "tool"` - - 助手增量 => `stream: "assistant"` + - assistant 增量 => `stream: "assistant"` - 生命周期事件 => `stream: "lifecycle"`(`phase: "start" | "end" | "error"`) 5. `agent.wait` 使用 `waitForAgentRun`: - - 等待 `runId` 的**生命周期结束/错误** + - 等待 `runId` 的 **lifecycle end/error** - 返回 `{ status: ok|error|timeout, startedAt, endedAt, error? }` -## 排队 + 并发 +## 队列 + 并发 -- 运行会按会话键串行化(会话 lane),并可选地通过全局 lane。 -- 这样可以防止工具/会话竞争,并保持会话历史一致。 -- 消息渠道可以选择队列模式(collect/steer/followup)并接入这套 lane 系统。 - 参见[命令队列](/zh-CN/concepts/queue)。 -- transcript 写入也受会话文件上的会话写锁保护。该锁具备进程感知能力且基于文件,因此可以捕获绕过进程内队列或来自其他进程的写入者。 -- 默认情况下,会话写锁不可重入。如果某个辅助函数有意在保持单一逻辑写入者的同时嵌套获取同一把锁,必须显式启用 +- 运行会按会话键(会话通道)串行化,并且可选地通过一个全局通道。 +- 这可以防止工具/会话竞争,并保持会话历史一致。 +- 消息渠道可以选择队列模式(collect/steer/followup)来接入这一通道系统。 + 参见 [Command Queue](/zh-CN/concepts/queue)。 +- 转录写入也受到会话文件上的会话写锁保护。该锁是进程感知且基于文件的,因此它可以捕获绕过进程内队列或来自其他进程的写入者。 +- 会话写锁默认是非可重入的。如果某个辅助函数在保持单一逻辑写入者的同时,有意嵌套获取同一把锁,则必须显式选择启用 `allowReentrant: true`。 ## 会话 + 工作区准备 -- 会解析并创建工作区;沙箱隔离运行可能会重定向到沙箱工作区根目录。 -- Skills 会被加载(或从快照复用),并注入到环境变量和提示中。 -- 引导/上下文文件会被解析,并注入系统提示报告中。 -- 会获取会话写锁;在开始流式传输前会打开并准备好 `SessionManager`。后续任何 transcript 重写、压缩或截断路径,在打开或修改 transcript 文件前都必须获取同一把锁。 +- 工作区会被解析并创建;沙箱隔离运行可能会重定向到沙箱工作区根目录。 +- Skills 会被加载(或从快照复用),并注入到环境变量和提示词中。 +- Bootstrap/上下文文件会被解析并注入到系统提示词报告中。 +- 会获取一个会话写锁;`SessionManager` 会在流式传输开始前打开并完成准备。任何后续的转录重写、压缩或截断路径,都必须在打开或修改转录文件之前获取同一把锁。 -## 提示组装 + 系统提示 +## 提示词组装 + 系统提示词 -- 系统提示由 OpenClaw 的基础提示、技能提示、引导上下文和每次运行覆盖项共同构建。 -- 会强制执行模型特定限制和压缩预留 token。 -- 有关模型看到的内容,请参阅[系统提示](/zh-CN/concepts/system-prompt)。 +- 系统提示词由 OpenClaw 的基础提示词、Skills 提示词、bootstrap 上下文和每次运行的覆盖项构建而成。 +- 会强制执行特定模型的限制和压缩预留 token。 +- 关于模型能看到什么,参见 [System prompt](/zh-CN/concepts/system-prompt)。 -## Hook 点(你可以拦截的位置) +## Hook 点(你可以在哪里拦截) -OpenClaw 有两套 hook 系统: +OpenClaw 有两套 Hook 系统: -- **内部 hooks**(Gateway hooks):用于命令和生命周期事件的事件驱动脚本。 -- **插件 hooks**:位于智能体/工具生命周期和 gateway 管道内部的扩展点。 +- **内部 Hook**(Gateway 网关 Hook):用于命令和生命周期事件的事件驱动脚本。 +- **插件 Hook**:位于智能体/工具生命周期和 Gateway 网关流水线中的扩展点。 -### 内部 hooks(Gateway hooks) +### 内部 Hook(Gateway 网关 Hook) -- **`agent:bootstrap`**:在系统提示最终确定前、构建引导文件时运行。 - 用于添加/移除引导上下文文件。 -- **命令 hooks**:`/new`、`/reset`、`/stop` 和其他命令事件(参见 Hooks 文档)。 +- **`agent:bootstrap`**:在系统提示词最终确定之前、构建 bootstrap 文件期间运行。 + 用它来添加/删除 bootstrap 上下文文件。 +- **命令 Hook**:`/new`、`/reset`、`/stop` 以及其他命令事件(参见 Hooks 文档)。 -设置和示例请参阅 [Hooks](/zh-CN/automation/hooks)。 +设置和示例参见 [Hooks](/zh-CN/automation/hooks)。 -### 插件 hooks(智能体 + gateway 生命周期) +### 插件 Hook(智能体 + Gateway 网关生命周期) -这些 hook 运行在智能体循环或 gateway 管道内部: +这些 Hook 在智能体循环或 Gateway 网关流水线内部运行: -- **`before_model_resolve`**:在会话前运行(无 `messages`),用于在模型解析前确定性地覆盖 provider/模型。 -- **`before_prompt_build`**:在会话加载后运行(带 `messages`),可在提示提交前注入 `prependContext`、`systemPrompt`、`prependSystemContext` 或 `appendSystemContext`。对每轮动态文本请使用 `prependContext`;对应位于系统提示空间中的稳定指导请使用系统上下文字段。 -- **`before_agent_start`**:旧版兼容 hook,可能在任一阶段运行;优先使用上面更明确的 hooks。 -- **`before_agent_reply`**:在线内动作之后、LLM 调用之前运行,允许插件接管当前轮次并返回合成回复,或完全静默该轮次。 -- **`agent_end`**:完成后检查最终消息列表和运行元数据。 +- **`before_model_resolve`**:在会话前运行(无 `messages`),用于在模型解析前以确定性方式覆盖 provider/model。 +- **`before_prompt_build`**:在会话加载后运行(带有 `messages`),用于在提交提示词前注入 `prependContext`、`systemPrompt`、`prependSystemContext` 或 `appendSystemContext`。对每轮动态文本使用 `prependContext`,对应该位于系统提示词空间中的稳定指导使用系统上下文字段。 +- **`before_agent_start`**:用于兼容旧版本的 Hook,可能在任一阶段运行;优先使用上面更明确的 Hook。 +- **`before_agent_reply`**:在内联操作之后、LLM 调用之前运行,允许插件接管该轮并返回一个合成回复,或者完全让该轮保持静默。 +- **`agent_end`**:在完成后检查最终消息列表和运行元数据。 - **`before_compaction` / `after_compaction`**:观察或标注压缩周期。 - **`before_tool_call` / `after_tool_call`**:拦截工具参数/结果。 - **`before_install`**:检查内置扫描结果,并可选择阻止 Skills 或插件安装。 -- **`tool_result_persist`**:在工具结果写入会话 transcript 之前,同步转换工具结果。 -- **`message_received` / `message_sending` / `message_sent`**:入站 + 出站消息 hooks。 +- **`tool_result_persist`**:在工具结果写入会话转录之前,同步转换工具结果。 +- **`message_received` / `message_sending` / `message_sent`**:入站 + 出站消息 Hook。 - **`session_start` / `session_end`**:会话生命周期边界。 -- **`gateway_start` / `gateway_stop`**:gateway 生命周期事件。 +- **`gateway_start` / `gateway_stop`**:Gateway 网关生命周期事件。 -出站/工具保护的 hook 决策规则: +用于出站/工具保护的 Hook 决策规则: -- `before_tool_call`:`{ block: true }` 是终止性的,并会停止较低优先级处理器。 -- `before_tool_call`:`{ block: false }` 是空操作,不会清除先前的阻止状态。 -- `before_install`:`{ block: true }` 是终止性的,并会停止较低优先级处理器。 -- `before_install`:`{ block: false }` 是空操作,不会清除先前的阻止状态。 -- `message_sending`:`{ cancel: true }` 是终止性的,并会停止较低优先级处理器。 -- `message_sending`:`{ cancel: false }` 是空操作,不会清除先前的取消状态。 +- `before_tool_call`:`{ block: true }` 是终局结果,会阻止较低优先级的处理器继续执行。 +- `before_tool_call`:`{ block: false }` 为无操作,不会清除先前的阻止状态。 +- `before_install`:`{ block: true }` 是终局结果,会阻止较低优先级的处理器继续执行。 +- `before_install`:`{ block: false }` 为无操作,不会清除先前的阻止状态。 +- `message_sending`:`{ cancel: true }` 是终局结果,会阻止较低优先级的处理器继续执行。 +- `message_sending`:`{ cancel: false }` 为无操作,不会清除先前的取消状态。 -有关 hook API 和注册细节,请参阅[插件 hooks](/zh-CN/plugins/architecture#provider-runtime-hooks)。 +关于 Hook API 和注册细节,参见 [Plugin hooks](/zh-CN/plugins/architecture-internals#provider-runtime-hooks)。 ## 流式传输 + 部分回复 -- 助手增量会从 pi-agent-core 流式传输,并作为 `assistant` 事件发出。 -- 分块流式传输可在 `text_end` 或 `message_end` 时发出部分回复。 -- reasoning 流式传输可以作为单独流发出,也可以作为分块回复发出。 -- 关于分块和分块回复行为,请参阅[流式传输](/zh-CN/concepts/streaming)。 +- assistant 增量由 pi-agent-core 流式传输,并作为 `assistant` 事件发出。 +- 分块流式传输可以在 `text_end` 或 `message_end` 时发出部分回复。 +- reasoning 流式传输可以作为单独的流发出,也可以作为块回复发出。 +- 关于分块和块回复行为,参见 [Streaming](/zh-CN/concepts/streaming)。 ## 工具执行 + 消息工具 - 工具 start/update/end 事件会在 `tool` 流上发出。 -- 工具结果在记录/发出前,会针对大小和图像 payload 进行清理。 -- 消息工具发送会被跟踪,以抑制重复的助手确认消息。 +- 在记录/发出前,工具结果会针对大小和图像负载进行清理。 +- 会跟踪消息工具发送,以抑制重复的 assistant 确认信息。 ## 回复整形 + 抑制 -- 最终 payload 由以下内容组装而成: - - 助手文本(以及可选 reasoning) +- 最终负载由以下内容组装而成: + - assistant 文本(以及可选的 reasoning) - 内联工具摘要(在 verbose + 允许时) - - 模型出错时的助手错误文本 + - 模型报错时的 assistant 错误文本 - 精确的静默 token `NO_REPLY` / `no_reply` 会从出站 - payload 中过滤掉。 -- 消息工具重复项会从最终 payload 列表中移除。 -- 如果没有可渲染的 payload 剩余,且某个工具出错,则会发出回退工具错误回复 - (除非某个消息工具已经发送了用户可见回复)。 + 负载中过滤掉。 +- 消息工具重复项会从最终负载列表中移除。 +- 如果没有可渲染的负载剩余且某个工具报错,则会发出回退工具错误回复 + (除非某个消息工具已经发送了用户可见的回复)。 ## 压缩 + 重试 - 自动压缩会发出 `compaction` 流事件,并且可能触发重试。 -- 发生重试时,内存缓冲区和工具摘要会被重置,以避免重复输出。 -- 关于压缩管道,请参阅[压缩](/zh-CN/concepts/compaction)。 +- 重试时,内存缓冲区和工具摘要会被重置,以避免重复输出。 +- 关于压缩流水线,参见 [Compaction](/zh-CN/concepts/compaction)。 ## 事件流(当前) -- `lifecycle`:由 `subscribeEmbeddedPiSession` 发出(以及在回退情况下由 `agentCommand` 发出) +- `lifecycle`:由 `subscribeEmbeddedPiSession` 发出(并由 `agentCommand` 作为回退发出) - `assistant`:来自 pi-agent-core 的流式增量 - `tool`:来自 pi-agent-core 的流式工具事件 ## 聊天渠道处理 -- 助手增量会被缓冲成聊天 `delta` 消息。 -- 会在**生命周期结束/错误**时发出聊天 `final`。 +- assistant 增量会被缓冲为聊天 `delta` 消息。 +- 在 **lifecycle end/error** 时会发出聊天 `final`。 ## 超时 -- `agent.wait` 默认值:30 秒(仅等待本身)。可通过 `timeoutMs` 参数覆盖。 -- 智能体运行时:`agents.defaults.timeoutSeconds` 默认 172800 秒(48 小时);在 `runEmbeddedPiAgent` 的中止计时器中强制执行。 -- LLM 空闲超时:`agents.defaults.llm.idleTimeoutSeconds` 会在空闲窗口内没有收到响应分块时中止模型请求。对于较慢的本地模型或 reasoning/工具调用 provider,请显式设置;设为 0 可禁用。如果未设置,OpenClaw 会在配置了 `agents.defaults.timeoutSeconds` 时使用它,否则使用 120 秒。对于没有显式 LLM 或智能体超时的 cron 触发运行,会禁用空闲 watchdog,并依赖 cron 外层超时。 +- `agent.wait` 默认值:30 秒(仅等待)。可由 `timeoutMs` 参数覆盖。 +- 智能体运行时:`agents.defaults.timeoutSeconds` 默认值为 172800 秒(48 小时);在 `runEmbeddedPiAgent` 的中止计时器中强制执行。 +- LLM 空闲超时:`agents.defaults.llm.idleTimeoutSeconds` 会在空闲窗口内未收到任何响应分块时中止模型请求。对于较慢的本地模型或 reasoning/工具调用 provider,请显式设置它;将其设为 0 可禁用。如果未设置,OpenClaw 会在已配置时使用 `agents.defaults.timeoutSeconds`,否则使用 120 秒。对于没有显式 LLM 或智能体超时的 cron 触发运行,会禁用空闲看门狗,并依赖 cron 外层超时。 ## 可能提前结束的地方 @@ -170,8 +170,8 @@ OpenClaw 有两套 hook 系统: ## 相关内容 -- [工具](/zh-CN/tools) —— 可用的智能体工具 -- [Hooks](/zh-CN/automation/hooks) —— 由智能体生命周期事件触发的事件驱动脚本 -- [压缩](/zh-CN/concepts/compaction) —— 长对话如何被总结 -- [Exec 审批](/zh-CN/tools/exec-approvals) —— shell 命令的审批门槛 -- [Thinking](/zh-CN/tools/thinking) —— thinking/reasoning 级别配置 +- [Tools](/zh-CN/tools) — 可用的智能体工具 +- [Hooks](/zh-CN/automation/hooks) — 由智能体生命周期事件触发的事件驱动脚本 +- [Compaction](/zh-CN/concepts/compaction) — 长对话如何被总结 +- [Exec Approvals](/zh-CN/tools/exec-approvals) — shell 命令的审批门控 +- [Thinking](/zh-CN/tools/thinking) — thinking/reasoning 级别配置 diff --git a/docs/zh-CN/plugins/architecture-internals.md b/docs/zh-CN/plugins/architecture-internals.md new file mode 100644 index 000000000..972f092be --- /dev/null +++ b/docs/zh-CN/plugins/architecture-internals.md @@ -0,0 +1,875 @@ +--- +read_when: + - 实现提供商运行时钩子、渠道生命周期或软件包打包集 + - 调试插件加载顺序或注册表状态 + - 添加新的插件能力或上下文引擎插件 +summary: 插件架构内部机制:加载管线、注册表、运行时钩子、HTTP 路由和参考表格 +title: 插件架构内部机制 +x-i18n: + generated_at: "2026-04-24T03:06:49Z" + model: gpt-5.4 + provider: openai + source_hash: 243ccb0cb5b55c4ba08ac387f5b19949391b3a4f1772f6d7ac889f3d8f548a47 + source_path: plugins/architecture-internals.md + workflow: 15 +--- + +关于公开能力模型、插件形态以及所有权 / 执行契约,请参阅 [插件架构](/zh-CN/plugins/architecture)。本页是内部机制的参考文档:加载管线、注册表、运行时钩子、Gateway 网关 HTTP 路由、导入路径和 schema 表格。 + +## 加载管线 + +启动时,OpenClaw 大致会执行以下步骤: + +1. 发现候选插件根目录 +2. 读取原生或兼容 bundle 清单以及包元数据 +3. 拒绝不安全的候选项 +4. 规范化插件配置(`plugins.enabled`、`allow`、`deny`、`entries`、`slots`、`load.paths`) +5. 为每个候选项决定是否启用 +6. 加载已启用的原生模块:已构建的内置模块使用原生加载器;未构建的原生插件使用 `jiti` +7. 调用原生 `register(api)` 钩子,并将注册内容收集到插件注册表中 +8. 将注册表暴露给命令 / 运行时表面 + + +`activate` 是 `register` 的旧别名 —— 加载器会解析存在的那个(`def.register ?? def.activate`),并在同一时机调用它。所有内置插件都使用 `register`;新插件请优先使用 `register`。 + + +安全门禁会在运行时执行**之前**发生。当入口逃逸出插件根目录、路径可被所有人写入,或者对于非内置插件来说路径所有权看起来可疑时,候选项会被阻止。 + +### Manifest 优先行为 + +manifest 是控制平面的事实来源。OpenClaw 用它来: + +- 标识插件 +- 发现已声明的渠道 / Skills / 配置 schema 或 bundle 能力 +- 验证 `plugins.entries..config` +- 增强 Control UI 标签 / 占位符 +- 显示安装 / catalog 元数据 +- 在不加载插件运行时的情况下,保留低成本的激活与设置描述符 + +对于原生插件,运行时模块是数据平面部分。它负责注册实际行为,例如钩子、工具、命令或提供商流程。 + +可选的 manifest `activation` 和 `setup` 块仍然属于控制平面。它们只是用于激活规划和设置发现的纯元数据描述符;不会替代运行时注册、`register(...)` 或 `setupEntry`。 +首批实时激活使用方现在会利用 manifest 的命令、渠道和提供商提示,在更广泛的注册表物化之前缩小插件加载范围: + +- CLI 加载会缩小到拥有所请求主命令的插件 +- 渠道设置 / 插件解析会缩小到拥有所请求渠道 id 的插件 +- 显式提供商设置 / 运行时解析会缩小到拥有所请求提供商 id 的插件 + +设置发现现在优先使用描述符自有 id,例如 `setup.providers` 和 `setup.cliBackends`,以便在回退到 `setup-api` 之前先缩小候选插件范围;`setup-api` 仅用于那些仍需要设置期运行时钩子的插件。如果发现的多个插件声称拥有同一个规范化后的设置提供商或 CLI 后端 id,设置查找会拒绝这个存在歧义的所有者,而不是依赖发现顺序。 + +### 加载器会缓存什么 + +OpenClaw 会在进程内维护短期缓存,用于: + +- 发现结果 +- manifest 注册表数据 +- 已加载的插件注册表 + +这些缓存可以减少突发启动和重复命令的开销。可以把它们看作短生命周期的性能缓存,而不是持久化机制。 + +性能说明: + +- 设置 `OPENCLAW_DISABLE_PLUGIN_DISCOVERY_CACHE=1` 或 `OPENCLAW_DISABLE_PLUGIN_MANIFEST_CACHE=1` 可禁用这些缓存。 +- 使用 `OPENCLAW_PLUGIN_DISCOVERY_CACHE_MS` 和 `OPENCLAW_PLUGIN_MANIFEST_CACHE_MS` 调整缓存窗口。 + +## 注册表模型 + +已加载的插件不会直接随意修改核心全局状态。它们会注册到一个中心化的插件注册表中。 + +注册表会跟踪: + +- 插件记录(标识、来源、源头、状态、诊断信息) +- 工具 +- 旧式钩子和类型化钩子 +- 渠道 +- 提供商 +- Gateway 网关 RPC 处理器 +- HTTP 路由 +- CLI 注册器 +- 后台服务 +- 插件自有命令 + +然后,核心功能会从这个注册表中读取,而不是直接与插件模块交互。这让加载保持单向: + +- 插件模块 -> 注册表注册 +- 核心运行时 -> 注册表消费 + +这种分离对可维护性非常重要。它意味着大多数核心表面只需要一个集成点:“读取注册表”,而不是“为每个插件模块做特殊处理”。 + +## 会话绑定回调 + +绑定会话的插件可以在审批完成时作出响应。 + +使用 `api.onConversationBindingResolved(...)` 可以在绑定请求被批准或拒绝后接收回调: + +```ts +export default { + id: "my-plugin", + register(api) { + api.onConversationBindingResolved(async (event) => { + if (event.status === "approved") { + // 现在已为该插件 + 会话建立绑定。 + console.log(event.binding?.conversationId); + return; + } + + // 请求已被拒绝;清理任何本地挂起状态。 + console.log(event.request.conversation.conversationId); + }); + }, +}; +``` + +回调负载字段: + +- `status`:`"approved"` 或 `"denied"` +- `decision`:`"allow-once"`、`"allow-always"` 或 `"deny"` +- `binding`:已批准请求的已解析绑定 +- `request`:原始请求摘要、分离提示、发送者 id 和会话元数据 + +这个回调仅用于通知。它不会改变谁被允许绑定会话,并且会在核心审批处理完成后运行。 + +## 提供商运行时钩子 + +提供商插件有三层: + +- **Manifest 元数据**,用于廉价的运行前查找:`providerAuthEnvVars`、`providerAuthAliases`、`providerAuthChoices` 和 `channelEnvVars`。 +- **配置期钩子**:`catalog`(旧版 `discovery`)以及 `applyConfigDefaults`。 +- **运行时钩子**:40 多个可选钩子,涵盖凭证、模型解析、流包装、思考级别、重放策略和用量端点。完整列表见 [钩子顺序与用法](#hook-order-and-usage)。 + +OpenClaw 仍然负责通用智能体循环、故障切换、转录处理和工具策略。这些钩子是提供商特定行为的扩展表面,因此无需整套自定义推理传输也能实现相应能力。 + +当提供商具有基于环境变量的凭证,并且希望通用凭证 / 状态 / 模型选择器路径在不加载插件运行时的情况下也能看到这些信息时,请使用 manifest `providerAuthEnvVars`。当某个提供商 id 应复用另一个提供商 id 的环境变量、凭证配置文件、基于配置的凭证和 API 密钥新手引导选项时,请使用 manifest `providerAuthAliases`。当新手引导 / 凭证选择 CLI 表面需要在不加载提供商运行时的情况下了解该提供商的 choice id、分组标签和简单的单标志凭证接线方式时,请使用 manifest `providerAuthChoices`。请将提供商运行时 `envVars` 保留给面向操作员的提示,例如新手引导标签或 OAuth client-id / client-secret 设置变量。 + +当某个渠道具有由环境变量驱动的凭证或设置,并且希望通用 shell 环境变量回退、配置 / 状态检查或设置提示在不加载渠道运行时的情况下也能看到这些信息时,请使用 manifest `channelEnvVars`。 + +### 钩子顺序与用法 + +对于模型 / 提供商插件,OpenClaw 大致会按以下顺序调用钩子。 +“何时使用”列是快速决策指南。 + +| # | 钩子 | 作用 | 何时使用 | +| --- | --------------------------------- | -------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | `catalog` | 在生成 `models.json` 期间,将提供商配置发布到 `models.providers` 中 | 提供商拥有 catalog 或 base URL 默认值 | +| 2 | `applyConfigDefaults` | 在配置物化期间应用提供商自有的全局配置默认值 | 默认值依赖于凭证模式、环境变量或提供商模型族语义 | +| -- | _(内置模型查找)_ | OpenClaw 会先尝试常规的注册表 / catalog 路径 | _(不是插件钩子)_ | +| 3 | `normalizeModelId` | 在查找前规范化旧版或预览版 model-id 别名 | 提供商拥有在规范模型解析之前进行别名清理的逻辑 | +| 4 | `normalizeTransport` | 在通用模型组装前规范化提供商族的 `api` / `baseUrl` | 提供商需要为同一传输族中的自定义提供商 id 执行传输清理 | +| 5 | `normalizeConfig` | 在运行时 / 提供商解析前规范化 `models.providers.` | 提供商需要将配置清理逻辑放在插件中;内置的 Google 族辅助逻辑也会为受支持的 Google 配置项提供兜底 | +| 6 | `applyNativeStreamingUsageCompat` | 将原生流式使用量兼容性重写应用到配置提供商 | 提供商需要基于端点驱动修复原生流式使用量元数据 | +| 7 | `resolveConfigApiKey` | 在加载运行时凭证前,为配置提供商解析 env-marker 凭证 | 提供商拥有自己的 env-marker API 密钥解析逻辑;`amazon-bedrock` 在这里也有一个内置的 AWS env-marker 解析器 | +| 8 | `resolveSyntheticAuth` | 在不持久化明文的情况下暴露本地 / 自托管或基于配置的凭证 | 提供商可以使用 synthetic / 本地凭证标记运行 | +| 9 | `resolveExternalAuthProfiles` | 叠加提供商自有的外部凭证配置文件;CLI / app 自有凭证的默认 `persistence` 为 `runtime-only` | 提供商可复用外部凭证,而无需持久化复制的 refresh token;请在 manifest 中声明 `contracts.externalAuthProviders` | +| 10 | `shouldDeferSyntheticProfileAuth` | 将已存储的 synthetic 配置文件占位符降级到 env / 基于配置的凭证之后 | 提供商会存储 synthetic 占位配置文件,而这些配置文件不应获得更高优先级 | +| 11 | `resolveDynamicModel` | 为尚未出现在本地注册表中的提供商自有模型 id 提供同步回退 | 提供商接受任意上游模型 id | +| 12 | `prepareDynamicModel` | 异步预热,然后再次运行 `resolveDynamicModel` | 提供商在解析未知 id 之前需要网络元数据 | +| 13 | `normalizeResolvedModel` | 在嵌入式 runner 使用已解析模型之前进行最终重写 | 提供商需要进行传输重写,但仍然使用核心传输 | +| 14 | `contributeResolvedModelCompat` | 为位于另一兼容传输之后的供应商模型提供兼容性标志 | 提供商可以在代理传输上识别自己的模型,而无需接管该提供商 | +| 15 | `capabilities` | 由共享核心逻辑使用的提供商自有转录 / 工具元数据 | 提供商需要处理转录或提供商族特有的差异 | +| 16 | `normalizeToolSchemas` | 在嵌入式 runner 看到工具 schema 之前进行规范化 | 提供商需要进行传输族的 schema 清理 | +| 17 | `inspectToolSchemas` | 在规范化后暴露提供商自有的 schema 诊断信息 | 提供商希望提供关键字警告,而无需让核心理解特定于提供商的规则 | +| 18 | `resolveReasoningOutputMode` | 选择原生还是带标签的推理输出契约 | 提供商需要带标签的推理 / 最终输出,而不是原生字段 | +| 19 | `prepareExtraParams` | 在通用流选项包装器之前进行请求参数规范化 | 提供商需要默认请求参数或按提供商进行参数清理 | +| 20 | `createStreamFn` | 使用自定义传输完全替换常规流路径 | 提供商需要自定义线协议,而不只是包装器 | +| 21 | `wrapStreamFn` | 在应用通用包装器之后再包装流函数 | 提供商需要请求头 / 请求体 / 模型兼容性包装器,但不需要自定义传输 | +| 22 | `resolveTransportTurnState` | 附加原生的逐轮传输请求头或元数据 | 提供商希望通用传输发送提供商原生的轮次标识 | +| 23 | `resolveWebSocketSessionPolicy` | 附加原生 WebSocket 请求头或会话冷却策略 | 提供商希望通用 WS 传输调整会话请求头或回退策略 | +| 24 | `formatApiKey` | 凭证配置文件格式化器:已存储的配置文件会变成运行时 `apiKey` 字符串 | 提供商存储了额外凭证元数据,并需要自定义的运行时 token 形态 | +| 25 | `refreshOAuth` | 针对自定义刷新端点或刷新失败策略的 OAuth 刷新覆盖 | 提供商不适用于共享的 `pi-ai` 刷新器 | +| 26 | `buildAuthDoctorHint` | 当 OAuth 刷新失败时附加修复提示 | 提供商需要在刷新失败后提供其自有的凭证修复指引 | +| 27 | `matchesContextOverflowError` | 提供商自有的上下文窗口溢出匹配器 | 提供商存在通用启发式无法捕获的原始溢出错误 | +| 28 | `classifyFailoverReason` | 提供商自有的故障切换原因分类 | 提供商可以将原始 API / 传输错误映射为速率限制、过载等 | +| 29 | `isCacheTtlEligible` | 面向代理 / 回传提供商的 prompt-cache 策略 | 提供商需要代理特定的缓存 TTL 门禁 | +| 30 | `buildMissingAuthMessage` | 替换通用的缺失凭证恢复消息 | 提供商需要特定于提供商的缺失凭证恢复提示 | +| 31 | `suppressBuiltInModel` | 抑制过时的上游模型,并可选替换为面向用户的错误提示 | 提供商需要隐藏过时的上游条目,或用供应商提示替换它们 | +| 32 | `augmentModelCatalog` | 在发现后附加 synthetic / 最终 catalog 条目 | 提供商需要在 `models list` 和选择器中加入 synthetic 的前向兼容条目 | +| 33 | `resolveThinkingProfile` | 设置模型特定的 `/think` 级别、显示标签和默认值 | 提供商为选定模型公开自定义的思考梯度或二元标签 | +| 34 | `isBinaryThinking` | 开 / 关推理切换兼容性钩子 | 提供商只支持二元的思考开 / 关 | +| 35 | `supportsXHighThinking` | `xhigh` 推理支持兼容性钩子 | 提供商希望仅在部分模型上支持 `xhigh` | +| 36 | `resolveDefaultThinkingLevel` | 默认 `/think` 级别兼容性钩子 | 提供商拥有某个模型族的默认 `/think` 策略 | +| 37 | `isModernModelRef` | 用于实时配置文件过滤和 smoke 选择的现代模型匹配器 | 提供商拥有实时 / smoke 首选模型匹配逻辑 | +| 38 | `prepareRuntimeAuth` | 在推理前立即将已配置的凭证交换为实际运行时 token / 密钥 | 提供商需要 token 交换或短期请求凭证 | +| 39 | `resolveUsageAuth` | 为 `/usage` 及相关状态表面解析使用量 / 计费凭证 | 提供商需要自定义使用量 / 配额 token 解析,或需要不同的使用量凭证 | +| 40 | `fetchUsageSnapshot` | 在凭证解析后获取并规范化特定于提供商的使用量 / 配额快照 | 提供商需要特定于提供商的使用量端点或负载解析器 | +| 41 | `createEmbeddingProvider` | 为内存 / 搜索构建提供商自有的 embedding 适配器 | Memory embedding 行为应归属于提供商插件 | +| 42 | `buildReplayPolicy` | 返回一个控制该提供商转录处理方式的重放策略 | 提供商需要自定义转录策略(例如去除 thinking 块) | +| 43 | `sanitizeReplayHistory` | 在通用转录清理后重写重放历史 | 提供商需要共享压缩辅助逻辑之外的特定于提供商的重放重写 | +| 44 | `validateReplayTurns` | 在嵌入式 runner 执行前,对重放轮次进行最终验证或重塑 | 提供商传输在通用清洗后需要更严格的轮次验证 | +| 45 | `onModelSelected` | 在模型被选中后运行提供商自有的副作用 | 当模型变为活动状态时,提供商需要遥测或提供商自有状态 | + +`normalizeModelId`、`normalizeTransport` 和 `normalizeConfig` 会先检查已匹配的提供商插件,然后继续落到其他具备钩子能力的提供商插件,直到某个插件实际修改了模型 id 或传输 / 配置。这样可以让别名 / 兼容提供商 shim 正常工作,而不要求调用方知道哪个内置插件拥有该重写逻辑。如果没有任何提供商钩子重写受支持的 Google 族配置项,内置的 Google 配置规范化器仍会应用该兼容性清理。 + +如果提供商需要完全自定义的线协议或自定义请求执行器,那属于另一类扩展。这些钩子适用于仍运行在 OpenClaw 常规推理循环上的提供商行为。 + +### 提供商示例 + +```ts +api.registerProvider({ + id: "example-proxy", + label: "Example Proxy", + auth: [], + catalog: { + order: "simple", + run: async (ctx) => { + const apiKey = ctx.resolveProviderApiKey("example-proxy").apiKey; + if (!apiKey) { + return null; + } + return { + provider: { + baseUrl: "https://proxy.example.com/v1", + apiKey, + api: "openai-completions", + models: [{ id: "auto", name: "Auto" }], + }, + }; + }, + }, + resolveDynamicModel: (ctx) => ({ + id: ctx.modelId, + name: ctx.modelId, + provider: "example-proxy", + api: "openai-completions", + baseUrl: "https://proxy.example.com/v1", + reasoning: false, + input: ["text"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 128000, + maxTokens: 8192, + }), + prepareRuntimeAuth: async (ctx) => { + const exchanged = await exchangeToken(ctx.apiKey); + return { + apiKey: exchanged.token, + baseUrl: exchanged.baseUrl, + expiresAt: exchanged.expiresAt, + }; + }, + resolveUsageAuth: async (ctx) => { + const auth = await ctx.resolveOAuthToken(); + return auth ? { token: auth.token } : null; + }, + fetchUsageSnapshot: async (ctx) => { + return await fetchExampleProxyUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn); + }, +}); +``` + +### 内置示例 + +内置提供商插件会组合使用上述钩子,以适配各个供应商在 catalog、凭证、思考、重放和使用量方面的需求。权威的钩子集合与各插件一起保存在 `extensions/` 下;本页用于说明其形态,而不是镜像列出完整清单。 + + + + OpenRouter、Kilocode、Z.AI、xAI 会注册 `catalog`,以及 `resolveDynamicModel` / `prepareDynamicModel`,这样它们就能在 OpenClaw 的静态 catalog 之前暴露上游模型 id。 + + + GitHub Copilot、Gemini CLI、ChatGPT Codex、MiniMax、Xiaomi、z.ai 会将 `prepareRuntimeAuth` 或 `formatApiKey` 与 `resolveUsageAuth` + `fetchUsageSnapshot` 配对使用,以接管 token 交换和 `/usage` 集成。 + + + 共享的命名族(`google-gemini`、`passthrough-gemini`、`anthropic-by-model`、`hybrid-anthropic-openai`)让提供商可以通过 `buildReplayPolicy` 选择加入转录策略,而不是让每个插件各自重新实现清理逻辑。 + + + `byteplus`、`cloudflare-ai-gateway`、`huggingface`、`kimi-coding`、`nvidia`、`qianfan`、`synthetic`、`together`、`venice`、`vercel-ai-gateway` 和 `volcengine` 只注册 `catalog`,并复用共享推理循环。 + + + Beta 请求头、`/fast` / `serviceTier` 以及 `context1m` 位于 Anthropic 插件公开的 `api.ts` / `contract-api.ts` 接缝中(`wrapAnthropicProviderStream`、`resolveAnthropicBetas`、`resolveAnthropicFastMode`、`resolveAnthropicServiceTier`),而不是位于通用 SDK 中。 + + + +## 运行时辅助工具 + +插件可以通过 `api.runtime` 访问选定的核心辅助工具。对于 TTS: + +```ts +const clip = await api.runtime.tts.textToSpeech({ + text: "Hello from OpenClaw", + cfg: api.config, +}); + +const result = await api.runtime.tts.textToSpeechTelephony({ + text: "Hello from OpenClaw", + cfg: api.config, +}); + +const voices = await api.runtime.tts.listVoices({ + provider: "elevenlabs", + cfg: api.config, +}); +``` + +说明: + +- `textToSpeech` 返回文件 / 语音笔记表面使用的常规核心 TTS 输出负载。 +- 使用核心 `messages.tts` 配置和提供商选择逻辑。 +- 返回 PCM 音频缓冲区 + 采样率。插件必须为提供商自行重采样 / 编码。 +- `listVoices` 对每个提供商来说都是可选的。可将其用于供应商自有的语音选择器或设置流程。 +- 语音列表可以包含更丰富的元数据,例如语言区域、性别和个性标签,以支持提供商感知的选择器。 +- 目前支持电话语音的有 OpenAI 和 ElevenLabs。Microsoft 不支持。 + +插件也可以通过 `api.registerSpeechProvider(...)` 注册语音提供商。 + +```ts +api.registerSpeechProvider({ + id: "acme-speech", + label: "Acme Speech", + isConfigured: ({ config }) => Boolean(config.messages?.tts), + synthesize: async (req) => { + return { + audioBuffer: Buffer.from([]), + outputFormat: "mp3", + fileExtension: ".mp3", + voiceCompatible: false, + }; + }, +}); +``` + +说明: + +- 将 TTS 策略、回退和回复投递保留在核心中。 +- 对供应商自有的合成行为使用语音提供商。 +- 旧版 Microsoft `edge` 输入会被规范化为 `microsoft` 提供商 id。 +- 首选的所有权模型是面向公司的:随着 OpenClaw 增加这些能力契约,一个供应商插件可以同时拥有文本、语音、图像以及未来的媒体提供商。 + +对于图像 / 音频 / 视频理解,插件会注册一个类型化的媒体理解提供商,而不是通用的键值包: + +```ts +api.registerMediaUnderstandingProvider({ + id: "google", + capabilities: ["image", "audio", "video"], + describeImage: async (req) => ({ text: "..." }), + transcribeAudio: async (req) => ({ text: "..." }), + describeVideo: async (req) => ({ text: "..." }), +}); +``` + +说明: + +- 将编排、回退、配置和渠道接线保留在核心中。 +- 将供应商行为保留在提供商插件中。 +- 增量扩展应保持类型化:新的可选方法、新的可选结果字段、新的可选能力。 +- 视频生成已经遵循相同模式: + - 核心拥有能力契约和运行时辅助工具 + - 供应商插件注册 `api.registerVideoGenerationProvider(...)` + - 功能 / 渠道插件消费 `api.runtime.videoGeneration.*` + +对于媒体理解运行时辅助工具,插件可以调用: + +```ts +const image = await api.runtime.mediaUnderstanding.describeImageFile({ + filePath: "/tmp/inbound-photo.jpg", + cfg: api.config, + agentDir: "/tmp/agent", +}); + +const video = await api.runtime.mediaUnderstanding.describeVideoFile({ + filePath: "/tmp/inbound-video.mp4", + cfg: api.config, +}); +``` + +对于音频转录,插件既可以使用媒体理解运行时,也可以使用较旧的 STT 别名: + +```ts +const { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({ + filePath: "/tmp/inbound-audio.ogg", + cfg: api.config, + // 当无法可靠推断 MIME 时可选: + mime: "audio/ogg", +}); +``` + +说明: + +- `api.runtime.mediaUnderstanding.*` 是图像 / 音频 / 视频理解的首选共享表面。 +- 使用核心媒体理解音频配置(`tools.media.audio`)和提供商回退顺序。 +- 当未产生转录输出时返回 `{ text: undefined }`(例如输入被跳过 / 不受支持)。 +- `api.runtime.stt.transcribeAudioFile(...)` 仍然保留作为兼容性别名。 + +插件也可以通过 `api.runtime.subagent` 启动后台子智能体运行: + +```ts +const result = await api.runtime.subagent.run({ + sessionKey: "agent:main:subagent:search-helper", + message: "Expand this query into focused follow-up searches.", + provider: "openai", + model: "gpt-4.1-mini", + deliver: false, +}); +``` + +说明: + +- `provider` 和 `model` 是每次运行的可选覆盖项,不是持久化的会话更改。 +- OpenClaw 只会为受信任调用方应用这些覆盖字段。 +- 对于插件自有的回退运行,操作员必须通过 `plugins.entries..subagent.allowModelOverride: true` 明确启用。 +- 使用 `plugins.entries..subagent.allowedModels` 可将受信任插件限制为特定的规范 `provider/model` 目标,或设为 `"*"` 以明确允许任意目标。 +- 非受信任插件的子智能体运行仍然可用,但覆盖请求会被拒绝,而不是静默回退。 + +对于 Web 搜索,插件可以消费共享运行时辅助工具,而不是深入智能体工具接线: + +```ts +const providers = api.runtime.webSearch.listProviders({ + config: api.config, +}); + +const result = await api.runtime.webSearch.search({ + config: api.config, + args: { + query: "OpenClaw plugin runtime helpers", + count: 5, + }, +}); +``` + +插件也可以通过 `api.registerWebSearchProvider(...)` 注册 Web 搜索提供商。 + +说明: + +- 将提供商选择、凭证解析和共享请求语义保留在核心中。 +- 对供应商特定的搜索传输使用 Web 搜索提供商。 +- `api.runtime.webSearch.*` 是功能 / 渠道插件在不依赖智能体工具包装器的情况下需要搜索行为时的首选共享表面。 + +### `api.runtime.imageGeneration` + +```ts +const result = await api.runtime.imageGeneration.generate({ + config: api.config, + args: { prompt: "A friendly lobster mascot", size: "1024x1024" }, +}); + +const providers = api.runtime.imageGeneration.listProviders({ + config: api.config, +}); +``` + +- `generate(...)`:使用已配置的图像生成提供商链生成图像。 +- `listProviders(...)`:列出可用的图像生成提供商及其能力。 + +## Gateway 网关 HTTP 路由 + +插件可以通过 `api.registerHttpRoute(...)` 暴露 HTTP 端点。 + +```ts +api.registerHttpRoute({ + path: "/acme/webhook", + auth: "plugin", + match: "exact", + handler: async (_req, res) => { + res.statusCode = 200; + res.end("ok"); + return true; + }, +}); +``` + +路由字段: + +- `path`:Gateway 网关 HTTP 服务器下的路由路径。 +- `auth`:必填。使用 `"gateway"` 以要求常规 Gateway 网关认证,或使用 `"plugin"` 进行插件管理的认证 / webhook 验证。 +- `match`:可选。`"exact"`(默认)或 `"prefix"`。 +- `replaceExisting`:可选。允许同一插件替换自己已存在的路由注册。 +- `handler`:当路由处理了请求时返回 `true`。 + +说明: + +- `api.registerHttpHandler(...)` 已移除,并会导致插件加载错误。请改用 `api.registerHttpRoute(...)`。 +- 插件路由必须显式声明 `auth`。 +- 除非设置了 `replaceExisting: true`,否则精确的 `path + match` 冲突会被拒绝,而且一个插件不能替换另一个插件的路由。 +- 不同 `auth` 级别的重叠路由会被拒绝。请仅在相同认证级别内保持 `exact` / `prefix` 贯穿链。 +- `auth: "plugin"` 路由**不会**自动接收操作员运行时作用域。它们用于插件管理的 webhook / 签名验证,而不是特权 Gateway 网关辅助调用。 +- `auth: "gateway"` 路由会在 Gateway 网关请求运行时作用域内运行,但该作用域刻意保持保守: + - 共享密钥 bearer 认证(`gateway.auth.mode = "token"` / `"password"`)会将插件路由运行时作用域固定为 `operator.write`,即使调用方发送了 `x-openclaw-scopes` 也是如此 + - 受信任的、携带身份的 HTTP 模式(例如 `trusted-proxy`,或私有入口上的 `gateway.auth.mode = "none"`)仅在显式存在该请求头时才会采纳 `x-openclaw-scopes` + - 如果这些携带身份的插件路由请求中缺少 `x-openclaw-scopes`,运行时作用域会回退为 `operator.write` +- 实用规则:不要假设经过 Gateway 网关认证的插件路由就是隐式管理员表面。如果你的路由需要仅管理员可用的行为,请要求使用携带身份的认证模式,并记录显式的 `x-openclaw-scopes` 请求头契约。 + +## 插件 SDK 导入路径 + +在编写新插件时,请使用狭窄的 SDK 子路径,而不是单体式的 `openclaw/plugin-sdk` 根 barrel。核心子路径: + +| 子路径 | 用途 | +| ----------------------------------- | -------------------------------------------------- | +| `openclaw/plugin-sdk/plugin-entry` | 插件注册原语 | +| `openclaw/plugin-sdk/channel-core` | 渠道入口 / 构建辅助工具 | +| `openclaw/plugin-sdk/core` | 通用共享辅助工具和总括契约 | +| `openclaw/plugin-sdk/config-schema` | 根 `openclaw.json` Zod schema(`OpenClawSchema`) | + +渠道插件应从一组狭窄接缝中进行选择 —— `channel-setup`、`setup-runtime`、`setup-adapter-runtime`、`setup-tools`、`channel-pairing`、`channel-contract`、`channel-feedback`、`channel-inbound`、`channel-lifecycle`、`channel-reply-pipeline`、`command-auth`、`secret-input`、`webhook-ingress`、`channel-targets` 和 `channel-actions`。审批行为应统一到单一的 `approvalCapability` 契约上,而不是分散混用在不相关的插件字段中。参见 [渠道插件](/zh-CN/plugins/sdk-channel-plugins)。 + +运行时和配置辅助工具位于对应的 `*-runtime` 子路径下(`approval-runtime`、`config-runtime`、`infra-runtime`、`agent-runtime`、`lazy-runtime`、`directory-runtime`、`text-runtime`、`runtime-store` 等)。 + + +`openclaw/plugin-sdk/channel-runtime` 已弃用 —— 它只是面向旧插件的兼容性 shim。新代码应改为导入更狭窄的通用原语。 + + +仓库内部入口点(按每个内置插件包根目录划分): + +- `index.js` —— 内置插件入口 +- `api.js` —— 辅助工具 / 类型 barrel +- `runtime-api.js` —— 仅运行时 barrel +- `setup-entry.js` —— 设置插件入口 + +外部插件应只导入 `openclaw/plugin-sdk/*` 子路径。绝不要从核心或另一个插件中导入其他插件包的 `src/*`。 +通过 facade 加载的入口点会优先使用当前活动的运行时配置快照;如果不存在,再回退到磁盘上的已解析配置文件。 + +像 `image-generation`、`media-understanding` 和 `speech` 这样的能力专用子路径之所以存在,是因为内置插件目前正在使用它们。它们并不自动构成长期冻结的外部契约 —— 在依赖它们时,请查阅对应的 SDK 参考页面。 + +## 消息工具 schema + +对于 reaction、已读和投票这类非消息原语,插件应自行提供特定于渠道的 `describeMessageTool(...)` schema 贡献。共享发送呈现应使用通用的 `MessagePresentation` 契约,而不是提供商原生的按钮、组件、块或卡片字段。关于契约、回退规则、提供商映射以及插件作者检查清单,请参见 [Message Presentation](/zh-CN/plugins/message-presentation)。 + +具备发送能力的插件通过消息能力声明自己可以渲染的内容: + +- `presentation`:用于语义化呈现块(`text`、`context`、`divider`、`buttons`、`select`) +- `delivery-pin`:用于置顶投递请求 + +核心决定是原生渲染该 presentation,还是将其降级为文本。不要从通用消息工具中暴露提供商原生 UI 逃生口。 +面向旧原生 schema 的已弃用 SDK 辅助工具仍会为现有第三方插件导出,但新插件不应使用它们。 + +## 渠道目标解析 + +渠道插件应拥有特定于渠道的目标语义。保持共享出站宿主通用,并通过消息适配器表面处理提供商规则: + +- `messaging.inferTargetChatType({ to })` 用于在目录查找前判断规范化目标应被视为 `direct`、`group` 还是 `channel`。 +- `messaging.targetResolver.looksLikeId(raw, normalized)` 用于告知核心,某个输入是否应跳过目录搜索,直接进入类似 id 的解析。 +- `messaging.targetResolver.resolveTarget(...)` 是插件的回退路径,当核心在规范化之后或目录未命中之后需要最终的提供商自有解析时使用。 +- `messaging.resolveOutboundSessionRoute(...)` 在目标解析完成后负责构建特定于提供商的会话路由。 + +推荐拆分方式: + +- 对于应在搜索联系人 / 群组之前发生的类别判定,使用 `inferTargetChatType`。 +- 对于“把这个视为显式 / 原生目标 id”的检查,使用 `looksLikeId`。 +- 对于提供商特定的规范化回退,使用 `resolveTarget`,而不是用它做宽泛的目录搜索。 +- 将 chat id、thread id、JID、handle 和 room id 这类提供商原生 id 保留在 `target` 值或提供商专用参数中,而不是放入通用 SDK 字段。 + +## 基于配置的目录 + +对于从配置派生目录项的插件,应将该逻辑保留在插件内部,并复用 `openclaw/plugin-sdk/directory-runtime` 中的共享辅助工具。 + +适用于以下这类渠道需要基于配置的联系人 / 群组时: + +- 基于 allowlist 的私信联系人 +- 已配置的渠道 / 群组映射 +- 按账号作用域的静态目录回退 + +`directory-runtime` 中的共享辅助工具只处理通用操作: + +- 查询过滤 +- 限制数量应用 +- 去重 / 规范化辅助工具 +- 构建 `ChannelDirectoryEntry[]` + +特定于渠道的账号检查和 id 规范化应保留在插件实现中。 + +## 提供商 catalog + +提供商插件可以通过 `registerProvider({ catalog: { run(...) { ... } } })` 为推理定义模型 catalog。 + +`catalog.run(...)` 返回的形态与 OpenClaw 写入 `models.providers` 的内容相同: + +- `{ provider }`:单个提供商条目 +- `{ providers }`:多个提供商条目 + +当插件拥有特定于提供商的模型 id、base URL 默认值或受凭证控制的模型元数据时,请使用 `catalog`。 + +`catalog.order` 用于控制插件的 catalog 相对于 OpenClaw 内置隐式提供商的合并时机: + +- `simple`:普通 API 密钥或环境变量驱动的提供商 +- `profile`:当存在凭证配置文件时出现的提供商 +- `paired`:合成多个相关提供商条目的提供商 +- `late`:最后一轮,在其他隐式提供商之后 + +在键冲突时,后出现的提供商会胜出,因此插件可以有意使用相同的提供商 id 覆盖某个内置提供商条目。 + +兼容性: + +- `discovery` 仍然作为旧别名可用 +- 如果同时注册了 `catalog` 和 `discovery`,OpenClaw 会使用 `catalog` + +## 只读渠道检查 + +如果你的插件注册了一个渠道,请优先实现 `plugin.config.inspectAccount(cfg, accountId)`,并与 `resolveAccount(...)` 配套使用。 + +原因: + +- `resolveAccount(...)` 是运行时路径。它可以假定凭证已被完整物化,并且当缺少必需 secret 时可以快速失败。 +- 只读命令路径,例如 `openclaw status`、`openclaw status --all`、`openclaw channels status`、`openclaw channels resolve` 以及 doctor / config 修复流程,不应仅仅为了描述配置就去物化运行时凭证。 + +推荐的 `inspectAccount(...)` 行为: + +- 只返回描述性的账号状态。 +- 保留 `enabled` 和 `configured`。 +- 在相关时包含凭证来源 / 状态字段,例如: + - `tokenSource`、`tokenStatus` + - `botTokenSource`、`botTokenStatus` + - `appTokenSource`、`appTokenStatus` + - `signingSecretSource`、`signingSecretStatus` +- 仅为了报告只读可用性,并不需要返回原始 token 值。返回 `tokenStatus: "available"`(以及对应的来源字段)就足以支持状态类命令。 +- 当凭证通过 SecretRef 配置但在当前命令路径中不可用时,请使用 `configured_unavailable`。 + +这样,只读命令就可以报告“已配置,但在当前命令路径中不可用”,而不是崩溃或误报该账号未配置。 + +## 软件包打包集 + +插件目录可以包含一个带有 `openclaw.extensions` 的 `package.json`: + +```json +{ + "name": "my-pack", + "openclaw": { + "extensions": ["./src/safety.ts", "./src/tools.ts"], + "setupEntry": "./src/setup-entry.ts" + } +} +``` + +每个条目都会变成一个插件。如果该打包集列出多个扩展,插件 id 会变成 `name/`。 + +如果你的插件导入了 npm 依赖,请在该目录中安装它们,以便 `node_modules` 可用(`npm install` / `pnpm install`)。 + +安全护栏:每个 `openclaw.extensions` 条目在解析符号链接后都必须仍然位于插件目录内。逃逸出包目录的条目会被拒绝。 + +安全说明:`openclaw plugins install` 会使用 `npm install --omit=dev --ignore-scripts` 安装插件依赖(不运行生命周期脚本,运行时不安装开发依赖)。请保持插件依赖树为“纯 JS / TS”,并避免需要 `postinstall` 构建的包。 + +可选:`openclaw.setupEntry` 可以指向一个轻量级的仅设置模块。当 OpenClaw 需要为一个已禁用的渠道插件提供设置表面,或者当某个渠道插件已启用但尚未配置时,它会加载 `setupEntry`,而不是完整插件入口。这样在主插件入口还会接线工具、钩子或其他仅运行时代码时,可以让启动和设置更轻量。 + +可选:`openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen` 可以让一个渠道插件在 Gateway 网关 pre-listen 启动阶段也选择使用相同的 `setupEntry` 路径,即使该渠道已经配置完成。 + +仅当 `setupEntry` 完整覆盖 Gateway 网关开始监听之前必须存在的启动表面时,才应使用此选项。实际而言,这意味着设置入口必须注册启动所依赖的每一项渠道自有能力,例如: + +- 渠道注册本身 +- 任何必须在 Gateway 网关开始监听前可用的 HTTP 路由 +- 任何必须在同一时间窗口内存在的 Gateway 网关方法、工具或服务 + +如果你的完整入口仍拥有任何必需的启动能力,就不要启用这个标志。请保持插件采用默认行为,让 OpenClaw 在启动期间加载完整入口。 + +内置渠道还可以发布仅设置期的契约表面辅助工具,供核心在完整渠道运行时加载前查询。当前的设置提升表面包括: + +- `singleAccountKeysToMove` +- `namedAccountPromotionKeys` +- `resolveSingleAccountPromotionTarget(...)` + +当核心需要在不加载完整插件入口的情况下,将旧版单账号渠道配置提升到 `channels..accounts.*` 时,就会使用这组表面。Matrix 是当前的内置示例:当已存在命名账号时,它只会把凭证 / bootstrap 键移动到某个已命名的提升账号中,并且它能够保留一个已配置的非规范 default-account 键,而不是总是创建 `accounts.default`。 + +这些设置补丁适配器让内置契约表面发现保持惰性。导入时开销保持很轻;提升表面只会在首次使用时加载,而不是在模块导入时重新进入内置渠道启动过程。 + +当这些启动表面包含 Gateway 网关 RPC 方法时,请将它们保持在插件专用前缀下。核心管理命名空间(`config.*`、`exec.approvals.*`、`wizard.*`、`update.*`)仍然是保留的,并且始终解析为 `operator.admin`,即使某个插件请求了更窄的作用域也是如此。 + +示例: + +```json +{ + "name": "@scope/my-channel", + "openclaw": { + "extensions": ["./index.ts"], + "setupEntry": "./setup-entry.ts", + "startup": { + "deferConfiguredChannelFullLoadUntilAfterListen": true + } + } +} +``` + +### 渠道 catalog 元数据 + +渠道插件可以通过 `openclaw.channel` 公布设置 / 发现元数据,并通过 `openclaw.install` 公布安装提示。这样可以让核心 catalog 保持无数据。 + +示例: + +```json +{ + "name": "@openclaw/nextcloud-talk", + "openclaw": { + "extensions": ["./index.ts"], + "channel": { + "id": "nextcloud-talk", + "label": "Nextcloud Talk", + "selectionLabel": "Nextcloud Talk(自托管)", + "docsPath": "/channels/nextcloud-talk", + "docsLabel": "nextcloud-talk", + "blurb": "通过 Nextcloud Talk webhook 机器人实现的自托管聊天。", + "order": 65, + "aliases": ["nc-talk", "nc"] + }, + "install": { + "npmSpec": "@openclaw/nextcloud-talk", + "localPath": "", + "defaultChoice": "npm" + } + } +} +``` + +除了最小示例之外,还有一些有用的 `openclaw.channel` 字段: + +- `detailLabel`:用于更丰富的 catalog / 状态表面的次级标签 +- `docsLabel`:覆盖文档链接的链接文本 +- `preferOver`:该 catalog 条目应优先于的较低优先级插件 / 渠道 id +- `selectionDocsPrefix`、`selectionDocsOmitLabel`、`selectionExtras`:选择表面文案控制 +- `markdownCapable`:将该渠道标记为支持 Markdown,以供出站格式化决策使用 +- `exposure.configured`:设置为 `false` 时,在已配置渠道列表表面中隐藏该渠道 +- `exposure.setup`:设置为 `false` 时,在交互式设置 / 配置选择器中隐藏该渠道 +- `exposure.docs`:将该渠道标记为内部 / 私有,以供文档导航表面使用 +- `showConfigured` / `showInSetup`:出于兼容性仍接受的旧别名;优先使用 `exposure` +- `quickstartAllowFrom`:让该渠道接入标准快速开始 `allowFrom` 流程 +- `forceAccountBinding`:即使只存在一个账号,也要求显式账号绑定 +- `preferSessionLookupForAnnounceTarget`:在解析公告目标时优先进行会话查找 + +OpenClaw 还可以合并**外部渠道 catalog**(例如 MPM 注册表导出)。将 JSON 文件放到以下任一位置: + +- `~/.openclaw/mpm/plugins.json` +- `~/.openclaw/mpm/catalog.json` +- `~/.openclaw/plugins/catalog.json` + +或者将 `OPENCLAW_PLUGIN_CATALOG_PATHS`(或 `OPENCLAW_MPM_CATALOG_PATHS`)指向一个或多个 JSON 文件(以逗号 / 分号 / `PATH` 分隔)。每个文件应包含 `{ "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] }`。解析器还接受 `"packages"` 或 `"plugins"` 作为 `"entries"` 键的旧别名。 + +## 上下文引擎插件 + +上下文引擎插件负责会话上下文编排,包括摄取、组装和压缩。通过 `api.registerContextEngine(id, factory)` 从你的插件中注册它们,然后通过 `plugins.slots.contextEngine` 选择活动引擎。 + +当你的插件需要替换或扩展默认上下文管线,而不仅仅是添加内存搜索或钩子时,请使用此机制。 + +```ts +import { buildMemorySystemPromptAddition } from "openclaw/plugin-sdk/core"; + +export default function (api) { + api.registerContextEngine("lossless-claw", () => ({ + info: { id: "lossless-claw", name: "Lossless Claw", ownsCompaction: true }, + async ingest() { + return { ingested: true }; + }, + async assemble({ messages, availableTools, citationsMode }) { + return { + messages, + estimatedTokens: 0, + systemPromptAddition: buildMemorySystemPromptAddition({ + availableTools: availableTools ?? new Set(), + citationsMode, + }), + }; + }, + async compact() { + return { ok: true, compacted: false }; + }, + })); +} +``` + +如果你的引擎**不**拥有压缩算法,请保持 `compact()` 已实现,并显式委托它: + +```ts +import { + buildMemorySystemPromptAddition, + delegateCompactionToRuntime, +} from "openclaw/plugin-sdk/core"; + +export default function (api) { + api.registerContextEngine("my-memory-engine", () => ({ + info: { + id: "my-memory-engine", + name: "My Memory Engine", + ownsCompaction: false, + }, + async ingest() { + return { ingested: true }; + }, + async assemble({ messages, availableTools, citationsMode }) { + return { + messages, + estimatedTokens: 0, + systemPromptAddition: buildMemorySystemPromptAddition({ + availableTools: availableTools ?? new Set(), + citationsMode, + }), + }; + }, + async compact(params) { + return await delegateCompactionToRuntime(params); + }, + })); +} +``` + +## 添加新的能力 + +当插件需要当前 API 无法容纳的行为时,不要通过私有深度访问绕过插件系统。请添加缺失的能力。 + +推荐顺序: + +1. 定义核心契约 + 决定哪些共享行为应由核心拥有:策略、回退、配置合并、生命周期、面向渠道的语义,以及运行时辅助工具形态。 +2. 添加类型化的插件注册 / 运行时表面 + 用最小但有用的类型化能力表面扩展 `OpenClawPluginApi` 和 / 或 `api.runtime`。 +3. 接入核心 + 渠道 / 功能使用方 + 渠道和功能插件应通过核心消费这个新能力,而不是直接导入某个供应商实现。 +4. 注册供应商实现 + 然后由供应商插件针对该能力注册它们的后端实现。 +5. 添加契约覆盖 + 添加测试,使所有权和注册形态在长期演进中保持明确。 + +这正是 OpenClaw 在保持鲜明立场的同时,不会被硬编码到某个提供商世界观中的方式。关于具体文件检查清单和完整示例,请参见 [能力扩展手册](/zh-CN/plugins/architecture)。 + +### 能力检查清单 + +当你添加一个新能力时,实现通常应同时涉及以下表面: + +- `src//types.ts` 中的核心契约类型 +- `src//runtime.ts` 中的核心 runner / 运行时辅助工具 +- `src/plugins/types.ts` 中的插件 API 注册表面 +- `src/plugins/registry.ts` 中的插件注册表接线 +- 当功能 / 渠道插件需要消费它时,位于 `src/plugins/runtime/*` 中的插件运行时暴露 +- `src/test-utils/plugin-registration.ts` 中的捕获 / 测试辅助工具 +- `src/plugins/contracts/registry.ts` 中的所有权 / 契约断言 +- `docs/` 中面向操作员 / 插件的文档 + +如果这些表面中有某一项缺失,通常就说明该能力尚未完全集成。 + +### 能力模板 + +最小模式: + +```ts +// 核心契约 +export type VideoGenerationProviderPlugin = { + id: string; + label: string; + generateVideo: (req: VideoGenerationRequest) => Promise; +}; + +// 插件 API +api.registerVideoGenerationProvider({ + id: "openai", + label: "OpenAI", + async generateVideo(req) { + return await generateOpenAiVideo(req); + }, +}); + +// 面向功能 / 渠道插件的共享运行时辅助工具 +const clip = await api.runtime.videoGeneration.generate({ + prompt: "Show the robot walking through the lab.", + cfg, +}); +``` + +契约测试模式: + +```ts +expect(findVideoGenerationProviderIdsForPlugin("openai")).toEqual(["openai"]); +``` + +这样规则就很简单: + +- 核心拥有能力契约 + 编排 +- 供应商插件拥有供应商实现 +- 功能 / 渠道插件消费运行时辅助工具 +- 契约测试让所有权保持明确 + +## 相关内容 + +- [插件架构](/zh-CN/plugins/architecture) —— 公开能力模型和形态 +- [插件 SDK 子路径](/zh-CN/plugins/sdk-subpaths) +- [插件 SDK 设置](/zh-CN/plugins/sdk-setup) +- [构建插件](/zh-CN/plugins/building-plugins) diff --git a/docs/zh-CN/plugins/architecture.md b/docs/zh-CN/plugins/architecture.md index a701de647..fcf376f8b 100644 --- a/docs/zh-CN/plugins/architecture.md +++ b/docs/zh-CN/plugins/architecture.md @@ -1,35 +1,35 @@ --- read_when: - 构建或调试原生 OpenClaw 插件 - - 了解插件能力模型或归属边界 - - 处理插件加载管线或注册表 + - 理解插件能力模型或归属边界 + - 处理插件加载流水线或注册表 - 实现提供商运行时钩子或渠道插件 sidebarTitle: Internals -summary: 插件内部机制:能力模型、归属、契约、加载管线和运行时辅助工具 +summary: 插件内部机制:能力模型、归属、契约、加载流水线和运行时辅助工具 title: 插件内部机制 x-i18n: - generated_at: "2026-04-23T22:59:53Z" + generated_at: "2026-04-24T03:06:51Z" model: gpt-5.4 provider: openai - source_hash: fbb9edc7e7cc7327ebbfb48d812cae1272ee8c1463c02a0b3a88342f52b42a7a + source_hash: a10a3fde0468f6341fa044a7e766206a6412bcd8f3349ed8063abf6987301306 source_path: plugins/architecture.md workflow: 15 --- -这是 OpenClaw 插件系统的**深度架构参考**。对于实用指南,请先从下面这些聚焦页面之一开始。 +这是 OpenClaw 插件系统的**深度架构参考**。如需实用指南,请先从下面的聚焦页面之一开始。 - - 面向最终用户的指南,用于添加、启用和排查插件问题。 + + 面向最终用户的指南,用于添加、启用和故障排除插件。 - 首个插件教程,包含最小可工作的 manifest。 + 包含最小可用清单的首个插件教程。 - 构建消息渠道插件。 + 构建一个消息渠道插件。 - 构建模型提供商插件。 + 构建一个模型提供商插件。 import map 和注册 API 参考。 @@ -38,118 +38,117 @@ x-i18n: ## 公共能力模型 -Capabilities 是 OpenClaw 内部公共的**原生插件**模型。每个原生 OpenClaw 插件都会针对一个或多个能力类型进行注册: +Capabilities 是 OpenClaw 内部公开的**原生插件**模型。每个原生 OpenClaw 插件都会针对一个或多个能力类型进行注册: -| 能力 | 注册方法 | 示例插件 | -| ------------------------ | ------------------------------------------------ | ------------------------------------ | -| 文本推理 | `api.registerProvider(...)` | `openai`、`anthropic` | -| CLI 推理后端 | `api.registerCliBackend(...)` | `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` | -| Web 抓取 | `api.registerWebFetchProvider(...)` | `firecrawl` | -| Web 搜索 | `api.registerWebSearchProvider(...)` | `google` | -| 渠道 / 消息 | `api.registerChannel(...)` | `msteams`、`matrix` | +| 能力 | 注册方法 | 示例插件 | +| ---------------------- | ------------------------------------------------ | ------------------------------------ | +| 文本推理 | `api.registerProvider(...)` | `openai`, `anthropic` | +| CLI 推理后端 | `api.registerCliBackend(...)` | `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` | +| Web 抓取 | `api.registerWebFetchProvider(...)` | `firecrawl` | +| Web 搜索 | `api.registerWebSearchProvider(...)` | `google` | +| 渠道 / 消息传递 | `api.registerChannel(...)` | `msteams`, `matrix` | -一个插件如果注册了零个能力,但提供了 hooks、工具或服务,则它是一个**旧版仅 hook** 插件。该模式目前仍然被完整支持。 +如果某个插件注册了零个能力,但提供了 hooks、工具或服务,它就是**仅 legacy hook** 插件。这种模式仍然受到完全支持。 -### 对外兼容性立场 +### 外部兼容性立场 -能力模型已经在 core 中落地,并已被当前的内置 / 原生插件使用,但对外部插件的兼容性,仍需要比“它已导出,因此它被冻结了”更严格的标准。 +能力模型已经在 core 中落地,并已被今天的内置 / 原生插件使用,但外部插件兼容性仍需要比“它已导出,因此它已冻结”更严格的标准。 -| 插件情况 | 指导意见 | -| ------------------------------------------------ | ------------------------------------------------------------------------------------------------ | -| 现有外部插件 | 保持基于 hook 的集成继续可用;这是兼容性的基线。 | -| 新的内置 / 原生插件 | 优先采用显式能力注册,而不是面向供应商的特定深度接入或新的仅 hook 设计。 | -| 采用能力注册的外部插件 | 允许,但除非文档明确标记为稳定,否则应将能力专属的辅助接口视为持续演进中。 | +| 插件情况 | 指导建议 | +| ------------------------------------------------- | ------------------------------------------------------------------------------------------------ | +| 现有外部插件 | 保持基于 hook 的集成继续工作;这是兼容性的基线。 | +| 新的内置 / 原生插件 | 优先使用显式能力注册,而不是面向厂商的特定深入接入或新的仅 hook 设计。 | +| 采用能力注册的外部插件 | 允许这样做,但除非文档将其标记为稳定,否则应将特定能力的辅助接口视为仍在演进中。 | -能力注册是预期方向。旧版 hooks 在过渡期间仍然是对外部插件最安全、最不易破坏的路径。导出的辅助子路径并不都同等稳定——应优先使用范围窄、已文档化的契约,而不是偶然导出的辅助接口。 +能力注册是预期方向。对于处于过渡期的外部插件而言,legacy hooks 仍然是最安全、最不容易破坏兼容性的路径。已导出的辅助子路径并不完全等价——应优先选择范围窄、文档化的契约,而不是偶然暴露出来的辅助导出。 ### 插件形态 -OpenClaw 会根据每个已加载插件的实际注册行为,而不仅仅是静态元数据,将其分类为一种形态: +OpenClaw 会根据每个已加载插件的实际注册行为(而不仅仅是静态元数据)将其归类为某种形态: - **plain-capability**:恰好注册一种能力类型(例如仅提供 provider 的插件 `mistral`)。 - **hybrid-capability**:注册多种能力类型(例如 `openai` 同时拥有文本推理、语音、媒体理解和图像生成)。 -- **hook-only**:只注册 hooks(类型化或自定义),不注册任何能力、工具、命令或服务。 +- **hook-only**:只注册 hooks(类型化或自定义),不注册能力、工具、命令或服务。 - **non-capability**:注册工具、命令、服务或路由,但不注册能力。 -使用 `openclaw plugins inspect ` 可查看插件的形态和能力拆分。详情请参见 [CLI 参考](/zh-CN/cli/plugins#inspect)。 +使用 `openclaw plugins inspect ` 可以查看插件的形态和能力拆分。详见 [CLI 参考](/zh-CN/cli/plugins#inspect)。 -### 旧版 hooks +### Legacy hooks -`before_agent_start` hook 仍然作为兼容性路径支持,仅 hook 插件仍可使用。现实中的旧版插件仍然依赖它。 +`before_agent_start` hook 仍然作为仅 hook 插件的兼容性路径受到支持。现实中的 legacy 插件仍然依赖它。 -方向: +方向如下: - 保持其可用 -- 将其文档化为旧版能力 -- 对于模型 / 提供商覆盖工作,优先使用 `before_model_resolve` -- 对于提示词变更工作,优先使用 `before_prompt_build` -- 只有在真实用量下降,并且 fixture 覆盖证明迁移安全后,才移除它 +- 将其记录为 legacy +- 在模型 / provider 覆盖场景中优先使用 `before_model_resolve` +- 在 prompt 变更场景中优先使用 `before_prompt_build` +- 只有在真实使用量下降且 fixture 覆盖证明迁移安全之后才移除 ### 兼容性信号 -运行 `openclaw doctor` 或 `openclaw plugins inspect ` 时,你可能会看到以下标签之一: +当你运行 `openclaw doctor` 或 `openclaw plugins inspect ` 时,可能会看到以下标签之一: -| 信号 | 含义 | +| 信号 | 含义 | | -------------------------- | ------------------------------------------------------------ | -| **config valid** | 配置解析正常,且插件可成功解析 | -| **compatibility advisory** | 插件使用了受支持但较旧的模式(例如 `hook-only`) | -| **legacy warning** | 插件使用了 `before_agent_start`,该能力已弃用 | -| **hard error** | 配置无效,或插件加载失败 | +| **config valid** | 配置解析正常,且插件可成功解析 | +| **compatibility advisory** | 插件使用受支持但较旧的模式(例如 `hook-only`) | +| **legacy warning** | 插件使用了已弃用的 `before_agent_start` | +| **hard error** | 配置无效或插件加载失败 | -今天,`hook-only` 和 `before_agent_start` 都不会导致你的插件失效:`hook-only` 只是提示,`before_agent_start` 只会触发警告。这些信号也会出现在 `openclaw status --all` 和 `openclaw plugins doctor` 中。 +`hook-only` 和 `before_agent_start` 目前都不会导致你的插件失效:`hook-only` 只是提示性信息,而 `before_agent_start` 只会触发警告。这些信号也会出现在 `openclaw status --all` 和 `openclaw plugins doctor` 中。 ## 架构概览 OpenClaw 的插件系统有四层: -1. **Manifest + 发现** - OpenClaw 会从已配置路径、工作区根目录、全局插件根目录和内置插件中查找候选插件。发现阶段会优先读取原生 `openclaw.plugin.json` manifests,以及受支持的 bundle manifests。 -2. **启用 + 验证** - core 会决定已发现插件是启用、禁用、屏蔽,还是被选入某个排他槽位,例如 memory。 +1. **清单 + 发现** + OpenClaw 会从配置路径、workspace 根目录、全局插件根目录以及内置插件中查找候选插件。设备发现会先读取原生 `openclaw.plugin.json` 清单以及受支持的 bundle 清单。 +2. **启用 + 校验** + Core 决定某个已发现插件是启用、禁用、阻止,还是被选入某个独占槽位(例如 memory)。 3. **运行时加载** - 原生 OpenClaw 插件会通过 jiti 在进程内加载,并将能力注册到中央注册表中。兼容的 bundles 会被规范化为注册表记录,而无需导入运行时代码。 -4. **界面消费** - OpenClaw 的其余部分会读取注册表,以暴露工具、渠道、提供商设置、hooks、HTTP 路由、CLI 命令和服务。 + 原生 OpenClaw 插件通过 jiti 在进程内加载,并将能力注册到中央注册表中。兼容的 bundles 会被标准化为注册表记录,而无需导入运行时代码。 +4. **表面消费** + OpenClaw 的其余部分会读取注册表,以暴露工具、渠道、provider 设置、hooks、HTTP 路由、CLI 命令和服务。 -对于插件 CLI,根命令发现专门拆分为两个阶段: +对于插件 CLI 而言,根命令发现专门分为两个阶段: - 解析时元数据来自 `registerCli(..., { descriptors: [...] })` -- 真正的插件 CLI 模块可以保持惰性,并在首次调用时再注册 +- 真正的插件 CLI 模块可以保持懒加载,并在首次调用时注册 -这样既能让插件拥有的 CLI 代码保留在插件内部,又能让 OpenClaw 在解析之前预留根命令名称。 +这样既能将插件拥有的 CLI 代码保留在插件内部,又能让 OpenClaw 在解析前预留根命令名称。 -重要的设计边界: +重要的设计边界是: -- 发现 + 配置验证应基于 **manifest / schema 元数据** 完成,而无需执行插件代码 +- 设备发现 + 配置校验应当能够仅基于**清单 / schema 元数据**工作,而无需执行插件代码 - 原生运行时行为来自插件模块的 `register(api)` 路径 -这种拆分使 OpenClaw 可以在完整运行时尚未激活前,先验证配置、解释缺失 / 已禁用的插件,以及构建 UI / schema 提示。 +这种拆分让 OpenClaw 能够在完整运行时激活之前,就完成配置校验、解释缺失 / 已禁用插件,并构建 UI / schema 提示。 -### 渠道插件与共享消息工具 +### 渠道插件和共享 message 工具 -对于普通聊天动作,渠道插件无需单独注册发送 / 编辑 / 反应工具。OpenClaw 在 core 中保留一个共享的 `message` 工具,而渠道插件负责其背后的渠道专属发现和执行。 +对于常规聊天操作,渠道插件不需要单独注册发送 / 编辑 / 反应工具。OpenClaw 在 core 中保留了一个共享的 `message` 工具,而渠道插件则拥有其背后的渠道特定发现和执行逻辑。 当前边界如下: -- core 拥有共享 `message` 工具宿主、提示词接线、会话 / 线程簿记以及执行分发 -- 渠道插件拥有作用域内动作发现、能力发现以及任何渠道专属 schema 片段 -- 渠道插件拥有提供商专属的会话对话语法,例如对话 id 如何编码线程 id 或从父对话继承 +- core 拥有共享 `message` 工具宿主、prompt 接线、session / thread 记账以及执行分发 +- 渠道插件拥有作用域内动作发现、能力发现以及任何渠道特定的 schema 片段 +- 渠道插件拥有 provider 特定的 session 对话语法,例如会话 id 如何编码 thread id,或如何继承父对话 - 渠道插件通过其动作适配器执行最终动作 -对于渠道插件,SDK 界面是 -`ChannelMessageActionAdapter.describeMessageTool(...)`。这个统一的发现调用允许插件将其可见动作、能力以及 schema 贡献一起返回,从而避免这些部分彼此漂移。 +对于渠道插件,SDK 接口是 `ChannelMessageActionAdapter.describeMessageTool(...)`。这个统一的发现调用让插件可以一起返回其可见动作、能力和 schema 贡献,从而避免这些部分发生漂移。 -当某个渠道专属的消息工具参数携带媒体源,例如本地路径或远程媒体 URL 时,插件还应从 `describeMessageTool(...)` 返回 `mediaSourceParams`。core 会使用这个显式列表来应用沙箱路径规范化和出站媒体访问提示,而不是硬编码插件拥有的参数名。 -这里应优先使用按动作划分的映射,而不是某个渠道级的扁平列表,这样仅用于 profile 的媒体参数就不会在 `send` 等无关动作上被规范化。 +当某个渠道特定的 message-tool 参数携带媒体来源(例如本地路径或远程媒体 URL)时,插件还应从 `describeMessageTool(...)` 返回 `mediaSourceParams`。Core 使用这个显式列表来应用沙箱路径标准化和出站媒体访问提示,而不会硬编码插件拥有的参数名。 +这里应优先使用按动作划分的映射,而不是某个渠道范围内的单一平面列表,这样仅 profile 使用的媒体参数就不会在 `send` 这类无关动作上被标准化。 -core 会将运行时作用域传入该发现步骤。重要字段包括: +Core 会将运行时作用域传入该发现步骤。重要字段包括: - `accountId` - `currentChannelId` @@ -158,85 +157,83 @@ core 会将运行时作用域传入该发现步骤。重要字段包括: - `sessionKey` - `sessionId` - `agentId` -- 可信的入站 `requesterSenderId` +- 受信任的入站 `requesterSenderId` -这对于上下文敏感的插件非常重要。渠道可以根据当前活跃账户、当前房间 / 线程 / 消息或可信请求者身份,隐藏或暴露消息动作,而无需在 core 的 `message` 工具中硬编码渠道专属分支。 +这对于上下文敏感型插件很重要。一个渠道可以根据当前活跃账号、当前房间 / thread / message,或受信任的请求者身份来隐藏或暴露消息动作,而无需在 core 的 `message` 工具中硬编码渠道特定分支。 -这也是为什么嵌入式 runner 路由变更仍属于插件工作:runner 负责将当前聊天 / 会话身份转发到插件发现边界,以便共享 `message` 工具在当前轮次暴露正确的、由渠道拥有的界面。 +这也是为什么嵌入式 runner 路由变更仍然属于插件工作:runner 负责将当前 chat / session 身份转发到插件发现边界,使共享 `message` 工具能够为当前轮次暴露正确的渠道自有表面。 -对于由渠道拥有的执行辅助运行时,内置插件应将执行运行时保留在各自的 extension 模块内。core 不再在 `src/agents/tools` 下拥有 Discord、Slack、Telegram 或 WhatsApp 消息动作运行时。 -我们不会发布单独的 `plugin-sdk/*-action-runtime` 子路径,内置插件应直接从各自 extension 拥有的模块导入本地运行时代码。 +对于渠道拥有的执行辅助工具,内置插件应将执行运行时保留在各自的 extension 模块中。Core 不再拥有位于 `src/agents/tools` 下的 Discord、Slack、Telegram 或 WhatsApp 消息动作运行时。 +我们不会发布单独的 `plugin-sdk/*-action-runtime` 子路径,内置插件应直接从各自 extension 拥有的模块中导入本地运行时代码。 -同样的边界也适用于一般性的、以提供商命名的 SDK 接缝:core 不应导入 Slack、Discord、Signal、WhatsApp 或类似 extension 的渠道专属便捷 barrel。如果 core 需要某种行为,应当消费该内置插件自己的 `api.ts` / `runtime-api.ts` barrel,或者将该需求提升为共享 SDK 中的狭义通用能力。 +同样的边界也适用于一般意义上按 provider 命名的 SDK 接缝:core 不应导入针对 Slack、Discord、Signal、WhatsApp 或类似扩展的渠道特定便捷 barrel。如果 core 需要某项行为,要么消费该内置插件自己的 `api.ts` / `runtime-api.ts` barrel,要么将该需求提升为共享 SDK 中一个范围窄的通用能力。 -对于投票,具体有两条执行路径: +对于投票,当前有两条执行路径: - `outbound.sendPoll` 是适用于符合通用投票模型渠道的共享基线 -- `actions.handleAction("poll")` 是处理渠道专属投票语义或附加投票参数的首选路径 +- `actions.handleAction("poll")` 是适用于渠道特定投票语义或额外投票参数的首选路径 -现在,core 会在插件投票分发拒绝该动作之后,才延迟执行共享投票解析,因此由插件拥有的投票处理器可以接受渠道专属投票字段,而不会先被通用投票解析器阻塞。 +现在,只有在插件投票分发拒绝该动作之后,core 才会延后进行共享投票解析,因此插件自有的投票处理器可以接受渠道特定的投票字段,而不会先被通用投票解析器拦住。 -完整启动顺序请参见 [加载管线](#load-pipeline)。 +完整启动顺序见[加载流水线](#load-pipeline)。 ## 能力归属模型 -OpenClaw 将原生插件视为**公司**或**功能**的归属边界,而不是一堆互不相关集成的集合。 +OpenClaw 将一个原生插件视为某个**公司**或某项**功能**的归属边界,而不是一堆互不相关集成的杂物袋。 这意味着: -- 公司插件通常应拥有该公司的所有 OpenClaw 对外界面 -- 功能插件通常应拥有其引入的完整功能界面 -- 渠道应消费共享 core 能力,而不是临时重新实现提供商行为 +- 公司插件通常应拥有该公司面向 OpenClaw 的所有表面 +- 功能插件通常应拥有其引入的完整功能表面 +- 渠道应消费共享的 core 能力,而不是临时重新实现 provider 行为 - - **供应商多能力**:`openai` 拥有文本推理、语音、实时语音、媒体理解和图像生成。`google` 拥有文本推理,以及媒体理解、图像生成和 Web 搜索。`qwen` 拥有文本推理,以及媒体理解和视频生成。 - - **供应商单能力**:`elevenlabs` 和 `microsoft` 拥有语音;`firecrawl` 拥有 Web 抓取;`minimax` / `mistral` / `moonshot` / `zai` 拥有媒体理解后端。 - - **功能插件**:`voice-call` 拥有呼叫传输、工具、CLI、路由和 Twilio 媒体流桥接,但它消费共享的语音、实时转写和实时语音能力,而不是直接导入供应商插件。 + - **厂商多能力**:`openai` 拥有文本推理、语音、实时语音、媒体理解和图像生成。`google` 拥有文本推理,以及媒体理解、图像生成和 Web 搜索。`qwen` 拥有文本推理,以及媒体理解和视频生成。 + - **厂商单能力**:`elevenlabs` 和 `microsoft` 拥有语音;`firecrawl` 拥有 Web 抓取;`minimax` / `mistral` / `moonshot` / `zai` 拥有媒体理解后端。 + - **功能插件**:`voice-call` 拥有通话传输、工具、CLI、路由以及 Twilio 媒体流桥接,但它消费共享的语音、实时转录和实时语音能力,而不是直接导入厂商插件。 预期的最终状态是: -- OpenAI 即使同时涵盖文本模型、语音、图像和未来的视频,也仍然只存在于一个插件中 -- 其他供应商也可以用同样方式管理其自己的界面范围 -- 渠道并不关心哪个供应商插件拥有该 provider;它们只消费 core 暴露的共享能力契约 +- 即使 OpenAI 横跨文本模型、语音、图像以及未来的视频,它也应存在于一个插件中 +- 其他厂商也可以对自己的表面区域采取同样方式 +- 渠道并不关心哪个厂商插件拥有该 provider;它们消费的是由 core 暴露的共享能力契约 -这是关键区别: +这就是关键区别: - **plugin** = 归属边界 - **capability** = 可由多个插件实现或消费的 core 契约 -因此,如果 OpenClaw 新增了某个新领域,例如视频,首要问题并不是 -“哪个 provider 应该硬编码视频处理?” 首要问题是 “core 的视频能力契约是什么?” -一旦该契约存在,供应商插件就可以针对它进行注册,而渠道 / 功能插件也可以消费它。 +因此,如果 OpenClaw 新增了一个诸如视频之类的新领域,第一个问题不应是“哪个 provider 应该硬编码视频处理?”而应该是“core 的视频能力契约是什么?”一旦这个契约存在,厂商插件就可以针对它注册,渠道 / 功能插件也可以消费它。 如果该能力尚不存在,通常正确的做法是: 1. 在 core 中定义缺失的能力 2. 通过插件 API / 运行时以类型化方式暴露它 -3. 将渠道 / 功能与该能力接线 -4. 让供应商插件注册实现 +3. 让渠道 / 功能围绕该能力完成接线 +4. 让厂商插件注册实现 -这样可以保持归属明确,同时避免让 core 行为依赖某个单一供应商或一次性的插件专属代码路径。 +这样可以保持归属明确,同时避免让 core 行为依赖某个单一厂商或某条一次性的插件特定代码路径。 ### 能力分层 -在决定代码归属时,可使用以下思维模型: +在决定代码应放在哪里时,可以使用以下思维模型: -- **core 能力层**:共享编排、策略、回退、配置合并规则、投递语义和类型化契约 -- **供应商插件层**:供应商专属 API、认证、模型目录、语音合成、图像生成、未来的视频后端、用量端点 -- **渠道 / 功能插件层**:Slack / Discord / voice-call / 等集成,它们消费 core 能力并在某个界面上呈现出来 +- **core 能力层**:共享编排、策略、回退、配置合并规则、交付语义和类型化契约 +- **厂商插件层**:厂商特定 API、认证、模型目录、语音合成、图像生成、未来的视频后端、用量端点 +- **渠道 / 功能插件层**:Slack / Discord / voice-call / 等集成,它们消费 core 能力并将其呈现在某个表面上 例如,TTS 遵循以下形态: -- core 拥有回复时 TTS 策略、回退顺序、偏好和渠道投递 +- core 拥有回复时 TTS 策略、回退顺序、偏好和渠道交付 - `openai`、`elevenlabs` 和 `microsoft` 拥有合成实现 - `voice-call` 消费电话 TTS 运行时辅助工具 -未来能力也应优先遵循相同模式。 +未来的能力也应优先采用同样模式。 ### 多能力公司插件示例 -一个公司插件从外部看应该具备一致性。如果 OpenClaw 为模型、语音、实时转写、实时语音、媒体理解、图像生成、视频生成、Web 抓取和 Web 搜索提供共享契约,那么某个供应商就可以在一个地方拥有其全部界面: +从外部看,一个公司插件应当具有内聚性。如果 OpenClaw 针对模型、语音、实时转录、实时语音、媒体理解、图像生成、视频生成、Web 抓取和 Web 搜索都有共享契约,那么一个厂商就可以在一个地方拥有它的所有表面: ```ts import type { OpenClawPluginDefinition } from "openclaw/plugin-sdk/plugin-entry"; @@ -290,11 +287,11 @@ const plugin: OpenClawPluginDefinition = { export default plugin; ``` -关键不在于辅助函数的确切名称。重要的是这种形态: +重要的不是确切的辅助函数名称,而是这种形态: -- 一个插件拥有该供应商界面 +- 一个插件拥有该厂商表面 - core 仍然拥有能力契约 -- 渠道和功能插件消费 `api.runtime.*` 辅助工具,而不是供应商代码 +- 渠道和功能插件消费 `api.runtime.*` 辅助工具,而不是厂商代码 - 契约测试可以断言该插件确实注册了它声称拥有的能力 ### 能力示例:视频理解 @@ -302,1015 +299,97 @@ export default plugin; OpenClaw 已经将图像 / 音频 / 视频理解视为一种共享能力。相同的归属模型也适用于这里: 1. core 定义媒体理解契约 -2. 供应商插件按适用情况注册 `describeImage`、`transcribeAudio` 和 - `describeVideo` -3. 渠道和功能插件消费共享 core 行为,而不是直接接到供应商代码 +2. 厂商插件按适用情况注册 `describeImage`、`transcribeAudio` 和 `describeVideo` +3. 渠道和功能插件消费共享的 core 行为,而不是直接接入厂商代码 -这样可以避免将某个 provider 的视频假设直接固化到 core 中。插件拥有供应商界面;core 拥有能力契约和回退行为。 +这避免了将某个 provider 的视频假设固化进 core。插件拥有厂商表面;core 拥有能力契约和回退行为。 -视频生成已经采用了同样顺序:core 拥有类型化能力契约和运行时辅助工具,而供应商插件则针对它注册 -`api.registerVideoGenerationProvider(...)` 实现。 +视频生成已经采用同样顺序:core 拥有类型化能力契约和运行时辅助工具,而厂商插件针对它注册 `api.registerVideoGenerationProvider(...)` 实现。 -需要具体的发布清单吗?请参见 -[能力扩展手册](/zh-CN/plugins/architecture)。 +需要具体的发布清单吗?参见[能力扩展手册](/zh-CN/plugins/architecture)。 -## 契约与约束执行 +## 契约与强制执行 -插件 API 界面有意采用类型化,并集中定义在 -`OpenClawPluginApi` 中。该契约定义了受支持的注册点,以及插件可依赖的运行时辅助工具。 +插件 API 表面有意在 `OpenClawPluginApi` 中集中并进行类型化。该契约定义了受支持的注册点,以及插件可依赖的运行时辅助工具。 这很重要,因为: -- 插件作者获得了统一且稳定的内部标准 -- core 可以拒绝重复归属,例如两个插件注册相同的 provider id -- 启动时可以为格式错误的注册输出可操作的诊断信息 -- 契约测试可以强制执行内置插件归属,并防止无声漂移 +- 插件作者能获得一个稳定的内部标准 +- core 可以拒绝重复归属,例如两个插件注册同一个 provider id +- 启动时可以为格式错误的注册暴露可执行的诊断信息 +- 契约测试可以强制约束内置插件的归属,并防止无声漂移 -约束执行有两层: +这里有两层强制执行: -1. **运行时注册约束执行** - 插件注册表会在插件加载时验证注册。例如: - 重复的 provider id、重复的语音 provider id,以及格式错误的 - 注册,不会导致未定义行为,而是产生插件诊断信息。 +1. **运行时注册强制执行** + 插件加载时,插件注册表会校验注册内容。例如:重复的 provider id、重复的语音 provider id,以及格式错误的注册,都会产生插件诊断,而不是导致未定义行为。 2. **契约测试** - 内置插件会在测试运行期间被记录到契约注册表中,这样 OpenClaw - 就能显式断言归属。如今这用于模型 providers、语音 providers、Web 搜索 providers,以及内置注册归属。 + 在测试运行期间,内置插件会被捕获到契约注册表中,以便 OpenClaw 可以显式断言归属。当前,这用于模型 provider、语音 provider、Web 搜索 provider 以及内置注册归属。 -实际效果是,OpenClaw 能预先知道哪个插件拥有哪个界面。由于归属是声明式、类型化且可测试的,core 和渠道因此能够无缝组合,而不是依赖隐式约定。 +实际效果是,OpenClaw 能够预先知道哪个插件拥有哪个表面。这让 core 和渠道可以无缝组合,因为归属是声明式的、类型化的且可测试,而不是隐式的。 -### 什么应该属于契约 +### 什么应属于契约 -好的插件契约应当是: +好的插件契约应当: -- 类型化的 -- 小而精的 -- 能力专属的 +- 有类型 +- 小而精 +- 以能力为中心 - 由 core 拥有 - 可被多个插件复用 -- 可被渠道 / 功能消费,而无需了解供应商细节 +- 可被渠道 / 功能消费,而无需了解厂商细节 -不好的插件契约则包括: +糟糕的插件契约则是: -- 隐藏在 core 中的供应商专属策略 +- 隐藏在 core 中的厂商特定策略 - 绕过注册表的一次性插件逃生口 -- 渠道代码直接深入供应商实现 +- 渠道代码直接深入厂商实现 - 不属于 `OpenClawPluginApi` 或 `api.runtime` 的临时运行时对象 -如有疑问,请提高抽象层级:先定义能力,再让插件接入它。 +如果拿不准,就提升抽象层级:先定义能力,再让插件接入它。 ## 执行模型 -原生 OpenClaw 插件与 Gateway 网关 **在同一进程内** 运行。它们不在沙箱中。一个已加载的原生插件与 core 代码处于相同的进程级信任边界。 +原生 OpenClaw 插件在 Gateway 网关中**进程内**运行。它们不进行沙箱隔离。已加载的原生插件与 core 代码具有相同的进程级信任边界。 -这意味着: +影响包括: - 原生插件可以注册工具、网络处理器、hooks 和服务 - 原生插件中的 bug 可能导致 gateway 崩溃或不稳定 -- 恶意原生插件等同于在 OpenClaw 进程内执行任意代码 +- 恶意原生插件等同于在 OpenClaw 进程内部执行任意代码 -兼容 bundles 默认更安全,因为 OpenClaw 当前将它们视为元数据 / 内容包。在当前版本中,这主要意味着内置的 skills。 +兼容 bundles 默认更安全,因为 OpenClaw 目前将它们视为元数据 / 内容包。在当前版本中,这主要意味着内置 Skills。 -对于非内置插件,请使用 allowlist 和显式安装 / 加载路径。应将工作区插件视为开发阶段代码,而不是生产默认值。 +对于非内置插件,请使用 allowlist 和显式的安装 / 加载路径。应将 workspace 插件视为开发期代码,而非生产默认值。 -对于内置工作区 package 名称,请保持插件 id 锚定在 npm -名称中:默认使用 `@openclaw/`,或在插件有意暴露更窄角色时,使用经批准的类型化后缀,例如 -`-provider`、`-plugin`、`-speech`、`-sandbox` 或 `-media-understanding`。 +对于内置 workspace 包名,应让插件 id 锚定在 npm 名称中:默认使用 `@openclaw/`,或者在包有意暴露更窄的插件角色时,使用获批的类型化后缀,例如 `-provider`、`-plugin`、`-speech`、`-sandbox` 或 `-media-understanding`。 重要的信任说明: -- `plugins.allow` 信任的是 **plugin ids**,而不是源码来源。 -- 如果某个工作区插件与内置插件具有相同 id,当该工作区插件被启用 / 加入 allowlist 时,它会有意覆盖内置副本。 -- 这在本地开发、补丁测试和紧急修复中是正常且有用的。 -- 内置插件的信任是根据源码快照来解析的——即加载时磁盘上的 manifest 和代码——而不是根据安装元数据。受损或被替换的安装记录无法在实际源码声明之外,悄悄扩大某个内置插件的信任范围。 +- `plugins.allow` 信任的是**插件 id**,而不是来源出处。 +- 当某个与内置插件具有相同 id 的 workspace 插件被启用 / 加入 allowlist 时,它会有意覆盖内置副本。 +- 这很正常,也对本地开发、补丁测试和热修复很有用。 +- 内置插件信任是根据源代码快照解析的——也就是加载时磁盘上的清单和代码——而不是根据安装元数据解析。损坏或被替换的安装记录,不能在实际源代码声明范围之外,悄悄扩大某个内置插件的信任表面。 ## 导出边界 -OpenClaw 导出的是能力,而不是实现层面的便利接口。 +OpenClaw 导出的是能力,而不是实现便利性。 -应保持能力注册为公开能力。应收缩非契约辅助导出: +保持能力注册为公开接口。精简非契约辅助导出: -- 内置插件专属辅助子路径 -- 不打算作为公开 API 的运行时管线子路径 -- 供应商专属便捷辅助工具 +- 内置插件特定的辅助子路径 +- 不打算作为公共 API 的运行时管线子路径 +- 厂商特定的便捷辅助工具 - 属于实现细节的设置 / 新手引导辅助工具 -一些内置插件辅助子路径仍保留在生成的 SDK 导出映射中,以兼容旧用法和支持内置插件维护。当前示例包括 -`plugin-sdk/feishu`、`plugin-sdk/feishu-setup`、`plugin-sdk/zalo`、 -`plugin-sdk/zalo-setup`,以及若干 `plugin-sdk/matrix*` 接缝。应将这些视为保留的实现细节导出,而不是新第三方插件推荐采用的 SDK 模式。 +出于兼容性和内置插件维护的原因,一些内置插件辅助子路径仍保留在生成的 SDK 导出映射中。当前示例包括 `plugin-sdk/feishu`、`plugin-sdk/feishu-setup`、`plugin-sdk/zalo`、`plugin-sdk/zalo-setup`,以及若干 `plugin-sdk/matrix*` 接缝。应将这些视为保留的实现细节导出,而不是新第三方插件推荐采用的 SDK 模式。 -## 加载管线 +## 内部机制与参考 -在启动时,OpenClaw 大致会执行以下步骤: - -1. 发现候选插件根目录 -2. 读取原生或兼容 bundle 的 manifests 和 package 元数据 -3. 拒绝不安全的候选项 -4. 规范化插件配置(`plugins.enabled`、`allow`、`deny`、`entries`、 - `slots`、`load.paths`) -5. 决定每个候选项是否启用 -6. 加载已启用的原生模块:已构建的内置模块使用原生加载器;未构建的原生插件使用 jiti -7. 调用原生 `register(api)` hooks,并将注册收集到插件注册表中 -8. 向命令 / 运行时界面暴露该注册表 - - -`activate` 是 `register` 的旧版别名——加载器会解析两者中存在的那个(`def.register ?? def.activate`),并在相同阶段调用它。所有内置插件都使用 `register`;新插件应优先使用 `register`。 - - -安全门槛发生在**运行时代码执行之前**。如果入口逃逸出插件根目录、路径可被全体用户写入,或对于非内置插件来说路径归属可疑,则候选项会被阻止。 - -### Manifest 优先行为 - -manifest 是控制平面的事实来源。OpenClaw 用它来: - -- 标识插件 -- 发现声明的渠道 / skills / 配置 schema 或 bundle 能力 -- 验证 `plugins.entries..config` -- 增强 Control UI 标签 / 占位符 -- 显示安装 / 目录元数据 -- 在不加载插件运行时的前提下,保留轻量的激活和设置描述符 - -对于原生插件,运行时模块是数据平面部分。它会注册实际行为,例如 hooks、工具、命令或 provider 流程。 - -可选的 manifest `activation` 和 `setup` 块仍属于控制平面。它们只是用于激活规划和设置发现的纯元数据描述符;并不替代运行时注册、`register(...)` 或 `setupEntry`。 -首批实时激活使用方现在会利用 manifest 中的命令、渠道和 provider 提示,在更广泛的注册表物化之前先缩小插件加载范围: - -- CLI 加载会缩小到拥有所请求主命令的插件 -- 渠道设置 / 插件解析会缩小到拥有所请求渠道 id 的插件 -- 显式 provider 设置 / 运行时解析会缩小到拥有所请求 provider id 的插件 - -设置发现现在会优先使用描述符拥有的 id,例如 `setup.providers` 和 -`setup.cliBackends`,以便在回退到 `setup-api` 之前先缩小候选插件范围;`setup-api` 仅用于那些仍需要设置阶段运行时 hooks 的插件。如果多个已发现插件声明了相同的规范化设置 provider 或 CLI backend id,设置查找会拒绝这个有歧义的归属,而不是依赖发现顺序。 - -### 加载器会缓存什么 - -OpenClaw 会维护短生命周期的进程内缓存,用于: - -- 发现结果 -- manifest 注册表数据 -- 已加载的插件注册表 - -这些缓存可减少突发式启动成本和重复命令开销。应将它们视为短暂的性能缓存,而不是持久化机制。 - -性能说明: - -- 设置 `OPENCLAW_DISABLE_PLUGIN_DISCOVERY_CACHE=1` 或 - `OPENCLAW_DISABLE_PLUGIN_MANIFEST_CACHE=1` 可禁用这些缓存。 -- 使用 `OPENCLAW_PLUGIN_DISCOVERY_CACHE_MS` 和 - `OPENCLAW_PLUGIN_MANIFEST_CACHE_MS` 调整缓存窗口。 - -## 注册表模型 - -已加载的插件不会直接修改 core 中任意的全局状态。它们会注册到中央插件注册表中。 - -注册表会跟踪: - -- 插件记录(身份、来源、起源、状态、诊断) -- 工具 -- 旧版 hooks 和类型化 hooks -- 渠道 -- providers -- Gateway 网关 RPC handlers -- HTTP 路由 -- CLI registrars -- 后台服务 -- 由插件拥有的命令 - -然后,core 功能会从该注册表中读取,而不是直接与插件模块交互。这样可以保持加载方向单向: - -- plugin module -> registry registration -- core runtime -> registry consumption - -这种分离对可维护性非常重要。它意味着大多数 core 界面只需要一个集成点:“读取注册表”,而不是“为每个插件模块写特殊处理”。 - -## 对话绑定回调 - -绑定对话的插件可以在批准结果落定后作出响应。 - -使用 `api.onConversationBindingResolved(...)` 可在绑定请求被批准或拒绝后接收回调: - -```ts -export default { - id: "my-plugin", - register(api) { - api.onConversationBindingResolved(async (event) => { - if (event.status === "approved") { - // A binding now exists for this plugin + conversation. - console.log(event.binding?.conversationId); - return; - } - - // The request was denied; clear any local pending state. - console.log(event.request.conversation.conversationId); - }); - }, -}; -``` - -回调负载字段: - -- `status`:`"approved"` 或 `"denied"` -- `decision`:`"allow-once"`、`"allow-always"` 或 `"deny"` -- `binding`:已批准请求对应的解析后绑定 -- `request`:原始请求摘要、detach 提示、sender id 和 - 对话元数据 - -此回调仅用于通知。它不会改变谁有权绑定对话,并且会在 core 的批准处理完成后运行。 - -## provider 运行时 hooks - -provider 插件有三层: - -- **Manifest 元数据**,用于低成本的运行时前查找:`providerAuthEnvVars`、 - `providerAuthAliases`、`providerAuthChoices` 和 `channelEnvVars`。 -- **配置时 hooks**:`catalog`(旧称 `discovery`)以及 - `applyConfigDefaults`。 -- **运行时 hooks**:40 多个可选 hooks,涵盖认证、模型解析、 - 流包装、思考级别、重放策略和用量端点。完整列表请参见 - [Hook 顺序与用法](#hook-order-and-usage)。 - -OpenClaw 仍然拥有通用智能体循环、故障转移、转录处理和工具策略。这些 hooks 是 provider 专属行为的扩展界面,因此无需整套自定义推理传输也能扩展。 - -当某个 provider 使用基于环境变量的凭证,并且你希望通用认证 / 状态 / 模型选择器路径在不加载插件运行时的情况下也能看到它时,请使用 manifest `providerAuthEnvVars`。当某个 provider id 应复用另一个 provider id 的环境变量、auth 配置文件、配置支持的认证以及 API key 新手引导选项时,请使用 manifest `providerAuthAliases`。当新手引导 / 认证选择 CLI 界面需要在不加载 provider 运行时的情况下知道 provider 的 choice id、分组标签和简单的一键认证接线时,请使用 manifest `providerAuthChoices`。请将 provider 运行时的 `envVars` 保留给面向运维的提示,例如新手引导标签或 OAuth client-id / client-secret 设置变量。 - -当某个渠道具有基于环境变量驱动的认证或设置,并且你希望通用 shell 环境变量回退、配置 / 状态检查或设置提示在不加载渠道运行时的情况下看到它时,请使用 manifest `channelEnvVars`。 - -### Hook 顺序与用法 - -对于 model / provider 插件,OpenClaw 会大致按以下顺序调用 hooks。 -“何时使用”列是快速决策指南。 - -| # | Hook | 作用 | 何时使用 | -| --- | --------------------------------- | -------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -| 1 | `catalog` | 在生成 `models.json` 时,将 provider 配置发布到 `models.providers` 中 | provider 拥有目录或 base URL 默认值 | -| 2 | `applyConfigDefaults` | 在配置物化期间应用由 provider 拥有的全局配置默认值 | 默认值依赖认证模式、环境变量或 provider 模型家族语义 | -| -- | _(内置模型查找)_ | OpenClaw 会先尝试常规的注册表 / 目录路径 | _(不是插件 hook)_ | -| 3 | `normalizeModelId` | 在查找前规范化旧版或预览版模型 id 别名 | provider 在规范模型解析前拥有别名清理逻辑 | -| 4 | `normalizeTransport` | 在通用模型组装前规范化 provider 家族的 `api` / `baseUrl` | provider 在同一传输家族中为自定义 provider id 拥有传输清理逻辑 | -| 5 | `normalizeConfig` | 在运行时 / provider 解析前规范化 `models.providers.` | provider 需要将配置清理逻辑保留在插件中;内置的 Google 家族辅助工具也会为受支持的 Google 配置项提供兜底 | -| 6 | `applyNativeStreamingUsageCompat` | 对配置中的 providers 应用原生流式用量兼容性重写 | provider 需要基于端点修复原生流式用量元数据 | -| 7 | `resolveConfigApiKey` | 在加载运行时认证前,为配置中的 providers 解析 env-marker 认证 | provider 拥有基于 env-marker 的 API key 解析逻辑;`amazon-bedrock` 在这里也有内置的 AWS env-marker 解析器 | -| 8 | `resolveSyntheticAuth` | 在不持久化明文的情况下暴露本地 / 自托管或配置支持的认证 | provider 可以通过合成 / 本地凭证标记运行 | -| 9 | `resolveExternalAuthProfiles` | 叠加由 provider 拥有的外部 auth 配置文件;CLI / 应用拥有的凭证默认 `persistence` 为 `runtime-only` | provider 在不持久化复制的 refresh token 的情况下复用外部 auth 凭证;需在 manifest 中声明 `contracts.externalAuthProviders` | -| 10 | `shouldDeferSyntheticProfileAuth` | 将已存储的合成 profile 占位符优先级降低到 env / 配置支持的认证之后 | provider 存储了不应赢得优先级的合成占位 profile | -| 11 | `resolveDynamicModel` | 为尚未出现在本地注册表中的 provider 拥有模型 id 提供同步回退 | provider 接受任意上游模型 id | -| 12 | `prepareDynamicModel` | 先进行异步预热,然后再次运行 `resolveDynamicModel` | provider 在解析未知 id 之前需要网络元数据 | -| 13 | `normalizeResolvedModel` | 在嵌入式 runner 使用已解析模型之前进行最终重写 | provider 需要进行传输重写,但仍使用 core 传输 | -| 14 | `contributeResolvedModelCompat` | 为位于另一兼容传输后的供应商模型贡献兼容标志 | provider 能在代理传输中识别自己的模型,而无需接管该 provider | -| 15 | `capabilities` | 由 provider 拥有、供共享 core 逻辑使用的转录 / 工具元数据 | provider 需要转录 / provider 家族层面的特殊行为 | -| 16 | `normalizeToolSchemas` | 在嵌入式 runner 看到之前规范化工具 schema | provider 需要进行传输家族层面的 schema 清理 | -| 17 | `inspectToolSchemas` | 在规范化之后暴露由 provider 拥有的 schema 诊断 | provider 希望提供关键字警告,而无需让 core 学习 provider 专属规则 | -| 18 | `resolveReasoningOutputMode` | 选择原生推理输出契约还是带标签的推理输出契约 | provider 需要使用带标签的推理 / 最终输出,而不是原生字段 | -| 19 | `prepareExtraParams` | 在通用流选项包装器之前进行请求参数规范化 | provider 需要默认请求参数或按 provider 的参数清理 | -| 20 | `createStreamFn` | 使用自定义传输完全替换正常流路径 | provider 需要自定义线路协议,而不只是一个包装器 | -| 21 | `wrapStreamFn` | 在通用包装器应用之后对流函数再做包装 | provider 需要请求头 / 请求体 / 模型兼容包装器,而无需自定义传输 | -| 22 | `resolveTransportTurnState` | 附加原生的逐轮传输头或元数据 | provider 希望通用传输发送 provider 原生的轮次身份 | -| 23 | `resolveWebSocketSessionPolicy` | 附加原生 WebSocket headers 或会话冷却策略 | provider 希望通用 WS 传输调整会话 headers 或回退策略 | -| 24 | `formatApiKey` | auth 配置文件格式化器:将已存储 profile 转换为运行时 `apiKey` 字符串 | provider 存储了额外的 auth 元数据,并需要自定义运行时 token 形态 | -| 25 | `refreshOAuth` | 为自定义刷新端点或刷新失败策略提供 OAuth 刷新覆盖 | provider 不适配共享的 `pi-ai` 刷新器 | -| 26 | `buildAuthDoctorHint` | 在 OAuth 刷新失败时追加修复提示 | provider 需要在刷新失败后提供由 provider 拥有的认证修复指引 | -| 27 | `matchesContextOverflowError` | 由 provider 拥有的上下文窗口溢出匹配器 | provider 存在通用启发式无法识别的原始溢出错误 | -| 28 | `classifyFailoverReason` | 由 provider 拥有的故障转移原因分类 | provider 能将原始 API / 传输错误映射为限流 / 过载等类型 | -| 29 | `isCacheTtlEligible` | 面向代理 / 回传 provider 的提示词缓存策略 | provider 需要按代理特性控制 cache TTL | -| 30 | `buildMissingAuthMessage` | 替换通用的缺失认证恢复消息 | provider 需要 provider 专属的缺失认证恢复提示 | -| 31 | `suppressBuiltInModel` | 过时上游模型抑制,并可附带面向用户的错误提示 | provider 需要隐藏过时的上游记录,或用供应商提示替换它们 | -| 32 | `augmentModelCatalog` | 在发现后追加合成 / 最终目录记录 | provider 需要在 `models list` 和选择器中加入用于前向兼容的合成记录 | -| 33 | `resolveThinkingProfile` | 为特定模型设置 `/think` 级别、显示标签和默认值 | provider 为选定模型暴露自定义思考阶梯或二元标签 | -| 34 | `isBinaryThinking` | 开 / 关推理切换兼容性 hook | provider 只暴露二元的思考开 / 关 | -| 35 | `supportsXHighThinking` | `xhigh` 推理支持兼容性 hook | provider 希望仅在部分模型上启用 `xhigh` | -| 36 | `resolveDefaultThinkingLevel` | 默认 `/think` 级别兼容性 hook | provider 为某个模型家族拥有默认 `/think` 策略 | -| 37 | `isModernModelRef` | 用于实时 profile 过滤和 smoke 选择的现代模型匹配器 | provider 拥有实时 / smoke 首选模型匹配逻辑 | -| 38 | `prepareRuntimeAuth` | 在推理前将已配置的凭证交换为实际运行时 token / key | provider 需要 token 交换或短生命周期的请求凭证 | -| 39 | `resolveUsageAuth` | 为 `/usage` 及相关状态界面解析用量 / 计费凭证 | provider 需要自定义用量 / 配额 token 解析,或使用不同的用量凭证 | -| 40 | `fetchUsageSnapshot` | 在认证解析后获取并规范化 provider 专属的用量 / 配额快照 | provider 需要 provider 专属的用量端点或负载解析器 | -| 41 | `createEmbeddingProvider` | 为 memory / search 构建由 provider 拥有的 embedding 适配器 | memory embedding 行为应归属于 provider 插件 | -| 42 | `buildReplayPolicy` | 返回一个重放策略,用于控制该 provider 的转录处理 | provider 需要自定义转录策略(例如去除 thinking 块) | -| 43 | `sanitizeReplayHistory` | 在通用转录清理之后重写重放历史 | provider 需要共享压缩辅助工具之外的 provider 专属重放重写逻辑 | -| 44 | `validateReplayTurns` | 在嵌入式 runner 执行前,对重放轮次进行最终验证或重塑 | provider 传输在通用清理之后需要更严格的轮次验证 | -| 45 | `onModelSelected` | 在模型变为活跃后运行由 provider 拥有的选择后副作用 | provider 在模型激活时需要遥测或由 provider 拥有的状态 | - -`normalizeModelId`、`normalizeTransport` 和 `normalizeConfig` 会先检查匹配到的 provider 插件,然后再依次尝试其他具备 hook 能力的 provider 插件,直到某个插件实际更改了模型 id 或传输 / 配置。这样可以让别名 / 兼容 provider shim 继续工作,而无需调用方知道哪个内置插件拥有该重写逻辑。如果没有任何 provider hook 重写受支持的 Google 家族配置项,内置的 Google 配置规范化器仍会应用该兼容性清理。 - -如果 provider 需要完全自定义的线路协议或自定义请求执行器,那就属于另一类扩展。这些 hooks 用于仍然运行在 OpenClaw 正常推理循环上的 provider 行为。 - -### provider 示例 - -```ts -api.registerProvider({ - id: "example-proxy", - label: "Example Proxy", - auth: [], - catalog: { - order: "simple", - run: async (ctx) => { - const apiKey = ctx.resolveProviderApiKey("example-proxy").apiKey; - if (!apiKey) { - return null; - } - return { - provider: { - baseUrl: "https://proxy.example.com/v1", - apiKey, - api: "openai-completions", - models: [{ id: "auto", name: "Auto" }], - }, - }; - }, - }, - resolveDynamicModel: (ctx) => ({ - id: ctx.modelId, - name: ctx.modelId, - provider: "example-proxy", - api: "openai-completions", - baseUrl: "https://proxy.example.com/v1", - reasoning: false, - input: ["text"], - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, - contextWindow: 128000, - maxTokens: 8192, - }), - prepareRuntimeAuth: async (ctx) => { - const exchanged = await exchangeToken(ctx.apiKey); - return { - apiKey: exchanged.token, - baseUrl: exchanged.baseUrl, - expiresAt: exchanged.expiresAt, - }; - }, - resolveUsageAuth: async (ctx) => { - const auth = await ctx.resolveOAuthToken(); - return auth ? { token: auth.token } : null; - }, - fetchUsageSnapshot: async (ctx) => { - return await fetchExampleProxyUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn); - }, -}); -``` - -### 内置示例 - -内置 provider 插件会组合使用上述 hooks,以适配各供应商的目录、认证、thinking、重放和用量需求。权威的 hook 集合与各插件一起位于 `extensions/` 下;本页展示的是形态,而不是简单镜像整个列表。 - - - - OpenRouter、Kilocode、Z.AI、xAI 会注册 `catalog`,并配合 - `resolveDynamicModel` / `prepareDynamicModel`,从而能在 OpenClaw 的静态目录之前暴露上游模型 id。 - - - GitHub Copilot、Gemini CLI、ChatGPT Codex、MiniMax、Xiaomi、z.ai 会将 - `prepareRuntimeAuth` 或 `formatApiKey` 与 `resolveUsageAuth` + - `fetchUsageSnapshot` 配合使用,以拥有 token 交换和 `/usage` 集成。 - - - 共享命名家族(`google-gemini`、`passthrough-gemini`、 - `anthropic-by-model`、`hybrid-anthropic-openai`)允许 providers 通过 - `buildReplayPolicy` 接入转录策略,而不是让每个插件都重新实现清理逻辑。 - - - `byteplus`、`cloudflare-ai-gateway`、`huggingface`、`kimi-coding`、`nvidia`、 - `qianfan`、`synthetic`、`together`、`venice`、`vercel-ai-gateway` 和 - `volcengine` 只注册 `catalog`,并复用共享推理循环。 - - - Beta headers、`/fast` / `serviceTier` 和 `context1m` 位于 - Anthropic 插件的公共 `api.ts` / `contract-api.ts` 接缝中 - (`wrapAnthropicProviderStream`、`resolveAnthropicBetas`、 - `resolveAnthropicFastMode`、`resolveAnthropicServiceTier`),而不是位于通用 SDK 中。 - - - -## 运行时辅助工具 - -插件可以通过 `api.runtime` 访问部分选定的 core 辅助工具。对于 TTS: - -```ts -const clip = await api.runtime.tts.textToSpeech({ - text: "Hello from OpenClaw", - cfg: api.config, -}); - -const result = await api.runtime.tts.textToSpeechTelephony({ - text: "Hello from OpenClaw", - cfg: api.config, -}); - -const voices = await api.runtime.tts.listVoices({ - provider: "elevenlabs", - cfg: api.config, -}); -``` - -说明: - -- `textToSpeech` 返回文件 / 语音便笺界面所使用的常规 core TTS 输出负载。 -- 使用 core `messages.tts` 配置和 provider 选择逻辑。 -- 返回 PCM 音频缓冲区 + 采样率。插件必须为 providers 自行重采样 / 编码。 -- `listVoices` 对每个 provider 来说都是可选的。可将其用于由供应商拥有的语音选择器或设置流程。 -- 语音列表可以包含更丰富的元数据,例如语言区域、性别和个性标签,以支持 provider 感知型选择器。 -- 当前 OpenAI 和 ElevenLabs 支持电话场景。Microsoft 不支持。 - -插件也可以通过 `api.registerSpeechProvider(...)` 注册语音 providers。 - -```ts -api.registerSpeechProvider({ - id: "acme-speech", - label: "Acme Speech", - isConfigured: ({ config }) => Boolean(config.messages?.tts), - synthesize: async (req) => { - return { - audioBuffer: Buffer.from([]), - outputFormat: "mp3", - fileExtension: ".mp3", - voiceCompatible: false, - }; - }, -}); -``` - -说明: - -- 将 TTS 策略、回退和回复投递保留在 core 中。 -- 使用语音 providers 承载由供应商拥有的合成行为。 -- 旧版 Microsoft `edge` 输入会被规范化为 `microsoft` provider id。 -- 首选的归属模型是面向公司的:随着 OpenClaw 增加这些能力契约,一个供应商插件可以同时拥有文本、语音、图像以及未来的媒体 providers。 - -对于图像 / 音频 / 视频理解,插件应注册一个类型化的 -media-understanding provider,而不是通用的键值包: - -```ts -api.registerMediaUnderstandingProvider({ - id: "google", - capabilities: ["image", "audio", "video"], - describeImage: async (req) => ({ text: "..." }), - transcribeAudio: async (req) => ({ text: "..." }), - describeVideo: async (req) => ({ text: "..." }), -}); -``` - -说明: - -- 将编排、回退、配置和渠道接线保留在 core 中。 -- 将供应商行为保留在 provider 插件中。 -- 增量扩展应保持类型化:新的可选方法、新的可选结果字段、新的可选能力。 -- 视频生成已经遵循同样模式: - - core 拥有能力契约和运行时辅助工具 - - 供应商插件注册 `api.registerVideoGenerationProvider(...)` - - 功能 / 渠道插件消费 `api.runtime.videoGeneration.*` - -对于 media-understanding 运行时辅助工具,插件可以调用: - -```ts -const image = await api.runtime.mediaUnderstanding.describeImageFile({ - filePath: "/tmp/inbound-photo.jpg", - cfg: api.config, - agentDir: "/tmp/agent", -}); - -const video = await api.runtime.mediaUnderstanding.describeVideoFile({ - filePath: "/tmp/inbound-video.mp4", - cfg: api.config, -}); -``` - -对于音频转写,插件既可以使用 media-understanding 运行时, -也可以使用较旧的 STT 别名: - -```ts -const { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({ - filePath: "/tmp/inbound-audio.ogg", - cfg: api.config, - // Optional when MIME cannot be inferred reliably: - mime: "audio/ogg", -}); -``` - -说明: - -- `api.runtime.mediaUnderstanding.*` 是图像 / 音频 / 视频理解的首选共享界面。 -- 使用 core 的媒体理解音频配置(`tools.media.audio`)和 provider 回退顺序。 -- 当未产生转写输出时(例如输入被跳过 / 不受支持),返回 `{ text: undefined }`。 -- `api.runtime.stt.transcribeAudioFile(...)` 仍作为兼容性别名保留。 - -插件还可以通过 `api.runtime.subagent` 启动后台子智能体运行: - -```ts -const result = await api.runtime.subagent.run({ - sessionKey: "agent:main:subagent:search-helper", - message: "Expand this query into focused follow-up searches.", - provider: "openai", - model: "gpt-4.1-mini", - deliver: false, -}); -``` - -说明: - -- `provider` 和 `model` 是每次运行可选的覆盖项,而不是持久的会话变更。 -- OpenClaw 只会对受信任调用方生效这些覆盖字段。 -- 对于由插件拥有的回退运行,运维人员必须通过 `plugins.entries..subagent.allowModelOverride: true` 显式启用。 -- 使用 `plugins.entries..subagent.allowedModels` 可将受信任插件限制为特定规范 `provider/model` 目标,或使用 `"*"` 显式允许任意目标。 -- 不受信任插件的子智能体运行仍然可用,但覆盖请求会被拒绝,而不是悄悄回退。 - -对于 Web 搜索,插件可以消费共享运行时辅助工具,而不是深入智能体工具接线层: - -```ts -const providers = api.runtime.webSearch.listProviders({ - config: api.config, -}); - -const result = await api.runtime.webSearch.search({ - config: api.config, - args: { - query: "OpenClaw plugin runtime helpers", - count: 5, - }, -}); -``` - -插件也可以通过 -`api.registerWebSearchProvider(...)` 注册 Web 搜索 providers。 - -说明: - -- 将 provider 选择、凭证解析和共享请求语义保留在 core 中。 -- 使用 Web 搜索 providers 承载供应商专属搜索传输。 -- `api.runtime.webSearch.*` 是功能 / 渠道插件在不依赖智能体工具包装器的情况下需要搜索行为时的首选共享界面。 - -### `api.runtime.imageGeneration` - -```ts -const result = await api.runtime.imageGeneration.generate({ - config: api.config, - args: { prompt: "A friendly lobster mascot", size: "1024x1024" }, -}); - -const providers = api.runtime.imageGeneration.listProviders({ - config: api.config, -}); -``` - -- `generate(...)`:使用已配置的图像生成 provider 链生成图像。 -- `listProviders(...)`:列出可用的图像生成 providers 及其能力。 - -## Gateway 网关 HTTP 路由 - -插件可以使用 `api.registerHttpRoute(...)` 暴露 HTTP 端点。 - -```ts -api.registerHttpRoute({ - path: "/acme/webhook", - auth: "plugin", - match: "exact", - handler: async (_req, res) => { - res.statusCode = 200; - res.end("ok"); - return true; - }, -}); -``` - -路由字段: - -- `path`:Gateway 网关 HTTP 服务器下的路由路径。 -- `auth`:必填。使用 `"gateway"` 表示需要正常的 Gateway 网关认证,或使用 `"plugin"` 表示由插件管理认证 / webhook 验证。 -- `match`:可选。`"exact"`(默认)或 `"prefix"`。 -- `replaceExisting`:可选。允许同一插件替换自己已有的路由注册。 -- `handler`:当路由处理了请求时返回 `true`。 - -说明: - -- `api.registerHttpHandler(...)` 已被移除,并会导致插件加载错误。请改用 `api.registerHttpRoute(...)`。 -- 插件路由必须显式声明 `auth`。 -- 精确的 `path + match` 冲突会被拒绝,除非设置了 `replaceExisting: true`,并且一个插件不能替换另一个插件的路由。 -- 不同 `auth` 级别的重叠路由会被拒绝。请仅在相同认证级别内保留 `exact` / `prefix` 的贯穿链。 -- `auth: "plugin"` 路由**不会**自动获得运维人员运行时作用域。它们用于插件管理的 webhook / 签名校验,而不是特权 Gateway 网关辅助调用。 -- `auth: "gateway"` 路由运行在 Gateway 网关请求运行时作用域中,但该作用域有意保持保守: - - 共享密钥 bearer 认证(`gateway.auth.mode = "token"` / `"password"`)会将插件路由运行时作用域固定为 `operator.write`,即使调用方发送了 `x-openclaw-scopes` - - 带有受信身份的 HTTP 模式(例如 `trusted-proxy` 或私有入口上的 `gateway.auth.mode = "none"`)仅在显式存在该 header 时才会采纳 `x-openclaw-scopes` - - 对于这些带身份的插件路由请求,如果缺少 `x-openclaw-scopes`,运行时作用域会回退到 `operator.write` -- 实际规则:不要假设一个使用 gateway 认证的插件路由天然就是管理员界面。如果你的路由需要仅管理员行为,请要求使用带身份的认证模式,并文档化显式的 `x-openclaw-scopes` header 契约。 - -## 插件 SDK import 路径 - -编写新插件时,请使用狭义的 SDK 子路径,而不是单体式的 `openclaw/plugin-sdk` 根 barrel。 -core 子路径如下: - -| 子路径 | 用途 | -| ----------------------------------- | -------------------------------------------------- | -| `openclaw/plugin-sdk/plugin-entry` | 插件注册原语 | -| `openclaw/plugin-sdk/channel-core` | 渠道入口 / 构建辅助工具 | -| `openclaw/plugin-sdk/core` | 通用共享辅助工具和总括契约 | -| `openclaw/plugin-sdk/config-schema` | 根 `openclaw.json` Zod schema(`OpenClawSchema`) | - -渠道插件应从一组狭义接缝中进行选择——`channel-setup`、 -`setup-runtime`、`setup-adapter-runtime`、`setup-tools`、`channel-pairing`、 -`channel-contract`、`channel-feedback`、`channel-inbound`、`channel-lifecycle`、 -`channel-reply-pipeline`、`command-auth`、`secret-input`、`webhook-ingress`、 -`channel-targets` 和 `channel-actions`。批准行为应收敛到单一的 -`approvalCapability` 契约,而不是混用分散在无关插件字段中的行为。请参见 [渠道插件](/zh-CN/plugins/sdk-channel-plugins)。 - -运行时和配置辅助工具位于对应的 `*-runtime` 子路径下 -(`approval-runtime`、`config-runtime`、`infra-runtime`、`agent-runtime`、 -`lazy-runtime`、`directory-runtime`、`text-runtime`、`runtime-store` 等)。 - - -`openclaw/plugin-sdk/channel-runtime` 已弃用——它是为旧插件保留的兼容性 shim。新代码应改为导入更狭义的通用原语。 - - -仓库内部入口点(每个内置插件 package 根目录): - -- `index.js` — 内置插件入口 -- `api.js` — 辅助工具 / 类型 barrel -- `runtime-api.js` — 仅运行时 barrel -- `setup-entry.js` — 设置插件入口 - -外部插件应只导入 `openclaw/plugin-sdk/*` 子路径。绝不要从 core -或其他插件中导入另一个插件 package 的 `src/*`。 -通过 facade 加载的入口点会优先使用当前活跃的运行时配置快照;如不存在,则回退到磁盘上的已解析配置文件。 - -像 `image-generation`、`media-understanding` 和 `speech` 这样的能力专属子路径之所以存在,是因为内置插件今天就在使用它们。它们并不自动等同于长期冻结的外部契约——在依赖它们时,请查看对应的 SDK 参考页面。 - -## 消息工具 schema - -对于反应、已读和投票等非消息原语,插件应拥有渠道专属的 `describeMessageTool(...)` schema 贡献。 -共享发送呈现应使用通用的 `MessagePresentation` 契约,而不是 provider 原生的按钮、组件、块或卡片字段。 -有关契约、回退规则、provider 映射和插件作者检查清单,请参见 [消息呈现](/zh-CN/plugins/message-presentation)。 - -具备发送能力的插件通过消息能力声明它们能够渲染的内容: - -- `presentation` 用于语义呈现块(`text`、`context`、`divider`、`buttons`、`select`) -- `delivery-pin` 用于固定投递请求 - -由 core 决定是原生渲染该呈现,还是降级为文本。 -不要从通用消息工具中暴露 provider 原生 UI 的逃生口。 -为兼容现有第三方插件,面向旧版原生 schemas 的已弃用 SDK 辅助工具仍然保持导出,但新插件不应再使用它们。 - -## 渠道目标解析 - -渠道插件应拥有渠道专属的目标语义。请保持共享出站宿主的通用性,并通过消息适配器界面处理 provider 规则: - -- `messaging.inferTargetChatType({ to })` 决定某个规范化目标在目录查找前应被视为 `direct`、`group` 还是 `channel` -- `messaging.targetResolver.looksLikeId(raw, normalized)` 告诉 core 某个输入是否应直接跳过目录搜索,进入类似 id 的解析 -- `messaging.targetResolver.resolveTarget(...)` 是插件回退逻辑,当 core 在规范化之后或目录未命中后仍需要 provider 拥有的最终解析时调用 -- `messaging.resolveOutboundSessionRoute(...)` 在目标解析完成后,拥有 provider 专属的会话路由构造逻辑 - -推荐拆分: - -- 对于应在搜索 peers / groups 之前作出的分类决策,请使用 `inferTargetChatType` -- 对于“将此视为显式 / 原生目标 id”的判断,请使用 `looksLikeId` -- 对于 provider 专属的规范化回退,请使用 `resolveTarget`,而不要将其用于广泛的目录搜索 -- 请将 provider 原生 id,例如 chat id、thread id、JID、handle 和 room id,保留在 `target` 值或 provider 专属参数中,而不是放进通用 SDK 字段 - -## 配置支持的目录 - -如果某个插件会从配置中派生目录项,应将该逻辑保留在插件内,并复用 -`openclaw/plugin-sdk/directory-runtime` -中的共享辅助工具。 - -适用于以下情况:某个渠道需要由配置支持的 peers / groups,例如: - -- 基于 allowlist 的私信 peers -- 已配置的渠道 / 群组映射 -- 按账户划分的静态目录回退 - -`directory-runtime` 中的共享辅助工具只处理通用操作: - -- 查询过滤 -- 上限应用 -- 去重 / 规范化辅助工具 -- 构建 `ChannelDirectoryEntry[]` - -渠道专属的账户检查和 id 规范化应保留在插件实现中。 - -## provider 目录 - -provider 插件可以通过 -`registerProvider({ catalog: { run(...) { ... } } })` -为推理定义模型目录。 - -`catalog.run(...)` 返回的结构与 OpenClaw 写入 -`models.providers` 中的结构相同: - -- `{ provider }` 表示一个 provider 条目 -- `{ providers }` 表示多个 provider 条目 - -当插件拥有 provider 专属的模型 id、base URL 默认值或受认证门控的模型元数据时,请使用 `catalog`。 - -`catalog.order` 控制插件目录相对于 OpenClaw 内置隐式 providers 的合并时机: - -- `simple`:纯 API key 或环境变量驱动的 providers -- `profile`:存在 auth 配置文件时出现的 providers -- `paired`:合成多个相关 provider 条目的 providers -- `late`:最后一轮,在其他隐式 providers 之后 - -发生键冲突时,后出现的 provider 会获胜,因此插件可以有意覆盖同一 provider id 的内置 provider 条目。 - -兼容性说明: - -- `discovery` 仍可作为旧版别名使用 -- 如果同时注册了 `catalog` 和 `discovery`,OpenClaw 会使用 `catalog` - -## 只读渠道检查 - -如果你的插件注册了某个渠道,建议在 `resolveAccount(...)` 之外,同时实现 -`plugin.config.inspectAccount(cfg, accountId)`。 - -原因: - -- `resolveAccount(...)` 是运行时路径。它可以假设凭证已完全物化,并在缺失必要密钥时快速失败。 -- `openclaw status`、`openclaw status --all`、 - `openclaw channels status`、`openclaw channels resolve` 以及 doctor / 配置修复流程等只读命令路径,不应为了描述配置就必须物化运行时凭证。 - -推荐的 `inspectAccount(...)` 行为: - -- 只返回描述性的账户状态。 -- 保留 `enabled` 和 `configured`。 -- 在相关时包含凭证来源 / 状态字段,例如: - - `tokenSource`、`tokenStatus` - - `botTokenSource`、`botTokenStatus` - - `appTokenSource`、`appTokenStatus` - - `signingSecretSource`、`signingSecretStatus` -- 你不需要为了报告只读可用性而返回原始 token 值。对状态类命令来说,返回 `tokenStatus: "available"`(以及对应的来源字段)就足够了。 -- 当凭证是通过 SecretRef 配置,但在当前命令路径中不可用时,请使用 `configured_unavailable`。 - -这样可以让只读命令报告“已配置,但在当前命令路径中不可用”,而不是崩溃或错误地将账户报告为未配置。 - -## Package packs - -插件目录中可以包含一个带有 `openclaw.extensions` 的 `package.json`: - -```json -{ - "name": "my-pack", - "openclaw": { - "extensions": ["./src/safety.ts", "./src/tools.ts"], - "setupEntry": "./src/setup-entry.ts" - } -} -``` - -每个条目都会成为一个插件。如果 pack 列出了多个 extensions,则插件 id -会变成 `name/`。 - -如果你的插件导入了 npm 依赖,请在该目录中安装它们,以确保 -`node_modules` 可用(`npm install` / `pnpm install`)。 - -安全护栏:每个 `openclaw.extensions` 条目在解析符号链接之后,都必须仍位于插件目录内部。任何逃逸出 package 目录的条目都会被拒绝。 - -安全说明:`openclaw plugins install` 会使用 -`npm install --omit=dev --ignore-scripts` 安装插件依赖(无生命周期脚本,运行时也无开发依赖)。请保持插件依赖树为“纯 JS / TS”,并避免那些需要 `postinstall` 构建的包。 - -可选:`openclaw.setupEntry` 可以指向一个轻量的、仅用于设置的模块。 -当 OpenClaw 需要为已禁用的渠道插件提供设置界面,或者当某个渠道插件已启用但尚未配置时,它会加载 `setupEntry`,而不是完整的插件入口。这样在你的主插件入口还会接线工具、hooks 或其他仅运行时代码时,可以让启动和设置保持更轻量。 - -可选:`openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen` -可以让某个渠道插件在 Gateway 网关监听前的启动阶段,即使该渠道已经配置完毕,也改走相同的 `setupEntry` 路径。 - -只有当 `setupEntry` 能完全覆盖 Gateway 网关开始监听之前必须存在的启动界面时,才应使用它。实际来说,这意味着设置入口必须注册启动所依赖的每一种由渠道拥有的能力,例如: - -- 渠道注册本身 -- 所有必须在 Gateway 网关开始监听前就可用的 HTTP 路由 -- 同一时间窗口中必须存在的所有 Gateway 网关方法、工具或服务 - -如果你的完整入口仍然拥有任何必需的启动能力,就不要启用这个标志。应保持默认行为,让 OpenClaw 在启动期间加载完整入口。 - -内置渠道也可以发布仅用于设置的契约界面辅助工具,以便 core 在完整渠道运行时尚未加载前进行查询。当前的设置提升界面包括: - -- `singleAccountKeysToMove` -- `namedAccountPromotionKeys` -- `resolveSingleAccountPromotionTarget(...)` - -当 core 需要在不加载完整插件入口的情况下,将旧版单账户渠道配置提升到 -`channels..accounts.*` 中时,就会使用该界面。 -当前的内置示例是 Matrix:当已存在命名账户时,它只会将认证 / 引导键移动到某个命名提升账户中,并且它可以保留一个已配置的、非规范默认账户键,而不总是创建 -`accounts.default`。 - -这些设置补丁适配器使内置契约界面的发现保持惰性。导入时保持轻量;提升界面只在首次使用时加载,而不会在模块导入时重新进入内置渠道启动流程。 - -当这些启动界面包含 Gateway 网关 RPC 方法时,请将它们保留在插件专属前缀下。core 管理员命名空间(`config.*`、 -`exec.approvals.*`、`wizard.*`、`update.*`)始终保留,并且总是解析为 -`operator.admin`,即使某个插件请求了更窄的作用域。 - -示例: - -```json -{ - "name": "@scope/my-channel", - "openclaw": { - "extensions": ["./index.ts"], - "setupEntry": "./setup-entry.ts", - "startup": { - "deferConfiguredChannelFullLoadUntilAfterListen": true - } - } -} -``` - -### 渠道目录元数据 - -渠道插件可以通过 `openclaw.channel` 声明设置 / 发现元数据,并通过 `openclaw.install` 声明安装提示。这样可以让 core 目录保持无数据状态。 - -示例: - -```json -{ - "name": "@openclaw/nextcloud-talk", - "openclaw": { - "extensions": ["./index.ts"], - "channel": { - "id": "nextcloud-talk", - "label": "Nextcloud Talk", - "selectionLabel": "Nextcloud Talk(自托管)", - "docsPath": "/channels/nextcloud-talk", - "docsLabel": "nextcloud-talk", - "blurb": "通过 Nextcloud Talk webhook 机器人实现自托管聊天。", - "order": 65, - "aliases": ["nc-talk", "nc"] - }, - "install": { - "npmSpec": "@openclaw/nextcloud-talk", - "localPath": "", - "defaultChoice": "npm" - } - } -} -``` - -除最小示例之外,有用的 `openclaw.channel` 字段还包括: - -- `detailLabel`:用于更丰富目录 / 状态界面的次级标签 -- `docsLabel`:覆盖文档链接的文本 -- `preferOver`:此目录条目应优先于的低优先级插件 / 渠道 id -- `selectionDocsPrefix`、`selectionDocsOmitLabel`、`selectionExtras`:选择界面的文案控制 -- `markdownCapable`:将渠道标记为支持 Markdown,以用于出站格式决策 -- `exposure.configured`:设置为 `false` 时,在已配置渠道列表界面中隐藏该渠道 -- `exposure.setup`:设置为 `false` 时,在交互式设置 / 配置选择器中隐藏该渠道 -- `exposure.docs`:将该渠道标记为文档导航界面中的内部 / 私有项 -- `showConfigured` / `showInSetup`:为兼容性仍接受的旧版别名;优先使用 `exposure` -- `quickstartAllowFrom`:允许该渠道接入标准快速开始 `allowFrom` 流程 -- `forceAccountBinding`:即使只存在一个账户,也要求显式账户绑定 -- `preferSessionLookupForAnnounceTarget`:在解析 announce 目标时优先使用 session 查找 - -OpenClaw 还可以合并**外部渠道目录**(例如 MPM -注册表导出)。你可以将 JSON 文件放在以下任一位置: - -- `~/.openclaw/mpm/plugins.json` -- `~/.openclaw/mpm/catalog.json` -- `~/.openclaw/plugins/catalog.json` - -或者将 `OPENCLAW_PLUGIN_CATALOG_PATHS`(或 `OPENCLAW_MPM_CATALOG_PATHS`)指向一个或多个 JSON 文件(以逗号 / 分号 / `PATH` 分隔)。每个文件应包含 -`{ "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] }`。 -解析器也接受 `"packages"` 或 `"plugins"` 作为 `"entries"` 键的旧版别名。 - -## 上下文引擎插件 - -上下文引擎插件拥有会话上下文编排能力,包括摄取、组装和压缩。请在你的插件中通过 -`api.registerContextEngine(id, factory)` 注册它们,然后使用 -`plugins.slots.contextEngine` 选择活跃引擎。 - -当你的插件需要替换或扩展默认上下文管线,而不只是添加 memory 搜索或 hooks 时,请使用它。 - -```ts -import { buildMemorySystemPromptAddition } from "openclaw/plugin-sdk/core"; - -export default function (api) { - api.registerContextEngine("lossless-claw", () => ({ - info: { id: "lossless-claw", name: "Lossless Claw", ownsCompaction: true }, - async ingest() { - return { ingested: true }; - }, - async assemble({ messages, availableTools, citationsMode }) { - return { - messages, - estimatedTokens: 0, - systemPromptAddition: buildMemorySystemPromptAddition({ - availableTools: availableTools ?? new Set(), - citationsMode, - }), - }; - }, - async compact() { - return { ok: true, compacted: false }; - }, - })); -} -``` - -如果你的引擎**不**拥有压缩算法,请保持 `compact()` -已实现,并显式委托它: - -```ts -import { - buildMemorySystemPromptAddition, - delegateCompactionToRuntime, -} from "openclaw/plugin-sdk/core"; - -export default function (api) { - api.registerContextEngine("my-memory-engine", () => ({ - info: { - id: "my-memory-engine", - name: "My Memory Engine", - ownsCompaction: false, - }, - async ingest() { - return { ingested: true }; - }, - async assemble({ messages, availableTools, citationsMode }) { - return { - messages, - estimatedTokens: 0, - systemPromptAddition: buildMemorySystemPromptAddition({ - availableTools: availableTools ?? new Set(), - citationsMode, - }), - }; - }, - async compact(params) { - return await delegateCompactionToRuntime(params); - }, - })); -} -``` - -## 添加新能力 - -当某个插件需要的行为不适合当前 API 时,不要通过私有深度接入绕过插件系统。应添加缺失的能力。 - -推荐顺序: - -1. 定义 core 契约 - 决定 core 应拥有哪些共享行为:策略、回退、配置合并、 - 生命周期、面向渠道的语义和运行时辅助工具形态。 -2. 添加类型化的插件注册 / 运行时界面 - 以最小且有用的类型化能力界面扩展 `OpenClawPluginApi` 和 / 或 `api.runtime`。 -3. 接线 core + 渠道 / 功能使用方 - 渠道和功能插件应通过 core 消费新能力, - 而不是直接导入某个供应商实现。 -4. 注册供应商实现 - 然后由供应商插件针对该能力注册其后端实现。 -5. 添加契约覆盖 - 添加测试,使归属和注册形态随着时间保持明确。 - -这就是 OpenClaw 保持有主张、同时又不被某个 provider 视角硬编码的方式。具体文件清单和完整示例请参见 [能力扩展手册](/zh-CN/plugins/architecture)。 - -### 能力检查清单 - -添加新能力时,实现通常应同时涉及以下界面: - -- `src//types.ts` 中的 core 契约类型 -- `src//runtime.ts` 中的 core runner / 运行时辅助工具 -- `src/plugins/types.ts` 中的插件 API 注册界面 -- `src/plugins/registry.ts` 中的插件注册表接线 -- 当功能 / 渠道插件需要消费它时,位于 `src/plugins/runtime/*` - 中的插件运行时暴露层 -- `src/test-utils/plugin-registration.ts` 中的捕获 / 测试辅助工具 -- `src/plugins/contracts/registry.ts` 中的归属 / 契约断言 -- `docs/` 中面向运维 / 插件的文档 - -如果这些界面中缺少某一项,通常意味着该能力尚未真正集成完成。 - -### 能力模板 - -最小模式: - -```ts -// core contract -export type VideoGenerationProviderPlugin = { - id: string; - label: string; - generateVideo: (req: VideoGenerationRequest) => Promise; -}; - -// plugin API -api.registerVideoGenerationProvider({ - id: "openai", - label: "OpenAI", - async generateVideo(req) { - return await generateOpenAiVideo(req); - }, -}); - -// shared runtime helper for feature/channel plugins -const clip = await api.runtime.videoGeneration.generate({ - prompt: "Show the robot walking through the lab.", - cfg, -}); -``` - -契约测试模式: - -```ts -expect(findVideoGenerationProviderIdsForPlugin("openai")).toEqual(["openai"]); -``` - -这样规则就保持简单: - -- core 拥有能力契约 + 编排 -- 供应商插件拥有供应商实现 -- 功能 / 渠道插件消费运行时辅助工具 -- 契约测试保持归属明确 +关于加载流水线、注册表模型、provider 运行时钩子、Gateway 网关 HTTP 路由、消息工具 schema、渠道目标解析、provider 目录、上下文引擎插件以及新增能力的指南,请参见[插件架构内部机制](/zh-CN/plugins/architecture-internals)。 ## 相关内容 - [构建插件](/zh-CN/plugins/building-plugins) - [插件 SDK 设置](/zh-CN/plugins/sdk-setup) -- [插件 manifest](/zh-CN/plugins/manifest) +- [插件清单](/zh-CN/plugins/manifest) diff --git a/docs/zh-CN/plugins/building-plugins.md b/docs/zh-CN/plugins/building-plugins.md index fa0413679..9b9684f1a 100644 --- a/docs/zh-CN/plugins/building-plugins.md +++ b/docs/zh-CN/plugins/building-plugins.md @@ -1,27 +1,27 @@ --- read_when: - - 你想创建一个新的 OpenClaw 插件 - - 你需要一个插件开发的快速开始指南 - - 你正在为 OpenClaw 添加新的渠道、提供商、工具或其他能力 + - 你想要创建一个新的 OpenClaw 插件 + - 你需要一个用于插件开发的快速开始指南 + - 你正在为 OpenClaw 添加一个新的渠道、提供商、工具或其他能力 sidebarTitle: Getting Started summary: 在几分钟内创建你的第一个 OpenClaw 插件 title: 构建插件 x-i18n: - generated_at: "2026-04-23T22:59:52Z" + generated_at: "2026-04-24T03:06:50Z" model: gpt-5.4 provider: openai - source_hash: 90c2413c9c011c89f1e50e12e1a1ee4cca8207234827897b423cf421244203ce + source_hash: c14f4c4dc3ae853e385f6beeb9529ea9e360f3d9c5b99dc717cf0851ed02cbc8 source_path: plugins/building-plugins.md workflow: 15 --- -插件可通过新增能力来扩展 OpenClaw:渠道、模型提供商、语音、实时转写、实时语音、媒体理解、图像生成、视频生成、web 抓取、web 搜索、智能体工具,或这些能力的任意组合。 +插件通过新增能力来扩展 OpenClaw:渠道、模型提供商、语音、实时转写、实时语音、媒体理解、图像生成、视频生成、网页抓取、网页搜索、智能体工具,或这些能力的任意组合。 -你不需要把插件添加到 OpenClaw 仓库中。发布到 [ClawHub](/zh-CN/tools/clawhub) 或 npm 后,用户可通过 `openclaw plugins install ` 安装。OpenClaw 会先尝试 ClawHub,失败后再自动回退到 npm。 +你不需要将你的插件添加到 OpenClaw 仓库中。发布到 [ClawHub](/zh-CN/tools/clawhub) 或 npm,用户即可通过 `openclaw plugins install ` 安装。OpenClaw 会先尝试 ClawHub,再自动回退到 npm。 -## 先决条件 +## 前提条件 -- Node >= 22 和一个包管理器(npm 或 pnpm) +- Node >= 22,以及一个包管理器(npm 或 pnpm) - 熟悉 TypeScript(ESM) - 对于仓库内插件:已克隆仓库并执行 `pnpm install` @@ -29,25 +29,24 @@ x-i18n: - 将 OpenClaw 连接到消息平台(Discord、IRC 等) + 将 OpenClaw 连接到一个消息平台(Discord、IRC 等) 添加一个模型提供商(LLM、代理或自定义端点) - 注册智能体工具、事件 hook 或服务 —— 继续阅读下文 + 注册智能体工具、事件 hook 或服务——继续阅读下文 -对于在新手引导/设置运行时不保证已安装的渠道插件,请使用 -`openclaw/plugin-sdk/channel-setup` 中的 `createOptionalChannelSetupSurface(...)`。它会生成一个设置适配器 + 向导组合,用于声明安装要求,并在插件安装前对真实配置写入采用默认拒绝策略。 +对于在新手引导/设置运行时无法保证已安装的渠道插件,请使用 `openclaw/plugin-sdk/channel-setup` 中的 `createOptionalChannelSetupSurface(...)`。它会生成一个设置适配器 + 向导组合,用于提示安装要求,并在插件安装完成之前,对实际配置写入采取失败即关闭的策略。 ## 快速开始:工具插件 -本教程将创建一个注册智能体工具的最小插件。渠道插件和提供商插件请参阅上方链接的专门指南。 +本教程将创建一个注册智能体工具的最小插件。渠道插件和提供商插件有上方链接的专门指南。 - + ```json package.json { @@ -81,8 +80,7 @@ x-i18n: ``` - 每个插件都需要一个 manifest,即使没有配置也是如此。完整模式请参阅 - [Manifest](/zh-CN/plugins/manifest)。规范的 ClawHub 发布片段位于 `docs/snippets/plugin-publish/`。 + 每个插件都需要一个清单,即使没有配置也一样。完整 schema 请参见 [Manifest](/zh-CN/plugins/manifest)。规范的 ClawHub 发布代码片段位于 `docs/snippets/plugin-publish/`。 @@ -110,15 +108,13 @@ x-i18n: }); ``` - `definePluginEntry` 用于非渠道插件。对于渠道,请使用 - `defineChannelPluginEntry` —— 参见 [Channel Plugins](/zh-CN/plugins/sdk-channel-plugins)。 - 关于完整入口点选项,请参阅 [Entry Points](/zh-CN/plugins/sdk-entrypoints)。 + `definePluginEntry` 用于非渠道插件。对于渠道,请使用 `defineChannelPluginEntry` —— 参见 [渠道插件](/zh-CN/plugins/sdk-channel-plugins)。完整的入口点选项请参见 [入口点](/zh-CN/plugins/sdk-entrypoints)。 - **外部插件:** 通过 ClawHub 验证并发布,然后安装: + **外部插件:** 使用 ClawHub 验证并发布,然后安装: ```bash clawhub package publish your-org/your-plugin --dry-run @@ -128,7 +124,7 @@ x-i18n: 对于像 `@myorg/openclaw-my-plugin` 这样的裸包规格,OpenClaw 也会先检查 ClawHub,再检查 npm。 - **仓库内插件:** 放在内置插件工作区树下 —— 会自动发现。 + **仓库内插件:** 放在内置插件工作区目录树下——会被自动发现。 ```bash pnpm test -- /my-plugin/ @@ -139,60 +135,60 @@ x-i18n: ## 插件能力 -单个插件可通过 `api` 对象注册任意数量的能力: +一个插件可以通过 `api` 对象注册任意数量的能力: | 能力 | 注册方法 | 详细指南 | | ---------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------- | -| 文本推理(LLM) | `api.registerProvider(...)` | [Provider Plugins](/zh-CN/plugins/sdk-provider-plugins) | -| CLI 推理后端 | `api.registerCliBackend(...)` | [CLI Backends](/zh-CN/gateway/cli-backends) | -| 渠道 / 消息 | `api.registerChannel(...)` | [Channel Plugins](/zh-CN/plugins/sdk-channel-plugins) | -| 语音(TTS/STT) | `api.registerSpeechProvider(...)` | [Provider Plugins](/zh-CN/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) | -| 实时转写 | `api.registerRealtimeTranscriptionProvider(...)` | [Provider Plugins](/zh-CN/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) | -| 实时语音 | `api.registerRealtimeVoiceProvider(...)` | [Provider Plugins](/zh-CN/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) | -| 媒体理解 | `api.registerMediaUnderstandingProvider(...)` | [Provider Plugins](/zh-CN/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) | -| 图像生成 | `api.registerImageGenerationProvider(...)` | [Provider Plugins](/zh-CN/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) | -| 音乐生成 | `api.registerMusicGenerationProvider(...)` | [Provider Plugins](/zh-CN/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) | -| 视频生成 | `api.registerVideoGenerationProvider(...)` | [Provider Plugins](/zh-CN/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) | -| Web 抓取 | `api.registerWebFetchProvider(...)` | [Provider Plugins](/zh-CN/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) | -| Web 搜索 | `api.registerWebSearchProvider(...)` | [Provider Plugins](/zh-CN/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) | -| 嵌入式 Pi 扩展 | `api.registerEmbeddedExtensionFactory(...)` | [SDK 概览](/zh-CN/plugins/sdk-overview#registration-api) | -| 智能体工具 | `api.registerTool(...)` | 下文 | +| 文本推理(LLM) | `api.registerProvider(...)` | [提供商插件](/zh-CN/plugins/sdk-provider-plugins) | +| CLI 推理后端 | `api.registerCliBackend(...)` | [CLI 后端](/zh-CN/gateway/cli-backends) | +| 渠道 / 消息传递 | `api.registerChannel(...)` | [渠道插件](/zh-CN/plugins/sdk-channel-plugins) | +| 语音(TTS/STT) | `api.registerSpeechProvider(...)` | [提供商插件](/zh-CN/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) | +| 实时转写 | `api.registerRealtimeTranscriptionProvider(...)` | [提供商插件](/zh-CN/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) | +| 实时语音 | `api.registerRealtimeVoiceProvider(...)` | [提供商插件](/zh-CN/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) | +| 媒体理解 | `api.registerMediaUnderstandingProvider(...)` | [提供商插件](/zh-CN/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) | +| 图像生成 | `api.registerImageGenerationProvider(...)` | [提供商插件](/zh-CN/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) | +| 音乐生成 | `api.registerMusicGenerationProvider(...)` | [提供商插件](/zh-CN/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) | +| 视频生成 | `api.registerVideoGenerationProvider(...)` | [提供商插件](/zh-CN/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) | +| 网页抓取 | `api.registerWebFetchProvider(...)` | [提供商插件](/zh-CN/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) | +| 网页搜索 | `api.registerWebSearchProvider(...)` | [提供商插件](/zh-CN/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) | +| 内嵌 Pi 扩展 | `api.registerEmbeddedExtensionFactory(...)` | [SDK 概览](/zh-CN/plugins/sdk-overview#registration-api) | +| 智能体工具 | `api.registerTool(...)` | 见下文 | | 自定义命令 | `api.registerCommand(...)` | [入口点](/zh-CN/plugins/sdk-entrypoints) | | 事件 hook | `api.registerHook(...)` | [入口点](/zh-CN/plugins/sdk-entrypoints) | -| HTTP 路由 | `api.registerHttpRoute(...)` | [Internals](/zh-CN/plugins/architecture#gateway-http-routes) | +| HTTP 路由 | `api.registerHttpRoute(...)` | [内部机制](/zh-CN/plugins/architecture-internals#gateway-http-routes) | | CLI 子命令 | `api.registerCli(...)` | [入口点](/zh-CN/plugins/sdk-entrypoints) | -完整注册 API 请参阅 [SDK 概览](/zh-CN/plugins/sdk-overview#registration-api)。 +完整的注册 API 请参见 [SDK 概览](/zh-CN/plugins/sdk-overview#registration-api)。 -当插件需要 Pi 原生嵌入式运行器 hook(例如在最终工具结果消息发出前异步重写 `tool_result`)时,请使用 `api.registerEmbeddedExtensionFactory(...)`。如果该工作不需要 Pi 扩展时序,请优先使用常规 OpenClaw 插件 hook。 +当插件需要 Pi 原生的内嵌运行器 hook 时,请使用 `api.registerEmbeddedExtensionFactory(...)`,例如在最终工具结果消息发出之前,对异步 `tool_result` 进行重写。如果这项工作不需要 Pi 扩展时序,优先使用常规 OpenClaw 插件 hook。 -如果你的插件注册了自定义 Gateway 网关 RPC 方法,请保持在插件专属前缀下。核心管理命名空间(`config.*`、`exec.approvals.*`、`wizard.*`、`update.*`)是保留的,并且始终解析到 `operator.admin`,即使插件请求更窄的作用域也是如此。 +如果你的插件注册了自定义 Gateway 网关 RPC 方法,请为它们使用插件专属前缀。核心管理命名空间(`config.*`、`exec.approvals.*`、`wizard.*`、`update.*`)始终保留,并且总是解析为 `operator.admin`,即使插件请求了更窄的作用域也是如此。 需要注意的 hook 守卫语义: -- `before_tool_call`:`{ block: true }` 是终止性的,会阻止更低优先级的处理器继续执行。 -- `before_tool_call`:`{ block: false }` 视为未作决定。 -- `before_tool_call`:`{ requireApproval: true }` 会暂停智能体执行,并通过 exec 审批浮层、Telegram 按钮、Discord 交互,或任意渠道上的 `/approve` 命令提示用户审批。 -- `before_install`:`{ block: true }` 是终止性的,会阻止更低优先级的处理器继续执行。 -- `before_install`:`{ block: false }` 视为未作决定。 -- `message_sending`:`{ cancel: true }` 是终止性的,会阻止更低优先级的处理器继续执行。 -- `message_sending`:`{ cancel: false }` 视为未作决定。 -- `message_received`:当你需要入站线程/主题路由时,优先使用类型化的 `threadId` 字段。`metadata` 仅保留给渠道专属扩展信息。 +- `before_tool_call`:`{ block: true }` 为终止性结果,会阻止更低优先级的处理器继续执行。 +- `before_tool_call`:`{ block: false }` 会被视为未作决定。 +- `before_tool_call`:`{ requireApproval: true }` 会暂停智能体执行,并通过 exec 审批覆盖层、Telegram 按钮、Discord 交互,或任意渠道中的 `/approve` 命令提示用户批准。 +- `before_install`:`{ block: true }` 为终止性结果,会阻止更低优先级的处理器继续执行。 +- `before_install`:`{ block: false }` 会被视为未作决定。 +- `message_sending`:`{ cancel: true }` 为终止性结果,会阻止更低优先级的处理器继续执行。 +- `message_sending`:`{ cancel: false }` 会被视为未作决定。 +- `message_received`:当你需要入站线程/话题路由时,优先使用类型化的 `threadId` 字段。`metadata` 应保留给渠道专属的额外信息。 - `message_sending`:优先使用类型化的 `replyToId` / `threadId` 路由字段,而不是渠道专属的 metadata 键。 -`/approve` 命令同时处理 exec 审批和插件审批,并带有有界回退逻辑:当未找到某个 exec 审批 ID 时,OpenClaw 会使用同一个 ID 在插件审批中重试。插件审批转发可通过配置中的 `approvals.plugin` 独立设置。 +`/approve` 命令同时处理 exec 审批和插件审批,并带有有界回退机制:当找不到某个 exec 审批 id 时,OpenClaw 会使用同一个 id 重新尝试插件审批。插件审批转发可以通过配置中的 `approvals.plugin` 独立设置。 -如果自定义审批流程需要检测这个相同的有界回退场景,请优先使用 `openclaw/plugin-sdk/error-runtime` 中的 `isApprovalNotFoundError`,而不是手动匹配审批过期字符串。 +如果自定义审批流程需要检测这一相同的有界回退场景,请优先使用 `openclaw/plugin-sdk/error-runtime` 中的 `isApprovalNotFoundError`,而不是手动匹配审批过期字符串。 -详情请参阅 [SDK 概览 hook decision semantics](/zh-CN/plugins/sdk-overview#hook-decision-semantics)。 +详情请参见 [SDK 概览 hook 决策语义](/zh-CN/plugins/sdk-overview#hook-decision-semantics)。 ## 注册智能体工具 -工具是 LLM 可调用的类型化函数。它们可以是必需工具(始终可用),也可以是可选工具(用户选择启用): +工具是 LLM 可以调用的类型化函数。它们可以是必需的(始终可用),也可以是可选的(用户选择启用): ```typescript register(api) { - // Required tool — always available + // 必需工具——始终可用 api.registerTool({ name: "my_tool", description: "Do a thing", @@ -202,7 +198,7 @@ register(api) { }, }); - // Optional tool — user must add to allowlist + // 可选工具——用户必须添加到允许列表 api.registerTool( { name: "workflow_tool", @@ -225,83 +221,82 @@ register(api) { } ``` -- 工具名称不得与核心工具冲突(冲突时会被跳过) -- 对于有副作用或需要额外二进制依赖的工具,请使用 `optional: true` -- 用户可通过将插件 ID 添加到 `tools.allow` 来启用该插件的所有工具 +- 工具名称不得与核心工具冲突(冲突项会被跳过) +- 对于具有副作用或需要额外二进制依赖的工具,请使用 `optional: true` +- 用户可以通过将插件 id 添加到 `tools.allow` 来启用某个插件中的所有工具 ## 导入约定 -始终从聚焦的 `openclaw/plugin-sdk/` 路径导入: +始终从精确的 `openclaw/plugin-sdk/` 路径导入: ```typescript import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry"; import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store"; -// Wrong: monolithic root (deprecated, will be removed) +// 错误:单体根路径(已弃用,将被移除) import { ... } from "openclaw/plugin-sdk"; ``` -完整子路径参考请参阅 [SDK 概览](/zh-CN/plugins/sdk-overview)。 +完整的子路径参考请参见 [SDK 概览](/zh-CN/plugins/sdk-overview)。 -在你的插件内部,请使用本地 barrel 文件(`api.ts`、`runtime-api.ts`)进行内部导入——绝不要通过其 SDK 路径导入你自己的插件。 +在你的插件内部,请使用本地 barrel 文件(`api.ts`、`runtime-api.ts`)进行内部导入——绝不要通过它自己的 SDK 路径导入你自己的插件。 -对于提供商插件,请将提供商专属辅助工具保留在这些包根 barrel 中,除非该接缝确实是通用的。当前内置示例包括: +对于提供商插件,请将提供商专属辅助函数保留在这些包根级 barrel 中,除非该抽象确实是通用的。当前内置示例包括: -- Anthropic:Claude 流包装器,以及 `service_tier` / beta 辅助工具 -- OpenAI:provider 构建器、默认模型辅助工具、实时 provider -- OpenRouter:provider 构建器以及 onboarding/配置辅助工具 +- Anthropic:Claude 流包装器以及 `service_tier` / beta 辅助函数 +- OpenAI:提供商构建器、默认模型辅助函数、实时提供商 +- OpenRouter:提供商构建器以及新手引导/配置辅助函数 -如果某个辅助工具只在一个内置 provider 包内部有用,请将其保留在该包根接缝上,而不是提升到 `openclaw/plugin-sdk/*` 中。 +如果某个辅助函数只在一个内置提供商包内部有用,请将它保留在该包根级抽象层上,而不是将它提升到 `openclaw/plugin-sdk/*` 中。 -一些自动生成的 `openclaw/plugin-sdk/` 辅助接缝仍然存在,用于内置插件维护和兼容性,例如 -`plugin-sdk/feishu-setup` 或 `plugin-sdk/zalo-setup`。请将这些视为保留界面,而不是新第三方插件的默认模式。 +一些生成的 `openclaw/plugin-sdk/` 辅助抽象层仍然存在,用于内置插件的维护和兼容性,例如 `plugin-sdk/feishu-setup` 或 `plugin-sdk/zalo-setup`。请将这些视为保留接口,而不是新第三方插件的默认模式。 ## 提交前检查清单 **package.json** 具有正确的 `openclaw` 元数据 -**openclaw.plugin.json** manifest 已存在且有效 +**openclaw.plugin.json** 清单文件存在且有效 入口点使用 `defineChannelPluginEntry` 或 `definePluginEntry` -所有导入都使用聚焦的 `plugin-sdk/` 路径 +所有导入都使用精确的 `plugin-sdk/` 路径 内部导入使用本地模块,而不是 SDK 自导入 测试通过(`pnpm test -- /my-plugin/`) `pnpm check` 通过(仓库内插件) ## Beta 版本测试 -1. 关注 [openclaw/openclaw](https://github.com/openclaw/openclaw/releases) 的 GitHub 发布标签,并通过 `Watch` > `Releases` 订阅。Beta 标签形如 `v2026.3.N-beta.1`。你也可以为 OpenClaw 官方 X 账号 [@openclaw](https://x.com/openclaw) 开启通知,以获取发布公告。 -2. 一旦 beta 标签出现,尽快用它测试你的插件。稳定版发布前的窗口通常只有几小时。 -3. 测试完成后,在 `plugin-forum` Discord 渠道中你插件对应的讨论串里发布 `all good` 或说明出了什么问题。如果你还没有讨论串,请创建一个。 -4. 如果出现问题,请创建或更新一个标题为 `Beta blocker: - ` 的 issue,并添加 `beta-blocker` 标签。将 issue 链接放入你的讨论串。 -5. 向 `main` 提交一个标题为 `fix(): beta blocker - ` 的 PR,并在 PR 和你的 Discord 讨论串中都链接该 issue。贡献者不能为 PR 打标签,因此标题就是面向维护者和自动化的 PR 侧信号。有 PR 的阻塞问题会被合并;没有 PR 的阻塞问题则仍有可能随版本发布。维护者会在 beta 测试期间关注这些讨论串。 -6. 没有消息就表示一切正常。如果你错过了这个窗口,你的修复很可能只能在下一个周期发布。 +1. 关注 [openclaw/openclaw](https://github.com/openclaw/openclaw/releases) 上的 GitHub 发布标签,并通过 `Watch` > `Releases` 订阅。Beta 标签看起来像 `v2026.3.N-beta.1`。你也可以为 OpenClaw 官方 X 账号 [@openclaw](https://x.com/openclaw) 开启通知,以获取发布公告。 +2. 在 beta 标签一出现时,立即针对该 beta 标签测试你的插件。稳定版发布前的窗口期通常只有几个小时。 +3. 测试后,在 Discord 渠道 `plugin-forum` 中你插件所属的帖子里发送 `all good` 或说明哪里出问题了。如果你还没有帖子,就新建一个。 +4. 如果出现问题,请新建或更新一个标题为 `Beta blocker: - ` 的 issue,并添加 `beta-blocker` 标签。把 issue 链接发到你的帖子里。 +5. 向 `main` 提交一个标题为 `fix(): beta blocker - ` 的 PR,并在 PR 和你的 Discord 帖子中都附上该 issue 链接。贡献者不能给 PR 打标签,因此标题就是面向维护者和自动化系统的 PR 侧信号。有 PR 的阻塞问题会被合并;没有 PR 的阻塞问题也可能照样发布。维护者会在 beta 测试期间关注这些帖子。 +6. 没有消息就表示一切正常。如果你错过了这个窗口,你的修复很可能会在下一个周期发布。 -## 下一步 +## 后续步骤 - - 构建消息渠道插件 + + 构建一个消息渠道插件 - - 构建模型提供商插件 + + 构建一个模型提供商插件 - + 导入映射和注册 API 参考 - + 通过 api.runtime 使用 TTS、搜索、子智能体 - + 测试工具和模式 - - 完整 manifest 模式参考 + + 完整清单 schema 参考 ## 相关内容 -- [Plugin Architecture](/zh-CN/plugins/architecture) — 内部架构深入解析 +- [插件架构](/zh-CN/plugins/architecture) — 内部架构深入解析 - [SDK 概览](/zh-CN/plugins/sdk-overview) — 插件 SDK 参考 -- [Manifest](/zh-CN/plugins/manifest) — 插件 manifest 格式 -- [Channel Plugins](/zh-CN/plugins/sdk-channel-plugins) — 构建渠道插件 -- [Provider Plugins](/zh-CN/plugins/sdk-provider-plugins) — 构建提供商插件 +- [Manifest](/zh-CN/plugins/manifest) — 插件清单格式 +- [渠道插件](/zh-CN/plugins/sdk-channel-plugins) — 构建渠道插件 +- [提供商插件](/zh-CN/plugins/sdk-provider-plugins) — 构建提供商插件 diff --git a/docs/zh-CN/plugins/manifest.md b/docs/zh-CN/plugins/manifest.md index 8a9fa4446..6fdb0f9c2 100644 --- a/docs/zh-CN/plugins/manifest.md +++ b/docs/zh-CN/plugins/manifest.md @@ -1,21 +1,21 @@ --- read_when: - 你正在构建一个 OpenClaw 插件 - - 你需要发布一个插件配置 schema,或调试插件校验错误 -summary: 插件清单 + JSON schema 要求(严格配置校验) + - 你需要交付插件配置模式,或调试插件验证错误 +summary: 插件清单 + JSON 模式要求(严格配置验证) title: 插件清单 x-i18n: - generated_at: "2026-04-24T02:14:47Z" + generated_at: "2026-04-24T03:06:50Z" model: gpt-5.4 provider: openai - source_hash: 2edbf4056e6efb3caf367f1f60502b6d050cbd8c7b9173d1232703656e1bd3ba + source_hash: 2e9e38ce695faf9638538b6d4761ee64126f5adee944be1373a02e897853a49d source_path: plugins/manifest.md workflow: 15 --- 此页面仅适用于**原生 OpenClaw 插件清单**。 -对于兼容的 bundle 布局,请参阅 [Plugin bundles](/zh-CN/plugins/bundles)。 +如需兼容的 bundle 布局,请参阅 [Plugin bundles](/zh-CN/plugins/bundles)。 兼容的 bundle 格式使用不同的清单文件: @@ -23,31 +23,30 @@ x-i18n: - Claude bundle:`.claude-plugin/plugin.json`,或不带清单的默认 Claude 组件布局 - Cursor bundle:`.cursor-plugin/plugin.json` -OpenClaw 也会自动检测这些 bundle 布局,但它们不会根据此处描述的 `openclaw.plugin.json` schema 进行校验。 +OpenClaw 也会自动检测这些 bundle 布局,但它们不会根据此处描述的 `openclaw.plugin.json` 模式进行验证。 -对于兼容 bundle,在布局符合 OpenClaw 运行时预期时,OpenClaw 当前会读取 bundle 元数据、声明的 skill 根目录、Claude 命令根目录、Claude bundle `settings.json` 默认值、Claude bundle LSP 默认值,以及受支持的 hook pack。 +对于兼容 bundle,当前当其布局符合 OpenClaw 运行时预期时,OpenClaw 会读取 bundle 元数据,以及已声明的 skill 根目录、Claude 命令根目录、Claude bundle 的 `settings.json` 默认值、Claude bundle 的 LSP 默认值,以及受支持的 hook packs。 -每个原生 OpenClaw 插件**必须**在**插件根目录**中提供一个 `openclaw.plugin.json` 文件。OpenClaw 使用此清单在**不执行插件代码**的情况下校验配置。缺失或无效的清单会被视为插件错误,并阻止配置校验。 +每个原生 OpenClaw 插件**都必须**在**插件根目录**提供一个 `openclaw.plugin.json` 文件。OpenClaw 使用此清单在**不执行插件代码的情况下**验证配置。缺失或无效的清单会被视为插件错误,并阻止配置验证。 请参阅完整的插件系统指南:[Plugins](/zh-CN/tools/plugin)。 -有关原生能力模型和当前外部兼容性指南,请参阅: -[Capability model](/zh-CN/plugins/architecture#public-capability-model)。 +关于原生能力模型和当前外部兼容性指导,请参阅:[Capability model](/zh-CN/plugins/architecture#public-capability-model)。 ## 此文件的作用 -`openclaw.plugin.json` 是 OpenClaw 在**加载你的插件代码之前**读取的元数据。下面的所有内容都必须足够轻量,能够在不启动插件运行时的情况下完成检查。 +`openclaw.plugin.json` 是 OpenClaw 在**加载你的插件代码之前**读取的元数据。下面的所有内容都必须足够轻量,能够在不启动插件运行时的情况下进行检查。 **用于:** -- 插件标识、配置校验以及配置 UI 提示 -- 凭证、onboarding 和设置元数据(别名、自动启用、提供商环境变量、凭证选项) -- 控制平面界面的激活提示 -- 简写模型家族归属 +- 插件标识、配置验证和配置 UI 提示 +- 凭证、onboarding 和设置元数据(别名、自动启用、provider 环境变量、凭证选择) +- 控制平面表面的激活提示 +- 简写的模型家族归属 - 静态能力归属快照(`contracts`) -- 共享 `openclaw qa` 主机可检查的 QA 运行器元数据 -- 合并到目录和校验界面的渠道特定配置元数据 +- 供共享 `openclaw qa` 主机检查的 QA runner 元数据 +- 合并到 catalog 和验证表面的渠道特定配置元数据 -**不要用于:**注册运行时行为、声明代码入口点或 npm 安装元数据。这些属于你的插件代码和 `package.json`。 +**不要用于:**注册运行时行为、声明代码入口点或 npm 安装元数据。这些应放在你的插件代码和 `package.json` 中。 ## 最小示例 @@ -96,19 +95,19 @@ OpenClaw 也会自动检测这些 bundle 布局,但它们不会根据此处描 "provider": "openrouter", "method": "api-key", "choiceId": "openrouter-api-key", - "choiceLabel": "OpenRouter API 密钥", + "choiceLabel": "OpenRouter API key", "groupId": "openrouter", "groupLabel": "OpenRouter", "optionKey": "openrouterApiKey", "cliFlag": "--openrouter-api-key", "cliOption": "--openrouter-api-key ", - "cliDescription": "OpenRouter API 密钥", + "cliDescription": "OpenRouter API key", "onboardingScopes": ["text-inference"] } ], "uiHints": { "apiKey": { - "label": "API 密钥", + "label": "API key", "placeholder": "sk-or-v1-...", "sensitive": true } @@ -127,65 +126,65 @@ OpenClaw 也会自动检测这些 bundle 布局,但它们不会根据此处描 ## 顶层字段参考 -| 字段 | 必填 | 类型 | 含义 | -| ------------------------------------ | -------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `id` | 是 | `string` | 规范插件 id。这是在 `plugins.entries.` 中使用的 id。 | -| `configSchema` | 是 | `object` | 此插件配置的内联 JSON Schema。 | -| `enabledByDefault` | 否 | `true` | 将内置插件标记为默认启用。省略此字段,或将其设置为任何非 `true` 的值,以使插件默认保持禁用。 | -| `legacyPluginIds` | 否 | `string[]` | 会规范化为此规范插件 id 的旧版 id。 | -| `autoEnableWhenConfiguredProviders` | 否 | `string[]` | 当凭证、配置或模型引用提到这些提供商 id 时,应自动启用此插件。 | -| `kind` | 否 | `"memory"` \| `"context-engine"` | 声明由 `plugins.slots.*` 使用的独占插件类型。 | -| `channels` | 否 | `string[]` | 由此插件拥有的渠道 id。用于设备发现和配置校验。 | -| `providers` | 否 | `string[]` | 由此插件拥有的提供商 id。 | -| `modelSupport` | 否 | `object` | 由清单拥有的简写模型家族元数据,用于在运行时之前自动加载插件。 | -| `providerEndpoints` | 否 | `object[]` | 由清单拥有的端点 host/baseUrl 元数据,用于核心在提供商运行时加载前对提供商路由进行分类。 | -| `cliBackends` | 否 | `string[]` | 由此插件拥有的 CLI 推理后端 id。用于根据显式配置引用在启动时自动激活。 | -| `syntheticAuthRefs` | 否 | `string[]` | 提供商或 CLI 后端引用;在运行时加载前的冷启动模型发现期间,应探测其由插件拥有的 synthetic auth hook。 | -| `nonSecretAuthMarkers` | 否 | `string[]` | 由内置插件拥有的占位 API 密钥值,用于表示非密钥的本地、OAuth 或环境凭证状态。 | -| `commandAliases` | 否 | `object[]` | 由此插件拥有的命令名称;在运行时加载前,应生成带有插件感知能力的配置和 CLI 诊断信息。 | -| `providerAuthEnvVars` | 否 | `Record` | OpenClaw 无需加载插件代码即可检查的轻量提供商凭证环境变量元数据。 | -| `providerAuthAliases` | 否 | `Record` | 应复用另一个提供商 id 进行凭证查找的提供商 id,例如与基础提供商 API 密钥和凭证配置文件共享的编码提供商。 | -| `channelEnvVars` | 否 | `Record` | OpenClaw 无需加载插件代码即可检查的轻量渠道环境变量元数据。将其用于由环境变量驱动的渠道设置或凭证界面,以便通用启动 / 配置辅助逻辑能够识别。 | -| `providerAuthChoices` | 否 | `object[]` | 适用于 onboarding 选择器、首选提供商解析和简单 CLI 标志接线的轻量凭证选项元数据。 | -| `activation` | 否 | `object` | 提供商、命令、渠道、路由和能力触发加载的轻量激活提示。仅为元数据;实际行为仍由插件运行时负责。 | -| `setup` | 否 | `object` | 供设备发现和设置界面在不加载插件运行时的情况下检查的轻量设置 / onboarding 描述信息。 | -| `qaRunners` | 否 | `object[]` | 共享 `openclaw qa` 主机在插件运行时加载前使用的轻量 QA 运行器描述信息。 | -| `contracts` | 否 | `object` | 面向外部 auth hook、语音、实时转写、实时语音、媒体理解、图像生成、音乐生成、视频生成、web-fetch、Ollama Web 搜索 以及工具归属的静态内置能力快照。 | -| `mediaUnderstandingProviderMetadata` | 否 | `Record` | 为 `contracts.mediaUnderstandingProviders` 中声明的提供商 id 提供的轻量媒体理解默认元数据。 | -| `channelConfigs` | 否 | `Record` | 由清单拥有的渠道配置元数据,会在运行时加载前合并到设备发现和校验界面中。 | -| `skills` | 否 | `string[]` | 要加载的 Skill 目录,相对于插件根目录。 | -| `name` | 否 | `string` | 人类可读的插件名称。 | -| `description` | 否 | `string` | 显示在插件界面中的简短摘要。 | -| `version` | 否 | `string` | 信息性插件版本。 | -| `uiHints` | 否 | `Record` | 配置字段的 UI 标签、占位符和敏感性提示。 | +| 字段 | 必填 | 类型 | 含义 | +| ------------------------------------ | -------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `id` | 是 | `string` | 规范的插件 id。这是 `plugins.entries.` 中使用的 id。 | +| `configSchema` | 是 | `object` | 此插件配置的内联 JSON Schema。 | +| `enabledByDefault` | 否 | `true` | 将内置插件标记为默认启用。省略该字段,或将其设置为任何非 `true` 的值,都会让插件默认保持禁用。 | +| `legacyPluginIds` | 否 | `string[]` | 会规范化为此规范插件 id 的旧版 id。 | +| `autoEnableWhenConfiguredProviders` | 否 | `string[]` | 当凭证、配置或模型引用提到这些 provider id 时,应自动启用此插件。 | +| `kind` | 否 | `"memory"` \| `"context-engine"` | 声明由 `plugins.slots.*` 使用的排他性插件类型。 | +| `channels` | 否 | `string[]` | 由此插件拥有的渠道 id。用于设备发现和配置验证。 | +| `providers` | 否 | `string[]` | 由此插件拥有的 provider id。 | +| `modelSupport` | 否 | `object` | 由清单持有的简写模型家族元数据,用于在运行时之前自动加载插件。 | +| `providerEndpoints` | 否 | `object[]` | 由清单持有的端点 host/baseUrl 元数据,用于核心在 provider 运行时加载之前对 provider 路由进行分类。 | +| `cliBackends` | 否 | `string[]` | 由此插件拥有的 CLI 推理后端 id。用于根据显式配置引用在启动时自动激活。 | +| `syntheticAuthRefs` | 否 | `string[]` | 在运行时加载之前、冷模型发现期间,应探测其插件持有 synthetic auth hook 的 provider 或 CLI 后端引用。 | +| `nonSecretAuthMarkers` | 否 | `string[]` | 由内置插件持有的占位 API key 值,用于表示非密钥的本地、OAuth 或环境凭证状态。 | +| `commandAliases` | 否 | `object[]` | 由此插件拥有的命令名称,应在运行时加载之前生成带有插件感知的配置和 CLI 诊断信息。 | +| `providerAuthEnvVars` | 否 | `Record` | OpenClaw 可在不加载插件代码的情况下检查的轻量 provider 凭证环境变量元数据。 | +| `providerAuthAliases` | 否 | `Record` | 应复用另一个 provider id 进行凭证查找的 provider id,例如与基础 provider 共享 API key 和凭证配置文件的 coding provider。 | +| `channelEnvVars` | 否 | `Record` | OpenClaw 可在不加载插件代码的情况下检查的轻量渠道环境变量元数据。将其用于由环境变量驱动的渠道设置或凭证表面,以便通用启动/配置辅助工具能够识别。 | +| `providerAuthChoices` | 否 | `object[]` | 用于 onboarding 选择器、首选 provider 解析和简单 CLI 标志接线的轻量凭证选择元数据。 | +| `activation` | 否 | `object` | 用于 provider、命令、渠道、路由和能力触发加载的轻量激活提示。仅为元数据;插件运行时仍然拥有实际行为。 | +| `setup` | 否 | `object` | 设备发现和设置表面可在不加载插件运行时的情况下检查的轻量 setup/onboarding 描述符。 | +| `qaRunners` | 否 | `object[]` | 共享 `openclaw qa` 主机在插件运行时加载之前使用的轻量 QA runner 描述符。 | +| `contracts` | 否 | `object` | 针对外部凭证 hooks、语音、实时转录、实时语音、媒体理解、图像生成、音乐生成、视频生成、网页抓取、Web 搜索和工具归属的静态内置能力快照。 | +| `mediaUnderstandingProviderMetadata` | 否 | `Record` | 为 `contracts.mediaUnderstandingProviders` 中声明的 provider id 提供的轻量媒体理解默认元数据。 | +| `channelConfigs` | 否 | `Record` | 由清单持有的渠道配置元数据,在运行时加载之前合并到设备发现和验证表面中。 | +| `skills` | 否 | `string[]` | 要加载的 Skills 目录,相对于插件根目录。 | +| `name` | 否 | `string` | 人类可读的插件名称。 | +| `description` | 否 | `string` | 在插件表面中显示的简短摘要。 | +| `version` | 否 | `string` | 信息性插件版本。 | +| `uiHints` | 否 | `Record` | 配置字段的 UI 标签、占位符和敏感性提示。 | ## `providerAuthChoices` 参考 -每个 `providerAuthChoices` 条目描述一个 onboarding 或凭证选项。 -OpenClaw 会在提供商运行时加载前读取它。 +每个 `providerAuthChoices` 条目描述一个 onboarding 或凭证选择。 +OpenClaw 会在 provider 运行时加载之前读取这些内容。 -| 字段 | 必填 | 类型 | 含义 | -| --------------------- | -------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -| `provider` | 是 | `string` | 此选项所属的提供商 id。 | -| `method` | 是 | `string` | 要分发到的凭证方法 id。 | -| `choiceId` | 是 | `string` | 供 onboarding 和 CLI 流程使用的稳定凭证选项 id。 | -| `choiceLabel` | 否 | `string` | 面向用户的标签。如省略,OpenClaw 会回退到 `choiceId`。 | -| `choiceHint` | 否 | `string` | 选择器的简短辅助文本。 | -| `assistantPriority` | 否 | `number` | 在由助手驱动的交互式选择器中,值越小排序越靠前。 | -| `assistantVisibility` | 否 | `"visible"` \| `"manual-only"` | 在助手选择器中隐藏该选项,但仍允许通过手动 CLI 选择。 | -| `deprecatedChoiceIds` | 否 | `string[]` | 应将用户重定向到此替代选项的旧版选项 id。 | -| `groupId` | 否 | `string` | 用于对相关选项进行分组的可选分组 id。 | -| `groupLabel` | 否 | `string` | 该分组面向用户的标签。 | -| `groupHint` | 否 | `string` | 该分组的简短辅助文本。 | -| `optionKey` | 否 | `string` | 用于简单单标志凭证流程的内部选项键。 | -| `cliFlag` | 否 | `string` | CLI 标志名称,例如 `--openrouter-api-key`。 | -| `cliOption` | 否 | `string` | 完整的 CLI 选项形式,例如 `--openrouter-api-key `。 | -| `cliDescription` | 否 | `string` | CLI 帮助中使用的描述。 | -| `onboardingScopes` | 否 | `Array<"text-inference" \| "image-generation">` | 此选项应显示在哪些 onboarding 界面中。如省略,默认值为 `["text-inference"]`。 | +| 字段 | 必填 | 类型 | 含义 | +| --------------------- | -------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------ | +| `provider` | 是 | `string` | 此选择所属的 provider id。 | +| `method` | 是 | `string` | 要分发到的凭证方法 id。 | +| `choiceId` | 是 | `string` | onboarding 和 CLI 流程使用的稳定凭证选择 id。 | +| `choiceLabel` | 否 | `string` | 面向用户的标签。如省略,OpenClaw 会回退到 `choiceId`。 | +| `choiceHint` | 否 | `string` | 选择器中的简短辅助文本。 | +| `assistantPriority` | 否 | `number` | 在由 assistant 驱动的交互式选择器中,值越小排序越靠前。 | +| `assistantVisibility` | 否 | `"visible"` \| `"manual-only"` | 在 assistant 选择器中隐藏该选项,但仍允许手动 CLI 选择。 | +| `deprecatedChoiceIds` | 否 | `string[]` | 应将用户重定向到此替代选择的旧版 choice id。 | +| `groupId` | 否 | `string` | 用于对相关选择进行分组的可选分组 id。 | +| `groupLabel` | 否 | `string` | 该分组面向用户的标签。 | +| `groupHint` | 否 | `string` | 该分组的简短辅助文本。 | +| `optionKey` | 否 | `string` | 用于简单单标志凭证流程的内部选项键。 | +| `cliFlag` | 否 | `string` | CLI 标志名称,例如 `--openrouter-api-key`。 | +| `cliOption` | 否 | `string` | 完整的 CLI 选项形式,例如 `--openrouter-api-key `。 | +| `cliDescription` | 否 | `string` | CLI 帮助中使用的描述。 | +| `onboardingScopes` | 否 | `Array<"text-inference" \| "image-generation">` | 此选择应出现在哪些 onboarding 表面中。如省略,则默认是 `["text-inference"]`。 | ## `commandAliases` 参考 -当插件拥有某个运行时命令名,而用户可能误将其放入 `plugins.allow`,或尝试将其作为根级 CLI 命令运行时,请使用 `commandAliases`。OpenClaw 使用这些元数据生成诊断信息,而无需导入插件运行时代码。 +当插件拥有某个运行时命令名称,而用户可能会误将其放入 `plugins.allow` 中,或尝试将其作为根 CLI 命令运行时,请使用 `commandAliases`。OpenClaw 使用这些元数据生成诊断信息,而无需导入插件运行时代码。 ```json { @@ -199,19 +198,19 @@ OpenClaw 会在提供商运行时加载前读取它。 } ``` -| 字段 | 必填 | 类型 | 含义 | -| ------------ | -------- | ----------------- | ----------------------------------------------------------------------- | -| `name` | 是 | `string` | 属于此插件的命令名称。 | -| `kind` | 否 | `"runtime-slash"` | 将该别名标记为聊天斜杠命令,而不是根级 CLI 命令。 | -| `cliCommand` | 否 | `string` | 若存在,用于建议 CLI 操作的相关根级 CLI 命令。 | +| 字段 | 必填 | 类型 | 含义 | +| ------------ | -------- | ----------------- | ------------------------------------------------------------------ | +| `name` | 是 | `string` | 属于此插件的命令名称。 | +| `kind` | 否 | `"runtime-slash"` | 将该别名标记为聊天斜杠命令,而不是根 CLI 命令。 | +| `cliCommand` | 否 | `string` | 若存在,用于 CLI 操作时建议的相关根 CLI 命令。 | ## `activation` 参考 -当插件可以以低成本声明哪些控制平面事件应在后续激活它时,请使用 `activation`。 +当插件可以轻量声明哪些控制平面事件应在之后激活它时,请使用 `activation`。 ## `qaRunners` 参考 -当插件在共享 `openclaw qa` 根命令下贡献一个或多个传输运行器时,请使用 `qaRunners`。请保持这些元数据轻量且静态;实际的 CLI 注册仍由插件运行时通过导出 `qaRunnerCliRegistrations` 的轻量 `runtime-api.ts` 界面负责。 +当插件在共享 `openclaw qa` 根下贡献一个或多个传输 runner 时,请使用 `qaRunners`。保持这些元数据轻量且静态;插件运行时仍通过导出 `qaRunnerCliRegistrations` 的轻量 `runtime-api.ts` 表面拥有实际的 CLI 注册。 ```json { @@ -224,12 +223,13 @@ OpenClaw 会在提供商运行时加载前读取它。 } ``` -| 字段 | 必填 | 类型 | 含义 | -| ------------- | -------- | -------- | ------------------------------------------------------------------ | -| `commandName` | 是 | `string` | 挂载在 `openclaw qa` 下的子命令,例如 `matrix`。 | -| `description` | 否 | `string` | 当共享主机需要存根命令时使用的后备帮助文本。 | +| 字段 | 必填 | 类型 | 含义 | +| ------------- | -------- | -------- | ------------------------------------------------------------ | +| `commandName` | 是 | `string` | 挂载在 `openclaw qa` 下的子命令,例如 `matrix`。 | +| `description` | 否 | `string` | 当共享主机需要一个存根命令时使用的回退帮助文本。 | -此区块仅为元数据。它不会注册运行时行为,也不会替代 `register(...)`、`setupEntry` 或其他运行时 / 插件入口点。当前使用方会在更广泛的插件加载之前将其用作缩小范围的提示,因此缺少激活元数据通常只会带来性能损耗;在旧版清单归属回退机制仍存在的情况下,它不应改变正确性。 +此区块仅为元数据。它不会注册运行时行为,也不会替代 `register(...)`、`setupEntry` 或其他运行时/插件入口点。 +当前消费者在更广泛的插件加载之前将其用作收窄提示,因此缺少激活元数据通常只会带来性能成本;只要旧版清单归属回退仍然存在,它就不应影响正确性。 ```json { @@ -243,27 +243,27 @@ OpenClaw 会在提供商运行时加载前读取它。 } ``` -| 字段 | 必填 | 类型 | 含义 | -| ---------------- | -------- | ---------------------------------------------------- | ----------------------------------------------------------------- | -| `onProviders` | 否 | `string[]` | 请求这些提供商 id 时,应激活此插件。 | -| `onCommands` | 否 | `string[]` | 应激活此插件的命令 id。 | -| `onChannels` | 否 | `string[]` | 应激活此插件的渠道 id。 | -| `onRoutes` | 否 | `string[]` | 应激活此插件的路由类型。 | -| `onCapabilities` | 否 | `Array<"provider" \| "channel" \| "tool" \| "hook">` | 供控制平面激活规划使用的广义能力提示。 | +| 字段 | 必填 | 类型 | 含义 | +| ---------------- | -------- | ---------------------------------------------------- | -------------------------------------------------------- | +| `onProviders` | 否 | `string[]` | 请求这些 provider id 时应激活此插件。 | +| `onCommands` | 否 | `string[]` | 应激活此插件的命令 id。 | +| `onChannels` | 否 | `string[]` | 应激活此插件的渠道 id。 | +| `onRoutes` | 否 | `string[]` | 应激活此插件的路由类型。 | +| `onCapabilities` | 否 | `Array<"provider" \| "channel" \| "tool" \| "hook">` | 控制平面激活规划使用的宽泛能力提示。 | -当前在线使用方: +当前的实时消费者: -- 命令触发的 CLI 规划会回退到旧版 +- 由命令触发的 CLI 规划会回退到旧版 `commandAliases[].cliCommand` 或 `commandAliases[].name` -- 渠道触发的设置 / 渠道规划在缺少显式渠道激活元数据时,会回退到旧版 `channels[]` +- 由渠道触发的设置/渠道规划在缺少显式渠道激活元数据时,会回退到旧版 `channels[]` + 归属 +- 由 provider 触发的设置/运行时规划在缺少显式 provider + 激活元数据时,会回退到旧版 `providers[]` 和顶层 `cliBackends[]` 归属 -- 提供商触发的设置 / 运行时规划在缺少显式提供商 - 激活元数据时,会回退到旧版 - `providers[]` 和顶层 `cliBackends[]` 归属 ## `setup` 参考 -当设置和 onboarding 界面需要在运行时加载前读取由插件拥有的轻量元数据时,请使用 `setup`。 +当设置和 onboarding 表面需要在运行时加载之前使用由插件持有的轻量元数据时,请使用 `setup`。 ```json { @@ -282,28 +282,28 @@ OpenClaw 会在提供商运行时加载前读取它。 } ``` -顶层 `cliBackends` 仍然有效,并继续描述 CLI 推理后端。`setup.cliBackends` 是供控制平面 / 设置流程使用的、仅限元数据的设置专用描述界面。 +顶层 `cliBackends` 仍然有效,并继续描述 CLI 推理后端。`setup.cliBackends` 是用于控制平面/设置流程的、特定于 setup 的描述符表面,应保持为纯元数据。 -存在 `setup.providers` 和 `setup.cliBackends` 时,它们是设置发现阶段首选的描述优先查找界面。如果该描述仅用于缩小候选插件范围,而设置仍需要更丰富的设置时运行时 hook,请设置 `requiresRuntime: true`,并保留 `setup-api` 作为后备执行路径。 +存在时,`setup.providers` 和 `setup.cliBackends` 是设置发现时首选的描述符优先查找表面。如果描述符只用于收窄候选插件,而设置仍需要更丰富的设置时运行时 hook,请设置 `requiresRuntime: true`,并保留 `setup-api` 作为回退执行路径。 -由于设置查找可能会执行由插件拥有的 `setup-api` 代码,规范化后的 `setup.providers[].id` 和 `setup.cliBackends[]` 值在已发现插件之间必须保持唯一。归属不明确时会以失败关闭的方式处理,而不是按发现顺序挑选一个胜出者。 +由于设置查找可能会执行插件持有的 `setup-api` 代码,规范化后的 `setup.providers[].id` 和 `setup.cliBackends[]` 值必须在已发现插件之间保持唯一。归属不明确时会以失败关闭,而不是根据发现顺序选出一个赢家。 ### `setup.providers` 参考 -| 字段 | 必填 | 类型 | 含义 | +| 字段 | 必填 | 类型 | 含义 | | ------------- | -------- | ---------- | ------------------------------------------------------------------------------------ | -| `id` | 是 | `string` | 在设置或 onboarding 期间公开的提供商 id。请保持规范化 id 在全局范围内唯一。 | -| `authMethods` | 否 | `string[]` | 此提供商在不加载完整运行时的情况下支持的设置 / 凭证方法 id。 | -| `envVars` | 否 | `string[]` | 通用设置 / 状态界面可在插件运行时加载前检查的环境变量。 | +| `id` | 是 | `string` | 在设置或 onboarding 期间暴露的 provider id。保持规范化 id 在全局唯一。 | +| `authMethods` | 否 | `string[]` | 此 provider 在不加载完整运行时的情况下支持的 setup/凭证方法 id。 | +| `envVars` | 否 | `string[]` | 通用设置/状态表面可在插件运行时加载之前检查的环境变量。 | ### `setup` 字段 -| 字段 | 必填 | 类型 | 含义 | -| ------------------ | -------- | ---------- | --------------------------------------------------------------------------------------------------- | -| `providers` | 否 | `object[]` | 在设置和 onboarding 期间公开的提供商设置描述信息。 | -| `cliBackends` | 否 | `string[]` | 用于描述优先设置查找的设置时后端 id。请保持规范化 id 在全局范围内唯一。 | -| `configMigrations` | 否 | `string[]` | 由此插件设置界面拥有的配置迁移 id。 | -| `requiresRuntime` | 否 | `boolean` | 在描述查找之后,设置是否仍需要执行 `setup-api`。 | +| 字段 | 必填 | 类型 | 含义 | +| ------------------ | -------- | ---------- | ------------------------------------------------------------------------------------------------- | +| `providers` | 否 | `object[]` | 在设置和 onboarding 期间暴露的 provider 设置描述符。 | +| `cliBackends` | 否 | `string[]` | 用于描述符优先设置查找的设置时后端 id。保持规范化 id 在全局唯一。 | +| `configMigrations` | 否 | `string[]` | 属于此插件 setup 表面的配置迁移 id。 | +| `requiresRuntime` | 否 | `boolean` | 在描述符查找之后,设置是否仍需要执行 `setup-api`。 | ## `uiHints` 参考 @@ -324,18 +324,18 @@ OpenClaw 会在提供商运行时加载前读取它。 每个字段提示可包含: -| 字段 | 类型 | 含义 | -| ------------- | ---------- | --------------------------------------- | -| `label` | `string` | 面向用户的字段标签。 | -| `help` | `string` | 简短辅助文本。 | -| `tags` | `string[]` | 可选 UI 标签。 | -| `advanced` | `boolean` | 将该字段标记为高级。 | -| `sensitive` | `boolean` | 将该字段标记为密钥或敏感信息。 | -| `placeholder` | `string` | 表单输入的占位文本。 | +| 字段 | 类型 | 含义 | +| ------------- | ---------- | -------------------------------------- | +| `label` | `string` | 面向用户的字段标签。 | +| `help` | `string` | 简短辅助文本。 | +| `tags` | `string[]` | 可选的 UI 标签。 | +| `advanced` | `boolean` | 将该字段标记为高级项。 | +| `sensitive` | `boolean` | 将该字段标记为密钥或敏感项。 | +| `placeholder` | `string` | 表单输入的占位符文本。 | ## `contracts` 参考 -仅在 OpenClaw 可在不导入插件运行时的情况下读取静态能力归属元数据时使用 `contracts`。 +仅在 OpenClaw 能够在不导入插件运行时的情况下读取静态能力归属元数据时,使用 `contracts`。 ```json { @@ -358,28 +358,31 @@ OpenClaw 会在提供商运行时加载前读取它。 每个列表都是可选的: -| 字段 | 类型 | 含义 | -| -------------------------------- | ---------- | ----------------------------------------------------------------- | -| `embeddedExtensionFactories` | `string[]` | 内置插件可为其注册工厂的嵌入式运行时 id。 | -| `externalAuthProviders` | `string[]` | 此插件拥有其外部 auth 配置文件 hook 的提供商 id。 | -| `speechProviders` | `string[]` | 此插件拥有的语音提供商 id。 | -| `realtimeTranscriptionProviders` | `string[]` | 此插件拥有的实时转写提供商 id。 | -| `realtimeVoiceProviders` | `string[]` | 此插件拥有的实时语音提供商 id。 | -| `memoryEmbeddingProviders` | `string[]` | 此插件拥有的 Memory 嵌入提供商 id。 | -| `mediaUnderstandingProviders` | `string[]` | 此插件拥有的媒体理解提供商 id。 | -| `imageGenerationProviders` | `string[]` | 此插件拥有的图像生成提供商 id。 | -| `videoGenerationProviders` | `string[]` | 此插件拥有的视频生成提供商 id。 | -| `webFetchProviders` | `string[]` | 此插件拥有的 web-fetch 提供商 id。 | -| `webSearchProviders` | `string[]` | 此插件拥有的 Ollama Web 搜索 提供商 id。 | -| `tools` | `string[]` | 此插件为内置合约检查所拥有的智能体工具名称。 | +| 字段 | 类型 | 含义 | +| -------------------------------- | ---------- | ------------------------------------------------------------------ | +| `embeddedExtensionFactories` | `string[]` | 内置插件可为其注册工厂的嵌入式运行时 id。 | +| `externalAuthProviders` | `string[]` | 此插件拥有其外部凭证配置文件 hook 的 provider id。 | +| `speechProviders` | `string[]` | 此插件拥有的语音 provider id。 | +| `realtimeTranscriptionProviders` | `string[]` | 此插件拥有的实时转录 provider id。 | +| `realtimeVoiceProviders` | `string[]` | 此插件拥有的实时语音 provider id。 | +| `memoryEmbeddingProviders` | `string[]` | 此插件拥有的 Memory 嵌入 provider id。 | +| `mediaUnderstandingProviders` | `string[]` | 此插件拥有的媒体理解 provider id。 | +| `imageGenerationProviders` | `string[]` | 此插件拥有的图像生成 provider id。 | +| `videoGenerationProviders` | `string[]` | 此插件拥有的视频生成 provider id。 | +| `webFetchProviders` | `string[]` | 此插件拥有的网页抓取 provider id。 | +| `webSearchProviders` | `string[]` | 此插件拥有的 Web 搜索 provider id。 | +| `tools` | `string[]` | 此插件拥有的智能体工具名称,用于内置契约检查。 | -实现了 `resolveExternalAuthProfiles` 的提供商插件应声明 `contracts.externalAuthProviders`。未作此声明的插件仍会通过已弃用的兼容性回退路径运行,但该回退路径更慢,并将在迁移窗口结束后移除。 +实现 `resolveExternalAuthProfiles` 的 provider 插件应声明 +`contracts.externalAuthProviders`。未声明的插件仍会通过已弃用的兼容性回退路径运行,但该回退路径更慢,并将在迁移窗口结束后移除。 -内置的 Memory 嵌入提供商应为其公开的每个适配器 id 声明 `contracts.memoryEmbeddingProviders`,包括诸如 `local` 之类的内置适配器。独立 CLI 路径使用此清单合约,在完整 Gateway 网关 运行时注册提供商之前,仅加载拥有该能力的插件。 +内置的 Memory 嵌入 provider 应为其公开的每个适配器 id 声明 +`contracts.memoryEmbeddingProviders`,包括诸如 `local` 之类的内置适配器。 +独立 CLI 路径使用此清单契约,在完整 Gateway 网关运行时注册 provider 之前,仅加载所属插件。 ## `mediaUnderstandingProviderMetadata` 参考 -当媒体理解提供商具有默认模型、自动凭证回退优先级,或通用核心辅助逻辑在运行时加载前所需的原生文档支持时,请使用 `mediaUnderstandingProviderMetadata`。键也必须在 `contracts.mediaUnderstandingProviders` 中声明。 +当媒体理解 provider 具有默认模型、自动凭证回退优先级,或 generic core helpers 在运行时加载之前需要的原生文档支持时,请使用 `mediaUnderstandingProviderMetadata`。键也必须在 `contracts.mediaUnderstandingProviders` 中声明。 ```json { @@ -402,18 +405,18 @@ OpenClaw 会在提供商运行时加载前读取它。 } ``` -每个提供商条目可包含: +每个 provider 条目可包含: -| 字段 | 类型 | 含义 | -| ---------------------- | ----------------------------------- | ---------------------------------------------------------------------------- | -| `capabilities` | `("image" \| "audio" \| "video")[]` | 此提供商公开的媒体能力。 | -| `defaultModels` | `Record` | 当配置未指定模型时使用的“能力到模型”默认映射。 | -| `autoPriority` | `Record` | 用于基于凭证的自动提供商回退时,数值越小排序越靠前。 | -| `nativeDocumentInputs` | `"pdf"[]` | 提供商支持的原生文档输入。 | +| 字段 | 类型 | 含义 | +| ---------------------- | ----------------------------------- | ------------------------------------------------------------------------ | +| `capabilities` | `("image" \| "audio" \| "video")[]` | 此 provider 公开的媒体能力。 | +| `defaultModels` | `Record` | 当配置未指定模型时使用的“能力到模型”默认值。 | +| `autoPriority` | `Record` | 用于基于凭证的自动 provider 回退时,数字越小排序越靠前。 | +| `nativeDocumentInputs` | `"pdf"[]` | provider 支持的原生文档输入。 | ## `channelConfigs` 参考 -当渠道插件在运行时加载前需要轻量配置元数据时,请使用 `channelConfigs`。 +当渠道插件在运行时加载之前需要轻量配置元数据时,请使用 `channelConfigs`。 ```json { @@ -442,17 +445,17 @@ OpenClaw 会在提供商运行时加载前读取它。 每个渠道条目可包含: -| 字段 | 类型 | 含义 | +| 字段 | 类型 | 含义 | | ------------- | ------------------------ | ----------------------------------------------------------------------------------------- | -| `schema` | `object` | `channels.` 的 JSON Schema。每个已声明的渠道配置条目都必须提供该字段。 | -| `uiHints` | `Record` | 该渠道配置区块的可选 UI 标签 / 占位符 / 敏感性提示。 | -| `label` | `string` | 当运行时元数据尚未就绪时,合并到选择器和检查界面的渠道标签。 | -| `description` | `string` | 用于检查和目录界面的简短渠道描述。 | -| `preferOver` | `string[]` | 在选择界面中,此渠道应优先于的旧版或较低优先级插件 id。 | +| `schema` | `object` | `channels.` 的 JSON Schema。每个声明的渠道配置条目都必须提供。 | +| `uiHints` | `Record` | 该渠道配置部分的可选 UI 标签/占位符/敏感性提示。 | +| `label` | `string` | 当运行时元数据尚未就绪时,合并到选择器和检查表面的渠道标签。 | +| `description` | `string` | 用于检查和 catalog 表面的简短渠道描述。 | +| `preferOver` | `string[]` | 在选择表面中,此渠道应优先于其排序的旧版或较低优先级插件 id。 | ## `modelSupport` 参考 -当 OpenClaw 需要在插件运行时加载前,根据 `gpt-5.5` 或 `claude-sonnet-4.6` 之类的简写模型 id 推断出你的提供商插件时,请使用 `modelSupport`。 +当 OpenClaw 应在插件运行时加载之前,根据诸如 `gpt-5.5` 或 `claude-sonnet-4.6` 这样的简写模型 id 推断你的 provider 插件时,请使用 `modelSupport`。 ```json { @@ -465,67 +468,74 @@ OpenClaw 会在提供商运行时加载前读取它。 OpenClaw 按以下优先级应用: -- 显式 `provider/model` 引用使用拥有该能力的 `providers` 清单元数据 +- 显式的 `provider/model` 引用使用所属 `providers` 清单元数据 - `modelPatterns` 优先于 `modelPrefixes` - 如果一个非内置插件和一个内置插件都匹配,则非内置插件胜出 -- 剩余的歧义会被忽略,直到用户或配置明确指定提供商 +- 其余歧义会被忽略,直到用户或配置指定 provider 字段: -| 字段 | 类型 | 含义 | -| --------------- | ---------- | ------------------------------------------------------------------------------- | -| `modelPrefixes` | `string[]` | 对简写模型 id 使用 `startsWith` 进行匹配的前缀。 | -| `modelPatterns` | `string[]` | 在移除 profile 后缀后,对简写模型 id 进行匹配的正则表达式源码。 | +| 字段 | 类型 | 含义 | +| --------------- | ---------- | -------------------------------------------------------------------- | +| `modelPrefixes` | `string[]` | 使用 `startsWith` 针对简写模型 id 进行匹配的前缀。 | +| `modelPatterns` | `string[]` | 在移除 profile 后缀后,针对简写模型 id 进行匹配的正则表达式源。 | -旧版顶层能力键已弃用。使用 `openclaw doctor --fix` 将 `speechProviders`、`realtimeTranscriptionProviders`、`realtimeVoiceProviders`、`mediaUnderstandingProviders`、`imageGenerationProviders`、`videoGenerationProviders`、`webFetchProviders` 和 `webSearchProviders` 移动到 `contracts` 下;常规清单加载不再将这些顶层字段视为能力归属声明。 +旧版顶层能力键已弃用。使用 `openclaw doctor --fix` 将 +`speechProviders`、`realtimeTranscriptionProviders`、 +`realtimeVoiceProviders`、`mediaUnderstandingProviders`、 +`imageGenerationProviders`、`videoGenerationProviders`、 +`webFetchProviders` 和 `webSearchProviders` 移动到 `contracts` 下;常规清单加载不再将这些顶层字段视为能力归属。 -## 清单与 `package.json` 的区别 +## 清单与 package.json 的区别 这两个文件承担不同的职责: -| 文件 | 用途 | -| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------- | -| `openclaw.plugin.json` | 设备发现、配置校验、凭证选项元数据,以及必须在插件代码运行前存在的 UI 提示 | -| `package.json` | npm 元数据、依赖安装,以及用于入口点、安装门控、设置或目录元数据的 `openclaw` 区块 | +| 文件 | 用途 | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------ | +| `openclaw.plugin.json` | 设备发现、配置验证、凭证选择元数据,以及在插件代码运行前必须存在的 UI 提示 | +| `package.json` | npm 元数据、依赖安装,以及用于入口点、安装门控、setup 或 catalog 元数据的 `openclaw` 区块 | -如果你不确定某项元数据应放在哪里,请使用以下规则: +如果你不确定某段元数据应放在哪里,请使用以下规则: -- 如果 OpenClaw 必须在加载插件代码前知道它,就把它放在 `openclaw.plugin.json` 中 -- 如果它与打包、入口文件或 npm 安装行为有关,就把它放在 `package.json` 中 +- 如果 OpenClaw 必须在加载插件代码之前知道它,请将其放在 `openclaw.plugin.json` 中 +- 如果它与打包、入口文件或 npm 安装行为有关,请将其放在 `package.json` 中 ### 影响设备发现的 `package.json` 字段 -某些运行时前的插件元数据有意放在 `package.json` 的 `openclaw` 区块中,而不是 `openclaw.plugin.json` 中。 +某些运行时前插件元数据有意放在 `package.json` 的 `openclaw` 区块下,而不是 `openclaw.plugin.json` 中。 重要示例: -| 字段 | 含义 | +| 字段 | 含义 | | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `openclaw.extensions` | 声明原生插件入口点。必须位于插件包目录内。 | -| `openclaw.runtimeExtensions` | 为已安装包声明已构建的 JavaScript 运行时入口点。必须位于插件包目录内。 | -| `openclaw.setupEntry` | 在 onboarding、延后渠道启动以及只读渠道状态 / SecretRef 发现期间使用的轻量仅设置入口点。必须位于插件包目录内。 | -| `openclaw.runtimeSetupEntry` | 为已安装包声明已构建的 JavaScript 设置入口点。必须位于插件包目录内。 | -| `openclaw.channel` | 轻量渠道目录元数据,例如标签、文档路径、别名和选择文案。 | -| `openclaw.channel.configuredState` | 轻量“已配置状态”检查器元数据,可在不加载完整渠道运行时的情况下回答“是否已存在仅环境变量驱动的设置?”。 | -| `openclaw.channel.persistedAuthState` | 轻量持久化凭证状态检查器元数据,可在不加载完整渠道运行时的情况下回答“是否已有任何已登录状态?”。 | -| `openclaw.install.npmSpec` / `openclaw.install.localPath` | 内置插件和外部发布插件的安装 / 更新提示。 | -| `openclaw.install.defaultChoice` | 当有多个安装来源可用时的首选安装路径。 | -| `openclaw.install.minHostVersion` | 最低受支持的 OpenClaw 主机版本,使用如 `>=2026.3.22` 这样的 semver 下限。 | -| `openclaw.install.expectedIntegrity` | 预期的 npm 分发完整性字符串,例如 `sha512-...`;安装和更新流程会据此校验获取到的制品。 | -| `openclaw.install.allowInvalidConfigRecovery` | 当配置无效时,允许一个范围很窄的内置插件重装恢复路径。 | -| `openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen` | 允许仅设置渠道界面在启动期间先于完整渠道插件加载。 | +| `openclaw.extensions` | 声明原生插件入口点。必须保持在插件包目录内。 | +| `openclaw.runtimeExtensions` | 为已安装包声明已构建的 JavaScript 运行时入口点。必须保持在插件包目录内。 | +| `openclaw.setupEntry` | 在 onboarding、延迟渠道启动和只读渠道状态/SecretRef 发现期间使用的轻量仅 setup 入口点。必须保持在插件包目录内。 | +| `openclaw.runtimeSetupEntry` | 为已安装包声明已构建的 JavaScript setup 入口点。必须保持在插件包目录内。 | +| `openclaw.channel` | 轻量渠道 catalog 元数据,例如标签、文档路径、别名和选择文案。 | +| `openclaw.channel.configuredState` | 轻量 configured-state 检查器元数据,可在不加载完整渠道运行时的情况下回答“是否已存在仅由环境变量驱动的设置?”。 | +| `openclaw.channel.persistedAuthState` | 轻量持久化凭证检查器元数据,可在不加载完整渠道运行时的情况下回答“是否已有任何账户登录?”。 | +| `openclaw.install.npmSpec` / `openclaw.install.localPath` | 内置插件和外部发布插件的安装/更新提示。 | +| `openclaw.install.defaultChoice` | 当存在多个安装来源时的首选安装路径。 | +| `openclaw.install.minHostVersion` | 最低支持的 OpenClaw host 版本,使用如 `>=2026.3.22` 这样的 semver 下限。 | +| `openclaw.install.expectedIntegrity` | 预期的 npm 分发完整性字符串,例如 `sha512-...`;安装和更新流程会根据它验证获取的构件。 | +| `openclaw.install.allowInvalidConfigRecovery` | 当配置无效时,允许一个范围很窄的内置插件重新安装恢复路径。 | +| `openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen` | 允许仅 setup 的渠道表面在启动期间先于完整渠道插件加载。 | -清单元数据决定了在运行时加载前,哪些提供商 / 渠道 / 设置选项会出现在 onboarding 中。`package.json#openclaw.install` 则告诉 onboarding,当用户选择其中一个选项时,应如何获取或启用该插件。不要将安装提示移到 `openclaw.plugin.json` 中。 +清单元数据决定了在运行时加载之前,哪些 provider/渠道/setup 选项会出现在 onboarding 中。`package.json#openclaw.install` 则告诉 onboarding,当用户选择其中某个选项时,应如何获取或启用该插件。不要将安装提示移到 `openclaw.plugin.json` 中。 -`openclaw.install.minHostVersion` 会在安装期间和清单注册表加载期间强制执行。无效值会被拒绝;值若有效但要求的版本更高,则会在旧主机上跳过该插件。 +`openclaw.install.minHostVersion` 会在安装和清单注册表加载期间强制执行。无效值会被拒绝;较新的但有效的值会导致插件在较旧 host 上被跳过。 -精确的 npm 版本固定已在 `npmSpec` 中声明,例如 `"npmSpec": "@wecom/wecom-openclaw-plugin@1.2.3"`。如果你希望在获取到的 npm 制品不再匹配固定发布版本时让更新流程以失败关闭的方式处理,请将其与 `expectedIntegrity` 搭配使用。交互式 onboarding 会提供受信任注册表的 npm 规格,包括裸包名和 dist-tag。存在 `expectedIntegrity` 时,安装 / 更新流程会强制执行它;省略时,将记录注册表解析结果,但不会带完整性固定。 +精确的 npm 版本固定已经存在于 `npmSpec` 中,例如 +`"npmSpec": "@wecom/wecom-openclaw-plugin@1.2.3"`。当你希望如果获取到的 +npm 构件不再匹配固定版本时,更新流程以失败关闭的方式处理,就将其与 +`expectedIntegrity` 搭配使用。交互式 onboarding 会提供受信任注册表的 npm 规范,包括裸包名和 dist-tag。存在 `expectedIntegrity` 时,安装/更新流程会强制校验它;省略时,将记录注册表解析结果,但不会固定完整性。 -当状态、渠道列表或 SecretRef 扫描需要在不加载完整运行时的情况下识别已配置账号时,渠道插件应提供 `openclaw.setupEntry`。设置入口点应公开渠道元数据,以及对设置安全的配置、状态和 secrets 适配器;网络客户端、Gateway 网关 监听器和传输运行时应保留在主扩展入口点中。 +当状态、渠道列表或 SecretRef 扫描需要在不加载完整运行时的情况下识别已配置账户时,渠道插件应提供 `openclaw.setupEntry`。该 setup 入口点应公开渠道元数据,以及对 setup 安全的配置、状态和密钥适配器;将网络客户端、Gateway 网关监听器和传输运行时保留在主扩展入口点中。 -运行时入口点字段不会绕过针对源码入口点字段的包边界检查。例如,`openclaw.runtimeExtensions` 不能让一个越界的 `openclaw.extensions` 路径变得可加载。 +运行时入口点字段不会覆盖源码入口点字段的包边界检查。例如,`openclaw.runtimeExtensions` 不能让一个越界的 `openclaw.extensions` 路径变得可加载。 -`openclaw.install.allowInvalidConfigRecovery` 的设计范围刻意很窄。它不会让任意损坏的配置都变得可安装。当前它只允许安装流程从特定的过期内置插件升级失败中恢复,例如缺失的内置插件路径,或同一个内置插件下过期的 `channels.` 条目。无关的配置错误仍会阻止安装,并将操作人员引导到 `openclaw doctor --fix`。 +`openclaw.install.allowInvalidConfigRecovery` 的范围是有意收窄的。它不会让任意损坏的配置变得可安装。当前它只允许安装流程从特定的陈旧内置插件升级失败中恢复,例如缺失的内置插件路径,或同一内置插件下陈旧的 `channels.` 条目。无关的配置错误仍会阻止安装,并将操作人员引导到 `openclaw doctor --fix`。 `openclaw.channel.persistedAuthState` 是一个微型检查器模块的包元数据: @@ -543,9 +553,9 @@ OpenClaw 按以下优先级应用: } ``` -当设置、Doctor 或已配置状态流程需要在完整渠道插件加载前执行低成本的“是 / 否”凭证探测时,请使用它。目标导出应是一个只读取持久化状态的小函数;不要通过完整的渠道运行时 barrel 暴露它。 +当 setup、Doctor 或 configured-state 流程需要在完整渠道插件加载之前进行轻量的是/否凭证探测时,请使用它。目标导出应是一个只读取持久化状态的小函数;不要通过完整渠道运行时 barrel 转发它。 -`openclaw.channel.configuredState` 对仅环境变量驱动的低成本已配置检查使用相同的结构: +`openclaw.channel.configuredState` 对应廉价的仅环境变量 configured 检查,结构相同: ```json { @@ -561,57 +571,57 @@ OpenClaw 按以下优先级应用: } ``` -当一个渠道可以仅通过环境变量或其他微型非运行时输入回答已配置状态时,请使用它。如果检查需要完整配置解析或真实的渠道运行时,请将该逻辑保留在插件 `config.hasConfiguredState` hook 中。 +当一个渠道可以从环境变量或其他轻量非运行时输入回答 configured-state 时,请使用它。如果检查需要完整配置解析或真实渠道运行时,则应将该逻辑保留在插件的 `config.hasConfiguredState` hook 中。 ## 设备发现优先级(重复插件 id) -OpenClaw 会从多个根位置发现插件(内置、全局安装、工作区、配置中显式选择的路径)。如果两个发现结果共享同一个 `id`,则只保留**优先级最高**的清单;优先级较低的重复项会被丢弃,而不是与之并行加载。 +OpenClaw 会从多个根位置发现插件(内置、全局安装、工作区、显式配置选择的路径)。如果两个发现结果共享同一个 `id`,则只保留**优先级最高**的清单;优先级较低的重复项会被丢弃,而不是并行加载。 优先级从高到低如下: -1. **配置选定** —— 在 `plugins.entries.` 中显式固定的路径 +1. **配置选择** —— 在 `plugins.entries.` 中显式固定的路径 2. **内置** —— 随 OpenClaw 一起发布的插件 -3. **全局安装** —— 安装到全局 OpenClaw 插件根目录的插件 +3. **全局安装** —— 安装到全局 OpenClaw 插件根目录中的插件 4. **工作区** —— 相对于当前工作区发现的插件 -影响: +含义: -- 工作区中某个内置插件的 fork 或过期副本,不会遮蔽内置版本。 -- 若要真正用本地插件覆盖内置插件,请通过 `plugins.entries.` 固定它,使其凭优先级取胜,而不是依赖工作区发现。 -- 被丢弃的重复项会记录到日志中,以便 Doctor 和启动诊断指向被舍弃的副本。 +- 放在工作区中的某个内置插件分叉版或陈旧副本,不会遮蔽内置构建。 +- 如果要真正用本地插件覆盖内置插件,请通过 `plugins.entries.` 固定它,使其凭借优先级取胜,而不是依赖工作区发现。 +- 被丢弃的重复项会记录日志,以便 Doctor 和启动诊断能够指向被舍弃的副本。 ## JSON Schema 要求 - **每个插件都必须提供一个 JSON Schema**,即使它不接受任何配置。 -- 空 schema 也是允许的(例如 `{ "type": "object", "additionalProperties": false }`)。 -- Schema 会在配置读取 / 写入时校验,而不是在运行时校验。 +- 接受空 schema(例如 `{ "type": "object", "additionalProperties": false }`)。 +- Schema 会在配置读取/写入时验证,而不是在运行时验证。 -## 校验行为 +## 验证行为 -- 未知的 `channels.*` 键会被视为**错误**,除非该渠道 id 已由某个插件清单声明。 +- 未知的 `channels.*` 键是**错误**,除非该渠道 id 已由某个插件清单声明。 - `plugins.entries.`、`plugins.allow`、`plugins.deny` 和 `plugins.slots.*` - 必须引用**可发现的**插件 id。未知 id 会被视为**错误**。 -- 如果插件已安装,但其清单或 schema 损坏或缺失,校验将失败,Doctor 会报告该插件错误。 -- 如果插件配置存在,但插件处于**禁用**状态,则配置会被保留,并在 Doctor + 日志中显示**警告**。 + 必须引用**可发现**的插件 id。未知 id 属于**错误**。 +- 如果插件已安装,但其清单或 schema 损坏或缺失,验证会失败,Doctor 会报告该插件错误。 +- 如果插件配置存在,但插件已**禁用**,则配置会被保留,并且 Doctor + 日志中会显示**警告**。 -有关完整的 `plugins.*` schema,请参阅 [配置参考](/zh-CN/gateway/configuration)。 +有关完整的 `plugins.*` 模式,请参阅[配置参考](/zh-CN/gateway/configuration)。 ## 说明 -- **原生 OpenClaw 插件必须提供清单**,包括从本地文件系统加载的情况。运行时仍会单独加载插件模块;清单仅用于设备发现 + 校验。 -- 原生清单使用 JSON5 解析,因此接受注释、尾随逗号和未加引号的键,只要最终值仍是一个对象即可。 -- 清单加载器只会读取文档中说明的清单字段。请避免使用自定义顶层键。 -- 当插件不需要时,可以省略 `channels`、`providers`、`cliBackends` 和 `skills`。 -- 独占插件类型通过 `plugins.slots.*` 选择:`kind: "memory"` 通过 `plugins.slots.memory` 选择,`kind: "context-engine"` 通过 `plugins.slots.contextEngine` 选择(默认值为 `legacy`)。 -- 环境变量元数据(`providerAuthEnvVars`、`channelEnvVars`)仅具声明性。状态、审计、cron 投递校验以及其他只读界面在将某个环境变量视为已配置之前,仍会应用插件信任和有效激活策略。 -- 对于需要提供商代码的运行时向导元数据,请参阅 [提供商运行时 hooks](/zh-CN/plugins/architecture#provider-runtime-hooks)。 +- 对于**原生 OpenClaw 插件**,包括本地文件系统加载,清单都是**必需的**。运行时仍会单独加载插件模块;清单仅用于设备发现 + 验证。 +- 原生清单使用 JSON5 解析,因此只要最终值仍是对象,就接受注释、尾随逗号和未加引号的键。 +- 清单加载器只会读取文档中说明的清单字段。避免使用自定义顶层键。 +- 当插件不需要它们时,`channels`、`providers`、`cliBackends` 和 `skills` 都可以省略。 +- 排他性插件类型通过 `plugins.slots.*` 选择:`kind: "memory"` 通过 `plugins.slots.memory` 选择,`kind: "context-engine"` 通过 `plugins.slots.contextEngine` 选择(默认 `legacy`)。 +- 环境变量元数据(`providerAuthEnvVars`、`channelEnvVars`)仅为声明式。状态、审计、cron 投递验证以及其他只读表面,在将环境变量视为已配置之前,仍会应用插件信任和有效激活策略。 +- 有关需要 provider 代码的运行时向导元数据,请参阅 [Provider runtime hooks](/zh-CN/plugins/architecture-internals#provider-runtime-hooks)。 - 如果你的插件依赖原生模块,请记录构建步骤以及任何包管理器 allowlist 要求(例如 pnpm `allow-build-scripts` + `pnpm rebuild `)。 ## 相关内容 - 插件的入门指南。 + 插件快速开始。 内部架构和能力模型。 diff --git a/docs/zh-CN/plugins/message-presentation.md b/docs/zh-CN/plugins/message-presentation.md index 8f68da392..7b828ad8e 100644 --- a/docs/zh-CN/plugins/message-presentation.md +++ b/docs/zh-CN/plugins/message-presentation.md @@ -1,37 +1,39 @@ --- read_when: - - 新增或修改消息卡片、按钮或选择器渲染 - - 构建支持丰富出站消息的渠道插件 - - 更改消息工具呈现或投递能力 - - 调试提供商特定的卡片/块/组件渲染回归问题 -summary: 用于渠道插件的语义化消息卡片、按钮、选择器、回退文本和投递提示 + - 添加或修改消息卡片、按钮或选择器渲染 + - 构建支持富出站消息的渠道插件 + - 更改消息工具的呈现方式或投递能力 + - 调试提供商特定的卡片 / 块 / 组件渲染回归问题 +summary: 渠道插件的语义化消息卡片、按钮、选择器、后备文本和投递提示 title: 消息呈现 x-i18n: - generated_at: "2026-04-23T20:57:12Z" + generated_at: "2026-04-24T03:06:50Z" model: gpt-5.4 provider: openai - source_hash: 9052f4c801d6e1fbf4009d34af024dd3b9b0f2af70361833eb8a0abca7383bf9 + source_hash: 1c8c3903101310de330017b34bc2f0d641f4c8ea2b80a30532736b4409716510 source_path: plugins/message-presentation.md workflow: 15 --- -消息呈现是 OpenClaw 的共享契约,用于丰富的出站聊天 UI。 -它让智能体、CLI 命令、批准流程和插件只描述一次消息意图,而每个渠道插件则渲染出其所能提供的最佳原生形态。 +消息呈现是 OpenClaw 用于富出站聊天 UI 的共享契约。 +它让智能体、CLI 命令、审批流程和插件只需描述一次消息意图, +而每个渠道插件都可以尽可能渲染为最佳的原生形态。 -请将 presentation 用于可移植的消息 UI: +对可移植的消息 UI,请使用 presentation: - 文本区块 -- 小型上下文/页脚文本 +- 简短的上下文 / 页脚文本 - 分隔线 - 按钮 - 选择菜单 - 卡片标题和语气 -不要向共享消息工具中新增提供商原生字段,例如 Discord 的 `components`、Slack 的 `blocks`、Telegram 的 `buttons`、Teams 的 `card` 或 Feishu 的 `card`。这些都属于由渠道插件负责的渲染器输出。 +不要向共享消息工具中添加新的提供商原生字段,例如 Discord 的 `components`、Slack +的 `blocks`、Telegram 的 `buttons`、Teams 的 `card` 或 Feishu 的 `card`。这些字段是由渠道插件负责生成的渲染输出。 ## 契约 -插件作者可从以下位置导入公共契约: +插件作者从以下位置导入公共契约: ```ts import type { @@ -40,7 +42,7 @@ import type { } from "openclaw/plugin-sdk/interactive-runtime"; ``` -结构如下: +结构: ```ts type MessagePresentation = { @@ -81,16 +83,16 @@ type ReplyPayloadDelivery = { 按钮语义: -- `value` 是一个应用动作值;当渠道支持可点击控件时,它会通过该渠道现有的交互路径回传。 -- `url` 是一个链接按钮。它可以在没有 `value` 的情况下存在。 -- `label` 是必需的,并且也会用于文本回退。 -- `style` 是建议性的。渲染器应将不支持的样式映射为安全默认值,而不是让发送失败。 +- `value` 是应用动作值;当渠道支持可点击控件时,它会通过该渠道现有的交互路径路由回来。 +- `url` 是链接按钮。它可以在没有 `value` 的情况下存在。 +- `label` 是必填项,也会用于文本后备内容。 +- `style` 是提示性信息。渲染器应将不支持的样式映射到安全的默认值,而不是导致发送失败。 -选择菜单语义: +选择器语义: - `options[].value` 是被选中的应用值。 -- `placeholder` 是建议性的;对于没有原生选择支持的渠道,可忽略它。 -- 如果某个渠道不支持选择菜单,回退文本会列出各个标签。 +- `placeholder` 是提示性信息;对于没有原生选择器支持的渠道,可能会被忽略。 +- 如果某个渠道不支持选择器,后备文本会列出这些标签。 ## 生产者示例 @@ -114,7 +116,7 @@ type ReplyPayloadDelivery = { } ``` -仅 URL 链接按钮: +仅 URL 的链接按钮: ```json { @@ -155,7 +157,7 @@ openclaw message send --channel slack \ --presentation '{"title":"Deploy approval","tone":"warning","blocks":[{"type":"text","text":"Canary is ready."},{"type":"buttons","buttons":[{"label":"Approve","value":"deploy:approve","style":"success"},{"label":"Decline","value":"deploy:decline","style":"danger"}]}]}' ``` -固定投递: +置顶投递: ```bash openclaw message send --channel telegram \ @@ -164,7 +166,7 @@ openclaw message send --channel telegram \ --pin ``` -使用显式 JSON 的固定投递: +使用显式 JSON 的置顶投递: ```json { @@ -202,44 +204,46 @@ const adapter: ChannelOutboundAdapter = { }; ``` -能力字段有意保持为简单布尔值。它们描述的是渲染器能让哪些内容具备交互性,而不是平台原生限制的全部细节。渲染器仍自行负责平台特定限制,例如最大按钮数、最大块数和卡片大小。 +能力字段刻意保持为简单的布尔值。它们描述的是渲染器可以交互化呈现什么, +而不是每一个原生平台限制。渲染器仍然负责处理平台特定限制,例如最大按钮数、区块数和卡片大小。 ## 核心渲染流程 -当某个 `ReplyPayload` 或消息动作包含 `presentation` 时,核心会: +当 `ReplyPayload` 或消息动作包含 `presentation` 时,核心会: 1. 规范化 presentation 载荷。 2. 解析目标渠道的出站适配器。 3. 读取 `presentationCapabilities`。 4. 当适配器能够渲染该载荷时,调用 `renderPresentation`。 -5. 当适配器缺失或无法渲染时,回退到保守文本。 -6. 通过正常的渠道投递路径发送最终载荷。 -7. 在第一条消息成功发送后,应用诸如 `delivery.pin` 等投递元数据。 +5. 当适配器不存在或无法渲染时,回退为保守的文本。 +6. 通过正常的渠道投递路径发送结果载荷。 +7. 在第一条消息成功发送后,应用诸如 `delivery.pin` 之类的投递元数据。 -核心负责回退行为,这样生产者就可以保持渠道无关。渠道插件则负责原生渲染和交互处理。 +核心负责后备行为,因此生产者可以保持渠道无关。渠道插件则负责原生渲染和交互处理。 ## 降级规则 -Presentation 必须能够在能力有限的渠道上安全发送。 +Presentation 必须能在受限渠道上安全发送。 -回退文本包括: +后备文本包括: - `title` 作为第一行 -- `text` 块作为普通段落 -- `context` 块作为紧凑上下文行 -- `divider` 块作为视觉分隔符 -- 按钮标签,包括链接按钮的 URL -- 选择菜单的选项标签 +- `text` 区块作为普通段落 +- `context` 区块作为紧凑的上下文行 +- `divider` 区块作为视觉分隔符 +- 按钮标签,对于链接按钮还包括 URL +- 选择器选项标签 -不支持的原生控件应降级,而不是导致整次发送失败。 -例如: +不受支持的原生控件应降级处理,而不是导致整个发送失败。 +示例: -- 禁用了 inline buttons 的 Telegram 会发送文本回退。 -- 不支持选择菜单的渠道会将选项标签列为文本。 -- 仅 URL 按钮会变成原生链接按钮,或退化为一行 URL 文本。 -- 非必需的 pin 失败不会导致已投递消息失败。 +- 当 Telegram 的内联按钮被禁用时,会发送文本后备内容。 +- 没有选择器支持的渠道会将选择器选项列为文本。 +- 仅 URL 按钮会变成原生链接按钮,或者变成后备 URL 行。 +- 非必需的置顶失败不会导致已投递的消息失败。 -主要例外是 `delivery.pin.required: true`;如果请求了必需 pin,而该渠道无法固定已发送消息,则投递会报告失败。 +主要的例外是 `delivery.pin.required: true`;如果置顶被请求为必需, +而渠道无法置顶已发送的消息,则投递会报告失败。 ## 提供商映射 @@ -247,23 +251,25 @@ Presentation 必须能够在能力有限的渠道上安全发送。 | 渠道 | 原生渲染目标 | 说明 | | --------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -| Discord | Components 和 component containers | 为现有 provider 原生载荷生产者保留旧版 `channelData.discord.components`,但新的共享发送应使用 `presentation`。 | -| Slack | Block Kit | 为现有 provider 原生载荷生产者保留旧版 `channelData.slack.blocks`,但新的共享发送应使用 `presentation`。 | -| Telegram | 文本加 inline keyboards | 按钮/选择菜单要求目标界面具备 inline button 能力;否则使用文本回退。 | -| Mattermost | 文本加 interactive props | 其他块会降级为文本。 | -| Microsoft Teams | Adaptive Cards | 当同时提供普通 `message` 文本与卡片时,普通文本也会包含在卡片中。 | +| Discord | Components 和组件容器 | 为现有提供商原生载荷生产者保留旧版 `channelData.discord.components`,但新的共享发送应使用 `presentation`。 | +| Slack | Block Kit | 为现有提供商原生载荷生产者保留旧版 `channelData.slack.blocks`,但新的共享发送应使用 `presentation`。 | +| Telegram | 文本加内联键盘 | 按钮 / 选择器需要目标表面支持内联按钮能力;否则使用文本后备。 | +| Mattermost | 文本加交互式 props | 其他区块会降级为文本。 | +| Microsoft Teams | Adaptive Cards | 当同时提供普通 `message` 文本和卡片时,普通 `message` 文本会一并包含。 | | Feishu | 交互式卡片 | 卡片头部可使用 `title`;正文会避免重复该标题。 | -| 纯文本渠道 | 文本回退 | 没有渲染器的渠道仍会得到可读输出。 | +| 普通渠道 | 文本后备 | 没有渲染器的渠道仍会获得可读输出。 | -为现有回复生产者保留 provider 原生载荷兼容性是一种过渡措施。它不是向共享字段中新增原生字段的理由。 +提供商原生载荷兼容性只是为现有 reply 生产者提供的过渡便利。 +这并不是向共享原生字段中添加新字段的理由。 -## Presentation 与 InteractiveReply 的区别 +## Presentation 与 InteractiveReply -`InteractiveReply` 是较旧的内部子集,由 approval 和交互辅助工具使用。它支持: +`InteractiveReply` 是较早的内部子集,供审批和交互辅助工具使用。 +它支持: - 文本 - 按钮 -- 选择菜单 +- 选择器 `MessagePresentation` 是规范的共享发送契约。它新增了: @@ -272,7 +278,7 @@ Presentation 必须能够在能力有限的渠道上安全发送。 - 上下文 - 分隔线 - 仅 URL 按钮 -- 通过 `ReplyPayload.delivery` 提供通用投递元数据 +- 通过 `ReplyPayload.delivery` 提供的通用投递元数据 在桥接旧代码时,请使用 `openclaw/plugin-sdk/interactive-runtime` 中的辅助工具: @@ -285,38 +291,38 @@ import { } from "openclaw/plugin-sdk/interactive-runtime"; ``` -新代码应直接接收或生成 `MessagePresentation`。 +新代码应直接接受或生成 `MessagePresentation`。 -## 投递固定(Pin) +## 投递置顶 -固定是一种投递行为,而不是呈现。请使用 `delivery.pin`,而不是 -`channelData.telegram.pin` 之类的 provider 原生字段。 +置顶属于投递行为,不属于 presentation。请使用 `delivery.pin`,而不是 +`channelData.telegram.pin` 之类的提供商原生字段。 -语义如下: +语义: -- `pin: true` 会固定第一条成功投递的消息。 +- `pin: true` 会置顶第一条成功投递的消息。 - `pin.notify` 默认为 `false`。 - `pin.required` 默认为 `false`。 -- 可选 pin 失败会降级处理,并保留已发送消息。 -- 必需 pin 失败会导致投递失败。 -- 对于分块消息,会固定第一条已投递的块,而不是末尾块。 +- 非必需的置顶失败会降级处理,并保留已发送消息不变。 +- 必需的置顶失败会导致投递失败。 +- 分块消息会置顶第一个已投递的分块,而不是尾部分块。 -对于已存在消息,只要 provider 支持相关操作,手动 `pin`、`unpin` 和 `pins` 消息动作仍然存在。 +对于提供商支持这些操作的现有消息,手动 `pin`、`unpin` 和 `pins` 消息动作仍然存在。 ## 插件作者检查清单 -- 当某个渠道能够渲染或安全降级语义化呈现时,请从 `describeMessageTool(...)` 中声明 `presentation`。 -- 为运行时出站适配器添加 `presentationCapabilities`。 +- 当渠道能够渲染或安全降级语义化 presentation 时,在 `describeMessageTool(...)` 中声明 `presentation`。 +- 向运行时出站适配器添加 `presentationCapabilities`。 - 在运行时代码中实现 `renderPresentation`,而不是在控制平面插件设置代码中实现。 -- 将原生 UI 库保留在热路径设置/目录路径之外。 -- 在渲染器和测试中保留平台限制。 -- 为不支持的按钮、选择菜单、URL 按钮、标题/文本重复,以及混合 `message` + `presentation` 发送添加回退测试。 -- 只有当 provider 能固定已发送 message id 时,才通过 `deliveryCapabilities.pin` 和 `pinDeliveredMessage` 添加 pin 支持。 -- 不要通过共享消息动作 schema 暴露新的 provider 原生 card/block/component/button 字段。 +- 不要在高频的设置 / 目录路径中引入原生 UI 库。 +- 在渲染器和测试中保留平台限制处理。 +- 为不支持的按钮、选择器、URL 按钮、标题 / 文本重复,以及混合 `message` 加 `presentation` 发送添加后备测试。 +- 仅当提供商可以置顶已发送消息 id 时,才通过 `deliveryCapabilities.pin` 和 `pinDeliveredMessage` 添加置顶投递支持。 +- 不要通过共享消息动作 schema 暴露新的提供商原生卡片 / 区块 / 组件 / 按钮字段。 ## 相关文档 -- [消息 CLI](/zh-CN/cli/message) +- [Message CLI](/zh-CN/cli/message) - [插件 SDK 概览](/zh-CN/plugins/sdk-overview) -- [插件架构](/zh-CN/plugins/architecture#message-tool-schemas) +- [插件架构](/zh-CN/plugins/architecture-internals#message-tool-schemas) - [渠道呈现重构计划](/zh-CN/plan/ui-channels) diff --git a/docs/zh-CN/plugins/sdk-channel-plugins.md b/docs/zh-CN/plugins/sdk-channel-plugins.md index ca9eacf05..c005fde0e 100644 --- a/docs/zh-CN/plugins/sdk-channel-plugins.md +++ b/docs/zh-CN/plugins/sdk-channel-plugins.md @@ -2,110 +2,88 @@ read_when: - 你正在构建一个新的消息渠道插件 - 你想将 OpenClaw 连接到一个消息平台 - - 你需要了解 ChannelPlugin 适配器接口 + - 你需要理解 `ChannelPlugin` 适配器接口 surface sidebarTitle: Channel Plugins -summary: 为 OpenClaw 构建消息渠道插件的分步指南 +summary: 构建 OpenClaw 消息渠道插件的分步指南 title: 构建渠道插件 x-i18n: - generated_at: "2026-04-23T23:00:36Z" + generated_at: "2026-04-24T03:07:17Z" model: gpt-5.4 provider: openai - source_hash: 8829432f3d9671f7ab5a8fec6683e52ae363990aa4c7962dfbdda061d4b1b6c9 + source_hash: e08340e7984b4aa5307c4ba126b396a80fa8dcb3d6f72561f643806a8034fb88 source_path: plugins/sdk-channel-plugins.md workflow: 15 --- -本指南将带你逐步构建一个渠道插件,把 OpenClaw 连接到某个 -消息平台。完成后,你将拥有一个可用的渠道,支持私信安全、 -配对、回复线程以及出站消息。 +本指南将带你逐步构建一个渠道插件,用于将 OpenClaw 连接到某个消息平台。完成后,你将拥有一个可用的渠道,支持私信安全、配对、回复线程和出站消息。 如果你之前从未构建过任何 OpenClaw 插件,请先阅读 - [入门指南](/zh-CN/plugins/building-plugins),了解基础包 - 结构和 manifest 设置。 + [入门指南](/zh-CN/plugins/building-plugins) 了解基础包结构和 manifest 设置。 -## 渠道插件如何工作 +## 渠道插件的工作方式 -渠道插件不需要自己的发送 / 编辑 / 反应工具。OpenClaw 在核心中保留了一个 -共享的 `message` 工具。你的插件负责: +渠道插件不需要自带发送/编辑/反应工具。OpenClaw 在 core 中保留了一个共享的 `message` 工具。你的插件负责: -- **配置** — 账户解析和设置向导 +- **配置** — 账号解析和设置向导 - **安全** — 私信策略和允许列表 - **配对** — 私信批准流程 -- **会话语法** — 提供商特定的会话 ID 如何映射到基础聊天、线程 ID 和父级回退 +- **会话语法** — 提供商特定的会话 id 如何映射到基础聊天、线程 id 和父级回退 - **出站** — 向平台发送文本、媒体和投票 -- **线程处理** — 如何对回复进行线程化 -- **Heartbeat 输入状态** — 针对 heartbeat 投递目标的可选输入 / 忙碌信号 +- **线程处理** — 回复如何进入线程 +- **心跳输入状态** — 为心跳投递目标提供可选的输入中/忙碌信号 -核心负责共享消息工具、提示词接线、外层会话键形状、 -通用 `:thread:` 记账以及分发。 +core 负责共享的消息工具、提示词接线、外层会话键形状、通用的 `:thread:` 记录和分发。 -如果你的渠道支持在入站回复之外显示“正在输入”指示器,请在渠道插件上暴露 -`heartbeat.sendTyping(...)`。核心会在 heartbeat 模型运行开始前,用已解析的 -heartbeat 投递目标调用它,并使用共享的输入状态保活 / 清理生命周期。 -如果平台需要显式停止信号,请添加 `heartbeat.clearTyping(...)`。 +如果你的渠道支持在入站回复之外显示输入指示器,请在渠道插件上暴露 `heartbeat.sendTyping(...)`。core 会在心跳模型运行开始前,使用已解析的心跳投递目标调用它,并使用共享的输入状态保活/清理生命周期。如果平台需要显式停止信号,请再添加 `heartbeat.clearTyping(...)`。 -如果你的渠道为消息工具增加了携带媒体来源的参数,请通过 -`describeMessageTool(...).mediaSourceParams` 暴露这些参数名。核心会使用这个显式 -列表进行沙箱路径规范化以及出站媒体访问策略,因此插件无需为提供商特定的 -头像、附件或封面图参数添加共享核心特判。 -优先返回按操作键控的映射,例如 -`{ "set-profile": ["avatarUrl", "avatarPath"] }`,这样无关操作就不会继承其他操作的媒体参数。 -对于那些本就有意在所有暴露操作之间共享的参数,扁平数组仍然可用。 +如果你的渠道为消息工具添加了携带媒体来源的参数,请通过 `describeMessageTool(...).mediaSourceParams` 暴露这些参数名。core 使用这个显式列表进行沙箱路径标准化和出站媒体访问策略处理,因此插件不需要为提供商特定的头像、附件或封面图参数添加共享 core 特判。 +更推荐返回按 action 键控的映射,例如 +`{ "set-profile": ["avatarUrl", "avatarPath"] }`,这样无关 action 就不会继承其他 action 的媒体参数。对于有意在每个已暴露 action 之间共享的参数,扁平数组仍然可用。 -如果你的平台在会话 ID 中存储了额外作用域,请将该解析逻辑保留在插件中,使用 -`messaging.resolveSessionConversation(...)`。这是将 `rawId` 映射到基础会话 ID、 -可选线程 ID、显式 `baseConversationId` 以及任何 `parentConversationCandidates` -的规范钩子。当你返回 `parentConversationCandidates` 时,请按从最窄父级到 -最宽 / 基础会话的顺序排列。 +如果你的平台在会话 id 中存储了额外作用域,请将这类解析逻辑保留在插件中,使用 `messaging.resolveSessionConversation(...)`。这是将 `rawId` 映射为基础会话 id、可选线程 id、显式 `baseConversationId` 以及任意 `parentConversationCandidates` 的规范钩子。 +当你返回 `parentConversationCandidates` 时,请按从最窄父级到最宽/基础会话的顺序排列。 -对于那些在渠道注册表启动前就需要相同解析逻辑的内置插件, -也可以额外暴露一个顶层 `session-key-api.ts` 文件,并提供匹配的 -`resolveSessionConversation(...)` 导出。只有当运行时插件注册表尚不可用时, -核心才会使用这个对引导安全的接口。 +需要在渠道注册表启动之前进行同样解析的内置插件,也可以暴露一个顶层 `session-key-api.ts` 文件,并导出匹配的 `resolveSessionConversation(...)`。仅当运行时插件注册表尚不可用时,core 才会使用这个启动安全的接口。 -当某个插件只需要在通用 / 原始 ID 之上提供父级回退时, -`messaging.resolveParentConversationCandidates(...)` 仍然可用,作为旧版兼容回退。 -如果两个钩子都存在,核心会优先使用 -`resolveSessionConversation(...).parentConversationCandidates`,只有当规范钩子 -省略它们时,才会回退到 `resolveParentConversationCandidates(...)`。 +当插件只需要在通用/原始 id 之上添加父级回退时,`messaging.resolveParentConversationCandidates(...)` 仍然可作为旧版兼容回退使用。如果这两个钩子都存在,core 会优先使用 `resolveSessionConversation(...).parentConversationCandidates`,只有当规范钩子省略它们时,才会回退到 `resolveParentConversationCandidates(...)`。 -## 批准与渠道能力 +## 批准和渠道能力 -大多数渠道插件不需要特定于批准的代码。 +大多数渠道插件不需要编写专门的批准代码。 -- 核心负责同聊天中的 `/approve`、共享批准按钮负载以及通用回退投递。 -- 当渠道需要特定于批准的行为时,优先在渠道插件上使用一个 `approvalCapability` 对象。 -- `ChannelPlugin.approvals` 已移除。请将批准投递 / 原生 / 渲染 / auth 相关事实放到 `approvalCapability` 中。 -- `plugin.auth` 仅用于 login / logout;核心不再从该对象读取批准 auth 钩子。 -- `approvalCapability.authorizeActorAction` 和 `approvalCapability.getActionAvailabilityState` 是规范的批准 auth 扩展缝隙。 -- 对于同聊天批准 auth 可用性,请使用 `approvalCapability.getActionAvailabilityState`。 -- 如果你的渠道暴露原生 exec 批准,请在其发起界面 / 原生客户端状态与同聊天批准 auth 不同时,使用 `approvalCapability.getExecInitiatingSurfaceState`。核心会使用这个特定于 exec 的钩子区分 `enabled` 与 `disabled`,判断发起渠道是否支持原生 exec 批准,并在原生客户端回退指引中包含该渠道。`createApproverRestrictedNativeApprovalCapability(...)` 为常见场景补齐这一点。 -- 对于特定于渠道的负载生命周期行为,例如隐藏重复的本地批准提示,或在投递前发送输入状态,请使用 `outbound.shouldSuppressLocalPayloadPrompt` 或 `outbound.beforeDeliverPayload`。 -- 仅在原生批准路由或抑制回退时使用 `approvalCapability.delivery`。 -- 对于由渠道拥有的原生批准事实,请使用 `approvalCapability.nativeRuntime`。在高频渠道入口点上,请通过 `createLazyChannelApprovalNativeRuntimeAdapter(...)` 保持其懒加载,这样它可以按需导入你的运行时模块,同时仍让核心组装批准生命周期。 -- 只有当渠道确实需要自定义批准负载,而不是共享渲染器时,才使用 `approvalCapability.render`。 -- 当渠道希望禁用路径回复精确说明启用原生 exec 批准所需的配置旋钮时,请使用 `approvalCapability.describeExecApprovalSetup`。该钩子接收 `{ channel, channelLabel, accountId }`;具名账户渠道应渲染账户作用域路径,例如 `channels..accounts..execApprovals.*`,而不是顶层默认值。 -- 如果某个渠道可以从现有配置中推断出稳定的、类似所有者的私信身份,请使用 `openclaw/plugin-sdk/approval-runtime` 中的 `createResolvedApproverActionAuthAdapter` 来限制同聊天 `/approve`,而无需添加特定于批准的核心逻辑。 -- 如果某个渠道需要原生批准投递,请让渠道代码专注于目标规范化以及传输 / 展示事实。请使用 `openclaw/plugin-sdk/approval-runtime` 中的 `createChannelExecApprovalProfile`、`createChannelNativeOriginTargetResolver`、`createChannelApproverDmTargetResolver` 和 `createApproverRestrictedNativeApprovalCapability`。将渠道特定事实放在 `approvalCapability.nativeRuntime` 之后,理想情况下通过 `createChannelApprovalNativeRuntimeAdapter(...)` 或 `createLazyChannelApprovalNativeRuntimeAdapter(...)`,这样核心就可以组装处理器,并负责请求过滤、路由、去重、过期、Gateway 网关订阅以及“已路由到别处”通知。`nativeRuntime` 被拆分为几个更小的扩展缝隙: -- `availability` — 账户是否已配置,以及某个请求是否应被处理 -- `presentation` — 将共享批准视图模型映射为待处理 / 已解决 / 已过期的原生负载或最终操作 -- `transport` — 准备目标,并发送 / 更新 / 删除原生批准消息 -- `interactions` — 针对原生按钮或反应的可选 bind / unbind / clear-action 钩子 +- core 负责同聊天中的 `/approve`、共享批准按钮负载和通用回退投递。 +- 当渠道需要与批准相关的行为时,优先在渠道插件上使用单个 `approvalCapability` 对象。 +- `ChannelPlugin.approvals` 已移除。请将批准投递/原生/渲染/认证相关事实放到 `approvalCapability` 上。 +- `plugin.auth` 仅用于登录/登出;core 不再从该对象读取批准认证钩子。 +- `approvalCapability.authorizeActorAction` 和 `approvalCapability.getActionAvailabilityState` 是规范的批准认证接口。 +- 对于同聊天批准认证可用性,请使用 `approvalCapability.getActionAvailabilityState`。 +- 如果你的渠道暴露原生 exec 批准,请在发起界面/原生客户端状态与同聊天批准认证不同的情况下使用 `approvalCapability.getExecInitiatingSurfaceState`。core 使用这个 exec 专用钩子来区分 `enabled` 与 `disabled`、判断发起渠道是否支持原生 exec 批准,并在原生客户端回退指引中包含该渠道。`createApproverRestrictedNativeApprovalCapability(...)` 为常见情况填充了这部分。 +- 对于渠道特定的负载生命周期行为,例如隐藏重复的本地批准提示或在投递前发送输入指示器,请使用 `outbound.shouldSuppressLocalPayloadPrompt` 或 `outbound.beforeDeliverPayload`。 +- 仅在原生批准路由或回退抑制中使用 `approvalCapability.delivery`。 +- 对于渠道拥有的原生批准事实,请使用 `approvalCapability.nativeRuntime`。在高热度渠道入口点中,通过 `createLazyChannelApprovalNativeRuntimeAdapter(...)` 保持其惰性加载;这样它可以按需导入你的运行时模块,同时仍允许 core 组装批准生命周期。 +- 只有当渠道确实需要自定义批准负载而不是共享渲染器时,才使用 `approvalCapability.render`。 +- 当渠道希望在禁用路径的回复中解释启用原生 exec 批准所需的确切配置开关时,请使用 `approvalCapability.describeExecApprovalSetup`。该钩子接收 `{ channel, channelLabel, accountId }`;命名账号渠道应渲染账号作用域路径,例如 `channels..accounts..execApprovals.*`,而不是顶层默认值。 +- 如果渠道可以从现有配置中推断出稳定的类似所有者的私信身份,请使用 `openclaw/plugin-sdk/approval-runtime` 中的 `createResolvedApproverActionAuthAdapter` 来限制同聊天 `/approve`,而不要增加批准专用的 core 逻辑。 +- 如果渠道需要原生批准投递,请让渠道代码聚焦于目标标准化以及传输/展示事实。请使用 `openclaw/plugin-sdk/approval-runtime` 中的 `createChannelExecApprovalProfile`、`createChannelNativeOriginTargetResolver`、`createChannelApproverDmTargetResolver` 和 `createApproverRestrictedNativeApprovalCapability`。将渠道特定事实放在 `approvalCapability.nativeRuntime` 后面,理想情况下通过 `createChannelApprovalNativeRuntimeAdapter(...)` 或 `createLazyChannelApprovalNativeRuntimeAdapter(...)`,这样 core 就可以组装处理器并负责请求过滤、路由、去重、过期、Gateway 网关订阅和“已路由到其他位置”的通知。`nativeRuntime` 被拆分为几个更小的接口: +- `availability` — 账号是否已配置,以及请求是否应被处理 +- `presentation` — 将共享批准视图模型映射为待处理/已解决/已过期的原生负载或最终 action +- `transport` — 准备目标,并发送/更新/删除原生批准消息 +- `interactions` — 原生按钮或反应的可选 bind/unbind/clear-action 钩子 - `observe` — 可选的投递诊断钩子 -- 如果渠道需要运行时拥有的对象,例如客户端、token、Bolt 应用或 webhook 接收器,请通过 `openclaw/plugin-sdk/channel-runtime-context` 注册它们。通用的运行时上下文注册表让核心可以基于渠道启动状态引导能力驱动的处理器,而无需增加特定于批准的包装胶水代码。 -- 只有当能力驱动的扩展缝隙仍不足以表达需求时,才使用更底层的 `createChannelApprovalHandler` 或 `createChannelNativeApprovalRuntime`。 -- 原生批准渠道必须通过这些辅助函数同时传递 `accountId` 和 `approvalKind`。`accountId` 用于让多账户批准策略限定在正确的机器人账户内,`approvalKind` 用于让渠道在没有核心硬编码分支的情况下仍能区分 exec 与插件批准行为。 -- 核心现在也负责批准重路由通知。渠道插件不应再从 `createChannelNativeApprovalRuntime` 自行发送“批准已转到私信 / 另一个渠道”的后续消息;相反,应通过共享批准能力辅助函数暴露准确的发起端 + 批准人私信路由信息,并让核心在向发起聊天发送任何通知前聚合实际投递结果。 -- 端到端保留已投递批准的 id 类型。原生客户端不应根据渠道本地状态去猜测或改写 exec 与插件批准的路由。 -- 不同批准类型可以有意暴露不同的原生界面。 +- 如果渠道需要运行时持有的对象,例如 client、token、Bolt app 或 webhook receiver,请通过 `openclaw/plugin-sdk/channel-runtime-context` 注册它们。通用运行时上下文注册表使 core 能够从渠道启动状态中引导基于 capability 的处理器,而无需添加批准专用的包装胶水代码。 +- 只有当基于 capability 的接口还不足以表达需求时,才使用更底层的 `createChannelApprovalHandler` 或 `createChannelNativeApprovalRuntime`。 +- 原生批准渠道必须通过这些 helper 同时路由 `accountId` 和 `approvalKind`。`accountId` 可将多账号批准策略限定到正确的机器人账号上,而 `approvalKind` 可让渠道在无需 core 中硬编码分支的情况下区分 exec 与插件批准行为。 +- core 现在也负责批准重路由通知。渠道插件不应再从 `createChannelNativeApprovalRuntime` 自行发送“批准已发送到私信/其他渠道”这类后续消息;请改为通过共享批准 capability helper 暴露准确的 origin + approver 私信路由,让 core 在向发起聊天回发任何通知前聚合实际投递结果。 +- 在端到端流程中保留已投递批准 id 的 kind。原生客户端不应根据渠道本地状态猜测或重写 exec 与插件批准的路由。 +- 不同批准 kind 可以有意暴露不同的原生界面。 当前内置示例: - Slack 对 exec 和插件 id 都保留原生批准路由能力。 - - Matrix 对 exec 和插件批准保留相同的原生私信 / 渠道路由和反应 UX,同时仍允许按批准类型区分 auth。 -- `createApproverRestrictedNativeApprovalAdapter` 仍作为兼容包装器存在,但新代码应优先使用 capability 构建器,并在插件上暴露 `approvalCapability`。 + - Matrix 对 exec 和插件批准保持相同的原生私信/渠道路由和 reaction UX,同时仍允许认证按批准 kind 不同。 +- `createApproverRestrictedNativeApprovalAdapter` 仍作为兼容包装器存在,但新代码应优先使用 capability builder,并在插件上暴露 `approvalCapability`。 -对于高频渠道入口点,当你只需要这一系列中的某一部分时,请优先使用更窄的运行时子路径: +对于高热度渠道入口点,当你只需要该能力族中的某一部分时,优先使用更窄的运行时子路径: - `openclaw/plugin-sdk/approval-auth-runtime` - `openclaw/plugin-sdk/approval-client-runtime` @@ -124,86 +102,82 @@ heartbeat 投递目标调用它,并使用共享的输入状态保活 / 清理 `openclaw/plugin-sdk/reply-reference` 和 `openclaw/plugin-sdk/reply-chunking`。 -对于设置相关部分: +关于设置,具体如下: -- `openclaw/plugin-sdk/setup-runtime` 覆盖运行时安全的设置辅助函数: - 对导入安全的设置 patch 适配器(`createPatchedAccountSetupAdapter`、 +- `openclaw/plugin-sdk/setup-runtime` 覆盖运行时安全的设置 helper: + 可安全导入的设置补丁适配器(`createPatchedAccountSetupAdapter`、 `createEnvPatchedAccountSetupAdapter`、 - `createSetupInputPresenceValidator`)、查找说明输出、 + `createSetupInputPresenceValidator`)、查找备注输出、 `promptResolvedAllowFrom`、`splitSetupEntries` 以及委托式 - setup-proxy 构建器 -- `openclaw/plugin-sdk/setup-adapter-runtime` 是更窄的、支持环境变量的适配器 - 扩展缝隙,用于 `createEnvPatchedAccountSetupAdapter` -- `openclaw/plugin-sdk/channel-setup` 覆盖可选安装的设置 - 构建器,以及少量设置安全原语: + setup-proxy builder +- `openclaw/plugin-sdk/setup-adapter-runtime` 是面向环境感知适配器的窄接口, + 用于 `createEnvPatchedAccountSetupAdapter` +- `openclaw/plugin-sdk/channel-setup` 覆盖可选安装设置 builder 以及少量设置安全原语: `createOptionalChannelSetupSurface`、`createOptionalChannelSetupAdapter`、 -如果你的渠道支持由环境变量驱动的设置或 auth,并且通用启动 / 配置 -流程需要在运行时加载前知道这些环境变量名称,请在插件 manifest 中通过 -`channelEnvVars` 声明它们。渠道运行时的 `envVars` 或本地常量 -仅应用于面向运维人员的说明文本。 +如果你的渠道支持由环境变量驱动的设置或认证,并且通用启动/配置流程需要在运行时加载前知道这些环境变量名称,请在插件 manifest 中通过 `channelEnvVars` 声明它们。渠道运行时的 `envVars` 或本地常量应仅用于面向运维人员的文案。 -如果你的渠道可能会在插件运行时启动前出现在 `status`、`channels list`、 -`channels status` 或 SecretRef 扫描中,请在 `package.json` 中添加 -`openclaw.setupEntry`。该入口点应当能够安全地在只读命令路径中导入, -并返回这些摘要所需的渠道元数据、设置安全配置适配器、状态适配器以及 -渠道密钥目标元数据。不要从设置入口点启动客户端、监听器或传输运行时。 +如果你的渠道可能在插件运行时启动前出现在 `status`、`channels list`、`channels status` 或 SecretRef 扫描中,请在 `package.json` 中添加 `openclaw.setupEntry`。该入口点应当可以在只读命令路径中安全导入,并返回这些摘要所需的渠道元数据、设置安全配置适配器、状态适配器和渠道 secret 目标元数据。不要从 setup 入口启动 client、listener 或传输运行时。 `createOptionalChannelSetupWizard`、`DEFAULT_ACCOUNT_ID`、 `createTopLevelChannelDmPolicy`、`setSetupChannelEnabled` 和 `splitSetupEntries` -- 只有当你还需要更重的共享设置 / 配置辅助函数时,才使用更宽泛的 - `openclaw/plugin-sdk/setup` 扩展缝隙,例如 +- 仅当你还需要更重型的共享设置/配置 helper 时,才使用更宽泛的 + `openclaw/plugin-sdk/setup` 接口,例如 `moveSingleAccountChannelSectionToDefaultAccount(...)` -如果你的渠道只想在设置界面中提示“先安装此插件”,请优先使用 `createOptionalChannelSetupSurface(...)`。生成的 -适配器 / 向导会在配置写入和最终确认时以失败关闭方式处理,并在验证、完成和文档链接说明中复用同一条“需要安装”的消息。 +如果你的渠道只想在设置界面中提示“先安装这个插件”,请优先使用 `createOptionalChannelSetupSurface(...)`。生成的 adapter/wizard 在配置写入和最终完成上会失败关闭,并会在验证、完成和文档链接文案之间复用同一条“需要先安装”的消息。 -对于其他高频渠道路径,也请优先使用更窄的辅助函数,而不是更宽泛的旧版接口: +对于其他高热度渠道路径,也请优先使用窄 helper,而不是更宽泛的旧版接口: - `openclaw/plugin-sdk/account-core`、 `openclaw/plugin-sdk/account-id`、 `openclaw/plugin-sdk/account-resolution` 和 - `openclaw/plugin-sdk/account-helpers`,用于多账户配置以及 - 默认账户回退 + `openclaw/plugin-sdk/account-helpers`,用于多账号配置和 + 默认账号回退 - `openclaw/plugin-sdk/inbound-envelope` 和 - `openclaw/plugin-sdk/inbound-reply-dispatch`,用于入站路由 / envelope 以及 - record-and-dispatch 接线 -- `openclaw/plugin-sdk/messaging-targets`,用于目标解析 / 匹配 + `openclaw/plugin-sdk/inbound-reply-dispatch`,用于入站路由/envelope 以及 + 记录并分发接线 +- `openclaw/plugin-sdk/messaging-targets`,用于目标解析/匹配 - `openclaw/plugin-sdk/outbound-media` 和 `openclaw/plugin-sdk/outbound-runtime`,用于媒体加载以及出站 - 身份 / 发送委托和负载规划 -- 来自 `openclaw/plugin-sdk/channel-core` 的 `buildThreadAwareOutboundSessionRoute(...)`,当出站路由需要保留显式 `replyToId` / `threadId`,或者在基础会话键仍匹配后恢复当前 `:thread:` 会话时使用。如果平台拥有原生线程投递语义,提供商插件可以覆盖优先级、后缀行为和线程 ID 规范化。 + 身份/发送委托和负载规划 +- `buildThreadAwareOutboundSessionRoute(...)`,来自 + `openclaw/plugin-sdk/channel-core`,当出站路由应保留显式 + `replyToId`/`threadId`,或在基础会话键仍然匹配后恢复当前 + `:thread:` 会话时使用。提供商插件可以在其平台具有原生线程投递语义时, + 覆盖优先级、后缀行为和线程 id 标准化。 - `openclaw/plugin-sdk/thread-bindings-runtime`,用于线程绑定生命周期 和适配器注册 -- `openclaw/plugin-sdk/agent-media-payload`,仅在仍需要旧版智能体 / 媒体 +- `openclaw/plugin-sdk/agent-media-payload`,仅在仍然需要旧版 agent/media 负载字段布局时使用 - `openclaw/plugin-sdk/telegram-command-config`,用于 Telegram 自定义命令 - 规范化、重复 / 冲突验证,以及具备稳定回退能力的命令 + 标准化、重复/冲突校验,以及回退稳定的命令 配置契约 -仅 auth 的渠道通常可以止步于默认路径:核心负责批准,插件只需暴露出站 / auth 能力。像 Matrix、Slack、Telegram 以及自定义聊天传输这类原生批准渠道,应使用共享的原生辅助函数,而不是自行实现批准生命周期。 +仅认证型渠道通常可以停留在默认路径:core 处理批准,而插件只需暴露出站/认证能力。像 Matrix、Slack、Telegram 和自定义聊天传输这样的原生批准渠道,应使用共享的原生 helper,而不是自行实现批准生命周期。 ## 入站提及策略 请将入站提及处理拆分为两层: - 由插件负责的证据收集 -- 共享的策略评估 +- 共享策略评估 -请使用 `openclaw/plugin-sdk/channel-mention-gating` 进行提及策略决策。 -只有当你需要更宽泛的入站辅助函数集合时,才使用 `openclaw/plugin-sdk/channel-inbound`。 +提及策略决策请使用 `openclaw/plugin-sdk/channel-mention-gating`。 +只有在你需要更宽泛的入站 +helper barrel 时,才使用 `openclaw/plugin-sdk/channel-inbound`。 适合放在插件本地逻辑中的内容: -- 回复给机器人的检测 -- 引用机器人的检测 -- 线程参与情况检查 -- 服务 / 系统消息排除 -- 用于证明机器人参与情况的平台原生缓存 +- 回复机器人检测 +- 引用机器人检测 +- 线程参与检查 +- 服务/系统消息排除 +- 用于证明机器人参与的平台原生缓存 -适合放在共享辅助函数中的内容: +适合放在共享 helper 中的内容: - `requireMention` - 显式提及结果 @@ -214,7 +188,7 @@ heartbeat 投递目标调用它,并使用共享的输入状态保活 / 清理 推荐流程: 1. 计算本地提及事实。 -2. 将这些事实传给 `resolveInboundMentionDecision({ facts, policy })`。 +2. 将这些事实传入 `resolveInboundMentionDecision({ facts, policy })`。 3. 在你的入站门控中使用 `decision.effectiveWasMentioned`、`decision.shouldBypassMention` 和 `decision.shouldSkip`。 ```typescript @@ -254,7 +228,8 @@ const decision = resolveInboundMentionDecision({ if (decision.shouldSkip) return; ``` -`api.runtime.channel.mentions` 为已经依赖运行时注入的内置渠道插件暴露了同一组共享提及辅助函数: +对于已经依赖运行时注入的内置渠道插件, +`api.runtime.channel.mentions` 暴露了同一组共享提及 helper: - `buildMentionRegexes` - `matchesMentionPatterns` @@ -265,20 +240,20 @@ if (decision.shouldSkip) return; 如果你只需要 `implicitMentionKindWhen` 和 `resolveInboundMentionDecision`,请从 `openclaw/plugin-sdk/channel-mention-gating` 导入,以避免加载无关的入站 -运行时辅助函数。 +运行时 helper。 -旧版 `resolveMentionGating*` 辅助函数仍保留在 -`openclaw/plugin-sdk/channel-inbound` 中,但仅作为兼容性导出。 -新代码应使用 `resolveInboundMentionDecision({ facts, policy })`。 +较旧的 `resolveMentionGating*` helper 仍保留在 +`openclaw/plugin-sdk/channel-inbound` 上,但仅作为兼容导出。新代码 +应使用 `resolveInboundMentionDecision({ facts, policy })`。 ## 演练 - + 创建标准插件文件。`package.json` 中的 `channel` 字段 - 决定它是一个渠道插件。有关完整的包元数据接口, - 请参阅 [插件设置与配置](/zh-CN/plugins/sdk-setup#openclaw-channel): + 使其成为一个渠道插件。有关完整的包元数据接口, + 请参阅 [插件设置和配置](/zh-CN/plugins/sdk-setup#openclaw-channel): ```json package.json @@ -328,7 +303,7 @@ if (decision.shouldSkip) return; - `ChannelPlugin` 接口有许多可选适配器接口。请从最小集合开始—— + `ChannelPlugin` 接口包含许多可选的适配器接口。从最小集合开始—— `id` 和 `setup`——然后按需添加适配器。 创建 `src/channel.ts`: @@ -424,23 +399,24 @@ if (decision.shouldSkip) return; }); ``` - + 你无需手动实现底层适配器接口,而是传入声明式选项, - 由构建器负责组合它们: + 由 builder 负责组合它们: - | 选项 | 它接线的内容 | + | Option | What it wires | | --- | --- | - | `security.dm` | 根据配置字段解析带作用域的私信安全策略 | - | `pairing.text` | 基于文本代码交换的私信配对流程 | - | `threading` | reply-to 模式解析器(固定、账户作用域或自定义) | - | `outbound.attachedResults` | 返回结果元数据(消息 ID)的发送函数 | + | `security.dm` | Scoped DM security resolver from config fields | + | `pairing.text` | Text-based DM pairing flow with code exchange | + | `threading` | Reply-to-mode resolver (fixed, account-scoped, or custom) | + | `outbound.attachedResults` | Send functions that return result metadata (message IDs) | - 如果你需要完全控制,也可以传入原始适配器对象,而不是这些声明式选项。 + 如果你需要完全控制,也可以传入原始适配器对象, + 而不是声明式选项。 - + 创建 `index.ts`: ```typescript index.ts @@ -476,21 +452,21 @@ if (decision.shouldSkip) return; }); ``` - 请将渠道自有的 CLI 描述符放在 `registerCliMetadata(...)` 中,这样 OpenClaw - 就能在不激活完整渠道运行时的情况下,在根帮助中显示它们; - 而正常的完整加载仍会接入同一组描述符,用于真实命令注册。 - `registerFull(...)` 仍用于仅运行时工作。 - 如果 `registerFull(...)` 注册了 Gateway 网关 RPC 方法,请使用 - 插件专属前缀。核心管理命名空间(`config.*`、 - `exec.approvals.*`、`wizard.*`、`update.*`)是保留的,并始终 - 解析到 `operator.admin`。 + 将渠道自有的 CLI 描述符放在 `registerCliMetadata(...)` 中,这样 OpenClaw + 就可以在不激活完整渠道运行时的情况下,在根帮助中显示它们; + 同时常规完整加载仍会获取相同描述符,用于真实命令注册。 + `registerFull(...)` 保留给仅运行时的工作。 + 如果 `registerFull(...)` 注册 Gateway 网关 RPC 方法,请使用 + 插件专属前缀。core 管理命名空间(`config.*`、 + `exec.approvals.*`、`wizard.*`、`update.*`)保持保留, + 并始终解析到 `operator.admin`。 `defineChannelPluginEntry` 会自动处理注册模式拆分。有关全部 - 选项,请参阅 [入口点](/zh-CN/plugins/sdk-entrypoints#definechannelpluginentry)。 + 选项,请参见 [入口点](/zh-CN/plugins/sdk-entrypoints#definechannelpluginentry)。 - 创建 `setup-entry.ts`,用于在新手引导期间进行轻量加载: + 创建 `setup-entry.ts`,用于新手引导期间的轻量加载: ```typescript setup-entry.ts import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core"; @@ -500,20 +476,21 @@ if (decision.shouldSkip) return; ``` 当渠道被禁用或尚未配置时,OpenClaw 会加载这个入口,而不是完整入口。 - 这样可以避免在设置流程中拉入沉重的运行时代码。 - 详情请参阅 [设置与配置](/zh-CN/plugins/sdk-setup#setup-entry)。 + 这可以避免在设置流程中拉入沉重的运行时代码。 + 详情请参见 [设置和配置](/zh-CN/plugins/sdk-setup#setup-entry)。 将设置安全导出拆分到 sidecar - 模块中的内置工作区渠道,也可以使用 + 模块中的内置工作区渠道,如果还需要显式的 setup 时运行时 setter, + 可以使用 `openclaw/plugin-sdk/channel-entry-contract` 中的 - `defineBundledChannelSetupEntry(...)`,特别是当它们还需要一个 - 显式的设置时运行时 setter 时。 + `defineBundledChannelSetupEntry(...)`。 你的插件需要从平台接收消息,并将其转发给 - OpenClaw。典型模式是使用 webhook 验证请求,并通过你渠道的入站处理器进行分发: + OpenClaw。典型模式是使用 webhook 验证请求,然后通过你的渠道入站处理器 + 进行分发: ```typescript registerFull(api) { @@ -537,16 +514,16 @@ if (decision.shouldSkip) return; ``` - 入站消息处理是渠道特定的。每个渠道插件都拥有 - 自己的入站流程。请查看内置渠道插件 - (例如 Microsoft Teams 或 Google Chat 插件包)以了解真实模式。 + 入站消息处理是渠道特定的。每个渠道插件都负责 + 自己的入站处理流水线。请查看内置渠道插件 + (例如 Microsoft Teams 或 Google Chat 插件包)了解真实模式。 -编写同目录测试到 `src/channel.test.ts`: +在 `src/channel.test.ts` 中编写同目录测试: ```typescript src/channel.test.ts import { describe, it, expect } from "vitest"; @@ -584,7 +561,7 @@ if (decision.shouldSkip) return; pnpm test -- /acme-chat/ ``` - 有关共享测试辅助函数,请参阅 [测试](/zh-CN/plugins/sdk-testing)。 + 有关共享测试 helper,请参见 [测试](/zh-CN/plugins/sdk-testing)。 @@ -609,33 +586,33 @@ if (decision.shouldSkip) return; ## 高级主题 - - 固定、账户作用域或自定义回复模式 + + 固定、账号作用域或自定义回复模式 - describeMessageTool 与动作发现 + describeMessageTool 和 action 发现 - + inferTargetChatType、looksLikeId、resolveTarget - - 通过 api.runtime 使用 TTS、STT、媒体、子智能体 + + 通过 api.runtime 使用 TTS、STT、媒体、subagent -某些内置辅助扩展缝隙仍然存在,用于内置插件维护和 -兼容性。它们不是构建新渠道插件的推荐模式; -除非你正在直接维护该内置插件家族,否则请优先使用来自通用 SDK -接口的通用 channel / setup / reply / runtime 子路径。 +一些内置 helper 接口仍然存在,用于内置插件维护和 +兼容性。它们不是新渠道插件的推荐模式; +除非你是在直接维护该内置插件家族,否则请优先使用通用 SDK +接口中的 channel/setup/reply/runtime 子路径。 ## 后续步骤 -- [提供商插件](/zh-CN/plugins/sdk-provider-plugins) — 如果你的插件还提供模型 +- [提供商插件](/zh-CN/plugins/sdk-provider-plugins) — 如果你的插件也提供模型 - [SDK 概览](/zh-CN/plugins/sdk-overview) — 完整子路径导入参考 - [插件 SDK 测试](/zh-CN/plugins/sdk-testing) — 测试工具和契约测试 -- [插件清单](/zh-CN/plugins/manifest) — 完整 manifest schema +- [插件 Manifest](/zh-CN/plugins/manifest) — 完整 manifest schema ## 相关内容 diff --git a/docs/zh-CN/plugins/sdk-provider-plugins.md b/docs/zh-CN/plugins/sdk-provider-plugins.md index e82ea0db9..59f47c171 100644 --- a/docs/zh-CN/plugins/sdk-provider-plugins.md +++ b/docs/zh-CN/plugins/sdk-provider-plugins.md @@ -1,35 +1,35 @@ --- read_when: - 你正在构建一个新的模型提供商插件 - - 你想将一个 OpenAI 兼容代理或自定义 LLM 添加到 OpenClaw 中 - - 你需要了解提供商的身份验证、目录和运行时 hook + - 你想为 OpenClaw 添加一个兼容 OpenAI 的代理或自定义 LLM + - 你需要了解提供商认证、目录和运行时钩子 sidebarTitle: Provider plugins summary: 构建 OpenClaw 模型提供商插件的分步指南 title: 构建提供商插件 x-i18n: - generated_at: "2026-04-23T23:00:47Z" + generated_at: "2026-04-24T03:07:21Z" model: gpt-5.4 provider: openai - source_hash: d92a91da77880a685f4bcdf9fb20fca339de72d08ed6ff8541dc09120d33f243 + source_hash: bef17d1e9944f041c29a578ceab20835d82c8e846a401048676211237fdbc499 source_path: plugins/sdk-provider-plugins.md workflow: 15 --- -本指南将带你构建一个提供商插件,为 OpenClaw 添加一个模型提供商(LLM)。完成后,你将拥有一个具备模型目录、API 密钥身份验证和动态模型解析的提供商。 +本指南将带你逐步构建一个提供商插件,为 OpenClaw 添加一个模型提供商(LLM)。完成后,你将拥有一个带有模型目录、API key 认证和动态模型解析能力的提供商。 - 如果你还没有构建过任何 OpenClaw 插件,请先阅读 - [入门指南](/zh-CN/plugins/building-plugins),了解基础包结构和 manifest 设置。 + 如果你此前还没有构建过任何 OpenClaw 插件,请先阅读 + [入门指南](/zh-CN/plugins/building-plugins),了解基本的包结构和清单设置。 - 提供商插件会将模型接入 OpenClaw 的常规推理循环。如果该模型必须通过一个拥有线程、压缩或工具事件的原生智能体守护进程运行,请将该提供商与 [agent harness](/zh-CN/plugins/sdk-agent-harness) 配合使用,而不要把守护进程协议细节放入核心中。 + 提供商插件会将模型接入 OpenClaw 的常规推理循环中。如果模型必须通过一个原生智能体守护进程运行,并且该守护进程负责线程、压缩或工具事件,请将该提供商与 [agent harness](/zh-CN/plugins/sdk-agent-harness) 搭配使用,而不是把守护进程协议细节放进核心中。 -## 教程步骤 +## 演练 - + ```json package.json { @@ -87,8 +87,7 @@ x-i18n: ``` - manifest 声明了 `providerAuthEnvVars`,这样 OpenClaw 就可以在不加载你的插件运行时的情况下检测凭证。当某个提供商变体应复用另一个提供商 ID 的身份验证时,请添加 `providerAuthAliases`。`modelSupport` - 是可选项,它允许 OpenClaw 在运行时 hook 存在之前,就能从像 `acme-large` 这样的简写模型 ID 自动加载你的提供商插件。如果你要在 ClawHub 上发布该提供商,那么 `package.json` 中的这些 `openclaw.compat` 和 `openclaw.build` 字段是必需的。 + 该清单声明了 `providerAuthEnvVars`,这样 OpenClaw 就可以在不加载你的插件运行时的情况下检测凭证。当某个提供商变体应复用另一个提供商 id 的认证时,请添加 `providerAuthAliases`。`modelSupport` 是可选项,它允许 OpenClaw 在运行时钩子存在之前,就能根据像 `acme-large` 这样的简写模型 id 自动加载你的提供商插件。如果你在 ClawHub 上发布该提供商,那么 `package.json` 中的这些 `openclaw.compat` 和 `openclaw.build` 字段是必需的。 @@ -164,11 +163,11 @@ x-i18n: }); ``` - 这就是一个可用的提供商。用户现在可以执行 + 这就是一个可工作的提供商。用户现在可以执行 `openclaw onboard --acme-ai-api-key `,并选择 - `acme-ai/acme-large` 作为模型。 + `acme-ai/acme-large` 作为他们的模型。 - 如果上游提供商使用的控制标记与 OpenClaw 不同,请添加一个小型双向文本转换,而不是替换流路径: + 如果上游提供商使用的控制 token 与 OpenClaw 不同,请添加一个小型的双向文本转换,而不是替换流式路径: ```typescript api.registerTextTransforms({ @@ -185,10 +184,10 @@ x-i18n: }); ``` - `input` 会在传输前重写最终系统提示和文本消息内容。`output` 会在 OpenClaw 解析其自身控制标记或执行渠道投递前,重写助手文本增量和最终文本。 + `input` 会在传输之前,重写最终的系统提示词和文本消息内容。 + `output` 会在 OpenClaw 解析其自身控制标记或进行渠道投递之前,重写助手文本增量和最终文本。 - 对于仅注册一个文本提供商、使用 API 密钥身份验证并且只带有一个由 catalog 支撑的运行时的内置提供商,请优先使用更窄的 - `defineSingleProviderPluginEntry(...)` 辅助工具: + 对于只注册一个文本提供商、使用 API key 认证并且只有一个由 catalog 支持的运行时的内置提供商,优先使用更窄的 `defineSingleProviderPluginEntry(...)` 辅助工具: ```typescript import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry"; @@ -228,25 +227,19 @@ x-i18n: }); ``` - `buildProvider` 是 OpenClaw 能解析真实提供商身份验证时所使用的实时 catalog 路径。它可以执行提供商专属发现。 - `buildStaticProvider` 仅用于在配置身份验证前可安全显示的离线条目;它不得要求凭证,也不得发起网络请求。 - OpenClaw 的 `models list --all` 当前仅对内置提供商插件执行静态 catalog,并且执行环境是空配置、空环境,以及无 agent/workspace 路径。 + `buildProvider` 是实时 catalog 路径,当 OpenClaw 能够解析出真实的提供商认证信息时会使用它。它可以执行提供商特定的发现逻辑。只有在需要显示认证配置之前就可以安全展示的离线条目时,才使用 `buildStaticProvider`;它不得要求凭证,也不得发起网络请求。OpenClaw 当前的 `models list --all` 显示仅会对内置提供商插件执行静态 catalog,并且运行时使用空配置、空环境以及没有 agent / workspace 路径的上下文。 - 如果你的身份验证流程还需要在 onboarding 期间修补 `models.providers.*`、别名以及智能体默认模型,请使用 - `openclaw/plugin-sdk/provider-onboard` 中的 preset 辅助工具。最窄的辅助工具包括 + 如果你的认证流程在新手引导期间还需要修补 `models.providers.*`、别名以及智能体默认模型,请使用 `openclaw/plugin-sdk/provider-onboard` 中的 preset 辅助工具。最窄的辅助工具包括 `createDefaultModelPresetAppliers(...)`、 `createDefaultModelsPresetAppliers(...)` 和 `createModelCatalogPresetAppliers(...)`。 - 当某个提供商的原生端点在正常 `openai-completions` 传输上支持流式 usage 块时,请优先使用 - `openclaw/plugin-sdk/provider-catalog-shared` 中的共享 catalog 辅助工具,而不是硬编码 provider-id 检查。 - `supportsNativeStreamingUsageCompat(...)` 和 - `applyProviderNativeStreamingUsageCompat(...)` 会从端点能力映射中检测支持情况,因此即使插件使用的是自定义 provider ID,原生 Moonshot/DashScope 风格端点也仍可选择加入。 + 当某个提供商的原生端点在正常的 `openai-completions` 传输上支持分块流式传输 usage block 时,优先使用 `openclaw/plugin-sdk/provider-catalog-shared` 中的共享 catalog 辅助工具,而不是硬编码提供商 id 检查。`supportsNativeStreamingUsageCompat(...)` 和 `applyProviderNativeStreamingUsageCompat(...)` 会根据端点能力映射来检测支持情况,因此即便插件使用的是自定义提供商 id,原生 Moonshot AI / DashScope 风格的端点仍然可以选择启用。 - 如果你的提供商接受任意模型 ID(例如代理或路由器),请添加 `resolveDynamicModel`: + 如果你的提供商接受任意模型 id(例如代理或路由器),请添加 `resolveDynamicModel`: ```typescript api.registerProvider({ @@ -267,14 +260,14 @@ x-i18n: }); ``` - 如果解析需要网络调用,请使用 `prepareDynamicModel` 进行异步预热——完成后会再次运行 `resolveDynamicModel`。 + 如果解析需要网络调用,请使用 `prepareDynamicModel` 进行异步预热 —— 在它完成后,`resolveDynamicModel` 会再次运行。 - - 大多数提供商只需要 `catalog` + `resolveDynamicModel`。只有在你的提供商确实需要时,再逐步添加 hook。 + + 大多数提供商只需要 `catalog` + `resolveDynamicModel`。随着你的提供商需求增加,再逐步添加钩子。 - 共享辅助构建器现在已覆盖最常见的 replay/tool-compat 家族,因此插件通常不需要再手动逐个连接每个 hook: + 共享辅助构建器现在已经覆盖了最常见的 replay / 工具兼容系列,因此插件通常不需要再手动逐个接入每个钩子: ```typescript import { buildProviderReplayFamilyHooks } from "openclaw/plugin-sdk/provider-model-shared"; @@ -294,43 +287,43 @@ x-i18n: }); ``` - 当前可用的 replay 家族: + 当前可用的 replay 系列: - | 家族 | 它接入的内容 | 内置示例 | + | 系列 | 它会接入的内容 | 内置示例 | | --- | --- | --- | - | `openai-compatible` | 面向 OpenAI 兼容传输的共享 OpenAI 风格 replay 策略,包括工具调用 ID 清理、assistant-first 顺序修复,以及在传输需要时进行通用 Gemini 轮次校验 | `moonshot`, `ollama`, `xai`, `zai` | - | `anthropic-by-model` | 按 `modelId` 选择的 Claude 感知 replay 策略,因此只有当解析出的模型实际是 Claude ID 时,Anthropic message 传输才会获得 Claude 专属的 thinking block 清理 | `amazon-bedrock`, `anthropic-vertex` | - | `google-gemini` | 原生 Gemini replay 策略,加上 bootstrap replay 清理和带标签的推理输出模式 | `google`, `google-gemini-cli` | - | `passthrough-gemini` | 面向通过 OpenAI 兼容代理传输运行的 Gemini 模型的 Gemini thought-signature 清理;不会启用原生 Gemini replay 校验或 bootstrap 重写 | `openrouter`, `kilocode`, `opencode`, `opencode-go` | - | `hybrid-anthropic-openai` | 用于在一个插件中混合 Anthropic message 和 OpenAI 兼容模型界面的提供商的混合策略;可选的 Claude 专属 thinking block 丢弃仍仅作用于 Anthropic 一侧 | `minimax` | + | `openai-compatible` | 用于兼容 OpenAI 传输的共享 OpenAI 风格 replay 策略,包括 tool-call-id 清理、assistant-first 顺序修正,以及传输需要时的通用 Gemini turn 校验 | `moonshot`, `ollama`, `xai`, `zai` | + | `anthropic-by-model` | 按 `modelId` 选择具备 Claude 感知能力的 replay 策略,因此只有当解析后的模型实际是 Claude id 时,Anthropic-message 传输才会获得 Claude 特定的 thinking block 清理 | `amazon-bedrock`, `anthropic-vertex` | + | `google-gemini` | 原生 Gemini replay 策略,加上 bootstrap replay 清理和带标签的 reasoning-output 模式 | `google`, `google-gemini-cli` | + | `passthrough-gemini` | 为通过兼容 OpenAI 的代理传输运行的 Gemini 模型提供 Gemini thought-signature 清理;不会启用原生 Gemini replay 校验或 bootstrap 重写 | `openrouter`, `kilocode`, `opencode`, `opencode-go` | + | `hybrid-anthropic-openai` | 适用于在同一个插件中混合 Anthropic-message 和兼容 OpenAI 模型表面的提供商的混合策略;可选的仅 Claude thinking block 丢弃会保持在 Anthropic 一侧生效 | `minimax` | - 当前可用的 stream 家族: + 当前可用的 stream 系列: - | 家族 | 它接入的内容 | 内置示例 | + | 系列 | 它会接入的内容 | 内置示例 | | --- | --- | --- | - | `google-thinking` | 在共享流路径上对 Gemini thinking 负载进行标准化 | `google`, `google-gemini-cli` | - | `kilocode-thinking` | 在共享代理流路径上为 Kilo 推理提供包装器,并在 `kilo/auto` 和不支持的代理推理 ID 情况下跳过注入 thinking | `kilocode` | - | `moonshot-thinking` | 根据配置和 `/think` 级别对 Moonshot 二进制原生 thinking 负载进行映射 | `moonshot` | - | `minimax-fast-mode` | 在共享流路径上对 MiniMax fast mode 模型进行重写 | `minimax`, `minimax-portal` | - | `openai-responses-defaults` | 共享的原生 OpenAI/Codex Responses 包装器:归属头、`/fast`/`serviceTier`、文本详细度、原生 Codex web 搜索、reasoning 兼容负载整形,以及 Responses 上下文管理 | `openai`, `openai-codex` | - | `openrouter-thinking` | 面向代理路由的 OpenRouter 推理包装器,并集中处理不支持模型/`auto` 跳过逻辑 | `openrouter` | - | `tool-stream-default-on` | 对于像 Z.AI 这样除非显式禁用否则希望启用工具流式传输的提供商,默认开启 `tool_stream` 包装器 | `zai` | + | `google-thinking` | 在共享流式路径上对 Gemini thinking 载荷进行规范化 | `google`, `google-gemini-cli` | + | `kilocode-thinking` | 在共享代理流式路径上为 Kilo reasoning 提供包装器,其中 `kilo/auto` 和不受支持的代理 reasoning id 会跳过注入的 thinking | `kilocode` | + | `moonshot-thinking` | 根据配置和 `/think` 级别对 Moonshot AI 二进制 native-thinking 载荷进行映射 | `moonshot` | + | `minimax-fast-mode` | 在共享流式路径上对 MiniMax fast-mode 模型进行重写 | `minimax`, `minimax-portal` | + | `openai-responses-defaults` | 共享的原生 OpenAI / Codex Responses 包装器:归属头、`/fast` / `serviceTier`、文本详细度、原生 Codex web search、reasoning-compat 载荷整形,以及 Responses 上下文管理 | `openai`, `openai-codex` | + | `openrouter-thinking` | 面向代理路由的 OpenRouter reasoning 包装器,并集中处理 unsupported-model / `auto` 跳过逻辑 | `openrouter` | + | `tool-stream-default-on` | 面向像 Z.AI 这类希望默认启用 tool streaming、除非被显式禁用的提供商的默认开启 `tool_stream` 包装器 | `zai` | - - 每个 family builder 都由同一包中导出的更底层公共辅助工具组合而成。当某个提供商需要偏离常见模式时,你可以直接使用这些工具: + + 每个 family builder 都由同一包中导出的更底层公共辅助工具组合而成;当某个提供商需要偏离通用模式时,你可以直接使用这些工具: - - `openclaw/plugin-sdk/provider-model-shared` — `ProviderReplayFamily`、`buildProviderReplayFamilyHooks(...)`,以及原始 replay builder(`buildOpenAICompatibleReplayPolicy`、`buildAnthropicReplayPolicyForModel`、`buildGoogleGeminiReplayPolicy`、`buildHybridAnthropicOrOpenAIReplayPolicy`)。还导出 Gemini replay 辅助工具(`sanitizeGoogleGeminiReplayHistory`、`resolveTaggedReasoningOutputMode`)以及 endpoint/model 辅助工具(`resolveProviderEndpoint`、`normalizeProviderId`、`normalizeGooglePreviewModelId`、`normalizeNativeXaiModelId`)。 - - `openclaw/plugin-sdk/provider-stream` — `ProviderStreamFamily`、`buildProviderStreamFamilyHooks(...)`、`composeProviderStreamWrappers(...)`,以及共享的 OpenAI/Codex 包装器(`createOpenAIAttributionHeadersWrapper`、`createOpenAIFastModeWrapper`、`createOpenAIServiceTierWrapper`、`createOpenAIResponsesContextManagementWrapper`、`createCodexNativeWebSearchWrapper`)和共享的代理/提供商包装器(`createOpenRouterWrapper`、`createToolStreamWrapper`、`createMinimaxFastModeWrapper`)。 - - `openclaw/plugin-sdk/provider-tools` — `ProviderToolCompatFamily`、`buildProviderToolCompatFamilyHooks("gemini")`、底层 Gemini 模式辅助工具(`normalizeGeminiToolSchemas`、`inspectGeminiToolSchemas`),以及 xAI 兼容辅助工具(`resolveXaiModelCompatPatch()`、`applyXaiModelCompat(model)`)。内置 xAI 插件会配合这些工具使用 `normalizeResolvedModel` + `contributeResolvedModelCompat`,以确保 xAI 规则由该提供商自行维护。 + - `openclaw/plugin-sdk/provider-model-shared` — `ProviderReplayFamily`、`buildProviderReplayFamilyHooks(...)` 以及原始 replay builder(`buildOpenAICompatibleReplayPolicy`、`buildAnthropicReplayPolicyForModel`、`buildGoogleGeminiReplayPolicy`、`buildHybridAnthropicOrOpenAIReplayPolicy`)。还导出 Gemini replay 辅助工具(`sanitizeGoogleGeminiReplayHistory`、`resolveTaggedReasoningOutputMode`)以及端点 / 模型辅助工具(`resolveProviderEndpoint`、`normalizeProviderId`、`normalizeGooglePreviewModelId`、`normalizeNativeXaiModelId`)。 + - `openclaw/plugin-sdk/provider-stream` — `ProviderStreamFamily`、`buildProviderStreamFamilyHooks(...)`、`composeProviderStreamWrappers(...)`,以及共享的 OpenAI / Codex 包装器(`createOpenAIAttributionHeadersWrapper`、`createOpenAIFastModeWrapper`、`createOpenAIServiceTierWrapper`、`createOpenAIResponsesContextManagementWrapper`、`createCodexNativeWebSearchWrapper`)和共享的代理 / 提供商包装器(`createOpenRouterWrapper`、`createToolStreamWrapper`、`createMinimaxFastModeWrapper`)。 + - `openclaw/plugin-sdk/provider-tools` — `ProviderToolCompatFamily`、`buildProviderToolCompatFamilyHooks("gemini")`、底层 Gemini schema 辅助工具(`normalizeGeminiToolSchemas`、`inspectGeminiToolSchemas`)以及 xAI 兼容辅助工具(`resolveXaiModelCompatPatch()`、`applyXaiModelCompat(model)`)。内置的 xAI 插件将它们与 `normalizeResolvedModel` + `contributeResolvedModelCompat` 配合使用,从而使 xAI 规则仍由该提供商负责维护。 - 一些流辅助工具有意保留在 provider 本地。`@openclaw/anthropic-provider` 将 `wrapAnthropicProviderStream`、`resolveAnthropicBetas`、`resolveAnthropicFastMode`、`resolveAnthropicServiceTier`,以及更底层的 Anthropic 包装器构建器保留在其自己的公共 `api.ts` / `contract-api.ts` 接缝中,因为它们编码了 Claude OAuth beta 处理和 `context1m` 门控。xAI 插件同样将原生 xAI Responses 整形保留在其自己的 `wrapStreamFn` 中(`/fast` 别名、默认 `tool_stream`、不支持的严格工具清理、xAI 专属 reasoning 负载移除)。 + 某些 stream 辅助工具会有意保持为提供商本地实现。`@openclaw/anthropic-provider` 将 `wrapAnthropicProviderStream`、`resolveAnthropicBetas`、`resolveAnthropicFastMode`、`resolveAnthropicServiceTier` 以及更底层的 Anthropic 包装器 builder 保留在它自己的公共 `api.ts` / `contract-api.ts` 接缝中,因为它们编码了 Claude OAuth beta 处理和 `context1m` 门控。xAI 插件同样将原生 xAI Responses 整形保留在它自己的 `wrapStreamFn` 中(`/fast` 别名、默认 `tool_stream`、不受支持的 strict-tool 清理、xAI 特定的 reasoning-payload 移除)。 - 同样的包根模式也支撑着 `@openclaw/openai-provider`(provider 构建器、默认模型辅助工具、实时 provider 构建器)和 `@openclaw/openrouter-provider`(provider 构建器以及 onboarding/配置辅助工具)。 + 相同的包根模式也为 `@openclaw/openai-provider`(provider builder、default-model 辅助工具、realtime provider builder)和 `@openclaw/openrouter-provider`(provider builder 以及 onboarding / config 辅助工具)提供支持。 - - 对于需要在每次推理调用前执行令牌交换的提供商: + + 对于那些在每次推理调用前都需要进行 token 交换的提供商: ```typescript prepareRuntimeAuth: async (ctx) => { @@ -344,7 +337,7 @@ x-i18n: ``` - 对于需要自定义请求头或请求体修改的提供商: + 对于那些需要自定义请求头或请求体修改的提供商: ```typescript // wrapStreamFn returns a StreamFn derived from ctx.streamFn @@ -361,8 +354,8 @@ x-i18n: }, ``` - - 对于在通用 HTTP 或 WebSocket 传输上需要原生请求/会话头或元数据的提供商: + + 对于那些在通用 HTTP 或 WebSocket 传输上需要原生请求 / 会话头或元数据的提供商: ```typescript resolveTransportTurnState: (ctx) => ({ @@ -382,8 +375,8 @@ x-i18n: }), ``` - - 对于暴露用量/计费数据的提供商: + + 对于那些暴露 usage / 计费数据的提供商: ```typescript resolveUsageAuth: async (ctx) => { @@ -397,73 +390,73 @@ x-i18n: - - OpenClaw 按以下顺序调用 hook。大多数提供商只会用到 2-3 个: + + OpenClaw 会按以下顺序调用钩子。大多数提供商只会使用其中 2-3 个: - | # | Hook | 适用场景 | + | # | Hook | 何时使用 | | --- | --- | --- | - | 1 | `catalog` | 模型目录或 `baseUrl` 默认值 | - | 2 | `applyConfigDefaults` | 在配置实例化期间应用由提供商自有的全局默认值 | - | 3 | `normalizeModelId` | 在查找前清理旧版/预览版模型 ID 别名 | - | 4 | `normalizeTransport` | 在通用模型组装前清理 provider family 的 `api` / `baseUrl` | - | 5 | `normalizeConfig` | 标准化 `models.providers.` 配置 | - | 6 | `applyNativeStreamingUsageCompat` | 为配置提供商重写原生流式用量兼容性 | - | 7 | `resolveConfigApiKey` | 提供商自有的环境标记身份验证解析 | - | 8 | `resolveSyntheticAuth` | 本地/自托管或基于配置的合成身份验证 | - | 9 | `shouldDeferSyntheticProfileAuth` | 将合成存储配置文件占位符排在环境/配置身份验证之后 | - | 10 | `resolveDynamicModel` | 接受任意上游模型 ID | + | 1 | `catalog` | 模型目录或 base URL 默认值 | + | 2 | `applyConfigDefaults` | 在配置实体化期间应用由提供商负责的全局默认值 | + | 3 | `normalizeModelId` | 在查找之前清理旧版 / 预览模型 id 别名 | + | 4 | `normalizeTransport` | 在通用模型组装之前清理提供商系列的 `api` / `baseUrl` | + | 5 | `normalizeConfig` | 规范化 `models.providers.` 配置 | + | 6 | `applyNativeStreamingUsageCompat` | 为配置提供商执行原生 streaming-usage compat 重写 | + | 7 | `resolveConfigApiKey` | 由提供商负责的环境标记认证解析 | + | 8 | `resolveSyntheticAuth` | 本地 / 自托管或基于配置的 synthetic 认证 | + | 9 | `shouldDeferSyntheticProfileAuth` | 在 env / config 认证之后降低 synthetic 已存储 profile 占位符的优先级 | + | 10 | `resolveDynamicModel` | 接受任意上游模型 id | | 11 | `prepareDynamicModel` | 在解析前异步获取元数据 | - | 12 | `normalizeResolvedModel` | 在 runner 前重写传输 | - | 13 | `contributeResolvedModelCompat` | 为运行在另一兼容传输后的厂商模型提供兼容标志 | - | 14 | `capabilities` | 旧版静态能力包;仅为兼容性保留 | - | 15 | `normalizeToolSchemas` | 在注册前清理由提供商拥有的工具模式 | - | 16 | `inspectToolSchemas` | 由提供商拥有的工具模式诊断 | - | 17 | `resolveReasoningOutputMode` | 带标签与原生 reasoning-output 契约 | + | 12 | `normalizeResolvedModel` | 在 runner 之前进行传输重写 | + | 13 | `contributeResolvedModelCompat` | 为运行在另一种兼容传输上的厂商模型提供 compat 标志 | + | 14 | `capabilities` | 旧版静态 capability 包;仅用于兼容性 | + | 15 | `normalizeToolSchemas` | 在注册前对由提供商负责的 tool schema 进行清理 | + | 16 | `inspectToolSchemas` | 由提供商负责的 tool schema 诊断 | + | 17 | `resolveReasoningOutputMode` | 带标签还是原生的 reasoning-output 契约 | | 18 | `prepareExtraParams` | 默认请求参数 | - | 19 | `createStreamFn` | 完全自定义的 `StreamFn` 传输 | - | 20 | `wrapStreamFn` | 在正常流路径上包装自定义头/请求体 | - | 21 | `resolveTransportTurnState` | 原生的逐轮请求头/元数据 | - | 22 | `resolveWebSocketSessionPolicy` | 原生 WS 会话头/降级冷却 | - | 23 | `formatApiKey` | 自定义运行时令牌形态 | + | 19 | `createStreamFn` | 完全自定义的 StreamFn 传输 | + | 20 | `wrapStreamFn` | 在正常流式路径上添加自定义请求头 / 请求体包装器 | + | 21 | `resolveTransportTurnState` | 原生的逐轮请求头 / 元数据 | + | 22 | `resolveWebSocketSessionPolicy` | 原生 WS 会话头 / 冷却时间 | + | 23 | `formatApiKey` | 自定义运行时 token 形态 | | 24 | `refreshOAuth` | 自定义 OAuth 刷新 | - | 25 | `buildAuthDoctorHint` | 身份验证修复指导 | - | 26 | `matchesContextOverflowError` | 由提供商自有的上下文溢出检测 | - | 27 | `classifyFailoverReason` | 由提供商自有的限流/过载分类 | - | 28 | `isCacheTtlEligible` | 提示缓存 TTL 门控 | - | 29 | `buildMissingAuthMessage` | 自定义缺失身份验证提示 | + | 25 | `buildAuthDoctorHint` | 认证修复指导 | + | 26 | `matchesContextOverflowError` | 由提供商负责的上下文溢出检测 | + | 27 | `classifyFailoverReason` | 由提供商负责的速率限制 / 过载分类 | + | 28 | `isCacheTtlEligible` | 提示词缓存 TTL 门控 | + | 29 | `buildMissingAuthMessage` | 自定义缺失认证提示 | | 30 | `suppressBuiltInModel` | 隐藏过时的上游条目 | - | 31 | `augmentModelCatalog` | 合成的前向兼容条目 | - | 32 | `resolveThinkingProfile` | 模型专属 `/think` 选项集 | - | 33 | `isBinaryThinking` | 二元 thinking 开/关兼容性 | - | 34 | `supportsXHighThinking` | `xhigh` 推理支持兼容性 | + | 31 | `augmentModelCatalog` | synthetic 前向兼容条目 | + | 32 | `resolveThinkingProfile` | 模型特定的 `/think` 选项集 | + | 33 | `isBinaryThinking` | 二进制 thinking 开 / 关兼容性 | + | 34 | `supportsXHighThinking` | `xhigh` reasoning 支持兼容性 | | 35 | `resolveDefaultThinkingLevel` | 默认 `/think` 策略兼容性 | - | 36 | `isModernModelRef` | 实时/冒烟模型匹配 | - | 37 | `prepareRuntimeAuth` | 推理前的令牌交换 | - | 38 | `resolveUsageAuth` | 自定义用量凭证解析 | - | 39 | `fetchUsageSnapshot` | 自定义用量端点 | - | 40 | `createEmbeddingProvider` | 由提供商拥有的 memory/search 嵌入适配器 | - | 41 | `buildReplayPolicy` | 自定义转录回放/压缩策略 | - | 42 | `sanitizeReplayHistory` | 在通用清理后执行提供商专属回放重写 | - | 43 | `validateReplayTurns` | 在嵌入式运行器前进行严格回放轮次校验 | - | 44 | `onModelSelected` | 选择后的回调(例如遥测) | + | 36 | `isModernModelRef` | live / smoke 模型匹配 | + | 37 | `prepareRuntimeAuth` | 推理前的 token 交换 | + | 38 | `resolveUsageAuth` | 自定义 usage 凭证解析 | + | 39 | `fetchUsageSnapshot` | 自定义 usage 端点 | + | 40 | `createEmbeddingProvider` | 由提供商负责的 embedding 适配器,用于 memory / search | + | 41 | `buildReplayPolicy` | 自定义 transcript replay / compaction 策略 | + | 42 | `sanitizeReplayHistory` | 在通用清理之后执行提供商特定的 replay 重写 | + | 43 | `validateReplayTurns` | 在嵌入式 runner 之前进行严格 replay turn 校验 | + | 44 | `onModelSelected` | 选择模型后的回调(例如 telemetry) | - 运行时回退说明: + 运行时后备说明: - - `normalizeConfig` 会先检查匹配的提供商,然后再检查其他具备 hook 能力的提供商插件,直到某个插件实际修改了配置为止。如果没有任何提供商 hook 重写某个受支持的 Google family 配置项,仍会应用内置的 Google 配置标准化器。 - - `resolveConfigApiKey` 在暴露相应 hook 时会使用提供商 hook。内置 `amazon-bedrock` 路径在这里也有一个内建 AWS 环境标记解析器,尽管 Bedrock 运行时身份验证本身仍使用 AWS SDK 默认链。 - - `resolveSystemPromptContribution` 允许提供商为某个模型 family 注入具备缓存感知能力的系统提示指导。若行为属于某个单一提供商/模型 family,并且应保留稳定/动态缓存拆分,请优先使用它,而不是 `before_prompt_build`。 + - `normalizeConfig` 会先检查匹配到的提供商,然后再检查其他具备 hook 能力的提供商插件,直到其中一个实际改动了配置为止。如果没有任何提供商钩子重写受支持的 Google 系列配置项,内置的 Google 配置规范化器仍会生效。 + - `resolveConfigApiKey` 在提供商暴露该 hook 时会使用它。内置的 `amazon-bedrock` 路径在这里也有一个内建的 AWS 环境标记解析器,尽管 Bedrock 运行时认证本身仍然使用 AWS SDK 默认链。 + - `resolveSystemPromptContribution` 允许提供商为某个模型系列注入具备缓存感知能力的 system prompt 指导。当某个行为属于单一提供商 / 模型系列并且应保留稳定 / 动态缓存拆分时,优先使用它,而不是 `before_prompt_build`。 - 有关详细说明和真实示例,请参阅 [Internals: Provider Runtime Hooks](/zh-CN/plugins/architecture#provider-runtime-hooks)。 + 如需详细说明和真实示例,请参阅 [内部实现:提供商运行时钩子](/zh-CN/plugins/architecture-internals#provider-runtime-hooks)。 - 提供商插件除了文本推理外,还可以同时注册语音、实时转写、实时语音、媒体理解、图像生成、视频生成、web 抓取和 web 搜索。OpenClaw 将其归类为 - **hybrid-capability** 插件——这是公司级插件的推荐模式(每个厂商一个插件)。参见 - [Internals: Capability Ownership](/zh-CN/plugins/architecture#capability-ownership-model)。 + 提供商插件除了文本推理之外,还可以注册语音、实时转录、实时语音、媒体理解、图像生成、视频生成、web 抓取和 web 搜索。OpenClaw 将其归类为 + **hybrid-capability** 插件 —— 这是公司级插件(每个厂商一个插件)的推荐模式。请参阅 + [内部实现:能力归属](/zh-CN/plugins/architecture#capability-ownership-model)。 - 在 `register(api)` 中,与现有的 + 在 `register(api)` 中、与你现有的 `api.registerProvider(...)` 调用一起注册每项能力。只选择你需要的标签页: @@ -482,9 +475,8 @@ x-i18n: }); ``` - - 优先使用 `createRealtimeTranscriptionWebSocketSession(...)`——该共享辅助工具会处理代理捕获、重连退避、关闭时刷新、就绪握手、音频排队和关闭事件诊断。 - 你的插件只需要映射上游事件。 + + 优先使用 `createRealtimeTranscriptionWebSocketSession(...)` —— 这个共享辅助工具会处理代理捕获、重连退避、关闭时 flush、ready 握手、音频排队以及 close 事件诊断。你的插件只需要映射上游事件。 ```typescript api.registerRealtimeTranscriptionProvider({ @@ -522,9 +514,10 @@ x-i18n: }); ``` - 对于通过 POST multipart 音频的批量 STT 提供商,请使用 + 对于通过 `POST multipart` 音频进行提交的批量 STT 提供商,应使用 `openclaw/plugin-sdk/provider-http` 中的 - `buildAudioTranscriptionFormData(...)`。该辅助工具会标准化上传文件名,包括那些需要使用 M4A 风格文件名才能兼容转写 API 的 AAC 上传。 + `buildAudioTranscriptionFormData(...)`。该辅助工具会规范化上传文件名, + 包括那些为了兼容转录 API 而需要使用 M4A 风格文件名的 AAC 上传。 ```typescript @@ -555,11 +548,12 @@ x-i18n: ``` - 视频能力使用**具备模式感知**的结构:`generate`、 + 视频能力使用一种**模式感知**结构:`generate`、 `imageToVideo` 和 `videoToVideo`。像 - `maxInputImages` / `maxInputVideos` / `maxDurationSeconds` 这样的扁平聚合字段,不足以清晰表达变换模式支持或已禁用模式。 - 音乐生成也遵循同样的模式,使用显式的 `generate` / - `edit` 块。 + `maxInputImages` / `maxInputVideos` / `maxDurationSeconds` 这样的扁平聚合字段, + 不足以清晰表达 transform 模式支持或已禁用模式。 + 音乐生成也遵循相同模式,使用显式的 `generate` / + `edit` 区块。 ```typescript api.registerImageGenerationProvider({ @@ -656,7 +650,7 @@ clawhub package publish your-org/your-plugin --dry-run clawhub package publish your-org/your-plugin ``` -这里不要使用旧版仅适用于 Skills 的发布别名;插件包应使用 +这里不要使用旧版仅 Skills 的发布别名;插件包应使用 `clawhub package publish`。 ## 文件结构 @@ -664,30 +658,30 @@ clawhub package publish your-org/your-plugin ``` /acme-ai/ ├── package.json # openclaw.providers metadata -├── openclaw.plugin.json # 包含 provider auth metadata 的 manifest +├── openclaw.plugin.json # Manifest with provider auth metadata ├── index.ts # definePluginEntry + registerProvider └── src/ - ├── provider.test.ts # 测试 - └── usage.ts # 用量端点(可选) + ├── provider.test.ts # Tests + └── usage.ts # Usage endpoint (optional) ``` ## Catalog 顺序参考 -`catalog.order` 控制你的 catalog 相对于内置提供商的合并时机: +`catalog.order` 控制你的 catalog 相对于内置提供商何时合并: | 顺序 | 时机 | 使用场景 | | --------- | ------------- | ----------------------------------------------- | -| `simple` | 第一轮 | 纯 API 密钥提供商 | -| `profile` | `simple` 之后 | 由 auth profiles 门控的提供商 | -| `paired` | `profile` 之后 | 合成多个相关条目 | +| `simple` | 第一轮 | 普通 API key 提供商 | +| `profile` | 在 simple 之后 | 依赖认证 profile 的提供商 | +| `paired` | 在 profile 之后 | 合成多个相关条目 | | `late` | 最后一轮 | 覆盖现有提供商(冲突时胜出) | -## 下一步 +## 后续步骤 -- [Channel Plugins](/zh-CN/plugins/sdk-channel-plugins) — 如果你的插件同时也提供渠道 -- [插件运行时](/zh-CN/plugins/sdk-runtime) — `api.runtime` 辅助工具(TTS、搜索、子智能体) -- [SDK 概览](/zh-CN/plugins/sdk-overview) — 完整子路径导入参考 -- [插件内部机制](/zh-CN/plugins/architecture#provider-runtime-hooks) — hook 细节和内置示例 +- [渠道插件](/zh-CN/plugins/sdk-channel-plugins) —— 如果你的插件也提供一个渠道 +- [SDK 运行时](/zh-CN/plugins/sdk-runtime) —— `api.runtime` 辅助工具(TTS、搜索、subagent) +- [SDK 概览](/zh-CN/plugins/sdk-overview) —— 完整的子路径导入参考 +- [插件内部实现](/zh-CN/plugins/architecture-internals#provider-runtime-hooks) —— 钩子细节和内置示例 ## 相关内容