chore(i18n): refresh zh-CN translations
This commit is contained in:
parent
344160c3af
commit
577fe157d3
@ -1,21 +1,21 @@
|
||||
---
|
||||
read_when:
|
||||
- 调度后台作业或唤醒任务
|
||||
- 安排后台作业或唤醒任务
|
||||
- 将外部触发器(webhook、Gmail)接入 OpenClaw
|
||||
- 为计划任务决定使用 heartbeat 还是 cron
|
||||
sidebarTitle: Scheduled tasks
|
||||
summary: Gateway 网关调度器的计划任务、webhook 和 Gmail PubSub 触发器
|
||||
summary: Gateway 网关 调度器的计划任务、webhook 和 Gmail PubSub 触发器
|
||||
title: 计划任务
|
||||
x-i18n:
|
||||
generated_at: "2026-04-27T23:11:45Z"
|
||||
generated_at: "2026-04-28T01:19:04Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: f82730951db08f4c14ce1137d6be07660d20841df50b013a9aa80b3e5d44c54d
|
||||
source_hash: c1a044f3d76acebec45e631b6b5c3bffc753543afd21f8e1a16326d1c50aa50b
|
||||
source_path: automation/cron-jobs.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
Cron 是 Gateway 网关的内置调度器。它会持久化作业,在正确的时间唤醒智能体,并且可以将输出回传到聊天渠道或 webhook 端点。
|
||||
Cron 是 Gateway 网关 的内置调度器。它会持久化作业,在正确时间唤醒智能体,并且可以将输出回传到聊天渠道或 webhook 端点。
|
||||
|
||||
## 快速开始
|
||||
|
||||
@ -26,7 +26,7 @@ Cron 是 Gateway 网关的内置调度器。它会持久化作业,在正确的
|
||||
--name "Reminder" \
|
||||
--at "2026-02-01T16:00:00Z" \
|
||||
--session main \
|
||||
--system-event "提醒:检查 cron 文档草稿" \
|
||||
--system-event "Reminder: check the cron docs draft" \
|
||||
--wake now \
|
||||
--delete-after-run
|
||||
```
|
||||
@ -44,90 +44,90 @@ Cron 是 Gateway 网关的内置调度器。它会持久化作业,在正确的
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## cron 的工作原理
|
||||
## cron 的工作方式
|
||||
|
||||
- Cron 在 **Gateway 网关** 进程**内部**运行(而不是在模型内部)。
|
||||
- 作业定义会持久化到 `~/.openclaw/cron/jobs.json`,因此重启不会丢失调度。
|
||||
- 运行时执行状态会持久化到同目录下的 `~/.openclaw/cron/jobs-state.json`。如果你用 git 跟踪 cron 定义,请跟踪 `jobs.json` 并将 `jobs-state.json` 加入 gitignore。
|
||||
- 在拆分之后,旧版本的 OpenClaw 仍然可以读取 `jobs.json`,但可能会将作业视为新作业,因为运行时字段现在位于 `jobs-state.json` 中。
|
||||
- 当在 Gateway 网关运行中或停止时编辑 `jobs.json`,OpenClaw 会将变更后的调度字段与待执行的运行时槽位元数据进行比较,并清除陈旧的 `nextRunAtMs` 值。纯格式化或仅键顺序变更的重写会保留待执行槽位。
|
||||
- Cron 在 **Gateway 网关** 进程内部运行(不在模型内部)。
|
||||
- 作业定义会持久化到 `~/.openclaw/cron/jobs.json`,因此重启不会丢失计划。
|
||||
- 运行时执行状态会持久化到它旁边的 `~/.openclaw/cron/jobs-state.json`。如果你在 git 中跟踪 cron 定义,请跟踪 `jobs.json`,并将 `jobs-state.json` 加入 gitignore。
|
||||
- 在拆分之后,旧版 OpenClaw 仍然可以读取 `jobs.json`,但可能会把作业视为全新作业,因为运行时字段现在位于 `jobs-state.json` 中。
|
||||
- 当 Gateway 网关 运行中或停止时编辑了 `jobs.json`,OpenClaw 会将变更后的调度字段与待运行的运行时槽位元数据进行比较,并清除过期的 `nextRunAtMs` 值。纯格式化或仅键顺序变化的重写会保留待运行槽位。
|
||||
- 所有 cron 执行都会创建[后台任务](/zh-CN/automation/tasks)记录。
|
||||
- 一次性作业(`--at`)默认会在成功后自动删除。
|
||||
- 独立 cron 运行会尽力在运行完成时关闭其 `cron:<jobId>` 会话所跟踪的浏览器标签页/进程,这样分离式浏览器自动化就不会留下孤儿进程。
|
||||
- 独立 cron 运行还会防止陈旧的确认回复。如果第一个结果只是中间状态更新(如 `on it`、`pulling everything together` 以及类似提示),并且没有后代子智能体运行仍负责给出最终答案,OpenClaw 会在交付前再次提示一次以获取实际结果。
|
||||
- 独立 cron 运行会优先使用嵌入式运行中的结构化执行拒绝元数据,然后再回退到已知的最终摘要/输出标记,例如 `SYSTEM_RUN_DENIED` 和 `INVALID_REQUEST`,这样被阻止的命令就不会被报告为绿色运行。
|
||||
- 即使没有生成回复负载,独立 cron 运行也会将运行级别的智能体失败视为作业错误,因此模型/提供商失败会增加错误计数并触发失败通知,而不是将作业标记为成功。
|
||||
- 隔离的 cron 运行会在运行完成后,尽力关闭其 `cron:<jobId>` 会话所跟踪的浏览器标签页/进程,因此分离的浏览器自动化不会留下孤儿进程。
|
||||
- 隔离的 cron 运行还会防止过时的确认性回复。如果第一个结果只是中间状态更新(如 `on it`、`pulling everything together` 以及类似提示),并且没有后代子智能体运行仍在负责最终答案,OpenClaw 会再提示一次以获取实际结果,然后再进行交付。
|
||||
- 隔离的 cron 运行会优先使用来自嵌入式运行的结构化执行拒绝元数据,然后再回退到已知的最终摘要/输出标记,例如 `SYSTEM_RUN_DENIED` 和 `INVALID_REQUEST`,从而避免把被阻止的命令误报为绿色运行。
|
||||
- 隔离的 cron 运行还会将运行级别的智能体失败视为作业错误,即使没有生成任何回复负载也一样,因此模型/提供商失败会增加错误计数并触发失败通知,而不是将作业清除为成功。
|
||||
|
||||
<a id="maintenance"></a>
|
||||
|
||||
<Note>
|
||||
cron 的任务对账优先由运行时负责,其次依赖持久历史:只要 cron 运行时仍将某个作业跟踪为运行中,即使旧的子会话行仍然存在,一个活动的 cron 任务也会保持活动状态。一旦运行时不再拥有该作业,并且 5 分钟宽限窗口已过,维护检查会针对匹配的 `cron:<jobId>:<startedAt>` 运行检查持久化的运行日志和作业状态。如果该持久历史显示终态结果,任务账本会据此完成终结;否则,由 Gateway 网关拥有的维护流程可以将任务标记为 `lost`。离线 CLI 审计可以从持久历史中恢复,但它不会将自身空的进程内活动作业集视为 Gateway 网关拥有的 cron 运行已经消失的证据。
|
||||
cron 的任务对账会先以运行时归属为准,其次才依赖持久历史:只要 cron 运行时仍在跟踪某个作业处于运行中,该活动 cron 任务就保持存活,即使仍存在一条旧的子会话记录也是如此。一旦运行时不再拥有该作业,且 5 分钟宽限窗口已过,维护检查就会为匹配的 `cron:<jobId>:<startedAt>` 运行检查已持久化的运行日志和作业状态。如果该持久历史显示终态结果,任务账本就会据此完成;否则,由 Gateway 网关 拥有的维护逻辑可以将任务标记为 `lost`。离线 CLI 审计可以基于持久历史进行恢复,但它不会把自身空的进程内活动作业集合视为 Gateway 网关 拥有的 cron 运行已消失的证据。
|
||||
</Note>
|
||||
|
||||
## 调度类型
|
||||
|
||||
| 类型 | CLI 标志 | 描述 |
|
||||
| ------- | -------- | ------------------------------------------------ |
|
||||
| `at` | `--at` | 一次性时间戳(ISO 8601 或类似 `20m` 的相对时间) |
|
||||
| `every` | `--every`| 固定间隔 |
|
||||
| `cron` | `--cron` | 5 字段或 6 字段的 cron 表达式,可选 `--tz` |
|
||||
| 类型 | CLI 标志 | 说明 |
|
||||
| ------- | -------- | --------------------------------------------------- |
|
||||
| `at` | `--at` | 一次性时间戳(ISO 8601 或类似 `20m` 的相对时间) |
|
||||
| `every` | `--every`| 固定间隔 |
|
||||
| `cron` | `--cron` | 5 字段或 6 字段 cron 表达式,可选配 `--tz` |
|
||||
|
||||
不带时区的时间戳会被视为 UTC。添加 `--tz America/New_York` 可按本地挂钟时间进行调度。
|
||||
|
||||
每小时整点的循环表达式会自动错峰,最多延后 5 分钟,以减少负载尖峰。使用 `--exact` 可强制精确时间,或使用 `--stagger 30s` 指定明确的错峰窗口。
|
||||
每小时整点的循环表达式会自动错峰,最多延后 5 分钟,以减少负载尖峰。使用 `--exact` 可强制精确时间,或使用 `--stagger 30s` 指定显式错峰窗口。
|
||||
|
||||
### day-of-month 和 day-of-week 使用 OR 逻辑
|
||||
|
||||
Cron 表达式由 [croner](https://github.com/Hexagon/croner) 解析。当 day-of-month 和 day-of-week 字段都不是通配符时,croner 会在**任一**字段匹配时触发,而不是两个都匹配。这是标准的 Vixie cron 行为。
|
||||
Cron 表达式由 [croner](https://github.com/Hexagon/croner) 解析。当 day-of-month 和 day-of-week 字段都不是通配符时,croner 会在 **任一** 字段匹配时触发——而不是两个都匹配时才触发。这是标准的 Vixie cron 行为。
|
||||
|
||||
```
|
||||
# 预期:“每月 15 日上午 9 点,但仅当这一天是周一”
|
||||
# 实际:“每个 15 日上午 9 点,以及每个周一上午 9 点”
|
||||
# 预期:"每月 15 日上午 9 点,且只有在星期一时"
|
||||
# 实际:"每个 15 日上午 9 点,以及每个星期一上午 9 点"
|
||||
0 9 15 * 1
|
||||
```
|
||||
|
||||
这会导致每月触发约 5–6 次,而不是 0–1 次。OpenClaw 在这里使用 Croner 默认的 OR 行为。若要要求两个条件都满足,请使用 Croner 的 `+` day-of-week 修饰符(`0 9 15 * +1`),或者只基于一个字段调度,再在你的作业提示或命令中对另一个字段进行判断。
|
||||
这样每月会触发约 5–6 次,而不是 0–1 次。OpenClaw 在这里使用 Croner 默认的 OR 行为。若要同时要求两个条件,请使用 Croner 的 `+` day-of-week 修饰符(`0 9 15 * +1`),或者只按一个字段调度,并在作业的提示词或命令中对另一个条件进行判断。
|
||||
|
||||
## 执行方式
|
||||
## 执行样式
|
||||
|
||||
| 方式 | `--session` 值 | 运行位置 | 最适合 |
|
||||
| -------------- | ----------------- | ------------------------ | ------------------------------ |
|
||||
| 主会话 | `main` | 下一个 heartbeat 轮次 | 提醒、系统事件 |
|
||||
| 独立 | `isolated` | 专用 `cron:<jobId>` | 报告、后台杂务 |
|
||||
| 当前会话 | `current` | 在创建时绑定 | 依赖上下文的循环工作 |
|
||||
| 自定义会话 | `session:custom-id` | 持久化命名会话 | 基于历史持续推进的工作流 |
|
||||
| 样式 | `--session` 值 | 运行位置 | 最适合 |
|
||||
| -------------- | ------------------- | ------------------------ | ------------------------------ |
|
||||
| 主会话 | `main` | 下一次 heartbeat 回合 | 提醒、系统事件 |
|
||||
| 隔离 | `isolated` | 专用 `cron:<jobId>` | 报告、后台杂务 |
|
||||
| 当前会话 | `current` | 在创建时绑定 | 依赖上下文的周期性工作 |
|
||||
| 自定义会话 | `session:custom-id` | 持久命名会话 | 基于历史逐步构建的工作流 |
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="主会话、独立和自定义的区别">
|
||||
**主会话**作业会入队一个系统事件,并可选择唤醒 heartbeat(`--wake now` 或 `--wake next-heartbeat`)。这些系统事件不会延长目标会话的每日/空闲重置新鲜度。**独立**作业会使用一个全新会话运行一次专用的智能体轮次。**自定义会话**(`session:xxx`)会在多次运行之间保留上下文,从而支持例如基于前一天摘要继续推进的每日站会等工作流。
|
||||
<Accordion title="主会话 vs 隔离 vs 自定义">
|
||||
**主会话** 作业会将系统事件加入队列,并可选择唤醒 heartbeat(`--wake now` 或 `--wake next-heartbeat`)。这些系统事件不会延长目标会话的每日/空闲重置新鲜度。**隔离** 作业会在一个全新会话中运行专用智能体回合。**自定义会话**(`session:xxx`)会跨运行保留上下文,从而支持诸如每日站会这类基于之前摘要持续推进的工作流。
|
||||
</Accordion>
|
||||
<Accordion title="独立作业中的“全新会话”是什么意思">
|
||||
对于独立作业,“全新会话”意味着每次运行都会创建新的 transcript/session id。OpenClaw 可能会保留安全的偏好设置,例如 thinking/fast/verbose 设置、标签,以及用户显式选择的 model/auth 覆盖,但它不会继承旧 cron 记录中的环境会话上下文:渠道/群组路由、发送或队列策略、提权、来源,或 ACP 运行时绑定。如果一个循环作业应该有意基于同一会话上下文持续推进,请使用 `current` 或 `session:<id>`。
|
||||
<Accordion title="隔离作业中的“全新会话”是什么意思">
|
||||
对于隔离作业来说,“全新会话”意味着每次运行都会有新的 transcript/session id。OpenClaw 可能会保留安全偏好,例如 thinking/fast/verbose 设置、标签以及用户显式选择的模型/凭证覆盖,但不会从旧的 cron 记录继承环境式会话上下文:渠道/群组路由、发送或排队策略、提权、来源或 ACP 运行时绑定。如果某个周期性作业需要有意构建在同一会话上下文上,请使用 `current` 或 `session:<id>`。
|
||||
</Accordion>
|
||||
<Accordion title="运行时清理">
|
||||
对于独立作业,运行时拆除现在包括尽力清理该 cron 会话的浏览器。清理失败会被忽略,因此实际的 cron 结果仍然优先生效。
|
||||
对于隔离作业,运行时拆除现在还包括对该 cron 会话执行尽力而为的浏览器清理。清理失败会被忽略,因此真正的 cron 结果仍然优先生效。
|
||||
|
||||
独立 cron 运行还会通过共享的运行时清理路径,释放为该作业创建的任何内置 MCP 运行时实例。这与主会话和自定义会话的 MCP 客户端销毁方式一致,因此独立 cron 作业不会在多次运行之间泄漏 stdio 子进程或长期存在的 MCP 连接。
|
||||
隔离的 cron 运行还会通过共享的运行时清理路径,处置为该作业创建的所有内置 MCP 运行时实例。这与主会话和自定义会话的 MCP 客户端清理方式一致,因此隔离的 cron 作业不会在多次运行之间泄漏 stdio 子进程或长生命周期的 MCP 连接。
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="子智能体和 Discord 交付">
|
||||
当独立 cron 运行编排子智能体时,交付也会优先使用最终后代输出,而不是陈旧的父级中间文本。如果后代仍在运行,OpenClaw 会抑制该部分父级更新,而不是直接对外宣布。
|
||||
<Accordion title="子智能体与 Discord 交付">
|
||||
当隔离的 cron 运行编排子智能体时,交付也会优先采用最终后代输出,而不是过时的父级中间文本。如果后代仍在运行,OpenClaw 会抑制该不完整的父级更新,而不是将其发布出去。
|
||||
|
||||
对于纯文本的 Discord 通知目标,OpenClaw 会只发送一次规范化的最终助手文本,而不会重放流式/中间文本负载和最终答案。媒体和结构化 Discord 负载仍会作为独立负载发送,以避免附件和组件丢失。
|
||||
对于仅文本的 Discord announce 目标,OpenClaw 会只发送一次规范的最终 assistant 文本,而不是同时重放流式/中间文本负载和最终答案。媒体和结构化 Discord 负载仍会作为单独负载交付,以避免附件和组件被丢弃。
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### 独立作业的负载选项
|
||||
### 隔离作业的负载选项
|
||||
|
||||
<ParamField path="--message" type="string" required>
|
||||
提示文本(独立模式必填)。
|
||||
提示词文本(隔离模式必填)。
|
||||
</ParamField>
|
||||
<ParamField path="--model" type="string">
|
||||
模型覆盖;使用为该作业选择的允许模型。
|
||||
</ParamField>
|
||||
<ParamField path="--thinking" type="string">
|
||||
Thinking 级别覆盖。
|
||||
thinking 级别覆盖。
|
||||
</ParamField>
|
||||
<ParamField path="--light-context" type="boolean">
|
||||
跳过工作区引导文件注入。
|
||||
@ -136,42 +136,44 @@ Cron 表达式由 [croner](https://github.com/Hexagon/croner) 解析。当 day-o
|
||||
限制作业可使用的工具,例如 `--tools exec,read`。
|
||||
</ParamField>
|
||||
|
||||
`--model` 会将选定的允许模型用作该作业的主模型。它不同于聊天会话中的 `/model` 覆盖:当该作业主模型失败时,已配置的回退链仍然会生效。如果请求的模型不被允许,cron 会记录警告,并回退到该作业的智能体/默认模型选择。
|
||||
`--model` 会将所选允许模型用作该作业的主模型。它不同于聊天会话中的 `/model` 覆盖:当作业主模型失败时,已配置的回退链仍会继续生效。如果请求的模型不被允许,cron 会记录一条警告,并回退到该作业的智能体/默认模型选择。
|
||||
|
||||
Cron 作业还可以携带负载级别的 `fallbacks`。存在该字段时,这个列表会替换该作业的已配置回退链。当你希望某次严格的 cron 运行只尝试所选模型时,请在作业负载/API 中使用 `fallbacks: []`。如果作业设置了 `--model`,但既没有负载回退也没有已配置回退,OpenClaw 会传递一个显式的空回退覆盖,这样智能体主模型就不会作为隐藏的额外重试目标被追加。
|
||||
Cron 作业还可以携带负载级别的 `fallbacks`。存在时,该列表会替换该作业已配置的回退链。当你希望严格的 cron 运行只尝试所选模型时,可在作业负载/API 中使用 `fallbacks: []`。如果作业有 `--model`,但负载和配置中都没有 fallbacks,OpenClaw 会传递一个显式的空回退覆盖,这样智能体主模型就不会被作为隐藏的额外重试目标附加进去。
|
||||
|
||||
独立作业的模型选择优先级如下:
|
||||
隔离作业的模型选择优先级如下:
|
||||
|
||||
1. Gmail hook 模型覆盖(当运行来自 Gmail 且该覆盖被允许时)
|
||||
2. 每个作业负载中的 `model`
|
||||
3. 用户选择并存储的 cron 会话模型覆盖
|
||||
4. 智能体/默认模型选择
|
||||
|
||||
Fast 模式也遵循已解析的实时选择。如果所选模型配置包含 `params.fastMode`,独立 cron 默认会使用它。无论方向如何,已存储会话中的 `fastMode` 覆盖仍然优先于配置。
|
||||
Fast 模式也会遵循已解析的实时选择。如果所选模型配置带有 `params.fastMode`,隔离的 cron 默认会使用它。已存储的会话 `fastMode` 覆盖在任意方向上都仍然优先生效。
|
||||
|
||||
如果某次独立运行遇到实时模型切换交接,cron 会使用切换后的提供商/模型重试,并在重试前为当前运行持久化该实时选择。当切换还携带新的认证配置文件时,cron 也会为当前运行持久化该认证配置文件覆盖。重试次数是有界的:初始尝试后最多再进行 2 次切换重试,之后 cron 会中止,而不是无限循环。
|
||||
如果隔离运行遇到实时模型切换交接,cron 会使用切换后的提供商/模型重试,并在重试前为当前运行持久化该实时选择。如果切换还携带了新的凭证配置文件,cron 也会为当前运行持久化该凭证配置文件覆盖。重试是有界的:初次尝试之后最多再进行 2 次切换重试,超过后 cron 会中止,而不是无限循环。
|
||||
|
||||
在隔离的 cron 运行进入智能体运行器之前,OpenClaw 会检查已配置 `api: "ollama"` 和 `api: "openai-completions"` 且其 `baseUrl` 为 loopback、私有网络或 `.local` 的本地提供商端点是否可达。如果该端点不可用,此次运行会被记录为 `skipped`,并带有清晰的提供商/模型错误,而不是开始模型调用。该端点检查结果会缓存 5 分钟,因此多个到期作业如果使用同一个不可用的本地 Ollama、vLLM、SGLang 或 LM Studio 服务器,只会共享一次小型探测,而不会形成请求风暴。因提供商预检而被跳过的运行不会增加执行错误退避;如果你希望重复收到 skip 通知,请启用 `failureAlert.includeSkipped`。
|
||||
|
||||
## 交付与输出
|
||||
|
||||
| 模式 | 处理方式 |
|
||||
| ---------- | ------------------------------------------------------------ |
|
||||
| `announce` | 如果智能体未发送,则回退为向目标交付最终文本 |
|
||||
| `webhook` | 将完成事件负载以 POST 方式发送到某个 URL |
|
||||
| `none` | 不执行运行器回退交付 |
|
||||
| 模式 | 结果 |
|
||||
| ---------- | --------------------------------------------------------------- |
|
||||
| `announce` | 如果智能体没有发送,则回退交付最终文本到目标 |
|
||||
| `webhook` | 向某个 URL POST 已完成事件负载 |
|
||||
| `none` | 不进行运行器回退交付 |
|
||||
|
||||
使用 `--announce --channel telegram --to "-1001234567890"` 进行渠道交付。对于 Telegram 论坛主题,请使用 `-1001234567890:topic:123`;直接 RPC/配置调用方也可以将 `delivery.threadId` 作为字符串或数字传入。Slack/Discord/Mattermost 目标应使用显式前缀(`channel:<id>`、`user:<id>`)。Matrix 房间 ID 区分大小写;请使用精确的房间 ID,或使用 Matrix 中的 `room:!room:server` 形式。
|
||||
使用 `--announce --channel telegram --to "-1001234567890"` 可交付到渠道。对于 Telegram forum topic,请使用 `-1001234567890:topic:123`;直接 RPC/配置调用方也可以把 `delivery.threadId` 作为字符串或数字传入。Slack/Discord/Mattermost 目标应使用显式前缀(`channel:<id>`、`user:<id>`)。Matrix 房间 ID 区分大小写;请使用精确的房间 ID,或使用来自 Matrix 的 `room:!room:server` 形式。
|
||||
|
||||
对于独立作业,聊天交付是共享的。如果聊天路由可用,即使作业使用 `--no-deliver`,智能体也可以使用 `message` 工具。如果智能体发送到了已配置/当前目标,OpenClaw 会跳过回退通知。否则,`announce`、`webhook` 和 `none` 只控制运行器在智能体轮次结束后如何处理最终回复。
|
||||
对于隔离作业,聊天交付是共享的。如果存在聊天路由,即使作业使用 `--no-deliver`,智能体也可以使用 `message` 工具。如果智能体已发送到配置的/当前目标,OpenClaw 会跳过回退 announce。否则,`announce`、`webhook` 和 `none` 只控制运行器在智能体回合结束后如何处理最终回复。
|
||||
|
||||
当智能体从活动聊天中创建独立提醒时,OpenClaw 会为回退通知路由保存保留的实时交付目标。内部会话键可能是小写的;当当前聊天上下文可用时,不会根据这些键重建提供商交付目标。
|
||||
当智能体从活动聊天中创建隔离提醒时,OpenClaw 会为回退 announce 路由存储保留的实时交付目标。内部会话键可能是小写;当当前聊天上下文可用时,不会根据这些键重建提供商交付目标。
|
||||
|
||||
失败通知遵循单独的目标路径:
|
||||
|
||||
- `cron.failureDestination` 为失败通知设置全局默认目标。
|
||||
- `job.delivery.failureDestination` 可按作业覆盖该设置。
|
||||
- 如果两者都未设置,并且该作业已经通过 `announce` 进行交付,失败通知现在会回退到该主通知目标。
|
||||
- `job.delivery.failureDestination` 可为单个作业覆盖该设置。
|
||||
- 如果两者都未设置,且该作业已经通过 `announce` 交付,失败通知现在会回退到该主 announce 目标。
|
||||
- `delivery.failureDestination` 仅在 `sessionTarget="isolated"` 的作业上受支持,除非主交付模式是 `webhook`。
|
||||
- `failureAlert.includeSkipped: true` 可让某个作业或全局 cron 告警策略选择加入重复的跳过运行告警。跳过运行会维护单独的连续跳过计数器,因此不会影响执行错误退避。
|
||||
- `failureAlert.includeSkipped: true` 可让单个作业或全局 cron 告警策略选择加入重复的 skipped 运行告警。Skipped 运行会维护单独的连续跳过计数器,因此不会影响执行错误退避。
|
||||
|
||||
## CLI 示例
|
||||
|
||||
@ -182,18 +184,18 @@ Fast 模式也遵循已解析的实时选择。如果所选模型配置包含 `p
|
||||
--name "Calendar check" \
|
||||
--at "20m" \
|
||||
--session main \
|
||||
--system-event "下一个 heartbeat:检查日历。" \
|
||||
--system-event "Next heartbeat: check calendar." \
|
||||
--wake now
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="循环的独立作业">
|
||||
<Tab title="循环隔离作业">
|
||||
```bash
|
||||
openclaw cron add \
|
||||
--name "Morning brief" \
|
||||
--cron "0 7 * * *" \
|
||||
--tz "America/Los_Angeles" \
|
||||
--session isolated \
|
||||
--message "总结一夜之间的更新。" \
|
||||
--message "Summarize overnight updates." \
|
||||
--announce \
|
||||
--channel slack \
|
||||
--to "channel:C1234567890"
|
||||
@ -206,7 +208,7 @@ Fast 模式也遵循已解析的实时选择。如果所选模型配置包含 `p
|
||||
--cron "0 6 * * 1" \
|
||||
--tz "America/Los_Angeles" \
|
||||
--session isolated \
|
||||
--message "对项目进展进行每周深度分析。" \
|
||||
--message "Weekly deep analysis of project progress." \
|
||||
--model "opus" \
|
||||
--thinking high \
|
||||
--announce
|
||||
@ -214,9 +216,9 @@ Fast 模式也遵循已解析的实时选择。如果所选模型配置包含 `p
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## webhook
|
||||
## Webhook
|
||||
|
||||
Gateway 网关可以暴露 HTTP webhook 端点供外部触发器使用。在配置中启用:
|
||||
Gateway 网关 可以暴露 HTTP webhook 端点以接收外部触发器。在配置中启用:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -230,22 +232,22 @@ Gateway 网关可以暴露 HTTP webhook 端点供外部触发器使用。在配
|
||||
|
||||
### 认证
|
||||
|
||||
每个请求都必须通过请求头包含 hook token:
|
||||
每个请求都必须通过请求头携带 hook token:
|
||||
|
||||
- `Authorization: Bearer <token>`(推荐)
|
||||
- `x-openclaw-token: <token>`
|
||||
|
||||
不接受查询字符串中的 token。
|
||||
查询字符串中的 token 会被拒绝。
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="POST /hooks/wake">
|
||||
为主会话入队一个系统事件:
|
||||
为主会话加入一个系统事件:
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:18789/hooks/wake \
|
||||
-H 'Authorization: Bearer SECRET' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"text":"收到新邮件","mode":"now"}'
|
||||
-d '{"text":"New email received","mode":"now"}'
|
||||
```
|
||||
|
||||
<ParamField path="text" type="string" required>
|
||||
@ -257,31 +259,31 @@ Gateway 网关可以暴露 HTTP webhook 端点供外部触发器使用。在配
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="POST /hooks/agent">
|
||||
运行一次独立的智能体轮次:
|
||||
运行一次隔离的智能体回合:
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:18789/hooks/agent \
|
||||
-H 'Authorization: Bearer SECRET' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"message":"总结收件箱","name":"Email","model":"openai/gpt-5.4"}'
|
||||
-d '{"message":"Summarize inbox","name":"Email","model":"openai/gpt-5.4"}'
|
||||
```
|
||||
|
||||
字段:`message`(必填)、`name`、`agentId`、`wakeMode`、`deliver`、`channel`、`to`、`model`、`fallbacks`、`thinking`、`timeoutSeconds`。
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="映射的 hooks(POST /hooks/<name>)">
|
||||
<Accordion title="映射 hooks(POST /hooks/<name>)">
|
||||
自定义 hook 名称通过配置中的 `hooks.mappings` 解析。映射可以使用模板或代码转换,将任意负载转换为 `wake` 或 `agent` 动作。
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
<Warning>
|
||||
请将 hook 端点放在 loopback、tailnet 或受信任的反向代理之后。
|
||||
将 hook 端点放在 loopback、tailnet 或受信任的反向代理之后。
|
||||
|
||||
- 使用专用 hook token;不要复用 gateway 认证 token。
|
||||
- 使用专用 hook token;不要复用 gateway 凭证 token。
|
||||
- 将 `hooks.path` 保持在专用子路径下;`/` 会被拒绝。
|
||||
- 设置 `hooks.allowedAgentIds` 以限制显式的 `agentId` 路由。
|
||||
- 除非你需要由调用方选择会话,否则请保持 `hooks.allowRequestSessionKey=false`。
|
||||
- 如果你启用了 `hooks.allowRequestSessionKey`,还应设置 `hooks.allowedSessionKeyPrefixes` 来约束允许的会话键形态。
|
||||
- 设置 `hooks.allowedAgentIds` 以限制显式 `agentId` 路由。
|
||||
- 除非你确实需要由调用方选择会话,否则请保持 `hooks.allowRequestSessionKey=false`。
|
||||
- 如果你启用了 `hooks.allowRequestSessionKey`,也请同时设置 `hooks.allowedSessionKeyPrefixes`,以限制允许的会话键形态。
|
||||
- 默认情况下,hook 负载会被安全边界包装。
|
||||
</Warning>
|
||||
|
||||
@ -299,17 +301,17 @@ Gateway 网关可以暴露 HTTP webhook 端点供外部触发器使用。在配
|
||||
openclaw webhooks gmail setup --account openclaw@gmail.com
|
||||
```
|
||||
|
||||
这会写入 `hooks.gmail` 配置、启用 Gmail 预设,并使用 Tailscale Funnel 作为 push 端点。
|
||||
这会写入 `hooks.gmail` 配置,启用 Gmail preset,并为推送端点使用 Tailscale Funnel。
|
||||
|
||||
### Gateway 网关自动启动
|
||||
### Gateway 网关 自动启动
|
||||
|
||||
当 `hooks.enabled=true` 且设置了 `hooks.gmail.account` 时,Gateway 网关会在启动时运行 `gog gmail watch serve` 并自动续订 watch。设置 `OPENCLAW_SKIP_GMAIL_WATCHER=1` 可选择退出。
|
||||
当 `hooks.enabled=true` 且设置了 `hooks.gmail.account` 时,Gateway 网关 会在启动时运行 `gog gmail watch serve` 并自动续订 watch。设置 `OPENCLAW_SKIP_GMAIL_WATCHER=1` 可选择退出。
|
||||
|
||||
### 手动一次性设置
|
||||
|
||||
<Steps>
|
||||
<Step title="选择 GCP 项目">
|
||||
选择由 `gog` 使用的 OAuth 客户端所属的 GCP 项目:
|
||||
选择 `gog` 所用 OAuth 客户端所属的 GCP 项目:
|
||||
|
||||
```bash
|
||||
gcloud auth login
|
||||
@ -318,7 +320,7 @@ openclaw webhooks gmail setup --account openclaw@gmail.com
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step title="创建主题并授予 Gmail push 访问权限">
|
||||
<Step title="创建 topic 并授予 Gmail 推送访问权限">
|
||||
```bash
|
||||
gcloud pubsub topics create gog-gmail-watch
|
||||
gcloud pubsub topics add-iam-policy-binding gog-gmail-watch \
|
||||
@ -355,13 +357,13 @@ openclaw webhooks gmail setup --account openclaw@gmail.com
|
||||
# 列出所有作业
|
||||
openclaw cron list
|
||||
|
||||
# 显示单个作业,包括已解析的交付路由
|
||||
# 显示单个作业,包括解析后的交付路由
|
||||
openclaw cron show <jobId>
|
||||
|
||||
# 编辑作业
|
||||
openclaw cron edit <jobId> --message "更新后的提示" --model "opus"
|
||||
openclaw cron edit <jobId> --message "Updated prompt" --model "opus"
|
||||
|
||||
# 立即强制运行作业
|
||||
# 立即强制运行一个作业
|
||||
openclaw cron run <jobId>
|
||||
|
||||
# 仅在到期时运行
|
||||
@ -374,19 +376,19 @@ openclaw cron runs --id <jobId> --limit 50
|
||||
openclaw cron remove <jobId>
|
||||
|
||||
# 智能体选择(多智能体设置)
|
||||
openclaw cron add --name "Ops sweep" --cron "0 6 * * *" --session isolated --message "检查运维队列" --agent ops
|
||||
openclaw cron add --name "Ops sweep" --cron "0 6 * * *" --session isolated --message "Check ops queue" --agent ops
|
||||
openclaw cron edit <jobId> --clear-agent
|
||||
```
|
||||
|
||||
<Note>
|
||||
模型覆盖说明:
|
||||
|
||||
- `openclaw cron add|edit --model ...` 会更改作业的选定模型。
|
||||
- 如果该模型被允许,该确切 provider/模型 会传递到独立智能体运行中。
|
||||
- 如果不被允许,cron 会发出警告,并回退到该作业的智能体/默认模型选择。
|
||||
- 已配置的回退链仍然适用,因为 cron `--model` 是作业主模型,而不是会话 `/model` 覆盖。
|
||||
- 负载 `fallbacks` 会替换该作业的已配置回退;`fallbacks: []` 会禁用回退并使该运行变为严格模式。
|
||||
- 仅使用普通 `--model` 且没有显式或已配置的回退列表时,不会静默回落到智能体主模型作为额外重试目标。
|
||||
- `openclaw cron add|edit --model ...` 会更改作业选定的模型。
|
||||
- 如果该模型被允许,那个精确的提供商/模型会传递到隔离智能体运行中。
|
||||
- 如果不被允许,cron 会发出警告,并回退到作业的智能体/默认模型选择。
|
||||
- 已配置的回退链仍会生效,因为 cron 的 `--model` 是作业主模型,而不是会话 `/model` 覆盖。
|
||||
- 负载中的 `fallbacks` 会替换该作业已配置的 fallbacks;`fallbacks: []` 会禁用回退,使运行变为严格模式。
|
||||
- 只有普通的 `--model` 且没有显式或已配置的回退列表时,不会静默回退到智能体主模型作为额外重试目标。
|
||||
</Note>
|
||||
|
||||
## 配置
|
||||
@ -409,23 +411,23 @@ openclaw cron edit <jobId> --clear-agent
|
||||
}
|
||||
```
|
||||
|
||||
`maxConcurrentRuns` 同时限制计划中的 cron 分发和独立智能体轮次执行。独立 cron 智能体轮次在内部使用队列的专用 `cron-nested` 执行通道,因此提高该值可以让彼此独立的 cron LLM 运行并行推进,而不是只启动它们外层的 cron 包装器。共享的非 cron `nested` 通道不会因为这个设置而扩宽。
|
||||
`maxConcurrentRuns` 同时限制计划的 cron 分发和隔离智能体回合执行。隔离的 cron 智能体回合在内部使用队列专用的 `cron-nested` 执行通道,因此提高该值可以让彼此独立的 cron LLM 运行并行推进,而不是仅启动其外层 cron 包装器。共享的非 cron `nested` 通道不会因该设置而变宽。
|
||||
|
||||
运行时状态 sidecar 派生自 `cron.store`:例如 `~/clawd/cron/jobs.json` 这样的 `.json` 存储会使用 `~/clawd/cron/jobs-state.json`,而没有 `.json` 后缀的存储路径则会追加 `-state.json`。
|
||||
运行时状态 sidecar 由 `cron.store` 推导而来:像 `~/clawd/cron/jobs.json` 这样的 `.json` 存储会使用 `~/clawd/cron/jobs-state.json`,而没有 `.json` 后缀的存储路径则会附加 `-state.json`。
|
||||
|
||||
如果你手动编辑 `jobs.json`,请不要将 `jobs-state.json` 纳入源码控制。OpenClaw 使用该 sidecar 保存待执行槽位、活动标记、最近运行元数据,以及调度标识,用于让调度器判断某个外部编辑过的作业何时需要新的 `nextRunAtMs`。
|
||||
如果你手动编辑 `jobs.json`,请不要将 `jobs-state.json` 纳入源码控制。OpenClaw 使用该 sidecar 存储待运行槽位、活动标记、最近运行元数据,以及用于告知调度器“外部编辑的作业何时需要新的 `nextRunAtMs`”的调度标识。
|
||||
|
||||
禁用 cron:`cron.enabled: false` 或 `OPENCLAW_SKIP_CRON=1`。
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="重试行为">
|
||||
**一次性重试**:瞬时错误(限流、过载、网络、服务器错误)最多重试 3 次,并采用指数退避。永久性错误会立即禁用。
|
||||
**一次性重试**:瞬时错误(限流、过载、网络、服务器错误)最多重试 3 次,并使用指数退避。永久错误会立即禁用。
|
||||
|
||||
**循环重试**:在重试之间使用指数退避(30 秒到 60 分钟)。在下一次成功运行后,退避会重置。
|
||||
**循环重试**:重试之间采用指数退避(30 秒到 60 分钟)。在下一次成功运行后,退避会重置。
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="维护">
|
||||
`cron.sessionRetention`(默认 `24h`)会清理独立运行的会话条目。`cron.runLog.maxBytes` / `cron.runLog.keepLines` 会自动清理运行日志文件。
|
||||
`cron.sessionRetention`(默认 `24h`)会清理隔离运行的会话条目。`cron.runLog.maxBytes` / `cron.runLog.keepLines` 会自动清理运行日志文件。
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@ -447,25 +449,25 @@ openclaw doctor
|
||||
<AccordionGroup>
|
||||
<Accordion title="Cron 未触发">
|
||||
- 检查 `cron.enabled` 和 `OPENCLAW_SKIP_CRON` 环境变量。
|
||||
- 确认 Gateway 网关在持续运行。
|
||||
- 对于 `cron` 调度,验证时区(`--tz`)与主机时区是否一致。
|
||||
- 运行输出中的 `reason: not-due` 表示你使用 `openclaw cron run <jobId> --due` 检查了手动运行,但该作业尚未到期。
|
||||
- 确认 Gateway 网关 持续运行中。
|
||||
- 对于 `cron` 调度,检查时区(`--tz`)与主机时区是否一致。
|
||||
- 运行输出中的 `reason: not-due` 表示使用 `openclaw cron run <jobId> --due` 检查手动运行时,该作业尚未到期。
|
||||
</Accordion>
|
||||
<Accordion title="Cron 已触发但没有交付">
|
||||
- 交付模式为 `none` 表示不应期待运行器回退发送。如果聊天路由可用,智能体仍可以通过 `message` 工具直接发送。
|
||||
- 交付目标缺失/无效(`channel`/`to`)表示出站已被跳过。
|
||||
- 对于 Matrix,复制的或旧版作业如果 `delivery.to` 房间 ID 被转成小写,可能会失败,因为 Matrix 房间 ID 区分大小写。请将该作业编辑为 Matrix 中精确的 `!room:server` 或 `room:!room:server` 值。
|
||||
- 渠道认证错误(`unauthorized`、`Forbidden`)表示交付被凭证阻止。
|
||||
- 如果独立运行只返回静默 token(`NO_REPLY` / `no_reply`),OpenClaw 会抑制直接出站交付,也会抑制回退的队列摘要路径,因此不会向聊天回发任何内容。
|
||||
- 如果智能体应该自行向用户发消息,请检查该作业是否具有可用路由(`channel: "last"` 且有先前聊天,或显式的渠道/目标)。
|
||||
- 交付模式 `none` 表示不会有运行器回退发送。若聊天路由可用,智能体仍可直接使用 `message` 工具发送。
|
||||
- 缺少/无效的交付目标(`channel`/`to`)意味着外发被跳过。
|
||||
- 对于 Matrix,复制而来或旧作业里被小写化的 `delivery.to` 房间 ID 可能会失败,因为 Matrix 房间 ID 区分大小写。请将作业编辑为来自 Matrix 的精确 `!room:server` 或 `room:!room:server` 值。
|
||||
- 渠道凭证错误(`unauthorized`、`Forbidden`)表示交付被凭证阻止。
|
||||
- 如果隔离运行只返回静默 token(`NO_REPLY` / `no_reply`),OpenClaw 会抑制直接外发交付,也会抑制回退的排队摘要路径,因此不会向聊天回发任何内容。
|
||||
- 如果智能体应当自行给用户发消息,请检查该作业是否有可用路由(`channel: "last"` 且存在之前聊天,或显式的渠道/目标)。
|
||||
</Accordion>
|
||||
<Accordion title="Cron 或 heartbeat 似乎阻止了 /new 风格轮转">
|
||||
- 每日和空闲重置新鲜度并不是基于 `updatedAt`;参见[会话管理](/zh-CN/concepts/session#session-lifecycle)。
|
||||
- Cron 唤醒、heartbeat 运行、exec 通知和 gateway 记账可能会更新会话行用于路由/状态,但它们不会延长 `sessionStartedAt` 或 `lastInteractionAt`。
|
||||
- 对于这些字段出现前创建的旧版行,如果文件仍可用,OpenClaw 可以从 transcript JSONL 会话头中恢复 `sessionStartedAt`。没有 `lastInteractionAt` 的旧版空闲行会使用该恢复出的开始时间作为其空闲基线。
|
||||
<Accordion title="Cron 或 heartbeat 看起来阻止了 /new-style rollover">
|
||||
- 每日和空闲重置的新鲜度并不是基于 `updatedAt`;参见[会话管理](/zh-CN/concepts/session#session-lifecycle)。
|
||||
- Cron 唤醒、heartbeat 运行、exec 通知和 gateway 记账都可能更新会话行以进行路由/状态记录,但它们不会延长 `sessionStartedAt` 或 `lastInteractionAt`。
|
||||
- 对于这些字段出现之前创建的旧记录,只要 transcript JSONL 会话头文件仍可用,OpenClaw 就可以从中恢复 `sessionStartedAt`。对于没有 `lastInteractionAt` 的旧空闲记录,会使用恢复出的开始时间作为其空闲基线。
|
||||
</Accordion>
|
||||
<Accordion title="时区注意事项">
|
||||
- 不带 `--tz` 的 cron 使用 gateway 主机时区。
|
||||
- 不带 `--tz` 的 cron 会使用 gateway 主机时区。
|
||||
- 不带时区的 `at` 调度会被视为 UTC。
|
||||
- Heartbeat `activeHours` 使用已配置的时区解析。
|
||||
</Accordion>
|
||||
@ -473,7 +475,7 @@ openclaw doctor
|
||||
|
||||
## 相关内容
|
||||
|
||||
- [自动化与任务](/zh-CN/automation) —— 所有自动化机制一览
|
||||
- [后台任务](/zh-CN/automation/tasks) —— cron 执行的任务账本
|
||||
- [Heartbeat](/zh-CN/gateway/heartbeat) —— 周期性的主会话轮次
|
||||
- [时区](/zh-CN/concepts/timezone) —— 时区配置
|
||||
- [Automation & Tasks](/zh-CN/automation) — 所有自动化机制一览
|
||||
- [Background Tasks](/zh-CN/automation/tasks) — cron 执行的任务账本
|
||||
- [Heartbeat](/zh-CN/gateway/heartbeat) — 周期性的主会话回合
|
||||
- [Timezone](/zh-CN/concepts/timezone) — 时区配置
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
---
|
||||
read_when:
|
||||
- 你需要计划任务和唤醒功能
|
||||
- 你想要计划任务和唤醒功能
|
||||
- 你正在调试 cron 执行和日志
|
||||
summary: '`openclaw cron` 的 CLI 参考(调度并运行后台作业)'
|
||||
summary: '`openclaw cron` 的 CLI 参考(用于调度和运行后台作业)'
|
||||
title: Cron
|
||||
x-i18n:
|
||||
generated_at: "2026-04-27T23:11:45Z"
|
||||
generated_at: "2026-04-28T01:18:57Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 65eaeefbe94d8905d1dcade6510c2cc527e799bb25809eaa0afc327c603de500
|
||||
source_hash: 35a9ba4c89c6022bda7e8c4dfb81d2efae7c541d73f4c4b36683fd128af0ba4d
|
||||
source_path: cli/cron.md
|
||||
workflow: 15
|
||||
---
|
||||
@ -18,7 +18,7 @@ x-i18n:
|
||||
管理 Gateway 网关调度器的 cron 作业。
|
||||
|
||||
<Tip>
|
||||
运行 `openclaw cron --help` 查看完整命令面。概念性指南请参见 [Cron 作业](/zh-CN/automation/cron-jobs)。
|
||||
运行 `openclaw cron --help` 查看完整命令面。概念性指南请参阅 [Cron 作业](/zh-CN/automation/cron-jobs)。
|
||||
</Tip>
|
||||
|
||||
## 会话
|
||||
@ -28,35 +28,35 @@ x-i18n:
|
||||
<AccordionGroup>
|
||||
<Accordion title="会话键">
|
||||
- `main` 绑定到智能体的主会话。
|
||||
- `isolated` 为每次运行创建全新的转录和会话 id。
|
||||
- `isolated` 为每次运行创建全新的对话记录和会话 ID。
|
||||
- `current` 绑定到创建时的活动会话。
|
||||
- `session:<id>` 固定到显式的持久会话键。
|
||||
- `session:<id>` 固定到显式的持久化会话键。
|
||||
</Accordion>
|
||||
<Accordion title="隔离会话语义">
|
||||
隔离运行会重置环境会话上下文。渠道与群组路由、发送/排队策略、提权、来源以及 ACP 运行时绑定都会为新运行重置。安全偏好设置以及用户显式选择的模型或凭证覆盖项可以在多次运行之间延续。
|
||||
隔离运行会重置环境对话上下文。渠道和群组路由、发送/排队策略、提权、来源以及 ACP 运行时绑定都会为新的运行重置。安全偏好以及用户显式选择的模型或凭证覆盖可以在多次运行之间继承。
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## 传递
|
||||
|
||||
`openclaw cron list` 和 `openclaw cron show <job-id>` 会预览解析后的传递路由。对于 `channel: "last"`,预览会显示该路由是从主会话还是当前会话解析而来,或者会以失败关闭。
|
||||
`openclaw cron list` 和 `openclaw cron show <job-id>` 会预览解析后的传递路由。对于 `channel: "last"`,预览会显示该路由是从主会话还是当前会话解析而来,或者将会以故障关闭方式失败。
|
||||
|
||||
<Note>
|
||||
隔离的 `cron add` 作业默认使用 `--announce` 传递。使用 `--no-deliver` 可将输出保留为内部。`--deliver` 仍然是 `--announce` 的已弃用别名。
|
||||
隔离的 `cron add` 作业默认使用 `--announce` 传递。使用 `--no-deliver` 可将输出保留为内部可见。`--deliver` 仍然保留为 `--announce` 的已弃用别名。
|
||||
</Note>
|
||||
|
||||
### 传递归属
|
||||
### 传递所有权
|
||||
|
||||
隔离 cron 聊天传递由智能体和运行器共同负责:
|
||||
隔离 cron 聊天传递由智能体和运行器共享负责:
|
||||
|
||||
- 当聊天路由可用时,智能体可以使用 `message` 工具直接发送。
|
||||
- 只有当智能体没有直接发送到解析出的目标时,`announce` 才会以回退方式传递最终回复。
|
||||
- `webhook` 会将完成的负载发送到某个 URL。
|
||||
- `none` 会禁用运行器回退传递。
|
||||
- 只有当智能体未直接发送到已解析目标时,`announce` 才会作为后备方式传递最终回复。
|
||||
- `webhook` 会将完成后的负载发送到某个 URL。
|
||||
- `none` 会禁用运行器后备传递。
|
||||
|
||||
`--announce` 是用于最终回复的运行器回退传递。`--no-deliver` 会禁用该回退,但在聊天路由可用时不会移除智能体的 `message` 工具。
|
||||
`--announce` 是针对最终回复的运行器后备传递。`--no-deliver` 会禁用该后备方式,但在聊天路由可用时不会移除智能体的 `message` 工具。
|
||||
|
||||
从活动聊天创建的提醒会保留实时聊天传递目标,以用于回退 announce 传递。内部会话键可能是小写;不要将它们用作区分大小写的提供商 ID(例如 Matrix 房间 ID)的真实来源。
|
||||
从活动聊天创建的提醒会保留实时聊天传递目标,用于后备 announce 传递。内部会话键可能是小写;不要将它们用作区分大小写的提供商 ID(例如 Matrix 房间 ID)的真实依据。
|
||||
|
||||
### 失败传递
|
||||
|
||||
@ -67,32 +67,34 @@ x-i18n:
|
||||
3. 作业的主 announce 目标(当未设置显式失败目标时)。
|
||||
|
||||
<Note>
|
||||
主会话作业只有在主传递模式为 `webhook` 时才能使用 `delivery.failureDestination`。隔离作业在所有模式下都接受它。
|
||||
主会话作业仅可在主传递模式为 `webhook` 时使用 `delivery.failureDestination`。隔离作业在所有模式下都接受它。
|
||||
</Note>
|
||||
|
||||
注意:隔离的 cron 运行会将运行级智能体失败视为作业错误,即使没有产生回复负载也是如此,因此模型/提供商失败仍会增加错误计数并触发失败通知。
|
||||
注意:隔离 cron 运行会将运行级别的智能体失败视为作业错误,即使没有生成回复负载也是如此,因此模型/提供商失败仍会增加错误计数器并触发失败通知。
|
||||
|
||||
## 调度
|
||||
|
||||
### 一次性作业
|
||||
### 单次作业
|
||||
|
||||
`--at <datetime>` 用于调度一次性运行。没有偏移量的日期时间会被视为 UTC,除非你同时传入 `--tz <iana>`,此时将按给定时区解释该墙上时钟时间。
|
||||
`--at <datetime>` 会调度一次性运行。不带偏移量的日期时间默认按 UTC 处理,除非你同时传入 `--tz <iana>`,此时会按给定时区中的墙上时间进行解释。
|
||||
|
||||
<Note>
|
||||
一次性作业默认在成功后删除。使用 `--keep-after-run` 可保留它们。
|
||||
单次作业在成功后默认删除。使用 `--keep-after-run` 可保留它们。
|
||||
</Note>
|
||||
|
||||
### 周期性作业
|
||||
|
||||
周期性作业在连续出错后使用指数退避重试:30 秒、1 分钟、5 分钟、15 分钟、60 分钟。下一次成功运行后,调度将恢复正常。
|
||||
周期性作业在连续出错后使用指数退避重试:30 秒、1 分钟、5 分钟、15 分钟、60 分钟。下一次成功运行后,调度会恢复正常。
|
||||
|
||||
跳过的运行会与执行错误分开跟踪。它们不会影响重试退避,但 `openclaw cron edit <job-id> --failure-alert-include-skipped` 可以让失败警报选择加入重复的跳过运行通知。
|
||||
跳过的运行会与执行错误分开跟踪。它们不会影响重试退避,但 `openclaw cron edit <job-id> --failure-alert-include-skipped` 可以让失败告警选择加入重复的跳过运行通知。
|
||||
|
||||
注意:cron 作业定义存放在 `jobs.json` 中,而待处理的运行时状态存放在 `jobs-state.json` 中。如果 `jobs.json` 被外部编辑,Gateway 网关会重新加载变更后的调度并清除过期的待处理槽位;仅格式化重写不会清除待处理槽位。
|
||||
对于以本地已配置模型提供商为目标的隔离作业,cron 会在启动智能体轮次之前运行轻量级提供商预检。`api: "ollama"` 的 loopback、私有网络和 `.local` 提供商会在 `/api/tags` 上探测;本地兼容 OpenAI 的提供商(如 vLLM、SGLang 和 LM Studio)会在 `/models` 上探测。如果端点不可达,该次运行会被记录为 `skipped`,并在后续计划中重试;匹配的失效端点会缓存 5 分钟,以避免大量作业反复冲击同一个本地服务器。
|
||||
|
||||
注意:cron 作业定义保存在 `jobs.json` 中,而待处理的运行时状态保存在 `jobs-state.json` 中。如果 `jobs.json` 被外部编辑,Gateway 网关会重新加载已变更的调度并清除陈旧的待处理槽位;仅格式化层面的重写不会清除该待处理槽位。
|
||||
|
||||
### 手动运行
|
||||
|
||||
`openclaw cron run` 会在手动运行进入队列后立即返回。成功响应包含 `{ ok: true, enqueued: true, runId }`。使用 `openclaw cron runs --id <job-id>` 跟踪最终结果。
|
||||
`openclaw cron run` 会在手动运行入队后立即返回。成功响应会包含 `{ ok: true, enqueued: true, runId }`。使用 `openclaw cron runs --id <job-id>` 来跟踪最终结果。
|
||||
|
||||
<Note>
|
||||
`openclaw cron run <job-id>` 默认会强制运行。使用 `--due` 可保留旧的“仅在到期时运行”行为。
|
||||
@ -100,49 +102,49 @@ x-i18n:
|
||||
|
||||
## 模型
|
||||
|
||||
`cron add|edit --model <ref>` 为作业选择一个允许的模型。
|
||||
`cron add|edit --model <ref>` 为作业选择一个允许使用的模型。
|
||||
|
||||
<Warning>
|
||||
如果该模型不被允许,cron 会发出警告,并回退到作业的智能体或默认模型选择。
|
||||
如果该模型不被允许,cron 会发出警告,并回退到作业的智能体模型或默认模型选择。
|
||||
</Warning>
|
||||
|
||||
Cron 的 `--model` 是**作业主模型**,而不是聊天会话的 `/model` 覆盖。这意味着:
|
||||
Cron 的 `--model` 是**作业主模型**,不是聊天会话的 `/model` 覆盖。这意味着:
|
||||
|
||||
- 当所选作业模型失败时,已配置的模型回退仍然适用。
|
||||
- 存在每作业负载 `fallbacks` 时,它会替换已配置的回退列表。
|
||||
- 当存在每作业负载 `fallbacks` 时,它会替换已配置的回退列表。
|
||||
- 空的每作业回退列表(作业负载/API 中的 `fallbacks: []`)会让 cron 运行变为严格模式。
|
||||
- 当作业设置了 `--model` 但未配置回退列表时,OpenClaw 会传递一个显式的空回退覆盖项,这样智能体主模型就不会作为隐藏的重试目标被追加。
|
||||
- 当作业带有 `--model` 但未配置回退列表时,OpenClaw 会传递一个显式的空回退覆盖,以避免将智能体主模型作为隐藏的重试目标追加进去。
|
||||
|
||||
### 隔离 cron 模型优先级
|
||||
|
||||
隔离 cron 按以下顺序解析活动模型:
|
||||
|
||||
1. Gmail-hook 覆盖项。
|
||||
1. Gmail-hook 覆盖。
|
||||
2. 每作业 `--model`。
|
||||
3. 已存储的 cron 会话模型覆盖项(当用户选择过时)。
|
||||
4. 智能体或默认模型选择。
|
||||
3. 已存储的 cron 会话模型覆盖(当用户选定了某个模型时)。
|
||||
4. 智能体模型或默认模型选择。
|
||||
|
||||
### 快速模式
|
||||
|
||||
隔离 cron 快速模式遵循解析后的实时模型选择。模型配置 `params.fastMode` 默认会生效,但已存储的会话 `fastMode` 覆盖项仍然优先于配置。
|
||||
隔离 cron 的快速模式会遵循已解析的实时模型选择。默认会应用模型配置 `params.fastMode`,但已存储的会话 `fastMode` 覆盖仍然优先于配置。
|
||||
|
||||
### 实时模型切换重试
|
||||
|
||||
如果某次隔离运行抛出 `LiveSessionModelSwitchError`,cron 会在重试前为当前运行持久化切换后的提供商和模型(以及存在时切换后的凭证配置覆盖项)。外层重试循环在初次尝试之后最多只允许两次切换重试,随后会中止,而不是无限循环。
|
||||
如果隔离运行抛出 `LiveSessionModelSwitchError`,cron 会在重试前为当前运行持久化已切换的提供商和模型(以及存在时已切换的凭证配置文件覆盖)。外层重试循环在初始尝试后最多只允许两次切换重试,然后会中止,而不是无限循环。
|
||||
|
||||
## 运行输出与拒绝
|
||||
|
||||
### 过期确认抑制
|
||||
### 过时确认抑制
|
||||
|
||||
隔离 cron 回合会启用对仅确认型过期回复的抑制。如果第一个结果只是中间状态更新,且没有后代子智能体运行负责最终答案,cron 会再次提示一次以获取真实结果后再传递。
|
||||
隔离 cron 轮次会抑制仅包含过时确认的回复。如果第一个结果只是中间状态更新,且最终答案并不是由某个后代子智能体运行负责,cron 会再次提示一次以获取真实结果,然后再进行传递。
|
||||
|
||||
### 静默令牌抑制
|
||||
### 静默 token 抑制
|
||||
|
||||
如果隔离 cron 运行只返回静默令牌(`NO_REPLY` 或 `no_reply`),cron 会同时抑制直接对外传递和回退排队摘要路径,因此不会向聊天回发任何内容。
|
||||
如果隔离 cron 运行只返回静默 token(`NO_REPLY` 或 `no_reply`),cron 会同时抑制直接对外传递和后备排队摘要路径,因此不会向聊天回发任何内容。
|
||||
|
||||
### 结构化拒绝
|
||||
|
||||
隔离 cron 运行会优先使用来自嵌入式运行的结构化执行拒绝元数据,然后回退到最终输出中的已知拒绝标记,例如 `SYSTEM_RUN_DENIED`、`INVALID_REQUEST` 以及审批绑定拒绝短语。
|
||||
隔离 cron 运行会优先使用来自嵌入式运行的结构化执行拒绝元数据,然后再回退到最终输出中的已知拒绝标记,例如 `SYSTEM_RUN_DENIED`、`INVALID_REQUEST` 以及与审批绑定的拒绝措辞。
|
||||
|
||||
`cron list` 和运行历史会显示拒绝原因,而不是把被阻止的命令报告为 `ok`。
|
||||
|
||||
@ -156,7 +158,7 @@ Cron 的 `--model` 是**作业主模型**,而不是聊天会话的 `/model`
|
||||
## 迁移旧作业
|
||||
|
||||
<Note>
|
||||
如果你有当前传递与存储格式之前创建的 cron 作业,请运行 `openclaw doctor --fix`。Doctor 会规范化旧版 cron 字段(`jobId`、`schedule.cron`、顶层传递字段,包括旧版 `threadId`、负载中的 `provider` 传递别名),并在配置了 `cron.webhook` 时,将简单的 `notify: true` webhook 回退作业迁移为显式 webhook 传递。
|
||||
如果你有来自当前传递和存储格式之前的 cron 作业,请运行 `openclaw doctor --fix`。Doctor 会规范化旧版 cron 字段(`jobId`、`schedule.cron`、顶层传递字段,包括旧版 `threadId`、负载中的 `provider` 传递别名),并在配置了 `cron.webhook` 时,将简单的 `notify: true` webhook 后备作业迁移为显式 webhook 传递。
|
||||
</Note>
|
||||
|
||||
## 常见编辑
|
||||
@ -167,7 +169,7 @@ Cron 的 `--model` 是**作业主模型**,而不是聊天会话的 `/model`
|
||||
openclaw cron edit <job-id> --announce --channel telegram --to "123456789"
|
||||
```
|
||||
|
||||
禁用隔离作业的传递:
|
||||
为隔离作业禁用传递:
|
||||
|
||||
```bash
|
||||
openclaw cron edit <job-id> --no-deliver
|
||||
@ -179,13 +181,13 @@ openclaw cron edit <job-id> --no-deliver
|
||||
openclaw cron edit <job-id> --light-context
|
||||
```
|
||||
|
||||
向特定渠道 announce:
|
||||
向特定渠道进行 announce:
|
||||
|
||||
```bash
|
||||
openclaw cron edit <job-id> --announce --channel slack --to "channel:C1234567890"
|
||||
```
|
||||
|
||||
创建一个带有轻量级引导上下文的隔离作业:
|
||||
创建带轻量级引导上下文的隔离作业:
|
||||
|
||||
```bash
|
||||
openclaw cron add \
|
||||
@ -197,7 +199,7 @@ openclaw cron add \
|
||||
--no-deliver
|
||||
```
|
||||
|
||||
`--light-context` 仅适用于隔离的智能体回合作业。对于 cron 运行,轻量模式会保持引导上下文为空,而不是注入完整的工作区引导集合。
|
||||
`--light-context` 仅适用于隔离的智能体轮次作业。对于 cron 运行,轻量模式会保持引导上下文为空,而不是注入完整的工作区引导集。
|
||||
|
||||
## 常见管理命令
|
||||
|
||||
@ -211,9 +213,9 @@ openclaw cron run <job-id> --due
|
||||
openclaw cron runs --id <job-id> --limit 50
|
||||
```
|
||||
|
||||
`cron runs` 条目包含传递诊断信息,其中包括预期的 cron 目标、解析后的目标、message 工具发送、回退使用情况以及已传递状态。
|
||||
`cron runs` 条目包括传递诊断信息,其中包含预期的 cron 目标、已解析目标、message 工具发送、后备使用情况以及已传递状态。
|
||||
|
||||
智能体与会话重定向:
|
||||
智能体和会话重定向:
|
||||
|
||||
```bash
|
||||
openclaw cron edit <job-id> --agent ops
|
||||
|
||||
@ -7,10 +7,10 @@ sidebarTitle: Testing
|
||||
summary: OpenClaw 插件的测试工具与模式
|
||||
title: 插件测试
|
||||
x-i18n:
|
||||
generated_at: "2026-04-28T01:06:38Z"
|
||||
generated_at: "2026-04-28T01:18:58Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 0f9a601a4e4eb479c242ea3c59771c6a033dad6b75d616d9977c1614087dc002
|
||||
source_hash: dcc9f0340a651ab742150101ceb78b65ea450b90720bc06e96bb19535db3d83d
|
||||
source_path: plugins/sdk-testing.md
|
||||
workflow: 15
|
||||
---
|
||||
@ -18,20 +18,18 @@ x-i18n:
|
||||
OpenClaw 插件的测试工具、模式以及 lint 强制规则参考。
|
||||
|
||||
<Tip>
|
||||
**在找测试示例?** 操作指南包含完整的测试示例:
|
||||
**在找测试示例吗?** 操作指南包含已完成的测试示例:
|
||||
[渠道插件测试](/zh-CN/plugins/sdk-channel-plugins#step-6-test) 和
|
||||
[提供商插件测试](/zh-CN/plugins/sdk-provider-plugins#step-6-test)。
|
||||
</Tip>
|
||||
|
||||
## 测试工具
|
||||
|
||||
**兼容性导入:** `openclaw/plugin-sdk/testing`
|
||||
|
||||
**插件 API mock 导入:** `openclaw/plugin-sdk/plugin-test-api`
|
||||
**插件 API 模拟导入:** `openclaw/plugin-sdk/plugin-test-api`
|
||||
|
||||
**渠道契约导入:** `openclaw/plugin-sdk/channel-contract-testing`
|
||||
|
||||
**渠道测试辅助导入:** `openclaw/plugin-sdk/channel-test-helpers`
|
||||
**渠道测试辅助工具导入:** `openclaw/plugin-sdk/channel-test-helpers`
|
||||
|
||||
**渠道目标测试导入:** `openclaw/plugin-sdk/channel-target-testing`
|
||||
|
||||
@ -45,9 +43,8 @@ OpenClaw 插件的测试工具、模式以及 lint 强制规则参考。
|
||||
|
||||
**通用夹具导入:** `openclaw/plugin-sdk/test-fixtures`
|
||||
|
||||
对于新的插件测试,优先使用下面这些聚焦的子路径。较宽泛的
|
||||
`openclaw/plugin-sdk/testing` barrel 仍保留,用于兼容较旧的测试
|
||||
以及尚未迁移到更窄且已有文档说明的公开接口的辅助工具。
|
||||
对于新的插件测试,优先使用下面这些更聚焦的子路径。宽泛的
|
||||
`openclaw/plugin-sdk/testing` barrel 仅用于兼容旧版。
|
||||
|
||||
```typescript
|
||||
import {
|
||||
@ -67,55 +64,55 @@ import { createCliRuntimeCapture, typedCases } from "openclaw/plugin-sdk/test-fi
|
||||
|
||||
### 可用导出
|
||||
|
||||
| 导出 | 用途 |
|
||||
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `createTestPluginApi` | 为直接注册单元测试构建最小化的插件 API mock。从 `plugin-sdk/plugin-test-api` 导入 |
|
||||
| `expectChannelInboundContextContract` | 断言渠道入站上下文的结构。从 `plugin-sdk/channel-contract-testing` 导入 |
|
||||
| `installChannelOutboundPayloadContractSuite` | 安装渠道出站负载契约测试用例。从 `plugin-sdk/channel-contract-testing` 导入 |
|
||||
| `createStartAccountContext` | 构建渠道账户生命周期上下文。从 `plugin-sdk/channel-test-helpers` 导入 |
|
||||
| `installChannelActionsContractSuite` | 安装通用的渠道消息操作契约测试用例。从 `plugin-sdk/channel-test-helpers` 导入 |
|
||||
| `installChannelSetupContractSuite` | 安装通用的渠道设置契约测试用例。从 `plugin-sdk/channel-test-helpers` 导入 |
|
||||
| `installChannelStatusContractSuite` | 安装通用的渠道 Status 契约测试用例。从 `plugin-sdk/channel-test-helpers` 导入 |
|
||||
| `expectDirectoryIds` | 从目录列表函数中断言渠道目录 id。从 `plugin-sdk/channel-test-helpers` 导入 |
|
||||
| `describePluginRegistrationContract` | 安装插件注册契约检查。从 `plugin-sdk/plugin-test-contracts` 导入 |
|
||||
| `registerSingleProviderPlugin` | 在加载器冒烟测试中注册一个 provider 插件。从 `plugin-sdk/plugin-test-runtime` 导入 |
|
||||
| `registerProviderPlugin` | 从单个插件中捕获所有 provider 类型。从 `plugin-sdk/plugin-test-runtime` 导入 |
|
||||
| `registerProviderPlugins` | 跨多个插件捕获 provider 注册。从 `plugin-sdk/plugin-test-runtime` 导入 |
|
||||
| `requireRegisteredProvider` | 断言某个 provider 集合包含一个 id。从 `plugin-sdk/plugin-test-runtime` 导入 |
|
||||
| `createRuntimeEnv` | 构建一个已 mock 的 CLI / 插件运行时环境。从 `plugin-sdk/plugin-test-runtime` 导入 |
|
||||
| `createPluginSetupWizardStatus` | 为渠道插件构建设置向导 Status 辅助工具。从 `plugin-sdk/plugin-test-runtime` 导入 |
|
||||
| `describeOpenAIProviderRuntimeContract` | 安装 provider 系列运行时契约检查。从 `plugin-sdk/provider-test-contracts` 导入 |
|
||||
| `installCommonResolveTargetErrorCases` | 用于目标解析错误处理的共享测试用例。从 `plugin-sdk/channel-target-testing` 导入 |
|
||||
| `shouldAckReaction` | 检查渠道是否应添加确认反应。从 `plugin-sdk/channel-feedback` 导入 |
|
||||
| `removeAckReactionAfterReply` | 在回复发送后移除确认反应。从 `plugin-sdk/channel-feedback` 导入 |
|
||||
| `createTestRegistry` | 构建渠道插件注册表夹具。从 `plugin-sdk/plugin-test-runtime` 或 `plugin-sdk/channel-test-helpers` 导入 |
|
||||
| `createEmptyPluginRegistry` | 构建空的插件注册表夹具。从 `plugin-sdk/plugin-test-runtime` 或 `plugin-sdk/channel-test-helpers` 导入 |
|
||||
| `setActivePluginRegistry` | 为插件运行时测试安装注册表夹具。从 `plugin-sdk/plugin-test-runtime` 或 `plugin-sdk/channel-test-helpers` 导入 |
|
||||
| `createRequestCaptureJsonFetch` | 在媒体辅助测试中捕获 JSON fetch 请求。从 `plugin-sdk/test-env` 导入 |
|
||||
| `withFetchPreconnect` | 在安装了预连接钩子的情况下运行 fetch 测试。从 `plugin-sdk/test-env` 导入 |
|
||||
| `withEnv` / `withEnvAsync` | 临时修补环境变量。从 `plugin-sdk/test-env` 导入 |
|
||||
| `createTempHomeEnv` / `withTempDir` | 创建隔离的文件系统测试夹具。从 `plugin-sdk/test-env` 导入 |
|
||||
| `createMockServerResponse` | 创建最小化的 HTTP 服务器响应 mock。从 `plugin-sdk/test-env` 导入 |
|
||||
| `createCliRuntimeCapture` | 在测试中捕获 CLI 运行时输出。从 `plugin-sdk/test-fixtures` 导入 |
|
||||
| `createSandboxTestContext` | 构建沙箱测试上下文。从 `plugin-sdk/test-fixtures` 导入 |
|
||||
| `writeSkill` | 写入 Skills 夹具。从 `plugin-sdk/test-fixtures` 导入 |
|
||||
| `makeAgentAssistantMessage` | 构建智能体对话消息夹具。从 `plugin-sdk/test-fixtures` 导入 |
|
||||
| `peekSystemEvents` / `resetSystemEventsForTest` | 检查并重置系统事件夹具。从 `plugin-sdk/test-fixtures` 导入 |
|
||||
| `sanitizeTerminalText` | 清理终端输出以便断言。从 `plugin-sdk/test-fixtures` 导入 |
|
||||
| `countLines` / `hasBalancedFences` | 断言分块输出的结构。从 `plugin-sdk/test-fixtures` 导入 |
|
||||
| `runProviderCatalog` | 使用测试依赖执行 provider 目录钩子 |
|
||||
| `resolveProviderWizardOptions` | 在契约测试中解析 provider 设置向导选项 |
|
||||
| `resolveProviderModelPickerEntries` | 在契约测试中解析 provider 模型选择器条目 |
|
||||
| `buildProviderPluginMethodChoice` | 为断言构建 provider 向导选项 id |
|
||||
| `setProviderWizardProvidersResolverForTest` | 为隔离测试注入 provider 向导提供商 |
|
||||
| `createProviderUsageFetch` | 构建 provider 使用量 fetch 夹具 |
|
||||
| `useFrozenTime` / `useRealTime` | 为时间敏感测试冻结和恢复计时器。从 `plugin-sdk/test-env` 导入 |
|
||||
| `createTestWizardPrompter` | 构建一个已 mock 的设置向导提示器 |
|
||||
| `createRuntimeTaskFlow` | 创建隔离的运行时任务流状态 |
|
||||
| `typedCases` | 为表驱动测试保留字面量类型。从 `plugin-sdk/test-fixtures` 导入 |
|
||||
| Export | 用途 |
|
||||
| ----------------------------------------------- | -------------------------------------------------------------------- |
|
||||
| `createTestPluginApi` | 为直接注册单元测试构建一个最小化的插件 API 模拟。从 `plugin-sdk/plugin-test-api` 导入 |
|
||||
| `expectChannelInboundContextContract` | 断言渠道入站上下文的结构。从 `plugin-sdk/channel-contract-testing` 导入 |
|
||||
| `installChannelOutboundPayloadContractSuite` | 安装渠道出站负载契约测试用例。从 `plugin-sdk/channel-contract-testing` 导入 |
|
||||
| `createStartAccountContext` | 构建渠道账户生命周期上下文。从 `plugin-sdk/channel-test-helpers` 导入 |
|
||||
| `installChannelActionsContractSuite` | 安装通用渠道消息动作契约测试用例。从 `plugin-sdk/channel-test-helpers` 导入 |
|
||||
| `installChannelSetupContractSuite` | 安装通用渠道设置契约测试用例。从 `plugin-sdk/channel-test-helpers` 导入 |
|
||||
| `installChannelStatusContractSuite` | 安装通用渠道 Status 契约测试用例。从 `plugin-sdk/channel-test-helpers` 导入 |
|
||||
| `expectDirectoryIds` | 从目录列表函数中断言渠道目录 id。从 `plugin-sdk/channel-test-helpers` 导入 |
|
||||
| `describePluginRegistrationContract` | 安装插件注册契约检查。从 `plugin-sdk/plugin-test-contracts` 导入 |
|
||||
| `registerSingleProviderPlugin` | 在加载器冒烟测试中注册一个提供商插件。从 `plugin-sdk/plugin-test-runtime` 导入 |
|
||||
| `registerProviderPlugin` | 从单个插件中捕获所有提供商类型。从 `plugin-sdk/plugin-test-runtime` 导入 |
|
||||
| `registerProviderPlugins` | 在多个插件之间捕获提供商注册。从 `plugin-sdk/plugin-test-runtime` 导入 |
|
||||
| `requireRegisteredProvider` | 断言一个提供商集合包含某个 id。从 `plugin-sdk/plugin-test-runtime` 导入 |
|
||||
| `createRuntimeEnv` | 构建一个模拟的 CLI / 插件运行时环境。从 `plugin-sdk/plugin-test-runtime` 导入 |
|
||||
| `createPluginSetupWizardStatus` | 为渠道插件构建设置向导 Status 辅助工具。从 `plugin-sdk/plugin-test-runtime` 导入 |
|
||||
| `describeOpenAIProviderRuntimeContract` | 安装提供商家族运行时契约检查。从 `plugin-sdk/provider-test-contracts` 导入 |
|
||||
| `installCommonResolveTargetErrorCases` | 目标解析错误处理的共享测试用例。从 `plugin-sdk/channel-target-testing` 导入 |
|
||||
| `shouldAckReaction` | 检查渠道是否应添加 ack reaction。从 `plugin-sdk/channel-feedback` 导入 |
|
||||
| `removeAckReactionAfterReply` | 在回复发送后移除 ack reaction。从 `plugin-sdk/channel-feedback` 导入 |
|
||||
| `createTestRegistry` | 构建一个渠道插件注册表夹具。从 `plugin-sdk/plugin-test-runtime` 或 `plugin-sdk/channel-test-helpers` 导入 |
|
||||
| `createEmptyPluginRegistry` | 构建一个空的插件注册表夹具。从 `plugin-sdk/plugin-test-runtime` 或 `plugin-sdk/channel-test-helpers` 导入 |
|
||||
| `setActivePluginRegistry` | 为插件运行时测试安装一个注册表夹具。从 `plugin-sdk/plugin-test-runtime` 或 `plugin-sdk/channel-test-helpers` 导入 |
|
||||
| `createRequestCaptureJsonFetch` | 在媒体辅助测试中捕获 JSON fetch 请求。从 `plugin-sdk/test-env` 导入 |
|
||||
| `withFetchPreconnect` | 在安装 preconnect 钩子的情况下运行 fetch 测试。从 `plugin-sdk/test-env` 导入 |
|
||||
| `withEnv` / `withEnvAsync` | 临时修改环境变量。从 `plugin-sdk/test-env` 导入 |
|
||||
| `createTempHomeEnv` / `withTempDir` | 创建隔离的文件系统测试夹具。从 `plugin-sdk/test-env` 导入 |
|
||||
| `createMockServerResponse` | 创建一个最小化的 HTTP 服务器响应模拟。从 `plugin-sdk/test-env` 导入 |
|
||||
| `createCliRuntimeCapture` | 在测试中捕获 CLI 运行时输出。从 `plugin-sdk/test-fixtures` 导入 |
|
||||
| `createSandboxTestContext` | 构建沙箱测试上下文。从 `plugin-sdk/test-fixtures` 导入 |
|
||||
| `writeSkill` | 写入 Skills 夹具。从 `plugin-sdk/test-fixtures` 导入 |
|
||||
| `makeAgentAssistantMessage` | 构建智能体转录消息夹具。从 `plugin-sdk/test-fixtures` 导入 |
|
||||
| `peekSystemEvents` / `resetSystemEventsForTest` | 检查并重置系统事件夹具。从 `plugin-sdk/test-fixtures` 导入 |
|
||||
| `sanitizeTerminalText` | 清理终端输出以便断言。从 `plugin-sdk/test-fixtures` 导入 |
|
||||
| `countLines` / `hasBalancedFences` | 断言分块输出的结构。从 `plugin-sdk/test-fixtures` 导入 |
|
||||
| `runProviderCatalog` | 使用测试依赖执行提供商目录钩子 |
|
||||
| `resolveProviderWizardOptions` | 在契约测试中解析提供商设置向导选项 |
|
||||
| `resolveProviderModelPickerEntries` | 在契约测试中解析提供商模型选择器条目 |
|
||||
| `buildProviderPluginMethodChoice` | 为断言构建提供商向导选项 id |
|
||||
| `setProviderWizardProvidersResolverForTest` | 为隔离测试注入提供商向导提供商 |
|
||||
| `createProviderUsageFetch` | 构建提供商用量 fetch 夹具 |
|
||||
| `useFrozenTime` / `useRealTime` | 冻结并恢复计时器,用于时间敏感测试。从 `plugin-sdk/test-env` 导入 |
|
||||
| `createTestWizardPrompter` | 构建一个模拟的设置向导提示器 |
|
||||
| `createRuntimeTaskFlow` | 创建隔离的运行时任务流状态 |
|
||||
| `typedCases` | 为表驱动测试保留字面量类型。从 `plugin-sdk/test-fixtures` 导入 |
|
||||
|
||||
内置插件契约测试套件也会使用 SDK 测试子路径中的仅测试用注册表、清单、公共产物和运行时夹具辅助工具。依赖内置 OpenClaw 清单的仅 core 测试套件仍保留在 `src/plugins/contracts` 下。
|
||||
新的扩展测试应放在有文档说明的聚焦 SDK 子路径上,例如
|
||||
内置插件契约测试套件也会使用 SDK 测试子路径中的仅测试用注册表、manifest、公开产物和运行时夹具辅助工具。依赖内置 OpenClaw 清单的仅核心测试套件仍保留在 `src/plugins/contracts` 下。
|
||||
新的扩展测试应保持使用已记录的聚焦 SDK 子路径,例如
|
||||
`plugin-sdk/plugin-test-api`、`plugin-sdk/channel-contract-testing`、
|
||||
`plugin-sdk/channel-test-helpers`、`plugin-sdk/plugin-test-contracts`、
|
||||
`plugin-sdk/plugin-test-runtime`、`plugin-sdk/provider-test-contracts`、
|
||||
@ -125,17 +122,15 @@ import { createCliRuntimeCapture, typedCases } from "openclaw/plugin-sdk/test-fi
|
||||
|
||||
### 类型
|
||||
|
||||
测试子路径还会重新导出一些在测试文件中很有用的类型:
|
||||
聚焦的测试子路径也会重新导出测试文件中有用的类型:
|
||||
|
||||
```typescript
|
||||
import type {
|
||||
ChannelAccountSnapshot,
|
||||
ChannelGatewayContext,
|
||||
OpenClawConfig,
|
||||
PluginRuntime,
|
||||
RuntimeEnv,
|
||||
MockFn,
|
||||
} from "openclaw/plugin-sdk/testing";
|
||||
} from "openclaw/plugin-sdk/channel-contract";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-types";
|
||||
import type { MockFn, PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk/plugin-test-runtime";
|
||||
```
|
||||
|
||||
## 测试目标解析
|
||||
@ -166,20 +161,19 @@ describe("my-channel target resolution", () => {
|
||||
|
||||
### 测试注册契约
|
||||
|
||||
将手写的 `api` mock 传给 `register(api)` 的单元测试,并不会覆盖
|
||||
OpenClaw 加载器的接入门禁。对于你的插件所依赖的每一种注册接口,至少添加一个由加载器驱动的冒烟测试,尤其是钩子以及像 memory 这类独占能力。
|
||||
将手写的 `api` mock 传给 `register(api)` 的单元测试,并不会覆盖 OpenClaw 加载器的接受门禁。对于你的插件所依赖的每个注册入口,至少添加一个由加载器驱动的冒烟测试,尤其是钩子和诸如 memory 之类的独占能力。
|
||||
|
||||
真实加载器会在缺少必需元数据,或插件调用了自己并不拥有的能力 API 时使插件注册失败。例如,
|
||||
`api.registerHook(...)` 需要一个钩子名称,而
|
||||
真实加载器会在缺少必需元数据,或者插件调用了自己并不拥有的能力 API 时拒绝插件注册。例如,
|
||||
`api.registerHook(...)` 需要一个 hook 名称,而
|
||||
`api.registerMemoryCapability(...)` 则要求插件 manifest 或导出的入口声明 `kind: "memory"`。
|
||||
|
||||
### 测试运行时配置访问
|
||||
|
||||
在测试内置渠道插件时,优先使用来自 `openclaw/plugin-sdk/channel-test-helpers` 的共享插件运行时 mock。
|
||||
其中已弃用的 `runtime.config.loadConfig()` 和
|
||||
`runtime.config.writeConfigFile(...)` mock 默认会抛出异常,这样测试就能捕获对兼容性 API 的新使用。只有当测试明确覆盖旧版兼容行为时,才重写这些 mock。
|
||||
在测试内置渠道插件时,优先使用来自 `openclaw/plugin-sdk/channel-test-helpers`
|
||||
的共享插件运行时 mock。它的已弃用 `runtime.config.loadConfig()` 和
|
||||
`runtime.config.writeConfigFile(...)` mock 默认会抛出错误,这样测试就能捕获对兼容性 API 的新使用。只有当测试明确覆盖旧版兼容行为时,才重写这些 mock。
|
||||
|
||||
### 对渠道插件进行单元测试
|
||||
### 渠道插件的单元测试
|
||||
|
||||
```typescript
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
@ -215,7 +209,7 @@ describe("my-channel plugin", () => {
|
||||
});
|
||||
```
|
||||
|
||||
### 对 provider 插件进行单元测试
|
||||
### 提供商插件的单元测试
|
||||
|
||||
```typescript
|
||||
import { describe, it, expect } from "vitest";
|
||||
@ -243,9 +237,9 @@ describe("my-provider plugin", () => {
|
||||
});
|
||||
```
|
||||
|
||||
### mock 插件运行时
|
||||
### 模拟插件运行时
|
||||
|
||||
对于使用 `createPluginRuntimeStore` 的代码,请在测试中 mock 运行时:
|
||||
对于使用 `createPluginRuntimeStore` 的代码,在测试中应模拟运行时:
|
||||
|
||||
```typescript
|
||||
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
||||
@ -276,9 +270,9 @@ store.setRuntime(mockRuntime);
|
||||
store.clearRuntime();
|
||||
```
|
||||
|
||||
### 使用实例级 stub 进行测试
|
||||
### 使用按实例 stub 进行测试
|
||||
|
||||
优先使用实例级 stub,而不是修改原型:
|
||||
优先使用按实例 stub,而不是修改原型:
|
||||
|
||||
```typescript
|
||||
// Preferred: per-instance stub
|
||||
@ -291,7 +285,7 @@ client.sendMessage = vi.fn().mockResolvedValue({ id: "msg-1" });
|
||||
|
||||
## 契约测试(仓库内插件)
|
||||
|
||||
内置插件带有用于验证注册归属的契约测试:
|
||||
内置插件有契约测试,用于验证注册归属:
|
||||
|
||||
```bash
|
||||
pnpm test -- src/plugins/contracts/
|
||||
@ -299,14 +293,14 @@ pnpm test -- src/plugins/contracts/
|
||||
|
||||
这些测试会断言:
|
||||
|
||||
- 哪些插件注册了哪些 provider
|
||||
- 哪些插件注册了哪些语音 provider
|
||||
- 注册结构的正确性
|
||||
- 运行时契约合规性
|
||||
- 哪些插件注册了哪些提供商
|
||||
- 哪些插件注册了哪些语音提供商
|
||||
- 注册结构是否正确
|
||||
- 运行时契约是否合规
|
||||
|
||||
### 运行特定范围的测试
|
||||
### 运行限定范围测试
|
||||
|
||||
对于某个特定插件:
|
||||
针对特定插件:
|
||||
|
||||
```bash
|
||||
pnpm test -- <bundled-plugin-root>/my-channel/
|
||||
@ -320,19 +314,19 @@ pnpm test -- src/plugins/contracts/auth.contract.test.ts
|
||||
pnpm test -- src/plugins/contracts/runtime.contract.test.ts
|
||||
```
|
||||
|
||||
## Lint 强制规则(仓库内插件)
|
||||
## lint 强制规则(仓库内插件)
|
||||
|
||||
对于仓库内插件,`pnpm check` 会强制执行三条规则:
|
||||
|
||||
1. **禁止使用单体根导入** —— 拒绝使用 `openclaw/plugin-sdk` 根 barrel
|
||||
1. **禁止整体式根导入** —— 会拒绝 `openclaw/plugin-sdk` 根 barrel
|
||||
2. **禁止直接导入 `src/`** —— 插件不能直接导入 `../../src/`
|
||||
3. **禁止自我导入** —— 插件不能导入它们自己的 `plugin-sdk/<name>` 子路径
|
||||
3. **禁止自导入** —— 插件不能导入自己的 `plugin-sdk/<name>` 子路径
|
||||
|
||||
外部插件不受这些 lint 规则约束,但仍建议遵循相同模式。
|
||||
|
||||
## 测试配置
|
||||
|
||||
OpenClaw 使用带有 V8 覆盖率阈值的 Vitest。对于插件测试:
|
||||
OpenClaw 使用 Vitest,并启用 V8 覆盖率阈值。对于插件测试:
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
---
|
||||
read_when:
|
||||
- 你想通过 Ollama 使用云端或本地模型运行 OpenClaw
|
||||
- 你想通过 Ollama 使用云端或本地模型来运行 OpenClaw
|
||||
- 你需要 Ollama 的设置和配置指南
|
||||
- 你想使用 Ollama 的视觉模型进行图像理解
|
||||
summary: 使用 Ollama 运行 OpenClaw(云端和本地模型)
|
||||
- 你想使用 Ollama 视觉模型进行图像理解
|
||||
summary: 使用 Ollama(云端和本地模型)运行 OpenClaw
|
||||
title: Ollama
|
||||
x-i18n:
|
||||
generated_at: "2026-04-27T23:44:15Z"
|
||||
generated_at: "2026-04-28T01:18:58Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 814d7e9380e31e7f5a8db7afd9230e42979d8bb1fcaf5473dd9bb1f415a8b2ac
|
||||
source_hash: 80709e808ebcb694574e95a4269d29ade0532fe69f0f0386c2796871627560e6
|
||||
source_path: providers/ollama.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
OpenClaw 通过 Ollama 的原生 API(`/api/chat`)集成托管云模型和本地/自托管 Ollama 服务器。你可以通过三种模式使用 Ollama:通过可访问的 Ollama 主机使用 `Cloud + Local`、针对 `https://ollama.com` 的 `Cloud only`,或针对可访问 Ollama 主机的 `Local only`。
|
||||
OpenClaw 通过 Ollama 的原生 API(`/api/chat`)集成已托管的云模型以及本地/自托管的 Ollama 服务器。你可以通过三种模式使用 Ollama:通过可访问的 Ollama 主机使用 `Cloud + Local`、针对 `https://ollama.com` 使用 `Cloud only`,或针对可访问的 Ollama 主机使用 `Local only`。
|
||||
|
||||
<Warning>
|
||||
**远程 Ollama 用户**:不要在 OpenClaw 中使用 `/v1` OpenAI 兼容 URL(`http://host:11434/v1`)。这会破坏工具调用,而且模型可能会将原始工具 JSON 作为纯文本输出。请改用原生 Ollama API URL:`baseUrl: "http://host:11434"`(不带 `/v1`)。
|
||||
**远程 Ollama 用户**:不要在 OpenClaw 中使用 `/v1` OpenAI 兼容 URL(`http://host:11434/v1`)。这会破坏工具调用,模型还可能将原始工具 JSON 作为纯文本输出。请改用原生 Ollama API URL:`baseUrl: "http://host:11434"`(不带 `/v1`)。
|
||||
</Warning>
|
||||
|
||||
Ollama provider 配置使用 `baseUrl` 作为规范键名。OpenClaw 也接受 `baseURL` 以兼容 OpenAI SDK 风格的示例,但新配置应优先使用 `baseUrl`。
|
||||
@ -29,28 +29,28 @@ Ollama provider 配置使用 `baseUrl` 作为规范键名。OpenClaw 也接受 `
|
||||
本地和局域网 Ollama 主机不需要真实的 bearer token。OpenClaw 仅对 loopback、私有网络、`.local` 和裸主机名的 Ollama base URL 使用本地 `ollama-local` 标记。
|
||||
</Accordion>
|
||||
<Accordion title="远程和 Ollama Cloud 主机">
|
||||
远程公共主机和 Ollama Cloud(`https://ollama.com`)需要通过 `OLLAMA_API_KEY`、auth profile 或 provider 的 `apiKey` 提供真实凭证。
|
||||
远程公共主机和 Ollama Cloud(`https://ollama.com`)需要通过 `OLLAMA_API_KEY`、认证配置文件或提供商的 `apiKey` 提供真实凭证。
|
||||
</Accordion>
|
||||
<Accordion title="自定义 provider id">
|
||||
设置了 `api: "ollama"` 的自定义 provider id 遵循相同规则。例如,指向私有局域网 Ollama 主机的 `ollama-remote` provider 可以使用 `apiKey: "ollama-local"`,子智能体会通过 Ollama provider hook 解析该标记,而不是将其视为缺失凭证。
|
||||
设置了 `api: "ollama"` 的自定义 provider id 遵循相同规则。例如,指向私有局域网 Ollama 主机的 `ollama-remote` provider 可以使用 `apiKey: "ollama-local"`,子智能体将通过 Ollama provider hook 解析该标记,而不是将其视为缺失凭证。
|
||||
</Accordion>
|
||||
<Accordion title="内存嵌入作用域">
|
||||
当 Ollama 用于内存嵌入时,bearer auth 的作用域限定为声明它的主机:
|
||||
<Accordion title="Memory 嵌入作用域">
|
||||
当 Ollama 用于 Memory 嵌入时,bearer 认证的作用域限定为声明它的主机:
|
||||
|
||||
- provider 级别的 key 仅发送到该 provider 的 Ollama 主机。
|
||||
- `agents.*.memorySearch.remote.apiKey` 仅发送到其远程嵌入主机。
|
||||
- 纯 `OLLAMA_API_KEY` 环境变量值被视为 Ollama Cloud 约定,默认不会发送到本地或自托管主机。
|
||||
- provider 级别的密钥只会发送到该 provider 的 Ollama 主机。
|
||||
- `agents.*.memorySearch.remote.apiKey` 只会发送到它自己的远程嵌入主机。
|
||||
- 纯 `OLLAMA_API_KEY` 环境变量值会被视为 Ollama Cloud 约定,默认不会发送到本地或自托管主机。
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## 入门指南
|
||||
|
||||
选择你偏好的设置方法和模式。
|
||||
选择你偏好的设置方式和模式。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="新手引导(推荐)">
|
||||
**最适合:** 以最快方式完成可用的 Ollama 云端或本地设置。
|
||||
**最适合:** 以最快路径完成可用的 Ollama 云端或本地设置。
|
||||
|
||||
<Steps>
|
||||
<Step title="运行新手引导">
|
||||
@ -58,15 +58,15 @@ Ollama provider 配置使用 `baseUrl` 作为规范键名。OpenClaw 也接受 `
|
||||
openclaw onboard
|
||||
```
|
||||
|
||||
在 provider 列表中选择 **Ollama**。
|
||||
从 provider 列表中选择 **Ollama**。
|
||||
</Step>
|
||||
<Step title="选择你的模式">
|
||||
- **Cloud + Local** — 本地 Ollama 主机,加上通过该主机路由的云模型
|
||||
- **Cloud only** — 通过 `https://ollama.com` 使用托管的 Ollama 模型
|
||||
- **Local only** — 仅使用本地模型
|
||||
- **Cloud + Local** —— 本地 Ollama 主机,加上通过该主机路由的云模型
|
||||
- **Cloud only** —— 通过 `https://ollama.com` 使用托管的 Ollama 模型
|
||||
- **Local only** —— 仅使用本地模型
|
||||
</Step>
|
||||
<Step title="选择模型">
|
||||
`Cloud only` 会提示输入 `OLLAMA_API_KEY` 并推荐托管云端默认模型。`Cloud + Local` 和 `Local only` 会要求提供 Ollama base URL,发现可用模型,并在所选本地模型尚不可用时自动拉取它。当 Ollama 报告已安装的 `:latest` 标签(例如 `gemma4:latest`)时,设置界面只显示该已安装模型一次,而不会同时显示 `gemma4` 和 `gemma4:latest`,也不会再次拉取裸别名。`Cloud + Local` 还会检查该 Ollama 主机是否已登录以访问云端。
|
||||
`Cloud only` 会提示输入 `OLLAMA_API_KEY` 并推荐托管云端默认模型。`Cloud + Local` 和 `Local only` 会要求提供一个 Ollama base URL,发现可用模型,并在所选本地模型尚不可用时自动拉取它。当 Ollama 报告已安装的 `:latest` 标签(例如 `gemma4:latest`)时,设置过程只会显示这个已安装模型一次,而不会同时显示 `gemma4` 和 `gemma4:latest`,也不会再次拉取不带标签的别名。`Cloud + Local` 还会检查该 Ollama 主机是否已登录以获得云访问权限。
|
||||
</Step>
|
||||
<Step title="验证模型可用">
|
||||
```bash
|
||||
@ -101,7 +101,7 @@ Ollama provider 配置使用 `baseUrl` 作为规范键名。OpenClaw 也接受 `
|
||||
<Steps>
|
||||
<Step title="选择云端或本地">
|
||||
- **Cloud + Local**:安装 Ollama,使用 `ollama signin` 登录,并通过该主机路由云请求
|
||||
- **Cloud only**:使用 `https://ollama.com` 并配置 `OLLAMA_API_KEY`
|
||||
- **Cloud only**:使用带有 `OLLAMA_API_KEY` 的 `https://ollama.com`
|
||||
- **Local only**:从 [ollama.com/download](https://ollama.com/download) 安装 Ollama
|
||||
</Step>
|
||||
<Step title="拉取本地模型(仅本地)">
|
||||
@ -114,7 +114,7 @@ Ollama provider 配置使用 `baseUrl` 作为规范键名。OpenClaw 也接受 `
|
||||
```
|
||||
</Step>
|
||||
<Step title="为 OpenClaw 启用 Ollama">
|
||||
对于 `Cloud only`,请使用真实的 `OLLAMA_API_KEY`。对于基于主机的设置,任意占位值都可以:
|
||||
对于 `Cloud only`,请使用你真实的 `OLLAMA_API_KEY`。对于依赖主机的设置,任何占位值都可以:
|
||||
|
||||
```bash
|
||||
# 云端
|
||||
@ -127,13 +127,13 @@ Ollama provider 配置使用 `baseUrl` 作为规范键名。OpenClaw 也接受 `
|
||||
openclaw config set models.providers.ollama.apiKey "OLLAMA_API_KEY"
|
||||
```
|
||||
</Step>
|
||||
<Step title="查看并设置你的模型">
|
||||
<Step title="检查并设置你的模型">
|
||||
```bash
|
||||
openclaw models list
|
||||
openclaw models set ollama/gemma4
|
||||
```
|
||||
|
||||
或在配置中设置默认值:
|
||||
或者在配置中设置默认值:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -154,25 +154,25 @@ Ollama provider 配置使用 `baseUrl` 作为规范键名。OpenClaw 也接受 `
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Cloud + Local">
|
||||
`Cloud + Local` 使用可访问的 Ollama 主机作为本地模型和云模型的统一控制点。这是 Ollama 推荐的混合流程。
|
||||
`Cloud + Local` 使用一个可访问的 Ollama 主机作为本地模型和云模型的统一控制点。这是 Ollama 首选的混合流程。
|
||||
|
||||
在设置期间使用 **Cloud + Local**。OpenClaw 会提示输入 Ollama base URL,从该主机发现本地模型,并检查该主机是否已通过 `ollama signin` 登录以访问云端。当主机已登录时,OpenClaw 还会推荐托管云端默认模型,例如 `kimi-k2.5:cloud`、`minimax-m2.7:cloud` 和 `glm-5.1:cloud`。
|
||||
在设置期间使用 **Cloud + Local**。OpenClaw 会提示输入 Ollama base URL,从该主机发现本地模型,并检查该主机是否已通过 `ollama signin` 登录以获得云访问权限。当主机已登录时,OpenClaw 还会推荐托管云端默认模型,例如 `kimi-k2.5:cloud`、`minimax-m2.7:cloud` 和 `glm-5.1:cloud`。
|
||||
|
||||
如果主机尚未登录,OpenClaw 会保持为仅本地设置,直到你运行 `ollama signin`。
|
||||
如果主机尚未登录,OpenClaw 会将设置保持为仅本地模式,直到你运行 `ollama signin`。
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="Cloud only">
|
||||
`Cloud only` 运行在 Ollama 的托管 API `https://ollama.com` 上。
|
||||
`Cloud only` 通过 Ollama 的托管 API `https://ollama.com` 运行。
|
||||
|
||||
在设置期间使用 **Cloud only**。OpenClaw 会提示输入 `OLLAMA_API_KEY`,设置 `baseUrl: "https://ollama.com"`,并填充托管云模型列表。此路径 **不** 需要本地 Ollama 服务器或 `ollama signin`。
|
||||
在设置期间使用 **Cloud only**。OpenClaw 会提示输入 `OLLAMA_API_KEY`,设置 `baseUrl: "https://ollama.com"`,并预填充托管云模型列表。此路径**不**需要本地 Ollama 服务器或 `ollama signin`。
|
||||
|
||||
`openclaw onboard` 期间显示的云模型列表会从 `https://ollama.com/api/tags` 实时填充,上限为 500 条,因此选择器反映的是当前托管目录,而不是静态种子列表。如果设置时 `ollama.com` 不可访问或未返回模型,OpenClaw 会回退到之前硬编码的建议值,以便新手引导仍可完成。
|
||||
在 `openclaw onboard` 期间显示的云模型列表会从 `https://ollama.com/api/tags` 实时获取,最多 500 个条目,因此模型选择器反映的是当前托管目录,而不是静态预设。如果设置时 `ollama.com` 无法访问或未返回模型,OpenClaw 会回退到之前的硬编码推荐,以便新手引导仍能完成。
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="Local only">
|
||||
在仅本地模式下,OpenClaw 会从已配置的 Ollama 实例发现模型。此路径适用于本地或自托管的 Ollama 服务器。
|
||||
在仅本地模式下,OpenClaw 会从配置的 Ollama 实例中发现模型。此路径适用于本地或自托管的 Ollama 服务器。
|
||||
|
||||
OpenClaw 当前推荐 `gemma4` 作为本地默认模型。
|
||||
|
||||
@ -181,27 +181,26 @@ Ollama provider 配置使用 `baseUrl` 作为规范键名。OpenClaw 也接受 `
|
||||
|
||||
## 模型发现(隐式 provider)
|
||||
|
||||
当你设置 `OLLAMA_API_KEY`(或 auth profile),并且**没有**定义 `models.providers.ollama` 或其他设置了 `api: "ollama"` 的自定义远程 provider 时,OpenClaw 会从 `http://127.0.0.1:11434` 上的本地 Ollama 实例发现模型。
|
||||
当你设置了 `OLLAMA_API_KEY`(或认证配置文件),并且**没有**定义 `models.providers.ollama` 或其他设置了 `api: "ollama"` 的自定义远程 provider 时,OpenClaw 会从 `http://127.0.0.1:11434` 的本地 Ollama 实例发现模型。
|
||||
|
||||
| 行为 | 详情 |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 目录查询 | 查询 `/api/tags` |
|
||||
| 能力检测 | 使用尽力而为的 `/api/show` 查询来读取 `contextWindow`、扩展的 `num_ctx` Modelfile 参数,以及包括 vision/tools 在内的能力 |
|
||||
| 视觉模型 | `/api/show` 报告具有 `vision` 能力的模型会被标记为支持图像(`input: ["text", "image"]`),因此 OpenClaw 会自动将图像注入提示中 |
|
||||
| 推理检测 | 使用模型名称启发式规则(`r1`、`reasoning`、`think`)标记 `reasoning` |
|
||||
| token 限制 | 将 `maxTokens` 设置为 OpenClaw 使用的默认 Ollama 最大 token 上限 |
|
||||
| 能力检测 | 使用尽力而为的 `/api/show` 查询来读取 `contextWindow`、扩展的 `num_ctx` Modelfile 参数,以及包括视觉/工具在内的能力 |
|
||||
| 视觉模型 | 由 `/api/show` 报告具有 `vision` 能力的模型会被标记为支持图像(`input: ["text", "image"]`),因此 OpenClaw 会自动将图像注入提示词 |
|
||||
| 推理检测 | 通过模型名称启发式(`r1`、`reasoning`、`think`)标记 `reasoning` |
|
||||
| Token 限制 | 将 `maxTokens` 设置为 OpenClaw 使用的默认 Ollama 最大 token 上限 |
|
||||
| 成本 | 将所有成本设置为 `0` |
|
||||
|
||||
这样可以避免手动录入模型条目,同时使目录与本地 Ollama 实例保持一致。你可以在本地 `infer model run` 中使用完整引用,例如 `ollama/<pulled-model>:latest`;OpenClaw 会从 Ollama 的实时目录中解析该已安装模型,而不需要手写 `models.json` 条目。
|
||||
这样可以避免手动录入模型条目,同时让目录与本地 Ollama 实例保持一致。你可以在本地 `infer model run` 中使用完整引用,如 `ollama/<pulled-model>:latest`;OpenClaw 会从 Ollama 的实时目录中解析这个已安装模型,而不需要手写 `models.json` 条目。
|
||||
|
||||
```bash
|
||||
# 查看有哪些模型可用
|
||||
# 查看可用模型
|
||||
ollama list
|
||||
openclaw models list
|
||||
```
|
||||
|
||||
若要进行一个避免完整智能体工具表面的精简文本生成冒烟测试,
|
||||
可在本地 `infer model run` 中使用完整的 Ollama 模型引用:
|
||||
如需进行一个只关注文本生成、避免完整智能体工具表面的精简冒烟测试,可在本地 `infer model run` 中使用完整的 Ollama 模型引用:
|
||||
|
||||
```bash
|
||||
OLLAMA_API_KEY=ollama-local \
|
||||
@ -212,13 +211,13 @@ OLLAMA_API_KEY=ollama-local \
|
||||
--json
|
||||
```
|
||||
|
||||
该路径仍然使用 OpenClaw 已配置的 provider、auth 和原生 Ollama
|
||||
传输方式,但它不会启动聊天智能体轮次,也不会加载 MCP/工具上下文。如果
|
||||
这一步成功而普通智能体回复失败,请接下来排查模型的智能体提示/工具能力。
|
||||
该路径仍然使用 OpenClaw 配置的 provider、认证和原生 Ollama 传输,但不会启动聊天智能体轮次,也不会加载 MCP/工具上下文。如果这里成功,而普通智能体回复失败,请接下来排查该模型的智能体提示词/工具能力。
|
||||
|
||||
当你使用 `/model ollama/<model>` 切换会话时,OpenClaw 会将其视为精确的用户选择。如果已配置的 Ollama `baseUrl` 无法访问,下一个回复会以 provider 错误失败,而不会静默改用另一个已配置的回退模型进行回答。
|
||||
当你使用 `/model ollama/<model>` 切换会话模型时,OpenClaw 会将其视为精确的用户选择。如果配置的 Ollama `baseUrl` 无法访问,下一次回复会直接因 provider 错误而失败,而不会悄悄改用其他已配置的后备模型。
|
||||
|
||||
通过以下命令可针对本地 Ollama 实时验证本地文本路径、原生流式路径和嵌入:
|
||||
隔离的 cron 作业在启动智能体轮次之前,会额外执行一次本地安全检查。如果所选模型解析到本地、私有网络或 `.local` 的 Ollama provider,且 `/api/tags` 无法访问,OpenClaw 会将该次 cron 运行记录为 `skipped`,并在错误文本中包含所选的 `ollama/<model>`。该端点预检会缓存 5 分钟,因此多个指向同一已停止 Ollama 守护进程的 cron 作业不会都去发起失败的模型请求。
|
||||
|
||||
使用以下命令对本地文本路径、原生流式路径以及针对本地 Ollama 的嵌入进行实时验证:
|
||||
|
||||
```bash
|
||||
OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_OLLAMA=1 OPENCLAW_LIVE_OLLAMA_WEB_SEARCH=0 \
|
||||
@ -231,24 +230,24 @@ OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_OLLAMA=1 OPENCLAW_LIVE_OLLAMA_WEB_SEARCH=0 \
|
||||
ollama pull mistral
|
||||
```
|
||||
|
||||
新模型将被自动发现并可立即使用。
|
||||
新模型会被自动发现并可立即使用。
|
||||
|
||||
<Note>
|
||||
如果你显式设置了 `models.providers.ollama`,或配置了诸如 `models.providers.ollama-cloud` 且带有 `api: "ollama"` 的自定义远程 provider,则会跳过自动发现,你必须手动定义模型。像 `http://127.0.0.2:11434` 这样的 loopback 自定义 provider 仍被视为本地。请参阅下方的显式配置部分。
|
||||
如果你显式设置了 `models.providers.ollama`,或者配置了诸如 `models.providers.ollama-cloud` 这类带有 `api: "ollama"` 的自定义远程 provider,则会跳过自动发现,你必须手动定义模型。像 `http://127.0.0.2:11434` 这样的 loopback 自定义 provider 仍会被视为本地。请参阅下方的显式配置部分。
|
||||
</Note>
|
||||
|
||||
## 视觉与图像描述
|
||||
## 视觉和图像描述
|
||||
|
||||
内置的 Ollama 插件将 Ollama 注册为支持图像的媒体理解 provider。这使 OpenClaw 能够通过本地或托管的 Ollama 视觉模型路由显式图像描述请求和已配置的图像模型默认值。
|
||||
内置的 Ollama 插件将 Ollama 注册为支持图像的媒体理解 provider。这使 OpenClaw 能够通过本地或托管的 Ollama 视觉模型来路由显式图像描述请求以及已配置的图像模型默认值。
|
||||
|
||||
对于本地视觉模型,请拉取一个支持图像的模型:
|
||||
对于本地视觉功能,请拉取一个支持图像的模型:
|
||||
|
||||
```bash
|
||||
ollama pull qwen2.5vl:7b
|
||||
export OLLAMA_API_KEY="ollama-local"
|
||||
```
|
||||
|
||||
然后使用 infer CLI 验证:
|
||||
然后使用 infer CLI 进行验证:
|
||||
|
||||
```bash
|
||||
openclaw infer image describe \
|
||||
@ -257,7 +256,7 @@ openclaw infer image describe \
|
||||
--json
|
||||
```
|
||||
|
||||
`--model` 必须是完整的 `<provider/model>` 引用。设置后,`openclaw infer image describe` 会直接运行该模型,而不会因为模型支持原生视觉而跳过描述。
|
||||
`--model` 必须是完整的 `<provider/model>` 引用。设置后,`openclaw infer image describe` 会直接运行该模型,而不会因为模型支持原生视觉能力就跳过描述。
|
||||
|
||||
要让 Ollama 成为入站媒体的默认图像理解模型,请配置 `agents.defaults.imageModel`:
|
||||
|
||||
@ -273,7 +272,7 @@ openclaw infer image describe \
|
||||
}
|
||||
```
|
||||
|
||||
较慢的本地视觉模型可能需要比云模型更长的图像理解超时时间。当 Ollama 在受限硬件上尝试分配其声明的完整视觉上下文时,它们也可能崩溃或停止。为此请设置能力超时,并在你只需要普通图像描述轮次时,为模型条目限制 `num_ctx`:
|
||||
较慢的本地视觉模型可能需要比云模型更长的图像理解超时时间。当 Ollama 在受限硬件上尝试分配完整声明的视觉上下文时,它们也可能崩溃或停止。对于只需要普通图像描述轮次的场景,请设置能力超时,并在模型条目上限制 `num_ctx`:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -302,16 +301,16 @@ openclaw infer image describe \
|
||||
}
|
||||
```
|
||||
|
||||
此超时适用于入站图像理解,也适用于智能体在轮次期间可调用的显式 `image` 工具。provider 级别的 `models.providers.ollama.timeoutSeconds` 仍然控制普通模型调用底层的 Ollama HTTP 请求保护。
|
||||
这个超时同时适用于入站图像理解,以及智能体在轮次中可以调用的显式 `image` 工具。provider 级别的 `models.providers.ollama.timeoutSeconds` 仍然控制普通模型调用底层 Ollama HTTP 请求的保护超时。
|
||||
|
||||
通过以下命令可针对本地 Ollama 实时验证显式图像工具:
|
||||
使用以下命令对本地 Ollama 的显式图像工具进行实时验证:
|
||||
|
||||
```bash
|
||||
OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_OLLAMA_IMAGE=1 \
|
||||
pnpm test:live -- src/agents/tools/image-tool.ollama.live.test.ts
|
||||
```
|
||||
|
||||
如果你手动定义 `models.providers.ollama.models`,请将视觉模型标记为支持图像输入:
|
||||
如果你手动定义了 `models.providers.ollama.models`,请将视觉模型标记为支持图像输入:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -323,26 +322,26 @@ OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_OLLAMA_IMAGE=1 \
|
||||
}
|
||||
```
|
||||
|
||||
OpenClaw 会拒绝未标记为支持图像的模型所发起的图像描述请求。对于隐式发现,当 `/api/show` 报告了 vision 能力时,OpenClaw 会从 Ollama 读取该信息。
|
||||
OpenClaw 会拒绝未标记为支持图像能力的模型的图像描述请求。在隐式发现模式下,当 `/api/show` 报告视觉能力时,OpenClaw 会从 Ollama 读取该信息。
|
||||
|
||||
## 配置
|
||||
|
||||
<Tabs>
|
||||
<Tab title="基础(隐式发现)">
|
||||
最简单的仅本地启用方式是通过环境变量:
|
||||
最简单的仅本地启用方式是使用环境变量:
|
||||
|
||||
```bash
|
||||
export OLLAMA_API_KEY="ollama-local"
|
||||
```
|
||||
|
||||
<Tip>
|
||||
如果设置了 `OLLAMA_API_KEY`,你可以在 provider 条目中省略 `apiKey`,OpenClaw 会在可用性检查时自动补全它。
|
||||
如果设置了 `OLLAMA_API_KEY`,你可以在 provider 条目中省略 `apiKey`,OpenClaw 会自动填充它以执行可用性检查。
|
||||
</Tip>
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="显式配置(手动模型)">
|
||||
当你希望使用托管云端设置、Ollama 运行在其他主机/端口上、你想强制指定特定上下文窗口或模型列表,或者你想完全手动定义模型时,请使用显式配置。
|
||||
<Tab title="显式(手动模型)">
|
||||
当你需要托管云端设置、Ollama 运行在其他主机/端口、你想强制指定特定上下文窗口或模型列表,或者你希望完全手动定义模型时,请使用显式配置。
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -372,7 +371,7 @@ OpenClaw 会拒绝未标记为支持图像的模型所发起的图像描述请
|
||||
</Tab>
|
||||
|
||||
<Tab title="自定义 base URL">
|
||||
如果 Ollama 运行在不同的主机或端口上(显式配置会禁用自动发现,因此请手动定义模型):
|
||||
如果 Ollama 运行在不同的主机或端口上(显式配置会禁用自动发现,因此需要手动定义模型):
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -380,9 +379,9 @@ OpenClaw 会拒绝未标记为支持图像的模型所发起的图像描述请
|
||||
providers: {
|
||||
ollama: {
|
||||
apiKey: "ollama-local",
|
||||
baseUrl: "http://ollama-host:11434", // 不要加 /v1 - 使用原生 Ollama API URL
|
||||
baseUrl: "http://ollama-host:11434", // 不要加 /v1 —— 使用原生 Ollama API URL
|
||||
api: "ollama", // 显式设置以确保原生工具调用行为
|
||||
timeoutSeconds: 300, // 可选:为冷启动的本地模型提供更长的连接和流式传输时间
|
||||
timeoutSeconds: 300, // 可选:给冷启动的本地模型更长的连接和流式传输时间
|
||||
models: [
|
||||
{
|
||||
id: "qwen3:32b",
|
||||
@ -411,7 +410,7 @@ OpenClaw 会拒绝未标记为支持图像的模型所发起的图像描述请
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="带自动发现的本地模型">
|
||||
当 Ollama 与 Gateway 网关运行在同一台机器上,并且你希望 OpenClaw 自动发现已安装模型时,请使用此方式。
|
||||
当 Ollama 与 Gateway 网关 运行在同一台机器上,并且你希望 OpenClaw 自动发现已安装模型时,请使用此方式。
|
||||
|
||||
```bash
|
||||
ollama serve
|
||||
@ -421,7 +420,7 @@ OpenClaw 会拒绝未标记为支持图像的模型所发起的图像描述请
|
||||
openclaw models set ollama/gemma4
|
||||
```
|
||||
|
||||
这种方式可让配置保持最简。除非你想手动定义模型,否则不要添加 `models.providers.ollama` 块。
|
||||
这种方式可以保持配置最简。除非你想手动定义模型,否则不要添加 `models.providers.ollama` 配置块。
|
||||
|
||||
</Accordion>
|
||||
|
||||
@ -463,7 +462,7 @@ OpenClaw 会拒绝未标记为支持图像的模型所发起的图像描述请
|
||||
}
|
||||
```
|
||||
|
||||
`contextWindow` 是 OpenClaw 侧的上下文预算。`params.num_ctx` 会随请求发送给 Ollama。当你的硬件无法运行模型声明的完整上下文时,请保持两者一致。
|
||||
`contextWindow` 是 OpenClaw 侧的上下文预算。`params.num_ctx` 会随请求发送给 Ollama。当你的硬件无法运行模型声明的完整上下文时,请让两者保持一致。
|
||||
|
||||
</Accordion>
|
||||
|
||||
@ -506,7 +505,7 @@ OpenClaw 会拒绝未标记为支持图像的模型所发起的图像描述请
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="通过已登录守护进程同时使用云端和本地">
|
||||
当本地或局域网 Ollama 守护进程已通过 `ollama signin` 登录,并且应同时提供本地模型与 `:cloud` 模型时,请使用此方式。
|
||||
当本地或局域网 Ollama 守护进程已通过 `ollama signin` 登录,并且应同时提供本地模型和 `:cloud` 模型时,请使用此方式。
|
||||
|
||||
```bash
|
||||
ollama signin
|
||||
@ -543,7 +542,7 @@ OpenClaw 会拒绝未标记为支持图像的模型所发起的图像描述请
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="多个 Ollama 主机">
|
||||
当你拥有多个 Ollama 服务器时,请使用自定义 provider ID。每个 provider 都拥有自己的主机、模型、凭证、超时和模型引用。
|
||||
当你拥有多个 Ollama 服务器时,请使用自定义 provider ID。每个 provider 都有自己的主机、模型、认证、超时和模型引用。
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -578,12 +577,12 @@ OpenClaw 会拒绝未标记为支持图像的模型所发起的图像描述请
|
||||
}
|
||||
```
|
||||
|
||||
当 OpenClaw 发送请求时,活动的 provider 前缀会被去掉,因此 `ollama-large/qwen3.5:27b` 到达 Ollama 时会变成 `qwen3.5:27b`。
|
||||
当 OpenClaw 发送请求时,活动 provider 前缀会被移除,因此 `ollama-large/qwen3.5:27b` 到达 Ollama 时会变成 `qwen3.5:27b`。
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="精简本地模型配置">
|
||||
一些本地模型可以回答简单提示,但难以应对完整的智能体工具表面。在修改全局运行时设置之前,先从限制工具和上下文开始。
|
||||
有些本地模型可以回答简单提示,但在完整智能体工具表面下表现不佳。开始时应先限制工具和上下文,再考虑更改全局运行时设置。
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -617,8 +616,8 @@ OpenClaw 会拒绝未标记为支持图像的模型所发起的图像描述请
|
||||
}
|
||||
```
|
||||
|
||||
仅当模型或服务器在工具 schema 上稳定失败时,才使用 `compat.supportsTools: false`。它是用智能体能力换取稳定性。
|
||||
`localModelLean` 会从智能体表面移除浏览器、cron 和消息工具,但不会更改 Ollama 的运行时上下文或 thinking 模式。对于会循环或将回复预算耗在隐藏推理上的小型 Qwen 风格 thinking 模型,请将它与显式 `params.num_ctx` 和 `params.thinking: false` 搭配使用。
|
||||
仅当模型或服务器在工具 schema 上稳定失败时,才使用 `compat.supportsTools: false`。它会以降低智能体能力为代价来换取稳定性。
|
||||
`localModelLean` 会从智能体表面移除浏览器、cron 和消息工具,但不会改变 Ollama 的运行时上下文或 thinking 模式。对于会循环或把响应预算消耗在隐藏推理上的小型 Qwen 风格 thinking 模型,请将它与显式 `params.num_ctx` 和 `params.thinking: false` 搭配使用。
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@ -640,12 +639,9 @@ OpenClaw 会拒绝未标记为支持图像的模型所发起的图像描述请
|
||||
}
|
||||
```
|
||||
|
||||
也支持自定义 Ollama provider ID。当模型引用使用活动
|
||||
provider 前缀时,例如 `ollama-spark/qwen3:32b`,OpenClaw 在调用 Ollama 之前只会去掉该
|
||||
前缀,因此服务器接收到的是 `qwen3:32b`。
|
||||
也支持自定义 Ollama provider ID。当模型引用使用活动 provider 前缀时,例如 `ollama-spark/qwen3:32b`,OpenClaw 在调用 Ollama 前只会移除该前缀,因此服务器接收到的是 `qwen3:32b`。
|
||||
|
||||
对于较慢的本地模型,优先考虑调优 provider 作用域的请求参数,而不是提高整个
|
||||
智能体运行时超时:
|
||||
对于较慢的本地模型,优先考虑按 provider 范围调整请求,而不是提高整个智能体运行时超时:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -666,18 +662,15 @@ provider 前缀时,例如 `ollama-spark/qwen3:32b`,OpenClaw 在调用 Ollama
|
||||
}
|
||||
```
|
||||
|
||||
`timeoutSeconds` 适用于模型 HTTP 请求,包括连接建立、
|
||||
请求头、响应体流式传输,以及整个受保护获取的中止控制。`params.keep_alive`
|
||||
会作为顶层 `keep_alive` 转发给原生 `/api/chat` 请求中的 Ollama;
|
||||
当首轮加载时间是瓶颈时,请按模型设置它。
|
||||
`timeoutSeconds` 适用于模型 HTTP 请求,包括连接建立、headers、body 流式传输以及整个受保护获取的中止。`params.keep_alive` 会作为顶层 `keep_alive` 转发给原生 `/api/chat` 请求中的 Ollama;当首轮加载时间是瓶颈时,请按模型设置它。
|
||||
|
||||
### 快速验证
|
||||
|
||||
```bash
|
||||
# 当前机器可见的 Ollama 守护进程
|
||||
# 此机器可见的 Ollama 守护进程
|
||||
curl http://127.0.0.1:11434/api/tags
|
||||
|
||||
# OpenClaw 目录和已选模型
|
||||
# OpenClaw 模型目录和当前选中的模型
|
||||
openclaw models list --provider ollama
|
||||
openclaw models status
|
||||
|
||||
@ -687,7 +680,7 @@ openclaw infer model run \
|
||||
--prompt "Reply with exactly: ok"
|
||||
```
|
||||
|
||||
对于远程主机,请将 `127.0.0.1` 替换为 `baseUrl` 中使用的主机。如果 `curl` 可用但 OpenClaw 不可用,请检查 Gateway 网关是否运行在不同的机器、容器或服务账户下。
|
||||
对于远程主机,请将 `127.0.0.1` 替换为 `baseUrl` 中使用的主机。如果 `curl` 可用但 OpenClaw 不可用,请检查 Gateway 网关 是否运行在不同的机器、容器或服务账户下。
|
||||
|
||||
## Ollama Web 搜索
|
||||
|
||||
@ -695,11 +688,11 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
|
||||
| 属性 | 详情 |
|
||||
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 主机 | 使用你配置的 Ollama 主机(设置了 `models.providers.ollama.baseUrl` 时使用该值,否则为 `http://127.0.0.1:11434`);`https://ollama.com` 直接使用托管 API |
|
||||
| 凭证 | 对已登录的本地 Ollama 主机无需 key;对于直接 `https://ollama.com` 搜索或受保护主机,使用 `OLLAMA_API_KEY` 或已配置的 provider 凭证 |
|
||||
| 要求 | 本地/自托管主机必须正在运行,并且已通过 `ollama signin` 登录;直接托管搜索需要 `baseUrl: "https://ollama.com"` 加上真实的 Ollama API key |
|
||||
| 主机 | 使用你配置的 Ollama 主机(设置了 `models.providers.ollama.baseUrl` 时使用该值,否则使用 `http://127.0.0.1:11434`);`https://ollama.com` 会直接使用托管 API |
|
||||
| 认证 | 对已登录的本地 Ollama 主机无需密钥;对于直接通过 `https://ollama.com` 进行搜索或受认证保护的主机,则需要 `OLLAMA_API_KEY` 或已配置的 provider 认证 |
|
||||
| 要求 | 本地/自托管主机必须正在运行并通过 `ollama signin` 登录;直接使用托管搜索则需要 `baseUrl: "https://ollama.com"` 加上真实的 Ollama API key |
|
||||
|
||||
在 `openclaw onboard` 或 `openclaw configure --section web` 期间选择 **Ollama Web 搜索**,或者设置:
|
||||
在 `openclaw onboard` 或 `openclaw configure --section web` 期间选择 **Ollama Web 搜索**,或设置:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -713,7 +706,7 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
}
|
||||
```
|
||||
|
||||
对于通过 Ollama Cloud 的直接托管搜索:
|
||||
如需通过 Ollama Cloud 直接使用托管搜索:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -738,7 +731,7 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
对于已登录的本地守护进程,OpenClaw 使用该守护进程的 `/api/experimental/web_search` 代理。对于 `https://ollama.com`,它会直接调用托管的 `/api/web_search` 端点。
|
||||
|
||||
<Note>
|
||||
有关完整的设置和行为详情,请参阅 [Ollama Web 搜索](/zh-CN/tools/ollama-search)。
|
||||
完整设置和行为详情,请参阅 [Ollama Web 搜索](/zh-CN/tools/ollama-search)。
|
||||
</Note>
|
||||
|
||||
## 高级配置
|
||||
@ -746,10 +739,10 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
<AccordionGroup>
|
||||
<Accordion title="旧版 OpenAI 兼容模式">
|
||||
<Warning>
|
||||
**在 OpenAI 兼容模式下,工具调用并不可靠。** 仅当你需要为代理使用 OpenAI 格式,并且不依赖原生工具调用行为时,才使用此模式。
|
||||
**在 OpenAI 兼容模式下,工具调用并不可靠。** 仅当你需要通过代理使用 OpenAI 格式,且不依赖原生工具调用行为时,才使用此模式。
|
||||
</Warning>
|
||||
|
||||
如果你必须改用 OpenAI 兼容端点(例如,在仅支持 OpenAI 格式的代理后面),请显式设置 `api: "openai-completions"`:
|
||||
如果你需要改用 OpenAI 兼容端点(例如位于仅支持 OpenAI 格式的代理之后),请显式设置 `api: "openai-completions"`:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -767,9 +760,9 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
}
|
||||
```
|
||||
|
||||
该模式可能无法同时支持流式传输和工具调用。你可能需要在模型配置中使用 `params: { streaming: false }` 禁用流式传输。
|
||||
在此模式下,可能无法同时支持流式传输和工具调用。你可能需要在模型配置中使用 `params: { streaming: false }` 关闭流式传输。
|
||||
|
||||
当 Ollama 使用 `api: "openai-completions"` 时,OpenClaw 默认会注入 `options.num_ctx`,以避免 Ollama 静默回退到 4096 上下文窗口。如果你的代理/上游拒绝未知的 `options` 字段,请禁用此行为:
|
||||
当 Ollama 与 `api: "openai-completions"` 一起使用时,OpenClaw 默认会注入 `options.num_ctx`,这样 Ollama 就不会静默回退到 4096 的上下文窗口。如果你的代理/上游会拒绝未知的 `options` 字段,请关闭此行为:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -790,11 +783,11 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="上下文窗口">
|
||||
对于自动发现的模型,OpenClaw 会在可用时使用 Ollama 报告的上下文窗口,包括来自自定义 Modelfile 的更大 `PARAMETER num_ctx` 值。否则,它会回退到 OpenClaw 所使用的默认 Ollama 上下文窗口。
|
||||
对于自动发现的模型,OpenClaw 会在可用时使用 Ollama 报告的上下文窗口,包括来自自定义 Modelfile 的更大 `PARAMETER num_ctx` 值。否则,它会回退到 OpenClaw 使用的默认 Ollama 上下文窗口。
|
||||
|
||||
你可以为该 Ollama provider 下的每个模型设置 provider 级别的 `contextWindow`、`contextTokens` 和 `maxTokens` 默认值,并在需要时按模型覆盖。`contextWindow` 是 OpenClaw 的提示与压缩预算。除非你显式配置 `params.num_ctx`,否则原生 Ollama 请求会保持 `options.num_ctx` 未设置,以便 Ollama 应用其自身模型、`OLLAMA_CONTEXT_LENGTH` 或基于 VRAM 的默认值。若要在不重建 Modelfile 的情况下限制或强制 Ollama 每次请求的运行时上下文,请设置 `params.num_ctx`;无效、零、负数和非有限值都会被忽略。OpenAI 兼容的 Ollama 适配器仍会默认根据已配置的 `params.num_ctx` 或 `contextWindow` 注入 `options.num_ctx`;如果你的上游拒绝 `options`,请使用 `injectNumCtxForOpenAICompat: false` 禁用该行为。
|
||||
你可以为该 Ollama provider 下的每个模型设置 provider 级别的默认 `contextWindow`、`contextTokens` 和 `maxTokens`,并在需要时按模型覆盖它们。`contextWindow` 是 OpenClaw 的提示词和压缩预算。原生 Ollama 请求默认不会设置 `options.num_ctx`,除非你显式配置 `params.num_ctx`,这样 Ollama 就可以应用其自己的模型、`OLLAMA_CONTEXT_LENGTH` 或基于 VRAM 的默认值。若要在不重建 Modelfile 的情况下限制或强制 Ollama 的单请求运行时上下文,请设置 `params.num_ctx`;无效、零、负数和非有限值都会被忽略。OpenAI 兼容的 Ollama 适配器仍会默认从配置的 `params.num_ctx` 或 `contextWindow` 注入 `options.num_ctx`;如果你的上游拒绝 `options`,可通过 `injectNumCtxForOpenAICompat: false` 关闭。
|
||||
|
||||
原生 Ollama 模型条目也接受 `params` 下常见的 Ollama 运行时选项,包括 `temperature`、`top_p`、`top_k`、`min_p`、`num_predict`、`stop`、`repeat_penalty`、`num_batch`、`num_thread` 和 `use_mmap`。OpenClaw 仅转发 Ollama 请求键,因此像 `streaming` 这样的 OpenClaw 运行时参数不会泄露给 Ollama。使用 `params.think` 或 `params.thinking` 发送顶层 Ollama `think`;`false` 会为 Qwen 风格 thinking 模型禁用 API 级 thinking。
|
||||
原生 Ollama 模型条目也接受 `params` 下常见的 Ollama 运行时选项,包括 `temperature`、`top_p`、`top_k`、`min_p`、`num_predict`、`stop`、`repeat_penalty`、`num_batch`、`num_thread` 和 `use_mmap`。OpenClaw 只会转发 Ollama 请求键,因此像 `streaming` 这样的 OpenClaw 运行时参数不会泄露给 Ollama。使用 `params.think` 或 `params.thinking` 可发送顶层的 Ollama `think`;对 Qwen 风格的 thinking 模型,`false` 会禁用 API 级别的 thinking。
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -821,12 +814,12 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
}
|
||||
```
|
||||
|
||||
按模型设置的 `agents.defaults.models["ollama/<model>"].params.num_ctx` 也有效。如果两者都已配置,则显式的 provider 模型条目优先于智能体默认值。
|
||||
每个模型的 `agents.defaults.models["ollama/<model>"].params.num_ctx` 也同样有效。如果两者都已配置,则显式的 provider 模型条目优先于智能体默认值。
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Thinking 控制">
|
||||
对于原生 Ollama 模型,OpenClaw 会按照 Ollama 期望的方式转发 thinking 控制:使用顶层 `think`,而不是 `options.think`。
|
||||
对于原生 Ollama 模型,OpenClaw 会按 Ollama 所期望的方式转发 thinking 控制:使用顶层 `think`,而不是 `options.think`。
|
||||
|
||||
```bash
|
||||
openclaw agent --model ollama/gemma4 --thinking off
|
||||
@ -849,12 +842,12 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
}
|
||||
```
|
||||
|
||||
按模型设置的 `params.think` 或 `params.thinking` 可以为特定已配置模型禁用或强制启用 Ollama API thinking。诸如 `/think off` 之类的运行时命令仍会应用到当前运行。
|
||||
每个模型的 `params.think` 或 `params.thinking` 可以为特定已配置模型禁用或强制启用 Ollama API thinking。运行时命令如 `/think off` 仍然会应用到当前运行。
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="推理模型">
|
||||
默认情况下,OpenClaw 会将名称中包含 `deepseek-r1`、`reasoning` 或 `think` 的模型视为具备推理能力。
|
||||
OpenClaw 默认将名称中包含 `deepseek-r1`、`reasoning` 或 `think` 的模型视为具备推理能力。
|
||||
|
||||
```bash
|
||||
ollama pull deepseek-r1:32b
|
||||
@ -865,23 +858,23 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="模型成本">
|
||||
Ollama 是免费的并且在本地运行,因此所有模型成本都设置为 $0。这同时适用于自动发现和手动定义的模型。
|
||||
Ollama 是免费的并且在本地运行,因此所有模型成本都设置为 $0。这同时适用于自动发现的模型和手动定义的模型。
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="内存嵌入">
|
||||
<Accordion title="Memory 嵌入">
|
||||
内置的 Ollama 插件为
|
||||
[memory search](/zh-CN/concepts/memory) 注册了一个内存嵌入 provider。它使用已配置的 Ollama base URL
|
||||
和 API key,调用 Ollama 当前的 `/api/embed` 端点,并在可能时将
|
||||
多个内存块批量合并到一次 `input` 请求中。
|
||||
[memory search](/zh-CN/concepts/memory) 注册了一个 Memory 嵌入 provider。它使用已配置的 Ollama base URL
|
||||
和 API key,调用 Ollama 当前的 `/api/embed` 端点,并在可能时将多个
|
||||
Memory 块批量合并为一次 `input` 请求。
|
||||
|
||||
| 属性 | 值 |
|
||||
| ------------- | ------------------- |
|
||||
| 默认模型 | `nomic-embed-text` |
|
||||
| 自动拉取 | 是 —— 如果嵌入模型在本地不存在,则会自动拉取 |
|
||||
| 自动拉取 | 是 —— 如果嵌入模型在本地不存在,会自动拉取 |
|
||||
|
||||
查询时嵌入会为需要或推荐此前缀的模型使用检索前缀,包括 `nomic-embed-text`、`qwen3-embedding` 和 `mxbai-embed-large`。内存文档批次保持原始格式,因此现有索引不需要格式迁移。
|
||||
查询时嵌入会对需要或建议此前缀的模型使用检索前缀,包括 `nomic-embed-text`、`qwen3-embedding` 和 `mxbai-embed-large`。Memory 文档批次保持原始格式,因此现有索引不需要进行格式迁移。
|
||||
|
||||
要选择 Ollama 作为 memory search 的嵌入 provider:
|
||||
要将 Ollama 选为 memory search 的嵌入 provider:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -890,7 +883,7 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
memorySearch: {
|
||||
provider: "ollama",
|
||||
remote: {
|
||||
// Ollama 的默认值。如果在更大主机上重建索引过慢,请提高此值。
|
||||
// Ollama 的默认值。如果较大的主机上重建索引太慢,可提高此值。
|
||||
nonBatchConcurrency: 1,
|
||||
},
|
||||
},
|
||||
@ -899,7 +892,7 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
}
|
||||
```
|
||||
|
||||
对于远程嵌入主机,请将凭证作用域限制在该主机:
|
||||
对于远程嵌入主机,请将认证限定在该主机范围内:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -922,12 +915,12 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="流式传输配置">
|
||||
OpenClaw 的 Ollama 集成默认使用 **原生 Ollama API**(`/api/chat`),可完整同时支持流式传输和工具调用。不需要特殊配置。
|
||||
OpenClaw 的 Ollama 集成默认使用**原生 Ollama API**(`/api/chat`),它完全支持同时进行流式传输和工具调用。不需要特殊配置。
|
||||
|
||||
对于原生 `/api/chat` 请求,OpenClaw 也会直接将 thinking 控制转发给 Ollama:`/think off` 和 `openclaw agent --thinking off` 会发送顶层 `think: false`,而 `/think low|medium|high` 会发送匹配的顶层 `think` 努力等级字符串。`/think max` 会映射到 Ollama 的最高原生努力等级,即 `think: "high"`。
|
||||
对于原生 `/api/chat` 请求,OpenClaw 还会直接将 thinking 控制转发给 Ollama:`/think off` 和 `openclaw agent --thinking off` 会发送顶层 `think: false`,而 `/think low|medium|high` 会发送对应的顶层 `think` 强度字符串。`/think max` 会映射为 Ollama 最高的原生强度,即 `think: "high"`。
|
||||
|
||||
<Tip>
|
||||
如果你需要使用 OpenAI 兼容端点,请参阅上方的“旧版 OpenAI 兼容模式”部分。在该模式下,流式传输和工具调用可能无法同时工作。
|
||||
如果你需要使用 OpenAI 兼容端点,请参阅上方“旧版 OpenAI 兼容模式”部分。在该模式下,流式传输和工具调用可能无法同时工作。
|
||||
</Tip>
|
||||
|
||||
</Accordion>
|
||||
@ -936,16 +929,16 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
## 故障排除
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="WSL2 崩溃循环(重复重启)">
|
||||
在带有 NVIDIA/CUDA 的 WSL2 上,官方 Ollama Linux 安装程序会创建一个 `ollama.service` systemd 单元,并设置 `Restart=always`。如果该服务在 WSL2 启动时自动启动并加载一个 GPU 支持的模型,Ollama 可能会在模型加载期间锁定主机内存。Hyper-V 内存回收并不总能回收这些被锁定的页,因此 Windows 可能会终止 WSL2 VM,systemd 又会再次启动 Ollama,如此循环往复。
|
||||
<Accordion title="WSL2 崩溃循环(反复重启)">
|
||||
在带有 NVIDIA/CUDA 的 WSL2 上,官方 Ollama Linux 安装程序会创建一个 `ollama.service` systemd 单元,并设置 `Restart=always`。如果该服务在 WSL2 启动期间自动启动并加载 GPU 支持的模型,Ollama 可能会在模型加载时锁定主机内存。Hyper-V 的内存回收并不总能回收这些被锁定的页面,因此 Windows 可能会终止 WSL2 VM,systemd 又会再次启动 Ollama,于是循环重复发生。
|
||||
|
||||
常见迹象:
|
||||
常见证据:
|
||||
|
||||
- 从 Windows 一侧看见 WSL2 反复重启或终止
|
||||
- WSL2 启动后不久,`app.slice` 或 `ollama.service` 出现高 CPU 使用
|
||||
- systemd 发出 SIGTERM,而不是 Linux OOM killer 事件
|
||||
- 从 Windows 侧看到 WSL2 反复重启或被终止
|
||||
- WSL2 启动后不久,`app.slice` 或 `ollama.service` 的 CPU 占用很高
|
||||
- 来自 systemd 的 SIGTERM,而不是 Linux OOM-killer 事件
|
||||
|
||||
当 OpenClaw 检测到 WSL2、启用了带 `Restart=always` 的 `ollama.service`,并且存在可见的 CUDA 标记时,它会记录一条启动警告。
|
||||
当 OpenClaw 检测到 WSL2、启用了带 `Restart=always` 的 `ollama.service`,并且检测到可见的 CUDA 标记时,会记录一条启动警告。
|
||||
|
||||
缓解方法:
|
||||
|
||||
@ -960,7 +953,7 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
autoMemoryReclaim=disabled
|
||||
```
|
||||
|
||||
在 Ollama 服务环境中设置更短的 keep-alive,或者仅在需要时手动启动 Ollama:
|
||||
在 Ollama 服务环境中设置更短的 keep-alive,或仅在需要时手动启动 Ollama:
|
||||
|
||||
```bash
|
||||
export OLLAMA_KEEP_ALIVE=5m
|
||||
@ -972,7 +965,7 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="未检测到 Ollama">
|
||||
请确认 Ollama 正在运行、你已设置 `OLLAMA_API_KEY`(或 auth profile),并且你**没有**定义显式的 `models.providers.ollama` 条目:
|
||||
请确保 Ollama 正在运行,并且你已设置 `OLLAMA_API_KEY`(或认证配置文件),同时**没有**定义显式的 `models.providers.ollama` 条目:
|
||||
|
||||
```bash
|
||||
ollama serve
|
||||
@ -987,10 +980,10 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="没有可用模型">
|
||||
如果列表中没有你的模型,请在本地拉取该模型,或在 `models.providers.ollama` 中显式定义它。
|
||||
如果你的模型未列出,请在本地拉取该模型,或在 `models.providers.ollama` 中显式定义它。
|
||||
|
||||
```bash
|
||||
ollama list # 查看已安装的内容
|
||||
ollama list # 查看已安装内容
|
||||
ollama pull gemma4
|
||||
ollama pull gpt-oss:20b
|
||||
ollama pull llama3.3 # 或其他模型
|
||||
@ -1011,8 +1004,8 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="远程主机用 curl 可用,但 OpenClaw 不可用">
|
||||
请从运行 Gateway 网关的同一台机器和运行时环境中进行验证:
|
||||
<Accordion title="远程主机可通过 curl 访问,但 OpenClaw 不行">
|
||||
请在运行 Gateway 网关 的同一台机器和同一运行时中进行验证:
|
||||
|
||||
```bash
|
||||
openclaw gateway status --deep
|
||||
@ -1021,17 +1014,17 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
|
||||
常见原因:
|
||||
|
||||
- `baseUrl` 指向 `localhost`,但 Gateway 网关运行在 Docker 中或另一台主机上。
|
||||
- `baseUrl` 指向 `localhost`,但 Gateway 网关 运行在 Docker 中或另一台主机上。
|
||||
- URL 使用了 `/v1`,这会选择 OpenAI 兼容行为,而不是原生 Ollama。
|
||||
- 远程主机需要在 Ollama 端调整防火墙或局域网绑定设置。
|
||||
- 远程主机在 Ollama 端需要调整防火墙或局域网绑定设置。
|
||||
- 模型存在于你笔记本电脑上的守护进程中,但不存在于远程守护进程中。
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="模型将工具 JSON 作为文本输出">
|
||||
这通常意味着 provider 正在使用 OpenAI 兼容模式,或者模型无法处理工具 schema。
|
||||
这通常意味着 provider 正在使用 OpenAI 兼容模式,或该模型无法处理工具 schema。
|
||||
|
||||
请优先使用原生 Ollama 模式:
|
||||
优先使用原生 Ollama 模式:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -1046,12 +1039,12 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
}
|
||||
```
|
||||
|
||||
如果小型本地模型仍然无法处理工具 schema,请在该模型条目上设置 `compat.supportsTools: false`,然后重新测试。
|
||||
如果较小的本地模型在工具 schema 上仍然失败,请在该模型条目上设置 `compat.supportsTools: false`,然后重新测试。
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="冷启动的本地模型超时">
|
||||
大型本地模型在开始流式传输之前,首次加载可能需要较长时间。请将超时限制在 Ollama provider 作用域内,并可选择要求 Ollama 在轮次之间保持模型已加载:
|
||||
大型本地模型在开始流式传输之前,首次加载可能需要较长时间。请将超时限制在 Ollama provider 范围内,并可选择要求 Ollama 在轮次之间保持模型已加载:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -1072,12 +1065,12 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
}
|
||||
```
|
||||
|
||||
如果主机本身接受连接就很慢,`timeoutSeconds` 也会延长该 provider 的受保护 Undici 连接超时。
|
||||
如果主机本身接受连接的速度较慢,`timeoutSeconds` 也会延长此 provider 的受保护 Undici 连接超时。
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="大上下文模型过慢或内存不足">
|
||||
许多 Ollama 模型声明的上下文大小超过了你的硬件能够轻松运行的范围。除非你设置 `params.num_ctx`,否则原生 Ollama 会使用 Ollama 自己的运行时上下文默认值。当你希望获得可预测的首个 token 延迟时,请同时限制 OpenClaw 的预算和 Ollama 的请求上下文:
|
||||
<Accordion title="大上下文模型太慢或耗尽内存">
|
||||
许多 Ollama 模型声明的上下文大小,超过了你的硬件可以舒适运行的范围。原生 Ollama 会使用它自己的运行时上下文默认值,除非你设置 `params.num_ctx`。当你希望获得可预测的首个 token 延迟时,请同时限制 OpenClaw 的预算和 Ollama 的请求上下文:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -1099,7 +1092,7 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
}
|
||||
```
|
||||
|
||||
如果是 OpenClaw 发送的提示过多,请先降低 `contextWindow`。如果是 Ollama 加载的运行时上下文对于机器来说过大,请降低 `params.num_ctx`。如果生成时间过长,请降低 `maxTokens`。
|
||||
如果 OpenClaw 发送的提示词过多,请先降低 `contextWindow`。如果 Ollama 加载的运行时上下文对机器来说过大,请降低 `params.num_ctx`。如果生成时间过长,请降低 `maxTokens`。
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@ -1111,8 +1104,8 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
## 相关内容
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="模型提供商" href="/zh-CN/concepts/model-providers" icon="layers">
|
||||
所有 provider、模型引用和故障切换行为的概览。
|
||||
<Card title="模型 provider" href="/zh-CN/concepts/model-providers" icon="layers">
|
||||
所有 provider、模型引用和故障转移行为的概览。
|
||||
</Card>
|
||||
<Card title="模型选择" href="/zh-CN/concepts/models" icon="brain">
|
||||
如何选择和配置模型。
|
||||
@ -1121,6 +1114,6 @@ OpenClaw 支持 **Ollama Web 搜索**,作为内置的 `web_search` provider。
|
||||
由 Ollama 驱动的 Web 搜索的完整设置和行为详情。
|
||||
</Card>
|
||||
<Card title="配置" href="/zh-CN/gateway/configuration" icon="gear">
|
||||
完整配置参考。
|
||||
完整的配置参考。
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@ -1,55 +1,55 @@
|
||||
---
|
||||
read_when:
|
||||
- 运行或修复测试
|
||||
summary: 如何在本地运行测试(vitest),以及何时使用 force / coverage 模式
|
||||
summary: 如何在本地运行测试(vitest),以及何时使用 force/coverage 模式
|
||||
title: 测试
|
||||
x-i18n:
|
||||
generated_at: "2026-04-28T00:34:04Z"
|
||||
generated_at: "2026-04-28T01:18:55Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 9b32904204717ae35dda88d74eba225c173fecbe6230209b066795419857cdee
|
||||
source_hash: df15d8967bd22e46336522aed44a32151f015f481c5aad24b66c7e0f10232e2f
|
||||
source_path: reference/test.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
- 完整测试工具包(测试套件、实时测试、Docker):[测试](/zh-CN/help/testing)
|
||||
|
||||
- `pnpm test:force`:终止任何仍占用默认控制端口的残留 Gateway 网关进程,然后使用隔离的 Gateway 网关端口运行完整 Vitest 测试套件,这样服务器测试就不会与正在运行的实例冲突。当先前的 Gateway 网关运行留下端口 `18789` 被占用时,请使用它。
|
||||
- `pnpm test:coverage`:使用 V8 覆盖率运行单元测试套件(通过 `vitest.unit.config.ts`)。这是针对已加载文件的单元覆盖率门禁,不是整个仓库所有文件的覆盖率。阈值为 70% 的行 / 函数 / 语句,以及 55% 的分支。由于 `coverage.all` 为 false,该门禁衡量的是被单元覆盖率套件加载的文件,而不是将每个拆分测试 lane 中的源文件都视为未覆盖。
|
||||
- `pnpm test:coverage:changed`:仅对相对于 `origin/main` 变更的文件运行单元覆盖率。
|
||||
- `pnpm test:changed`:低成本的智能变更测试运行。它会根据直接测试编辑、同级 `*.test.ts` 文件、显式源映射和本地导入图来运行精确目标。广泛的 / 配置 / 包变更会被跳过,除非它们映射到精确测试。
|
||||
- `OPENCLAW_TEST_CHANGED_BROAD=1 pnpm test:changed`:显式的广泛变更测试运行。当测试 harness / 配置 / 包编辑应回退到 Vitest 更广泛的变更测试行为时,请使用它。
|
||||
- `pnpm changed:lanes`:显示相对于 `origin/main` 的 diff 触发了哪些架构 lane。
|
||||
- `pnpm check:changed`:针对相对于 `origin/main` 的 diff 运行智能变更检查门禁。它会为受影响的架构 lane 运行类型检查、lint 和保护命令,但**不会**运行 Vitest 测试。若要获得测试证明,请使用 `pnpm test:changed` 或显式的 `pnpm test <target>`。
|
||||
- `pnpm test`:通过有范围的 Vitest lane 路由显式文件 / 目录目标。未指定目标的运行会使用固定分片组,并展开为叶子配置以便本地并行执行;extension 组始终会展开为每个 extension 的分片配置,而不是一个巨大的根项目进程。
|
||||
- 测试包装器运行结束时会输出简短的 `[test] passed|failed|skipped ... in ...` 摘要。Vitest 自己的耗时行仍保留每个分片的详细信息。
|
||||
- 完整、extension 和 include-pattern 分片运行会将本地耗时数据更新到 `.artifacts/vitest-shard-timings.json`;后续的整配置运行会使用这些耗时来平衡慢分片和快分片。include-pattern CI 分片会将分片名称附加到耗时键名中,这样可以保留筛选后分片的耗时,而不会替换整配置耗时数据。设置 `OPENCLAW_TEST_PROJECTS_TIMINGS=0` 可忽略本地耗时工件。
|
||||
- 某些选定的 `plugin-sdk` 和 `commands` 测试文件现在会通过专用的轻量 lane 路由,这些 lane 仅保留 `test/setup.ts`,而运行时较重的用例仍保留在其现有 lane 中。
|
||||
- 带有同级测试的源文件会优先映射到该同级测试,然后才回退到更宽泛的目录 glob。位于 `test/helpers/channels`、`src/plugin-sdk/test-helpers` 和 `src/plugins/contracts` 下的 helper 编辑会使用本地导入图来运行导入它们的测试,而不是在依赖路径精确时广泛运行每个分片。
|
||||
- `auto-reply` 现在也拆分为三个专用配置(`core`、`top-level`、`reply`),这样 reply harness 就不会主导更轻量的顶层状态 / token / helper 测试。
|
||||
- `pnpm test:force`:终止任何仍在占用默认控制端口的残留 Gateway 网关进程,然后使用隔离的 Gateway 网关端口运行完整的 Vitest 测试套件,以避免服务器测试与正在运行的实例发生冲突。当先前的 Gateway 网关运行导致端口 18789 仍被占用时,请使用此命令。
|
||||
- `pnpm test:coverage`:通过 `vitest.unit.config.ts` 使用 V8 覆盖率运行单元测试套件。这是一个针对已加载文件的单元覆盖率门禁,不是覆盖整个仓库所有文件的覆盖率。阈值为:行数 / 函数 / 语句 70%,分支 55%。由于 `coverage.all` 为 false,该门禁统计的是被单元覆盖率测试套件加载的文件,而不是将每个拆分测试 lane 的源文件都视为未覆盖。
|
||||
- `pnpm test:coverage:changed`:仅对自 `origin/main` 以来变更的文件运行单元覆盖率。
|
||||
- `pnpm test:changed`:低成本的智能变更测试运行。它会根据直接修改的测试文件、同级 `*.test.ts` 文件、显式源文件映射以及本地导入图来运行精确目标。除非能够映射到精确测试,否则广泛的 / 配置 / package 变更会被跳过。
|
||||
- `OPENCLAW_TEST_CHANGED_BROAD=1 pnpm test:changed`:显式启用广泛变更测试运行。当测试 harness / 配置 / package 的修改应回退到 Vitest 更广泛的变更测试行为时,请使用它。
|
||||
- `pnpm changed:lanes`:显示相对于 `origin/main` 的差异所触发的架构 lane。
|
||||
- `pnpm check:changed`:对相对于 `origin/main` 的差异运行智能变更检查门禁。它会为受影响的架构 lane 运行 typecheck、lint 和 guard 命令,但不会运行 Vitest 测试。测试验证请使用 `pnpm test:changed` 或显式的 `pnpm test <target>`。
|
||||
- `pnpm test`:通过带作用域的 Vitest lane 路由显式的文件 / 目录目标。未指定目标的运行会使用固定分片组,并展开为叶子配置,以便在本地并行执行;扩展组始终会展开为每个扩展 / 插件的分片配置,而不是使用一个庞大的根项目进程。
|
||||
- 测试包装器运行结束时会输出简短的 `[test] passed|failed|skipped ... in ...` 摘要。Vitest 自身的耗时行仍保留为每个分片的详细信息。
|
||||
- 完整测试、扩展测试和 include-pattern 分片运行会将本地耗时数据更新到 `.artifacts/vitest-shard-timings.json`;后续整套配置运行会使用这些耗时数据来平衡慢分片和快分片。include-pattern CI 分片会将分片名称附加到耗时键名中,这样可以在不替换整套配置耗时数据的前提下保留过滤后分片的耗时可见性。设置 `OPENCLAW_TEST_PROJECTS_TIMINGS=0` 可忽略本地耗时工件。
|
||||
- 选定的 `plugin-sdk` 和 `commands` 测试文件现在会路由到专用的轻量 lane,这些 lane 只保留 `test/setup.ts`,而运行时开销较重的用例仍保留在现有 lane 上。
|
||||
- 带有同级测试的源文件会优先映射到该同级测试,然后才回退到更宽泛的目录 glob。位于 `src/channels/plugins/contracts/test-helpers`、`src/plugin-sdk/test-helpers` 和 `src/plugins/contracts` 下的辅助文件修改,会使用本地导入图来运行导入它们的测试,而不是在依赖路径明确时广泛运行每个分片。
|
||||
- `auto-reply` 现在也拆分为三个专用配置(`core`、`top-level`、`reply`),这样 reply harness 就不会主导较轻量的顶层 status / token / helper 测试。
|
||||
- 基础 Vitest 配置现在默认使用 `pool: "threads"` 和 `isolate: false`,并在整个仓库配置中启用共享的非隔离运行器。
|
||||
- `pnpm test:channels` 运行 `vitest.channels.config.ts`。
|
||||
- `pnpm test:extensions` 和 `pnpm test extensions` 运行所有 extension / 插件分片。重型渠道插件、浏览器插件和 OpenAI 作为专用分片运行;其他插件组仍保持批处理。对单个内置插件 lane,请使用 `pnpm test extensions/<id>`。
|
||||
- `pnpm test:perf:imports`:启用 Vitest 导入耗时 + 导入细分报告,同时仍为显式文件 / 目录目标使用有范围的 lane 路由。
|
||||
- `pnpm test:perf:imports:changed`:同样的导入性能分析,但仅针对相对于 `origin/main` 变更的文件。
|
||||
- `pnpm test:perf:changed:bench -- --ref <git-ref>`:针对同一份已提交 git diff,将路由后的变更模式路径与原生根项目运行进行基准对比。
|
||||
- `pnpm test:perf:changed:bench -- --worktree`:无需先提交,即可对当前工作树的变更集进行基准对比。
|
||||
- `pnpm test:extensions` 和 `pnpm test extensions` 运行所有扩展 / 插件分片。重量级渠道插件、浏览器插件和 OpenAI 作为专用分片运行;其他插件组保持批处理。对某个内置插件 lane 使用 `pnpm test extensions/<id>`。
|
||||
- `pnpm test:perf:imports`:启用 Vitest 导入耗时 + 导入明细报告,同时仍对显式文件 / 目录目标使用带作用域的 lane 路由。
|
||||
- `pnpm test:perf:imports:changed`:相同的导入性能分析,但仅针对自 `origin/main` 以来变更的文件。
|
||||
- `pnpm test:perf:changed:bench -- --ref <git-ref>`:针对同一份已提交的 git diff,对路由后的 changed 模式路径和原生根项目运行进行基准对比。
|
||||
- `pnpm test:perf:changed:bench -- --worktree`:对当前工作区变更集进行基准测试,无需先提交。
|
||||
- `pnpm test:perf:profile:main`:为 Vitest 主线程写入 CPU profile(`.artifacts/vitest-main-profile`)。
|
||||
- `pnpm test:perf:profile:runner`:为单元测试运行器写入 CPU + 堆 profile(`.artifacts/vitest-runner-profile`)。
|
||||
- `pnpm test:perf:groups --full-suite --allow-failures --output .artifacts/test-perf/baseline-before.json`:串行运行每个完整套件 Vitest 叶子配置,并写入分组耗时数据以及每个配置的 JSON / 日志工件。测试性能 Agent 会将其用作尝试修复慢测试前的基线。
|
||||
- `pnpm test:perf:groups:compare .artifacts/test-perf/baseline-before.json .artifacts/test-perf/after-agent.json`:对比性能导向变更前后的分组报告。
|
||||
- Gateway 网关集成:通过 `OPENCLAW_TEST_INCLUDE_GATEWAY=1 pnpm test` 或 `pnpm test:gateway` 选择启用。
|
||||
- `pnpm test:e2e`:运行 Gateway 网关端到端冒烟测试(多实例 WS / HTTP / 节点配对)。在 `vitest.e2e.config.ts` 中默认使用 `threads` + `isolate: false` 以及自适应 worker;可通过 `OPENCLAW_E2E_WORKERS=<n>` 调整,并设置 `OPENCLAW_E2E_VERBOSE=1` 获取详细日志。
|
||||
- `pnpm test:live`:运行提供商实时测试(minimax / zai)。需要 API 密钥以及 `LIVE=1`(或特定提供商的 `*_LIVE_TEST=1`)来取消跳过。
|
||||
- `pnpm test:docker:all`:构建共享实时测试镜像,将 OpenClaw 打包一次为 npm tarball,构建 / 复用裸 Node / Git 运行器镜像和一个功能镜像(将该 tarball 安装到 `/app`),然后通过加权调度器在 `OPENCLAW_SKIP_DOCKER_BUILD=1` 下运行 Docker 冒烟 lane。裸镜像(`OPENCLAW_DOCKER_E2E_BARE_IMAGE`)用于安装器 / 更新 / 插件依赖 lane;这些 lane 挂载预构建 tarball,而不是使用复制的仓库源代码。功能镜像(`OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE`)用于正常的已构建应用功能 lane。`scripts/package-openclaw-for-docker.mjs` 是本地 / CI 唯一的包打包器,并会在 Docker 使用前验证 tarball 和 `dist/postinstall-inventory.json`。Docker lane 定义位于 `scripts/lib/docker-e2e-scenarios.mjs`;规划逻辑位于 `scripts/lib/docker-e2e-plan.mjs`;`scripts/test-docker-all.mjs` 执行所选计划。`node scripts/test-docker-all.mjs --plan-json` 会输出调度器拥有的 CI 计划,其中包含所选 lane、镜像类型、包 / 实时镜像需求和凭证检查,而不构建或运行 Docker。`OPENCLAW_DOCKER_ALL_PARALLELISM=<n>` 控制进程槽位,默认值为 10;`OPENCLAW_DOCKER_ALL_TAIL_PARALLELISM=<n>` 控制对提供商敏感的尾部池,默认值也为 10。重型 lane 上限默认为 `OPENCLAW_DOCKER_ALL_LIVE_LIMIT=9`、`OPENCLAW_DOCKER_ALL_NPM_LIMIT=10` 和 `OPENCLAW_DOCKER_ALL_SERVICE_LIMIT=7`;提供商上限默认是每个提供商一个重型 lane,通过 `OPENCLAW_DOCKER_ALL_LIVE_CLAUDE_LIMIT=4`、`OPENCLAW_DOCKER_ALL_LIVE_CODEX_LIMIT=4` 和 `OPENCLAW_DOCKER_ALL_LIVE_GEMINI_LIMIT=4` 控制。对于更大的主机,可使用 `OPENCLAW_DOCKER_ALL_WEIGHT_LIMIT` 或 `OPENCLAW_DOCKER_ALL_DOCKER_LIMIT`。如果某个 lane 在低并行主机上超过有效权重或资源上限,它仍可从空池启动,并会单独运行直到释放容量。默认情况下,lane 启动会错开 2 秒,以避免本地 Docker 守护进程创建风暴;可通过 `OPENCLAW_DOCKER_ALL_START_STAGGER_MS=<ms>` 覆盖。运行器默认会对 Docker 进行预检查、清理过期的 OpenClaw E2E 容器、每 30 秒输出一次活动 lane 状态、在兼容 lane 之间共享提供商 CLI 工具缓存、默认重试一次瞬态实时提供商失败(`OPENCLAW_DOCKER_ALL_LIVE_RETRIES=<n>`),并将 lane 耗时存储到 `.artifacts/docker-tests/lane-timings.json`,用于后续运行中的最长优先排序。使用 `OPENCLAW_DOCKER_ALL_DRY_RUN=1` 可打印 lane 清单而不运行 Docker,使用 `OPENCLAW_DOCKER_ALL_STATUS_INTERVAL_MS=<ms>` 可调整状态输出,或使用 `OPENCLAW_DOCKER_ALL_TIMINGS=0` 禁用耗时复用。对于仅确定性 / 本地 lane,请使用 `OPENCLAW_DOCKER_ALL_LIVE_MODE=skip`;对于仅实时提供商 lane,请使用 `OPENCLAW_DOCKER_ALL_LIVE_MODE=only`;对应的包别名为 `pnpm test:docker:local:all` 和 `pnpm test:docker:live:all`。仅实时模式会将主实时 lane 和尾部实时 lane 合并为一个最长优先池,以便提供商桶可以将 Claude、Codex 和 Gemini 工作一起打包。运行器在首次失败后会停止调度新的池化 lane,除非设置了 `OPENCLAW_DOCKER_ALL_FAIL_FAST=0`,并且每个 lane 都有 120 分钟的后备超时,可通过 `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS` 覆盖;部分选定的实时 / 尾部 lane 使用更严格的每 lane 上限。CLI 后端 Docker 设置命令有自己的超时,通过 `OPENCLAW_LIVE_CLI_BACKEND_SETUP_TIMEOUT_SECONDS` 控制(默认 180)。每个 lane 的日志、`summary.json`、`failures.json` 和阶段耗时会写入 `.artifacts/docker-tests/<run-id>/`;使用 `pnpm test:docker:timings <summary.json>` 查看慢 lane,使用 `pnpm test:docker:rerun <run-id|summary.json|failures.json>` 打印低成本的定向重跑命令。
|
||||
- `pnpm test:docker:browser-cdp-snapshot`:构建一个基于 Chromium 的源 E2E 容器,启动原始 CDP 和一个隔离的 Gateway 网关,运行 `browser doctor --deep`,并验证 CDP 角色快照是否包含链接 URL、通过光标提升的可点击项、iframe 引用和 frame 元数据。
|
||||
- CLI 后端实时 Docker 探测可以作为聚焦 lane 运行,例如 `pnpm test:docker:live-cli-backend:codex`、`pnpm test:docker:live-cli-backend:codex:resume` 或 `pnpm test:docker:live-cli-backend:codex:mcp`。Claude 和 Gemini 也有对应的 `:resume` 和 `:mcp` 别名。
|
||||
- `pnpm test:docker:openwebui`:启动 Docker 化的 OpenClaw + Open WebUI,通过 Open WebUI 登录,检查 `/api/models`,然后通过 `/api/chat/completions` 运行一次真实的代理聊天。需要可用的实时模型密钥(例如位于 `~/.profile` 中的 OpenAI),会拉取一个外部 Open WebUI 镜像,并不像普通 unit / e2e 测试套件那样被视为 CI 稳定测试。
|
||||
- `pnpm test:docker:mcp-channels`:启动一个已植入数据的 Gateway 网关容器和第二个客户端容器,后者会启动 `openclaw mcp serve`,然后验证路由后的会话发现、转录读取、附件元数据、实时事件队列行为、出站发送路由,以及通过真实 stdio bridge 的 Claude 风格渠道 + 权限通知。Claude 通知断言会直接读取原始 stdio MCP 帧,因此该冒烟测试反映的就是 bridge 实际发出的内容。
|
||||
- `pnpm test:perf:groups --full-suite --allow-failures --output .artifacts/test-perf/baseline-before.json`:串行运行完整测试套件中的每个 Vitest 叶子配置,并写入分组耗时数据以及每个配置对应的 JSON / 日志工件。Test Performance Agent 会在尝试修复慢测试之前,将此结果作为基线。
|
||||
- `pnpm test:perf:groups:compare .artifacts/test-perf/baseline-before.json .artifacts/test-perf/after-agent.json`:对比性能优化变更前后的分组报告。
|
||||
- Gateway 网关集成测试:通过 `OPENCLAW_TEST_INCLUDE_GATEWAY=1 pnpm test` 或 `pnpm test:gateway` 显式启用。
|
||||
- `pnpm test:e2e`:运行 Gateway 网关端到端 smoke 测试(多实例 WS / HTTP / 节点配对)。在 `vitest.e2e.config.ts` 中默认使用 `threads` + `isolate: false` 以及自适应 workers;可通过 `OPENCLAW_E2E_WORKERS=<n>` 调整,并设置 `OPENCLAW_E2E_VERBOSE=1` 以输出详细日志。
|
||||
- `pnpm test:live`:运行 provider 实时测试(minimax / zai)。需要 API key,并设置 `LIVE=1`(或 provider 专用的 `*_LIVE_TEST=1`)才能取消跳过。
|
||||
- `pnpm test:docker:all`:构建共享的实时测试镜像,将 OpenClaw 一次性打包为 npm tarball,构建 / 复用一个裸 Node/Git 运行器镜像以及一个功能镜像,后者会将该 tarball 安装到 `/app` 中,然后通过加权调度器以 `OPENCLAW_SKIP_DOCKER_BUILD=1` 运行 Docker smoke lane。裸镜像(`OPENCLAW_DOCKER_E2E_BARE_IMAGE`)用于安装器 / 更新 / 插件依赖 lane;这些 lane 会挂载预构建 tarball,而不是使用复制的仓库源码。功能镜像(`OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE`)用于常规的已构建应用功能 lane。`scripts/package-openclaw-for-docker.mjs` 是本地 / CI 唯一的 package 打包器,并会在 Docker 使用 tarball 之前校验该 tarball 以及 `dist/postinstall-inventory.json`。Docker lane 定义位于 `scripts/lib/docker-e2e-scenarios.mjs`;规划逻辑位于 `scripts/lib/docker-e2e-plan.mjs`;`scripts/test-docker-all.mjs` 负责执行选定计划。`node scripts/test-docker-all.mjs --plan-json` 会输出由调度器拥有的 CI 计划,其中包含选中的 lane、镜像类型、package / 实时镜像需求以及凭证检查,而不会构建或运行 Docker。`OPENCLAW_DOCKER_ALL_PARALLELISM=<n>` 控制进程槽位,默认值为 10;`OPENCLAW_DOCKER_ALL_TAIL_PARALLELISM=<n>` 控制对 provider 敏感的尾部池,默认值也为 10。重量级 lane 上限默认值分别为 `OPENCLAW_DOCKER_ALL_LIVE_LIMIT=9`、`OPENCLAW_DOCKER_ALL_NPM_LIMIT=10` 和 `OPENCLAW_DOCKER_ALL_SERVICE_LIMIT=7`;provider 上限默认按每个 provider 一个重量级 lane,通过 `OPENCLAW_DOCKER_ALL_LIVE_CLAUDE_LIMIT=4`、`OPENCLAW_DOCKER_ALL_LIVE_CODEX_LIMIT=4` 和 `OPENCLAW_DOCKER_ALL_LIVE_GEMINI_LIMIT=4` 控制。对于更大的主机,可使用 `OPENCLAW_DOCKER_ALL_WEIGHT_LIMIT` 或 `OPENCLAW_DOCKER_ALL_DOCKER_LIMIT`。如果某个 lane 在低并行度主机上超过了有效权重或资源上限,它仍可从空池中启动,并独占运行直到释放容量。默认会将 lane 启动间隔错开 2 秒,以避免本地 Docker daemon 在创建时发生风暴;可通过 `OPENCLAW_DOCKER_ALL_START_STAGGER_MS=<ms>` 覆盖。运行器默认会对 Docker 执行预检查、清理过期的 OpenClaw E2E 容器、每 30 秒输出一次活跃 lane status、在兼容 lane 之间共享 provider CLI 工具缓存、默认对临时实时 provider 失败重试一次(`OPENCLAW_DOCKER_ALL_LIVE_RETRIES=<n>`),并将 lane 耗时存储到 `.artifacts/docker-tests/lane-timings.json`,供后续运行按“最长优先”排序。使用 `OPENCLAW_DOCKER_ALL_DRY_RUN=1` 可仅打印 lane 清单而不运行 Docker,使用 `OPENCLAW_DOCKER_ALL_STATUS_INTERVAL_MS=<ms>` 可调整 status 输出频率,使用 `OPENCLAW_DOCKER_ALL_TIMINGS=0` 可禁用耗时复用。使用 `OPENCLAW_DOCKER_ALL_LIVE_MODE=skip` 可仅运行确定性 / 本地 lane,使用 `OPENCLAW_DOCKER_ALL_LIVE_MODE=only` 可仅运行实时 provider lane;对应的 package 别名为 `pnpm test:docker:local:all` 和 `pnpm test:docker:live:all`。仅实时模式会将主实时 lane 和尾部实时 lane 合并到一个按“最长优先”排序的池中,从而让 provider bucket 可以把 Claude、Codex 和 Gemini 的工作混合装箱。除非设置 `OPENCLAW_DOCKER_ALL_FAIL_FAST=0`,否则运行器在首次失败后会停止调度新的池化 lane;每个 lane 都有默认 120 分钟的后备超时,可通过 `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS` 覆盖;部分选定的实时 / 尾部 lane 使用更严格的单 lane 限制。CLI 后端 Docker 设置命令也有独立超时,通过 `OPENCLAW_LIVE_CLI_BACKEND_SETUP_TIMEOUT_SECONDS` 控制(默认 180)。每个 lane 的日志、`summary.json`、`failures.json` 以及阶段耗时会写入 `.artifacts/docker-tests/<run-id>/`;可使用 `pnpm test:docker:timings <summary.json>` 检查慢 lane,使用 `pnpm test:docker:rerun <run-id|summary.json|failures.json>` 打印低成本的定向重跑命令。
|
||||
- `pnpm test:docker:browser-cdp-snapshot`:构建一个基于 Chromium 的源码 E2E 容器,启动原始 CDP 和一个隔离的 Gateway 网关,运行 `browser doctor --deep`,并验证 CDP 角色快照中包含链接 URL、由光标提升的可点击元素、iframe 引用以及 frame 元数据。
|
||||
- CLI 后端实时 Docker 探针可以作为聚焦 lane 运行,例如 `pnpm test:docker:live-cli-backend:codex`、`pnpm test:docker:live-cli-backend:codex:resume` 或 `pnpm test:docker:live-cli-backend:codex:mcp`。Claude 和 Gemini 也有对应的 `:resume` 和 `:mcp` 别名。
|
||||
- `pnpm test:docker:openwebui`:启动 Docker 化的 OpenClaw + Open WebUI,通过 Open WebUI 登录,检查 `/api/models`,然后通过 `/api/chat/completions` 运行一次真实的代理聊天。需要可用的实时模型 key(例如 `~/.profile` 中的 OpenAI),会拉取外部 Open WebUI 镜像,并且不像普通的单元 / e2e 测试套件那样被期望具备 CI 稳定性。
|
||||
- `pnpm test:docker:mcp-channels`:启动一个带种子数据的 Gateway 网关容器和第二个客户端容器,后者会生成 `openclaw mcp serve`,然后验证通过真实 stdio bridge 进行的路由会话发现、transcript 读取、附件元数据、实时事件队列行为、出站发送路由,以及 Claude 风格的渠道 + 权限通知。Claude 通知断言会直接读取原始 stdio MCP frame,因此该 smoke 测试能够反映 bridge 实际发出的内容。
|
||||
|
||||
## 本地 PR 门禁
|
||||
|
||||
对于本地 PR 落地 / 门禁检查,请运行:
|
||||
对于本地 PR 提交 / 合并前的门禁检查,请运行:
|
||||
|
||||
- `pnpm check:changed`
|
||||
- `pnpm check`
|
||||
@ -58,12 +58,12 @@ x-i18n:
|
||||
- `pnpm test`
|
||||
- `pnpm check:docs`
|
||||
|
||||
如果 `pnpm test` 在负载较高的主机上出现波动,请先重跑一次,再将其视为回归;然后使用 `pnpm test <path/to/test>` 进行隔离。对于内存受限主机,请使用:
|
||||
如果 `pnpm test` 在负载较高的主机上出现偶发失败,请先重跑一次,再将其视为回归;然后使用 `pnpm test <path/to/test>` 进行隔离。对于内存受限的主机,请使用:
|
||||
|
||||
- `OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test`
|
||||
- `OPENCLAW_VITEST_FS_MODULE_CACHE_PATH=/tmp/openclaw-vitest-cache pnpm test:changed`
|
||||
|
||||
## 模型延迟基准测试(本地密钥)
|
||||
## 模型延迟基准测试(本地 key)
|
||||
|
||||
脚本:[`scripts/bench-model.ts`](https://github.com/openclaw/openclaw/blob/main/scripts/bench-model.ts)
|
||||
|
||||
@ -75,8 +75,8 @@ x-i18n:
|
||||
|
||||
最近一次运行(2025-12-31,20 次):
|
||||
|
||||
- minimax 中位数 1279ms(最小 1114,最大 2431)
|
||||
- opus 中位数 2454ms(最小 1224,最大 3170)
|
||||
- minimax 中位数 1279 ms(最小 1114,最大 2431)
|
||||
- opus 中位数 2454 ms(最小 1224,最大 3170)
|
||||
|
||||
## CLI 启动基准测试
|
||||
|
||||
@ -104,37 +104,37 @@ x-i18n:
|
||||
|
||||
- `startup`:`--version`、`--help`、`health`、`health --json`、`status --json`、`status`
|
||||
- `real`:`health`、`status`、`status --json`、`sessions`、`sessions --json`、`tasks --json`、`tasks list --json`、`tasks audit --json`、`agents list --json`、`gateway status`、`gateway status --json`、`gateway health --json`、`config get gateway.port`
|
||||
- `all`:同时包含两个预设
|
||||
- `all`:以上两个预设
|
||||
|
||||
输出内容包括每个命令的 `sampleCount`、平均值、p50、p95、最小 / 最大值、exit-code / signal 分布,以及最大 RSS 汇总。可选的 `--cpu-prof-dir` / `--heap-prof-dir` 会为每次运行写入 V8 profile,因此计时和 profile 捕获使用的是同一个 harness。
|
||||
输出内容包括每个命令的 `sampleCount`、平均值、p50、p95、最小值 / 最大值、退出码 / signal 分布,以及最大 RSS 摘要。可选的 `--cpu-prof-dir` / `--heap-prof-dir` 会为每次运行写入 V8 profile,这样耗时统计和 profile 采集会使用同一套 harness。
|
||||
|
||||
已保存输出约定:
|
||||
保存输出约定:
|
||||
|
||||
- `pnpm test:startup:bench:smoke` 会将定向 smoke 工件写入 `.artifacts/cli-startup-bench-smoke.json`
|
||||
- `pnpm test:startup:bench:save` 会使用 `runs=5` 和 `warmup=1` 将完整测试套件工件写入 `.artifacts/cli-startup-bench-all.json`
|
||||
- `pnpm test:startup:bench:update` 会使用 `runs=5` 和 `warmup=1` 刷新已检入的基线夹具 `test/fixtures/cli-startup-bench.json`
|
||||
- `pnpm test:startup:bench:update` 会使用 `runs=5` 和 `warmup=1` 刷新已检入的基线 fixture,路径为 `test/fixtures/cli-startup-bench.json`
|
||||
|
||||
已检入夹具:
|
||||
已检入的 fixture:
|
||||
|
||||
- `test/fixtures/cli-startup-bench.json`
|
||||
- 使用 `pnpm test:startup:bench:update` 刷新
|
||||
- 使用 `pnpm test:startup:bench:check` 将当前结果与该夹具进行比较
|
||||
- 使用 `pnpm test:startup:bench:check` 将当前结果与该 fixture 进行比较
|
||||
|
||||
## 新手引导 E2E(Docker)
|
||||
|
||||
Docker 是可选的;只有在进行容器化新手引导冒烟测试时才需要它。
|
||||
Docker 是可选的;仅在需要容器化的新手引导 smoke 测试时才需要。
|
||||
|
||||
在一个干净的 Linux 容器中执行完整冷启动流程:
|
||||
在一个干净的 Linux 容器中运行完整冷启动流程:
|
||||
|
||||
```bash
|
||||
scripts/e2e/onboard-docker.sh
|
||||
```
|
||||
|
||||
该脚本会通过 pseudo-tty 驱动交互式向导,验证配置 / 工作区 / 会话文件,然后启动 Gateway 网关并运行 `openclaw health`。
|
||||
该脚本会通过伪终端驱动交互式向导,验证 config / workspace / session 文件,然后启动 Gateway 网关并运行 `openclaw health`。
|
||||
|
||||
## QR 导入冒烟测试(Docker)
|
||||
## QR 导入 smoke 测试(Docker)
|
||||
|
||||
确保维护中的 QR 运行时辅助程序能够在受支持的 Docker Node 运行时下加载(默认 Node 24,兼容 Node 22):
|
||||
确保维护中的 QR 运行时 helper 能在受支持的 Docker Node 运行时下正确加载(默认 Node 24,兼容 Node 22):
|
||||
|
||||
```bash
|
||||
pnpm test:docker:qr
|
||||
|
||||
Loading…
Reference in New Issue
Block a user