Appearance
Cron 是 OpenClaw Gateway 内置的调度器,支持一次性(--at)、固定间隔(--every)和标准 cron 表达式三种调度类型,以及主 session、隔离 session、当前 session 和自定义 session 四种执行模式。配置时注意时区(--tz 或 UTC 默认)、模型优先级和重试参数;故障时用 openclaw cron list / openclaw cron runs 定位问题。Webhook 和 Gmail PubSub 集成需额外启用 hooks 并配置认证 token。
OpenClaw Cron 定时任务配置与排查指南
Cron 是 Gateway 的内置调度器。它持久化任务、在正确时间唤醒 Agent,并可以将输出回传到聊天频道或 Webhook 端点。
快速开始
添加一次性提醒
```bash
openclaw cron add \
--name "Reminder" \
--at "2026-02-01T16:00:00Z" \
--session main \
--system-event "提醒:检查 cron 文档草稿" \
--wake now \
--delete-after-run
```
查看任务列表
```bash
openclaw cron list
openclaw cron get <job-id>
openclaw cron show <job-id>
```
查看运行历史
```bash
openclaw cron runs --id <job-id>
```
Cron 工作原理
- Cron 在 Gateway 进程内部运行(不在模型内部)。
- 任务持久化在
~/.openclaw/cron/jobs.json,重启不丢失调度。 - 运行时执行状态持久化在相邻的
~/.openclaw/cron/jobs-state.json。如果要将 cron 定义纳入 git 管理,请只跟踪jobs.json,并在.gitignore中忽略jobs-state.json。分裂后,旧版 OpenClaw 仍可读取jobs.json,但因运行时字段现在位于jobs-state.json,可能将任务视为全新。 - 当在 Gateway 运行或停止状态下手动编辑
jobs.json时,OpenClaw 会比较修改的调度字段与待定运行时元数据,如果冲突则清除过时的nextRunAtMs值。纯格式或键顺序修改不会影响待定槽位。 - 所有 cron 执行都会创建后台任务记录。
- Gateway 启动时,过期的隔离 Agent 轮次任务会被重新调度到频道连接窗口之外,而不是立即重播,从而确保 Discord/Telegram 启动和原生命令设置恢复后保持响应。
- 一次性任务(
--at)默认在成功后自动删除。 - 隔离 cron 运行在完成后会尽力关闭该 cron session 追踪的浏览器标签/进程,避免留下孤立进程。
- 隔离 cron 运行获得窄 cron 自清理授权时,仍可读取调度器状态、自身任务的过滤列表以及该任务的运行历史,因此状态/心跳检查可检查自身调度而不获得更广泛的 cron 修改权限。
- 隔离 cron 运行还防范过时的确认回复:如果第一条结果只是中间状态更新(如“正在处理”之类)且没有子 Agent 继续负责最终答案,OpenClaw 会重新提示一次获取实际结果再进行交付。
- 隔离 cron 运行使用嵌入运行的执行拒绝元数据,包括节点主机
UNAVAILABLE包装器(其嵌套错误消息以SYSTEM_RUN_DENIED或INVALID_REQUEST开头),因此被阻止的命令不会被报告为正常运行,而普通助手表白不会被当作拒绝。 - 隔离 cron 运行还将运行级 Agent 失败视为任务错误,即使没有生成回复负载,模型/提供商失败也会增加错误计数器并触发失败通知,而不是将任务清除为成功。
- 当隔离 Agent 轮次任务达到
timeoutSeconds时,cron 中止底层 Agent 运行并给予短暂清理窗口。如果运行未排空,Gateway 拥有的清理会强制清除该运行的会话所有权,然后 cron 记录超时,从而避免排队的聊天工作被陈旧的处理会话阻塞。 - 如果隔离 Agent 轮次在运行程序启动前或第一次模型调用前停滞,cron 会记录阶段特定超时,如
setup timed out before runner start或stalled before first model call (last phase: context-engine)。这些看门狗覆盖嵌入提供商和 CLI 后端的提供商(在其外部 CLI 进程实际启动之前),且独立于较长的timeoutSeconds值,使冷启动/认证/上下文失败快速浮出水面。 - 如果使用系统 cron 或其他外部调度器运行
openclaw agent,请加上硬杀死升级(即使 CLI 已处理SIGTERM/SIGINT)。Gateway 后备运行会请求 Gateway 中止已接受的运行;本地和嵌入回退运行会收到相同的中止信号。对于 GNUtimeout,建议使用timeout -k 60 600 openclaw agent ...而非普通timeout 600 ...;-k值是进程无法排空时的监管后备。对于 systemd 单元,使用SIGTERM停止信号加上类似TimeoutStopSec的宽限窗口。如果重试重复使用--run-id而原始 Gateway 运行仍然活跃,重复运行会被报告为正在飞行中,而不是启动第二个运行。
INFO
任务协调由运行时拥有:只要 cron 运行时仍追踪该任务,活跃 cron 任务就会保持存活。一旦运行时不再拥有该任务且 5 分钟宽限窗口过期,维护进程从持久化运行日志和任务状态匹配 cron:<jobId>:<startedAt> 运行。如果持久历史显示终止结果,则从它最终确定任务账本;否则 Gateway 拥有的维护可将任务标记为 lost。离线 CLI 审计可从持久历史恢复,但不会将自身空进程中活跃任务集作为 Gateway 拥有的 cron 运行已消失的证据。
调度类型
| 类型 | CLI 参数 | 说明 |
|---|---|---|
at | --at | 一次性时间戳(ISO 8601 或相对时间如 20m) |
every | --every | 固定间隔 |
cron | --cron | 5 字段或 6 字段 cron 表达式,可配合 --tz |
不带时区的时间戳视为 UTC。使用 --tz America/New_York 按本地时钟调度。
整点重复表达式会自动错开最多 5 分钟以减少负载峰值。使用 --exact 强制精确时间,或用 --stagger 30s 指定错开窗口。
月份日期和星期几使用 OR 逻辑
Cron 表达式由 croner 解析。当月份日期和星期几次两个字段都是非通配符时,croner 匹配任一字段——不等同于两者都匹配。这是标准 Vixie cron 行为。
# 预期:"每月 15 号 9 AM,仅限于周一"
# 实际: "每月 15 号 9 AM,以及每个周一 9 AM"
0 9 15 * 1这会导致每月触发约 5-6 次而非 0-1 次。OpenClaw 使用 Croner 的默认 OR 行为。如需要求两者都满足,请使用 Croner 的 + 星期次修饰符(0 9 15 * +1),或在一个字段上调度并在任务提示词或命令中守卫另一个字段。
执行方式
| 方式 | --session 值 | 运行位置 | 适合场景 |
|---|---|---|---|
| 主 session | main | 专用 cron 唤醒队列 | 提醒、系统事件 |
| 隔离 | isolated | 专用 cron:<jobId> | 报告、后台任务 |
| 当前 session | current | 创建时绑定 | 上下文相关的周期工作 |
| 自定义 session | session:custom-id | 持久化命名 session | 需要积累历史的工作流 |
主 session 与隔离/自定义 session 区别
**主 session** 任务将系统事件排队到 cron 拥有的运行队列,并可选择唤醒心跳(`--wake now` 或 `--wake next-heartbeat`)。它们可使用目标主 session 的最后交付上下文进行回复,但不会将常规 cron 轮次追加到人类聊天队列,也不会延长目标 session 的每日/闲置重置新鲜度。**隔离**任务使用新 session 运行专用 Agent 轮次。**自定义 session**(`session:xxx`)跨次运行保留上下文,适合每日站会等需要基于历史摘要的工作流。
隔离任务的‘新 session’含义
对于隔离任务,“新 session”意味着每次运行都有新的对话记录/session id。OpenClaw 可能携带安全偏好(如思考/快速/冗长设置、标签、用户显式选择的模型/auth 覆盖),但不会从旧的 cron 行继承环境对话上下文(频道/组路由、发送或队列策略、提升、来源或 ACP 运行时绑定)。当周期任务需要有意建立在相同对话上下文上时,使用 `current` 或 `session:<id>`。
运行时清理
对于隔离任务,运行时拆卸包含对该 cron session 的尽力浏览器清理。清理失败不会影响 cron 实际结果。
隔离 cron 运行还会处理通过共享运行时清理路径为该任务创建的任何捆绑 MCP 运行时实例。这匹配主 session 和自定义 session MCP 客户端的拆除方式,因此隔离 cron 任务不会跨运行泄漏 stdio 子进程或长期 MCP 连接。
子 Agent 和 Discord 交付
当隔离 cron 运行编排子 Agent 时,交付也优先使用最终子 Agent 输出而非过时的父级临时文本。如果子 Agent 仍在运行,OpenClaw 会抑制部分父级更新,而不是宣布它。
对于纯文本 Discord announce 目标,OpenClaw 只发送规范的最终助手文本一次,而不是重播流式/中间文本 payload 和最终答案。媒体和结构化 Discord payload 仍作为单独 payload 交付,因此附件和组件不会丢失。
隔离任务的负载选项
--message string (必填)
提示词文本(隔离任务必填)。
--model string
模型覆盖;使用任务的已允许的选定模型。
--thinking string
思考级别覆盖。
--light-context boolean
跳过 workspace bootstrap 文件注入。
--tools string
限制任务可使用的工具,例如 --tools exec,read。
--model 使用任务的已允许的选定模型作为该任务的主模型。它不同于聊天 session 的 /model 覆盖:配置的回退链在任务主模型失败时仍然适用。如果请求的模型不被允许或无法解析,cron 会以显式验证错误失败运行,而不是静默回退到任务的 Agent/默认模型选择。
Cron 任务还可以携带 payload 级 fallbacks。当存在时,该列表替换为该任务配置的回退链。在任务 payload/API 中使用 fallbacks: [] 表示严格 cron 运行,只尝试选定模型。如果任务有 --model 但既没有 payload 也没有配置回退,OpenClaw 传递显式空回退覆盖,因此 Agent 主模型不会作为隐式额外重试目标附加。
隔离任务的模型选择优先级:
- Gmail hook 模型覆盖(当运行来自 Gmail 且该覆盖被允许时)
- 每任务 payload
model - 存储的 cron session 模型覆盖
- Agent/默认模型选择
Fast 模式也遵循解析后的实时选择。如果选中的模型配置有 params.fastMode,隔离 cron 默认使用它。存储的 session fastMode 覆盖仍然优先于任一方向的配置。
如果隔离运行遇到实时模型切换,cron 会用切换后的提供商/模型重试,并在重试前持久化该实时选择。切换同时携带新 auth profile 时,cron 也会持久化该 auth profile 覆盖。重试有上限:初始尝试加 2 次切换重试后,cron 会中止而非无限循环。
在隔离 cron 运行进入 Agent 运行程序之前,OpenClaw 会检查可访问的本地提供商端点,对于配置了 api: "ollama" 和 api: "openai-completions" 且 baseUrl 为 loopback、私有网络或 .local 的提供商。如果端点不可用,运行被记录为 skipped,并附带清晰的提供商/模型错误,而不是启动模型调用。端点结果缓存 5 分钟,因此多个到期任务共享一个小型探测。跳过的提供商预检运行不会增加执行错误退避;当需要重复跳过通知时启用 failureAlert.includeSkipped。
交付与输出
| 模式 | 行为 |
|---|---|
announce | 如果 Agent 未发送,将最终文本回退交付到目标 |
webhook | POST 完成事件 payload 到 URL |
none | 无运行程序回退交付 |
使用 --announce --channel telegram --to "-1001234567890" 进行频道交付。Telegram 论坛话题使用 -1001234567890:topic:123;直接 RPC/config 调用者也可传递 delivery.threadId 为字符串或数字。Slack/Discord/Mattermost 目标应使用显式前缀(channel:<id>、user:<id>)。Matrix 房间 ID 区分大小写;使用 Matrix 中的确切房间 ID 或 room:!room:server 格式。
当 announce 交付使用 channel: "last" 或省略 channel 时,提供商前缀目标如 telegram:123 可在 cron 回退到 session 历史或单个配置频道之前选择频道。只有加载的插件播发的前缀才是提供商选择器。如果 delivery.channel 是显式的,目标前缀必须命名同一提供商;例如,channel: "whatsapp" 与 to: "telegram:123" 会被拒绝,而不是让 WhatsApp 将 Telegram ID 解释为电话号码。目标类型和服务前缀如 channel:<id>、user:<id>、imessage:<handle> 和 sms:<number> 仍然是频道拥有的目标语法,不是提供商选择器。
对于隔离任务,聊天交付是共享的。如果聊天路由可用,Agent 可以使用 message 工具,即使任务使用了 --no-deliver。如果 Agent 发送到配置/当前目标,OpenClaw 跳过回退 announce。否则 announce、webhook 和 none 只控制运行程序在 Agent 轮次后对最终回复做什么。
当 Agent 从活跃聊天创建隔离提醒时,OpenClaw 存储保留的实时交付目标用于回退 announce 路由。内部 session 键可能小写;当当前聊天上下文可用时,提供商交付目标不会从这些键重建。
隐式 announce 交付使用配置的频道允许列表来验证和重新路由过期目标。DM 配对存储批准不是回退自动化收件人;当调度任务应主动发送到 DM 时,设置 delivery.to 或配置频道 allowFrom 条目。
失败通知走独立的目标路径:
cron.failureDestination设置失败通知的全局默认目标。job.delivery.failureDestination对每个任务单独覆盖。- 如果两者都未设置且任务已通过
announce交付,失败通知会回退到该主 announce 目标。 delivery.failureDestination只支持sessionTarget="isolated"任务,除非主交付模式是webhook。failureAlert.includeSkipped: true为任务或全局 cron 警报策略选择重复跳过运行警报。跳过运行保持单独的连续跳过计数器,因此不影响执行错误退避。
CLI 示例
一次性提醒
```bash
openclaw cron add \
--name "Calendar check" \
--at "20m" \
--session main \
--system-event "下次心跳:检查日历。" \
--wake now
```
周期隔离任务
```bash
openclaw cron add \
--name "Morning brief" \
--cron "0 7 * * *" \
--tz "America/Los_Angeles" \
--session isolated \
--message "总结隔夜更新。" \
--announce \
--channel slack \
--to "channel:C1234567890"
```
模型和思考级别覆盖
```bash
openclaw cron add \
--name "Deep analysis" \
--cron "0 6 * * 1" \
--tz "America/Los_Angeles" \
--session isolated \
--message "对项目进度进行每周深度分析。" \
--model "opus" \
--thinking high \
--announce
```
Webhooks
Gateway 可以暴露 HTTP Webhook 端点用于外部触发。在配置中启用:
json5
{
hooks: {
enabled: true,
token: "shared-secret",
path: "/hooks",
},
}认证
每个请求必须通过 header 携带 hook token:
Authorization: Bearer <token>(推荐)x-openclaw-token: <token>
查询字符串 token 会被拒绝。
POST /hooks/wake
为主 session 加入系统事件:
```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"}'
```
text string (必填)
事件描述。
mode string
`now` 或 `next-heartbeat`。
POST /hooks/agent
运行隔离 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"}'
```
字段:`message`(必填)、`name`、`agentId`、`wakeMode`、`deliver`、`channel`、`to`、`model`、`fallbacks`、`thinking`、`timeoutSeconds`。
映射 Hook(POST /hooks/<name>)
自定义 hook 名称通过配置中的 `hooks.mappings` 解析。映射可以将任意 payload 通过模板或代码转换为 `wake` 或 `agent` 动作。
WARNING
确保 hook 端点仅在 loopback、tailnet 或可信反向代理后。
- 使用专用 hook token,不要复用 gateway auth token。
- 将
hooks.path保持在专用子路径;/会被拒绝。 - 设置
hooks.allowedAgentIds限制显式agentId路由。 - 保持
hooks.allowRequestSessionKey=false,除非需要调用方选择 session。 - 如果启用了
hooks.allowRequestSessionKey,同时设置hooks.allowedSessionKeyPrefixes来约束允许的 session key 形状。 - Hook payload 默认由安全边界包裹。
Gmail PubSub 集成
通过 Google PubSub 将 Gmail 收件箱触发器接入 OpenClaw。
INFO
前置条件: gcloud CLI、gog(gogcli)、OpenClaw hooks 已启用、Tailscale 用于公网 HTTPS 端点。
向导配置(推荐)
bash
openclaw webhooks gmail setup --account openclaw@gmail.com这会写入 hooks.gmail 配置,启用 Gmail 预设,并使用 Tailscale Funnel 作为 push 端点。
Gateway 自动启动
当 hooks.enabled=true 且 hooks.gmail.account 已设置时,Gateway 会在启动时启动 gog gmail watch serve 并自动续订 watch。设置 OPENCLAW_SKIP_GMAIL_WATCHER=1 可退出。
手动一次性配置
选择 GCP 项目
选择拥有 `gog` 使用的 OAuth 客户端的 GCP 项目:
```bash
gcloud auth login
gcloud config set project <project-id>
gcloud services enable gmail.googleapis.com pubsub.googleapis.com
```
创建 topic 并授予 Gmail push 权限
```bash
gcloud pubsub topics create gog-gmail-watch
gcloud pubsub topics add-iam-policy-binding gog-gmail-watch \
--member=serviceAccount:gmail-api-push@system.gserviceaccount.com \
--role=roles/pubsub.publisher
```
启动 watch
```bash
gog gmail watch start \
--account openclaw@gmail.com \
--label INBOX \
--topic projects/<project-id>/topics/gog-gmail-watch
```
Gmail 模型覆盖
json5
{
hooks: {
gmail: {
model: "openrouter/meta-llama/llama-3.3-70b-instruct:free",
thinking: "off",
},
},
}管理任务
bash
# 列出所有任务
openclaw cron list
# 获取单个任务 JSON
openclaw cron get <jobId>
# 显示单个任务(含解析的交付路由)
openclaw cron show <jobId>
# 编辑任务
openclaw cron edit <jobId> --message "更新后的提示词" --model "opus"
# 立即强制运行任务
openclaw cron run <jobId>
# 强制运行并等待终端状态
openclaw cron run <jobId> --wait --wait-timeout 10m --poll-interval 2s
# 仅在到期时运行
openclaw cron run <jobId> --due
# 查看运行历史
openclaw cron runs --id <jobId> --limit 50
# 查看单次运行
openclaw cron runs --id <jobId> --run-id <runId>
# 删除任务
openclaw cron remove <jobId>
# 指定 Agent(多 Agent 配置)
openclaw cron add --name "Ops sweep" --cron "0 6 * * *" --session isolated --message "检查运维队列" --agent ops
openclaw cron edit <jobId> --clear-agentopenclaw cron run <jobId> 在将手动运行排队后返回。使用 --wait 用于关机钩子、维护脚本或其他必须阻塞直到排队运行完成的自动化。等待模式轮询确切的返回 runId;状态为 ok 时退出 0,为 error、skipped 或等待超时时退出非零。
INFO
模型覆盖说明:
openclaw cron add|edit --model ...更改任务的选定模型。- 如果模型被允许,确切的提供商/模型将到达隔离 Agent 运行。
- 如果模型不被允许或无法解析,cron 以显式验证错误失败运行。
- 配置的回退链仍然适用,因为 cron
--model是任务主模型,而不是 session/model覆盖。 - Payload
fallbacks替换该任务的配置回退;fallbacks: []禁用回退,使运行严格。 - 不带显式或配置回退列表的普通
--model不会将 Agent 主模型作为隐式额外重试目标附加。
配置
json5
{
cron: {
enabled: true,
store: "~/.openclaw/cron/jobs.json",
maxConcurrentRuns: 1,
retry: {
maxAttempts: 3,
backoffMs: [60000, 120000, 300000],
retryOn: ["rate_limit", "overloaded", "network", "server_error"],
},
webhookToken: "replace-with-dedicated-webhook-token",
sessionRetention: "24h",
runLog: { maxBytes: "2mb", keepLines: 2000 },
},
}maxConcurrentRuns 限制调度 cron 分发和隔离 Agent 运行执行。隔离 cron Agent 运行在内部使用队列的专用 cron-nested 执行通道,因此提高此值可使独立 cron LLM 运行并行推进,而不仅仅是启动其外部 cron 包装器。共享的非 cron nested 通道不会因此设置而扩大。
运行时状态侧车从 cron.store 派生:.json 存储如 ~/clawd/cron/jobs.json 使用 ~/clawd/cron/jobs-state.json,而存储路径不带 .json 后缀则附加 -state.json。
如果手动编辑 jobs.json,请将 jobs-state.json 排除在版本控制之外。OpenClaw 使用该侧车存储待定槽位、活跃标记、上次运行元数据以及调度标识,该标识告诉调度器外部编辑的任务何时需要新的 nextRunAtMs。
禁用 cron:cron.enabled: false 或 OPENCLAW_SKIP_CRON=1。
重试行为
**一次性重试**:临时错误(频率限制、过载、网络、服务器错误)最多重试 3 次,指数退避。永久性错误立即禁用。
**周期任务重试**:重试间隔指数退避(30s 到 60m)。下次成功运行后重置退避。
维护
`cron.sessionRetention`(默认 `24h`)清理隔离运行 session 条目。`cron.runLog.maxBytes` / `cron.runLog.keepLines` 自动清理运行日志文件。
故障排查
诊断命令梯
bash
openclaw status
openclaw gateway status
openclaw cron status
openclaw cron list
openclaw cron runs --id <jobId> --limit 20
openclaw system heartbeat last
openclaw logs --follow
openclaw doctorCron 未触发
- 检查 `cron.enabled` 和 `OPENCLAW_SKIP_CRON` 环境变量。
- 确认 Gateway 持续运行。
- 对于 `cron` 调度,验证时区(`--tz`)与宿主机时区的关系。
- 运行输出中 `reason: not-due` 意味着用 `openclaw cron run <jobId> --due` 手动运行时任务还未到期。
Cron 已触发但没有交付
- 交付模式为 `none` 意味着不期望运行程序回退发送。当聊天路由可用时,Agent 仍可使用 `message` 工具直接发送。
- 交付目标缺失/无效(`channel`/`to`)意味着出站被跳过。
- 对于 Matrix,从旧版复制或带有小写 `delivery.to` 房间 ID 的任务可能失败,因为 Matrix 房间 ID 区分大小写。编辑任务使用 Matrix 中的确切 `!room:server` 或 `room:!room:server` 值。
- 频道认证错误(`unauthorized`、`Forbidden`)意味着交付被凭证阻止。
- 如果隔离运行只返回静默 token(`NO_REPLY` / `no_reply`),OpenClaw 会同时抑制直接出站交付和回退的排队摘要路径,不会向聊天发任何内容。
- 如果 Agent 应自己向用户发送消息,检查任务是否有可用路由(`channel: "last"` 加上之前聊天历史,或显式 channel/target)。
Cron 或心跳似乎阻止 /new-style 轮换
- 每日和闲置重置新鲜度不是基于 `updatedAt`;参考[Session 管理](/ai/ai-tools/openclaw/concepts/session#session-lifecycle)。
- Cron 唤醒、心跳运行、exec 通知和 Gateway 记账可能更新 session 行用于路由/状态,但不会延长 `sessionStartedAt` 或 `lastInteractionAt`。
- 对于在这些字段存在之前创建的旧行,OpenClaw 可在文件仍可用时从转录 JSONL session 头恢复 `sessionStartedAt`。没有 `lastInteractionAt` 的旧闲置行使用该恢复的开始时间作为闲置基线。
时区陷阱
- 不带 `--tz` 的 cron 使用 gateway 宿主机时区。
- 不带时区的 `at` 调度视为 UTC。
- 心跳的 `activeHours` 使用配置的时区解析。
常见问题
隔离任务和主 session 任务有什么区别?
隔离任务在专用的 cron:<jobId> session 中运行,有独立上下文,互不干扰,适合报告、后台数据处理等任务。主 session 任务只是往心跳队列里加系统事件,轻量快速,适合提醒和状态触发。
如何让定时任务把结果发到 Telegram 群?
使用 --announce --channel telegram --to "<chat_id>" 参数,或者在配置里设置 delivery.announce 目标。Telegram 话题频道使用 <chat_id>:topic:<topic_id> 格式。确保频道允许列表已正确配置。
Cron 任务失败了会重试吗?
会。一次性任务遇到临时错误(频率限制、过载、网络错误)最多重试 3 次,间隔指数退避(默认 60s/120s/300s)。永久性错误直接禁用不重试。周期任务重试间隔从 30s 指数退避到 60m,下次成功运行后重置。