Skip to content

定时任务(网关调度器)

定时任务 vs 心跳? 参见 定时任务 vs 心跳 以获取何时使用每种方式的指导。

定时任务是网关的内置调度器。它持久化任务,在正确的时间唤醒代理,并可选择将输出传递回聊天。

如果你想要_"每天早上运行此任务""在 20 分钟后提醒代理"_, 定时任务就是这种机制。

TL;DR

  • 定时任务运行在网关内部(不在模型内部)。
  • 任务持久化在 ~/.openclaw/cron/ 下,因此重启不会丢失计划。
  • 两种执行方式:
    • 主会话: 将系统事件加入队列,然后在下一次心跳时运行。
    • 隔离: 在 cron:<jobId> 中运行专用的代理轮次,可选择传递输出。
  • 唤醒是一等公民:任务可以请求"立即唤醒"或"下次心跳"。

快速开始(可操作)

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

bash
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>

调度一个带传递的重复隔离任务:

bash
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"

工具调用等效项(网关定时任务工具)

有关规范的 JSON 形状和示例,请参见 工具调用的 JSON 模式

定时任务的存储位置

定时任务默认持久化在网关主机的 ~/.openclaw/cron/jobs.json。 网关将文件加载到内存中,并在更改时写回,因此手动编辑 仅在网关停止时才安全。对于更改,建议使用 openclaw cron add/edit 或定时任务 工具调用 API。

初学者友好概述

将定时任务视为:何时运行 + 做什么

  1. 选择计划

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

    • sessionTarget: "main" → 在下一次心跳时以主上下文运行。
    • sessionTarget: "isolated" → 在 cron:<jobId> 中运行专用的代理轮次。
  3. 选择负载

    • 主会话 → payload.kind = "systemEvent"
    • 隔离会话 → payload.kind = "agentTurn"

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

概念

任务

定时任务是一个存储的记录,包含:

  • 计划(何时运行),
  • 负载(做什么),
  • 可选的传递(输出应该发送到哪里)。
  • 可选的代理绑定(agentId): 在特定代理下运行任务;如果 缺失或未知,网关会回退到默认代理。

任务由稳定的 jobId 标识(由 CLI/网关 API 使用)。 在代理工具调用中,jobId 是规范的;遗留的 id 为了兼容性被接受。 任务可以通过 deleteAfterRun: true 在成功的一次性运行后自动删除。

计划

定时任务支持三种计划类型:

  • at: 一次性时间戳(自纪元以来的毫秒数)。网关接受 ISO 8601 并强制转换为 UTC。
  • every: 固定间隔(毫秒)。
  • cron: 5 字段 cron 表达式,带可选的 IANA 时区。

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

主会话 vs 隔离执行

主会话任务(系统事件)

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

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

当你想要正常的心跳提示 + 主会话上下文时,这是最合适的。 参见 心跳

隔离任务(专用定时任务会话)

隔离任务在会话 cron:<jobId> 中运行专用的代理轮次。

关键行为:

  • 提示带有 [cron:<jobId> <job name>] 前缀以便追踪。
  • 每次运行都启动一个新的会话 ID(没有先前对话的延续)。
  • 摘要被发布到主会话(前缀 Cron,可配置)。
  • wakeMode: "now" 在发布摘要后触发立即心跳。
  • 如果 payload.deliver: true,输出将传递到频道;否则保持内部。

对于不应该刷屏主聊天历史的嘈杂、频繁或"后台杂务", 使用隔离任务。

负载形状(运行什么)

支持两种负载类型:

  • systemEvent: 仅限主会话,通过心跳提示路由。
  • agentTurn: 仅限隔离会话,运行专用的代理轮次。

常见的 agentTurn 字段:

  • message: 必需的文本提示。
  • model / thinking: 可选覆盖(见下文)。
  • timeoutSeconds: 可选超时覆盖。
  • deliver: true 将输出发送到频道目标。
  • channel: last 或特定频道。
  • to: 特定于频道的目标(电话/聊天/频道 ID)。
  • bestEffortDeliver: 避免传递失败时任务失败。

隔离选项(仅适用于 session=isolated):

  • postToMainPrefix (CLI: --post-prefix): 主会话中系统事件的前缀。
  • postToMainMode: summary (默认) 或 full
  • postToMainMaxChars: 当 postToMainMode=full 时的最大字符数(默认 8000)。

模型和思考覆盖

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

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

注意: 你也可以在主会话任务上设置 model,但它会改变共享的主 会话模型。我们建议仅为隔离任务使用模型覆盖,以避免 意外的上下文转换。

解析优先级:

  1. 任务负载覆盖(最高)
  2. 钩子特定默认值(例如 hooks.gmail.model)
  3. 代理配置默认值

传递(频道 + 目标)

隔离任务可以将输出传递到频道。任务负载可以指定:

  • channel: whatsapp / telegram / discord / slack / mattermost (插件) / signal / imessage / last
  • to: 特定于频道的接收者目标

如果 channelto 被省略,定时任务可以回退到主会话的"最后路由" (代理回复的最后位置)。

传递说明:

  • 如果设置了 to,定时任务会自动传递代理的最终输出,即使省略了 deliver
  • 当你想要最后路由传递而不需要显式的 to 时,使用 deliver: true
  • 使用 deliver: false 即使存在 to 也保持输出内部。

目标格式提醒:

  • Slack/Discord/Mattermost(插件)目标应使用显式前缀(例如 channel:<id>user:<id>)以避免歧义。
  • Telegram 主题应使用 :topic: 形式(见下文)。

Telegram 传递目标(主题/论坛线程)

Telegram 通过 message_thread_id 支持论坛主题。对于定时任务传递,你可以将 主题/线程编码到 to 字段中:

  • -1001234567890 (仅聊天 ID)
  • -1001234567890:topic:123 (首选:显式主题标记)
  • -1001234567890:123 (简写:数字后缀)

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

  • telegram:group:-1001234567890:topic:123

工具调用的 JSON 模式

直接调用网关 cron.* 工具(代理工具调用或 RPC)时使用这些形状。 CLI 标志接受类似 20m 的人类持续时间,但工具调用对 atMseveryMs 使用纪元毫秒(at 时间接受 ISO 时间戳)。

cron.add 参数

一次性主会话任务(系统事件):

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

带传递的重复隔离任务:

json
{
  "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.kind: at (atMs)、every (everyMs) 或 cron (expr,可选 tz)。
  • atMseveryMs 是纪元毫秒。
  • sessionTarget 必须是 "main""isolated" 并且必须与 payload.kind 匹配。
  • 可选字段: agentIddescriptionenableddeleteAfterRunisolation
  • 省略时,wakeMode 默认为 "next-heartbeat"

cron.update 参数

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

注意:

  • jobId 是规范的;id 为了兼容性被接受。
  • 在补丁中使用 agentId: null 来清除代理绑定。

cron.run 和 cron.remove 参数

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

存储和历史

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

配置

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

完全禁用定时任务:

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

CLI 快速开始

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

bash
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

一次性提醒(主会话,立即唤醒):

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

重复隔离任务(传递到 WhatsApp):

bash
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"

重复隔离任务(传递到 Telegram 主题):

bash
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"

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

bash
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"

代理选择(多代理设置):

bash
# Pin a job to agent "ops" (falls back to default if that agent is missing)
openclaw cron add --name "Ops sweep" --cron "0 6 * * *" --session isolated --message "Check ops queue" --agent ops

# Switch or clear the agent on an existing job
openclaw cron edit <jobId> --agent ops
openclaw cron edit <jobId> --clear-agent

手动运行(调试):

bash
openclaw cron run <jobId> --force

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

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

运行历史:

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

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

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

网关 API 界面

  • cron.listcron.statuscron.addcron.updatecron.remove
  • cron.run(强制或到期)、cron.runs 对于不带任务的立即系统事件,使用 openclaw system event

故障排除

"什么都不运行"

  • 检查定时任务是否启用: cron.enabledOPENCLAW_SKIP_CRON
  • 检查网关是否持续运行(定时任务在网关进程内运行)。
  • 对于 cron 计划: 确认时区(--tz) vs 主机时区。

Telegram 传递到错误的位置

  • 对于论坛主题,使用 -100…:topic:<id> 使其明确且无歧义。
  • 如果你在日志或存储的"最后路由"目标中看到 telegram:... 前缀,这是正常的; 定时任务传递接受它们并且仍然正确解析主题 ID。