Cron jobs(Gateway 调度器)

Cron 还是 Heartbeat? 查看 Cron vs Heartbeat 了解如何选择。

Cron 是 Gateway 内置的调度器。它会持久化任务、在合适的时间唤醒 Agent,还可以选择把输出发送回聊天。

如果你想要 “每天早上运行这个”“20 分钟后提醒 Agent”,Cron 就是你需要的机制。

TL;DR

  • Cron 运行在 Gateway 内部(不是在模型里)。
  • 任务持久化在 ~/.openclaw/cron/ 下,重启不会丢失计划。
  • 两种执行方式:
    • Main session:将系统事件加入队列,在下次 Heartbeat 时运行。
    • Isolated:在 cron:<jobId> 中运行独立的 Agent 回合,可选择发送输出。
  • 唤醒是一等公民:任务可以请求”立即唤醒”或”下次 Heartbeat”。

快速开始(可操作)

创建一个一次性提醒,验证它存在,然后立即运行:

openclaw cron add \
  --name "Reminder" \
  --at "2026-02-01T16:00:00Z" \
  --session main \
  --system-event "Reminder: check the cron docs draft" \
  --wake now \
  --delete-after-run

openclaw cron list
openclaw cron run <job-id> --force
openclaw cron runs --id <job-id>

调度一个带发送功能的循环 Isolated 任务:

openclaw cron add \
  --name "Morning brief" \
  --cron "0 7 * * *" \
  --tz "America/Los_Angeles" \
  --session isolated \
  --message "Summarize overnight updates." \
  --deliver \
  --channel slack \
  --to "channel:C1234567890"

工具调用等效方式(Gateway cron 工具)

关于规范的 JSON 格式和示例,查看 工具调用的 JSON schema

Cron 任务存储位置

Cron 任务默认持久化在 Gateway 主机的 ~/.openclaw/cron/jobs.json。Gateway 会把文件加载到内存,修改时写回,所以手动编辑只有在 Gateway 停止时才安全。建议用 openclaw cron add/edit 或 Cron 工具调用 API 来修改。

新手友好概览

把 Cron 任务想象成:什么时候运行 + 做什么

  1. 选择调度方式

    • 一次性提醒 → schedule.kind = "at"(CLI:--at
    • 重复任务 → schedule.kind = "every"schedule.kind = "cron"
    • 如果你的 ISO 时间戳省略了时区,会被当作 UTC
  2. 选择运行位置

    • sessionTarget: "main" → 在下次 Heartbeat 时用主上下文运行。
    • sessionTarget: "isolated" → 在 cron:<jobId> 中运行独立的 Agent 回合。
  3. 选择负载

    • Main session → payload.kind = "systemEvent"
    • Isolated session → payload.kind = "agentTurn"

可选:deleteAfterRun: true 会在一次性任务成功后从存储中删除。

核心概念

Jobs

Cron 任务是一条存储记录,包含:

  • schedule(什么时候运行)
  • payload(做什么)
  • 可选的 delivery(输出发送到哪里)
  • 可选的 agent bindingagentId):在特定 Agent 下运行任务;如果缺失或未知,Gateway 会回退到默认 Agent。

任务通过稳定的 jobId 标识(CLI/Gateway API 使用)。在 Agent 工具调用中,jobId 是规范名称;为了兼容性也接受旧的 id。任务可以通过 deleteAfterRun: true 在一次性运行成功后自动删除。

Schedules

Cron 支持三种调度类型:

  • at:一次性时间戳(从 epoch 开始的毫秒数)。Gateway 接受 ISO 8601 并强制转换为 UTC。
  • every:固定间隔(毫秒)。
  • cron:5 字段 Cron 表达式,可选 IANA 时区。

Cron 表达式使用 croner。如果省略时区,会使用 Gateway 主机的本地时区。

Main vs Isolated 执行

Main session 任务(系统事件)

Main 任务会将系统事件加入队列,并可选择唤醒 Heartbeat 运行器。它们必须使用 payload.kind = "systemEvent"

  • wakeMode: "next-heartbeat"(默认):事件等待下次计划的 Heartbeat。
  • wakeMode: "now":事件触发立即的 Heartbeat 运行。

当你想要正常的 Heartbeat Prompt + Main session 上下文时,这是最佳选择。查看 Heartbeat

Isolated 任务(独立 Cron Session)

Isolated 任务在 Session cron:<jobId> 中运行独立的 Agent 回合。

关键行为:

  • Prompt 会加上 [cron:<jobId> <job name>] 前缀以便追踪。
  • 每次运行都启动一个全新的 Session ID(不会继承之前的对话)。
  • 摘要会发布到 Main session(前缀 Cron,可配置)。
  • wakeMode: "now" 会在发布摘要后触发立即的 Heartbeat。
  • 如果 payload.deliver: true,输出会发送到 Channel;否则保持内部。

对于嘈杂、频繁或”后台杂务”类任务,用 Isolated 任务可以避免刷屏你的主聊天历史。

Payload 格式(运行什么)

支持两种 Payload 类型:

  • systemEvent:仅限 Main session,通过 Heartbeat Prompt 路由。
  • agentTurn:仅限 Isolated session,运行独立的 Agent 回合。

常见的 agentTurn 字段:

  • message:必需的文本 Prompt。
  • model / thinking:可选覆盖(见下文)。
  • timeoutSeconds:可选超时覆盖。
  • delivertrue 表示发送输出到 Channel 目标。
  • channellast 或特定 Channel。
  • to:Channel 特定目标(电话/聊天/频道 ID)。
  • bestEffortDeliver:避免因发送失败而导致任务失败。

隔离选项(仅限 session=isolated):

  • postToMainPrefix(CLI:--post-prefix):Main 中系统事件的前缀。
  • postToMainModesummary(默认)或 full
  • postToMainMaxCharspostToMainMode=full 时的最大字符数(默认 8000)。

模型和思考覆盖

Isolated 任务(agentTurn)可以覆盖模型和思考级别:

  • model:Provider/模型字符串(如 anthropic/claude-sonnet-4-20250514)或别名(如 opus
  • thinking:思考级别(offminimallowmediumhighxhigh;仅限 GPT-5.2 + Codex 模型)

注意:你也可以在 Main session 任务上设置 model,但这会改变共享的 Main session 模型。我们建议只在 Isolated 任务上覆盖模型,避免意外的上下文切换。

解析优先级:

  1. 任务 Payload 覆盖(最高)
  2. Hook 特定默认值(如 hooks.gmail.model
  3. Agent 配置默认值

发送(Channel + 目标)

Isolated 任务可以将输出发送到 Channel。任务 Payload 可以指定:

  • channelwhatsapp / telegram / discord / slack / mattermost(Plugin)/ signal / imessage / last
  • to:Channel 特定的接收者目标

如果省略 channelto,Cron 可以回退到 Main session 的”最后路由”(Agent 最后回复的地方)。

发送注意事项:

  • 如果设置了 to,即使省略 deliver,Cron 也会自动发送 Agent 的最终输出。
  • 当你想要最后路由发送但没有明确的 to 时,使用 deliver: true
  • 使用 deliver: false 可以在有 to 的情况下仍保持输出内部。

目标格式提醒:

  • Slack/Discord/Mattermost(Plugin)目标应使用明确的前缀(如 channel:<id>user:<id>)以避免歧义。
  • Telegram 话题应使用 :topic: 形式(见下文)。

Telegram 发送目标(话题/论坛线程)

Telegram 通过 message_thread_id 支持论坛话题。对于 Cron 发送,你可以在 to 字段中编码话题/线程:

  • -1001234567890(仅聊天 ID)
  • -1001234567890:topic:123(推荐:明确的话题标记)
  • -1001234567890:123(简写:数字后缀)

也接受带前缀的目标,如 telegram:... / telegram:group:...

  • telegram:group:-1001234567890:topic:123

工具调用的 JSON schema

直接调用 Gateway cron.* 工具(Agent 工具调用或 RPC)时使用这些格式。CLI 标志接受人类可读的持续时间如 20m,但工具调用对 atMseveryMs 使用 epoch 毫秒数(at 时间接受 ISO 时间戳)。

cron.add 参数

一次性 Main session 任务(系统事件):

{
  "name": "Reminder",
  "schedule": { "kind": "at", "atMs": 1738262400000 },
  "sessionTarget": "main",
  "wakeMode": "now",
  "payload": { "kind": "systemEvent", "text": "Reminder text" },
  "deleteAfterRun": true
}

循环 Isolated 任务带发送:

{
  "name": "Morning brief",
  "schedule": { "kind": "cron", "expr": "0 7 * * *", "tz": "America/Los_Angeles" },
  "sessionTarget": "isolated",
  "wakeMode": "next-heartbeat",
  "payload": {
    "kind": "agentTurn",
    "message": "Summarize overnight updates.",
    "deliver": true,
    "channel": "slack",
    "to": "channel:C1234567890",
    "bestEffortDeliver": true
  },
  "isolation": { "postToMainPrefix": "Cron", "postToMainMode": "summary" }
}

注意:

  • schedule.kindatatMs)、everyeveryMs)或 cronexpr,可选 tz)。
  • atMseveryMs 是 epoch 毫秒数。
  • sessionTarget 必须是 "main""isolated",且必须匹配 payload.kind
  • 可选字段:agentIddescriptionenableddeleteAfterRunisolation
  • 省略时 wakeMode 默认为 "next-heartbeat"

cron.update 参数

{
  "jobId": "job-123",
  "patch": {
    "enabled": false,
    "schedule": { "kind": "every", "everyMs": 3600000 }
  }
}

注意:

  • jobId 是规范名称;为了兼容性接受 id
  • 在 patch 中使用 agentId: null 可以清除 Agent 绑定。

cron.run 和 cron.remove 参数

{ "jobId": "job-123", "mode": "force" }
{ "jobId": "job-123" }

存储和历史

  • 任务存储:~/.openclaw/cron/jobs.json(Gateway 管理的 JSON)。
  • 运行历史:~/.openclaw/cron/runs/<jobId>.jsonl(JSONL,自动修剪)。
  • 覆盖存储路径:配置中的 cron.store

配置

{
  cron: {
    enabled: true, // 默认 true
    store: "~/.openclaw/cron/jobs.json",
    maxConcurrentRuns: 1, // 默认 1
  },
}

完全禁用 Cron:

  • cron.enabled: false(配置)
  • OPENCLAW_SKIP_CRON=1(环境变量)

CLI 快速开始

一次性提醒(UTC ISO,成功后自动删除):

openclaw cron add \
  --name "Send reminder" \
  --at "2026-01-12T18:00:00Z" \
  --session main \
  --system-event "Reminder: submit expense report." \
  --wake now \
  --delete-after-run

一次性提醒(Main session,立即唤醒):

openclaw cron add \
  --name "Calendar check" \
  --at "20m" \
  --session main \
  --system-event "Next heartbeat: check calendar." \
  --wake now

循环 Isolated 任务(发送到 WhatsApp):

openclaw cron add \
  --name "Morning status" \
  --cron "0 7 * * *" \
  --tz "America/Los_Angeles" \
  --session isolated \
  --message "Summarize inbox + calendar for today." \
  --deliver \
  --channel whatsapp \
  --to "+15551234567"

循环 Isolated 任务(发送到 Telegram 话题):

openclaw cron add \
  --name "Nightly summary (topic)" \
  --cron "0 22 * * *" \
  --tz "America/Los_Angeles" \
  --session isolated \
  --message "Summarize today; send to the nightly topic." \
  --deliver \
  --channel telegram \
  --to "-1001234567890:topic:123"

带模型和思考覆盖的 Isolated 任务:

openclaw cron add \
  --name "Deep analysis" \
  --cron "0 6 * * 1" \
  --tz "America/Los_Angeles" \
  --session isolated \
  --message "Weekly deep analysis of project progress." \
  --model "opus" \
  --thinking high \
  --deliver \
  --channel whatsapp \
  --to "+15551234567"

Agent 选择(多 Agent 设置):

# 将任务固定到 Agent "ops"(如果该 Agent 缺失则回退到默认)
openclaw cron add --name "Ops sweep" --cron "0 6 * * *" --session isolated --message "Check ops queue" --agent ops

# 切换或清除现有任务的 Agent
openclaw cron edit <jobId> --agent ops
openclaw cron edit <jobId> --clear-agent

手动运行(调试):

openclaw cron run <jobId> --force

编辑现有任务(修补字段):

openclaw cron edit <jobId> \
  --message "Updated prompt" \
  --model "opus" \
  --thinking low

运行历史:

openclaw cron runs --id <jobId> --limit 50

不创建任务的立即系统事件:

openclaw system event --mode now --text "Next heartbeat: check battery."

Gateway API 接口

  • cron.listcron.statuscron.addcron.updatecron.remove
  • cron.run(force 或 due)、cron.runs 对于不创建任务的立即系统事件,使用 openclaw system event

故障排除

”什么都不运行”

  • 检查 Cron 是否启用:cron.enabledOPENCLAW_SKIP_CRON
  • 检查 Gateway 是否持续运行(Cron 在 Gateway 进程内运行)。
  • 对于 cron 调度:确认时区(--tz)与主机时区。

Telegram 发送到错误的地方

  • 对于论坛话题,使用 -100…:topic:<id> 以便明确无歧义。
  • 如果你在日志或存储的”最后路由”目标中看到 telegram:... 前缀,这是正常的;Cron 发送接受它们并仍能正确解析话题 ID。