问题:定时任务和对话互相打架

用 OpenClaw 搭了一套投资情报系统后,我的 Agent 身上挂了 20 多条定时任务(cron)——每天跑数据脚本、更新表格、推送日报。

听起来很美,直到我发现了一个致命问题:

所有 cron 都跑在 main session 里。

这意味着:

  • 我跟 Agent 聊天时,cron 触发了要排队等我对话结束
  • cron 脚本跑了 3 分钟,这 3 分钟我发消息 Agent 不回
  • 多条 cron 同时触发(比如早上 9:00 有 4 条),互相排队阻塞
  • 某条 cron 脚本挂了,整个 main session 卡住

简单来说:Agent 变成了一个单线程打工人,既要伺候老板,又要干杂活,谁都不满意。

为什么不一开始就用 Isolated Session?

因为之前试过,踩了一堆坑:

坑 1:GPT 在 isolated session 里会卡死

默认情况下,isolated session 会注入完整的 bootstrap context(SOUL.md、USER.md、MEMORY.md、一堆 skill 扫描),prompt 太大直接导致 LLM request timeout。

坑 2:--light-context 下工具调不动

加了 --light-context 能解决超时,但 GPT 在精简上下文下不会主动调用 message 工具推送消息。任务跑完了,你什么也收不到。

坑 3:内置投递机制不可靠

OpenClaw 有内置的 announce / deliver 机制(cron 跑完后自动把结果投递给你),但实测经常出现 delivered: true 但实际消息没发出的情况。

坑 4:Announce 重试风暴

isolated session 完成后的 announce 回调 main session,如果 main session 正忙(比如在做 context compaction),会重试 4 次,每次 60 秒 timeout。结果 Gateway 卡死需要重启。

所以之前的结论是:isolated 不可靠,全部走 main session。

转机:换一种投递思路

某天我重新想了一下这个问题,发现核心障碍不是 isolated session 本身,而是投递方式选错了

之前的思路:

cron → isolated session 跑任务 → 内置 announce/deliver 投递结果 ❌

新思路:

cron → isolated session 跑任务 → Agent 自己用 message 工具推送 ✅

关键区别:不依赖 OpenClaw 的内置投递机制,让 Agent 在 prompt 里直接用 message 工具往聊天里发消息。

同时把模型从 GPT 换成 Claude Sonnet(在 light-context 下工具调用更可靠),一举解决了之前的几个坑。

实施方案

第一步:试点验证

不敢一上来就全量迁移。先创建了一条最简单的试点 cron:

openclaw cron add "Cron Trial" \
  --cron "*/5 * * * *" \
  --session isolated \
  --no-deliver \
  --light-context \
  --timeout 120000 \
  --message '这是一条测试消息。用 message 工具推送到飞书私聊。'

关键参数:

  • --session isolated:不走 main session
  • --no-deliver:关闭内置投递
  • --light-context:减少 prompt 膨胀
  • prompt 里明确告诉 Agent 用 message 工具推送

第二步:连续验证

跑了 3 轮(每 5 分钟一次),全部成功:

Round状态耗时消息送达
1✅ ok7.2s
2✅ ok6.0s
3✅ ok6.3s

而且 main session 正在对话时,worker 也能正常执行和投递,互不干扰。

第三步:分层迁移

不是所有任务都一样复杂。按复杂度分了 5 个 Tier:

Tier说明示例
1纯脚本,无推送数据预拉取、快照
2脚本 + 数据库/表格更新价格更新、看板生成
3脚本 + 文字推送财报日历、日报
4脚本 + 图片推送持仓监控(含信息图)
5复杂链路(脚本+AI分析+图片+推送+归档)投研日报、周报

从 Tier 1 开始,每个 Tier 验证通过后再迁下一个。

第四步:集中验证技巧

这里有个实用技巧:不要等到正常触发时间才验证

把 cron 表达式临时改成马上触发的一次性时间:

# 原来是每天 09:20,临时改成今天 15:35
openclaw cron edit <job-id> --cron "35 15 23 3 *"

一个下午就能验证完所有任务。验证通过后再改回正确的 schedule。

完整的 Cron 配置模板

纯脚本任务(Tier 1)

openclaw cron edit <job-id> \
  --session isolated \
  --no-deliver \
  --light-context \
  --timeout 120000 \
  --message '运行数据脚本:
cd /path/to/workspace && python3 -u scripts/my_script.py 2>&1
成功后回复 HEARTBEAT_OK,失败则简要报告错误。'

脚本 + 表格更新(Tier 2)

openclaw cron edit <job-id> \
  --session isolated \
  --no-deliver \
  --timeout 300000 \
  --message '【定时任务】价格更新:
1. 运行: cd /path/to/workspace && python3 -u scripts/update_prices.py 2>&1
2. 读取 data/price_updates.json
3. 逐条用 feishu_bitable_update_record 更新表格
4. 全部完成后回复 HEARTBEAT_OK,失败则简要报告错误。'

脚本 + 推送消息(Tier 3)

openclaw cron edit <job-id> \
  --session isolated \
  --no-deliver \
  --timeout 180000 \
  --message '【定时任务】日报推送:
1. 运行: cd /path/to/workspace && python3 -u scripts/generate_report.py 2>&1
2. 读取 data/report.md
3. 用 message 工具推送到飞书 target: user:<your-open-id>
4. 完成后回复 HEARTBEAT_OK'

脚本 + 图片推送(Tier 4)

openclaw cron edit <job-id> \
  --session isolated \
  --no-deliver \
  --timeout 300000 \
  --message '【定时任务】监控报告:
1. 运行: cd /path/to/workspace && python3 -u scripts/generate_monitor.py 2>&1
2. 运行: python3 -u scripts/generate_infographic.py 2>&1
3. 读取 data/monitor.json,生成文字摘要
4. 用 message 工具推送文字摘要到飞书 target: user:<your-open-id>
5. 用 message 工具推送信息图(filePath: data/infographic.png, caption: "监控报告")
6. 完成后回复 HEARTBEAT_OK'

迁移前后对比

迁移前:
┌─────────────────────────┐
│      Main Session       │
│  ┌────┐ ┌────┐ ┌────┐  │
│  │对话│ │cron│ │cron│  │  ← 全部排队,互相阻塞
│  └────┘ └────┘ └────┘  │
└─────────────────────────┘

迁移后:
┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│ Main Session │  │ Isolated #1  │  │ Isolated #2  │
│   用户对话    │  │  cron 任务    │  │  cron 任务    │  ← 各跑各的
└──────────────┘  └──────────────┘  └──────────────┘

结果

指标迁移前迁移后
Main session cron 数量18 条0 条
对话被 cron 打断经常永不
Cron 互相排队阻塞经常不会
单条 cron 失败影响范围整个 main session仅该条
平均执行耗时与对话竞争6~200 秒独占

踩坑清单(帮你避雷)

1. 一定要加 --no-deliver

不加的话,内置投递和 Agent 的 message 工具会重复发送,或者内置投递静默失败。

2. 一定要加 --timeout

isolated session 没有人工干预,如果脚本挂了,没有 timeout 就永远不会结束。建议:

  • 简单脚本:120 秒
  • 中等复杂:300 秒
  • 长任务(数据拉取):1800 秒

3. --light-context 看情况

如果任务只需要 exec + message,加 --light-context 可以减少 token 消耗。但如果需要调用飞书表格等复杂工具,可能需要完整 context(实测 Sonnet 在 light-context 下也能用,但你的 mileage may vary)。

4. Prompt 要写清楚

Isolated session 的 Agent 没有你对话的上下文,也没有 SOUL.md 的人格设定。它只看到你给的 prompt。所以:

  • 脚本路径写绝对路径
  • 推送 target 写完整
  • 成功/失败的处理逻辑写清楚
  • 不要假设 Agent “知道"任何上下文

5. --session-key 在 isolated 模式下不生效为持久 session

每次触发都会新建 session。如果你的任务需要跨次记忆(比如"上次跑的结果”),目前 isolated 模式做不到。

6. 用一次性 cron 表达式验证

不要傻等到凌晨 5:30 才知道任务挂了。用 --cron "35 15 23 3 *" 这种一次性表达式集中验证。

7. 回滚只需要一条命令

openclaw cron edit <job-id> --session main

随时可以把某条任务切回 main session。

总结

OpenClaw 的 cron 系统其实很灵活,但默认的 main session + announce 模式在任务多了之后会变成瓶颈。

核心思路:

  1. 任务跑在 isolated session(不占 main session lane)
  2. 投递由 Agent 自己完成(用 message 工具,不依赖内置 announce)
  3. 分层迁移(从简到难,逐步验证)
  4. 保留回滚能力(一条命令切回 main)

如果你的 OpenClaw 上也挂了不少定时任务,而且开始感觉对话变卡了——试试这个方案。一个下午就能搞定。


实测环境:OpenClaw 2026.3.8-beta.1,Claude Sonnet,21 条 cron 全量迁移。