OpenClaw 内置 Cron 原理
OpenClaw 定时任务(内置 Cron) 原理
揭开OpenClaw“定时任务”的面纱,其实它的核心机制并不复杂。简单来说,它就像在进程内部装了一个精巧的Cron表达式调度器。这个调度器会不间断地计算每个任务的“下一次触发时间”,一旦时间到了,它就会麻利地把任务投递到执行器(可能是线程池、协程或者队列)里去跑起来。整个过程自动化程度很高,可靠性也不错。
定时任务配置
OpenClaw自带了一套Cron调度系统,所有相关的配置数据都存储在.openclaw/cron这个目录下。
举个例子,你可能会看到类似/root/openclaw-docker/data/.openclaw/cron/jobs.json这样的路径。这通常是由“OpenClaw的数据目录(data dir)+ 固定的子目录.openclaw/cron/ + 固定的文件名jobs.json”这几部分组合而成的。
换句话说,真正需要你操心、可能变动的地方,通常只有前半段的数据目录路径,比如/root/openclaw-docker/data。至于后面的相对路径和文件名,多半是程序内部硬编码写死的。
判断这个 jobs.json 路径由哪里决定
想要精准定位jobs.json文件到底在哪,得从启动方式入手,顺藤摸瓜。
1) 看容器是怎么起的(compose 还是 docker run)
第一步,先看看所有容器的情况,重点观察容器名、镜像、启动命令和挂载信息。可以用下面这个命令,信息展示得比较清楚:
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Command}}\t{{.Mounts}}'
2) 找到 openclaw 容器名后,导出完整启动/挂载信息
找到名字里带“openclaw”的容器后,比如可能是openclaw-docker-openclaw-gateway-1,接着用docker inspect命令深挖它的内情,关键看Binds或Mounts字段。把下面的容器名换成你实际查到的:
docker inspect openclaw-docker-openclaw-gateway-1 --format 'Name={{.Name}}Cmd={{json .Config.Cmd}}Entrypoint={{json .Config.Entrypoint}}Env={{json .Config.Env}}Binds={{json .HostConfig.Binds}}Mounts={{json .Mounts}}'
命令执行后,你可能会看到类似这样的输出:
Binds=["/root/openclaw-docker/data/.openclaw:/home/node/.openclaw:rw", ...]Mounts=[{"Type":"bind","Source":"/root/openclaw-docker/data/.openclaw","Destination":"/home/node/.openclaw", ...}]
这段输出信息量不小,我们来解读一下:它表明宿主机的目录/root/openclaw-docker/data/.openclaw,通过bind mount的方式,映射到了容器内部的/home/node/.openclaw路径。
而OpenClaw的Cron数据文件,就存放在.openclaw/cron/jobs.json这个相对路径下。这样一来,路径就非常明确了:
- 在容器内部,文件位于:
/home/node/.openclaw/cron/jobs.json - 在宿主机上,对应的文件就在:
/root/openclaw-docker/data/.openclaw/cron/jobs.json
另外,环境变量(Env)的配置也值得留意,例如:
Env=["HTTP_PROXY=http://172.17.0.1:1080","TERM=xterm-256color","NODE_OPTIONS=--use-env-proxy","NO_PROXY=localhost,127.0.0.1,172.17.0.0/16,10.0.0.0/8","HOME=/home/node"
看到没?环境变量里设置了HOME=/home/node。很多程序都有个习惯,默认会把用户数据放在$HOME/.openclaw目录下,这就进一步印证了容器内确实会使用/home/node/.openclaw这个路径。
jobs.json 配置说明
理解了文件在哪,接下来看看它里面长什么样。下面是一个典型的jobs.json配置示例:
{
"version": 1,
"jobs": [{
"id": "UUID-EXAMPLE-0001",
"agentId": "main",
"name": "定时执行脚本任务(示例)",
"enabled": true,
"createdAtMs": 1700000000000,
"updatedAtMs": 1700003600000,
"schedule": {
"kind": "every",
"everyMs": 43200000},
"sessionTarget": "isolated",
"wakeMode": "next-heartbeat",
"payload": {
"kind": "agentTurn",
"message": "执行 bash /home/node//task.sh \"\" 2"},
"state": {
"nextRunAtMs": 1700043200000,
"lastRunAtMs": 1700003600000,
"lastStatus": "ok",
"lastDurationMs": 16204}}]
}
顶层
version: 文件格式的版本号,主要用于后续的兼容性检查和升级迁移。jobs: 一个数组,里面包含了所有定义好的定时任务。
job 基本信息
这一部分描述了任务的身份和状态:
id: 任务的唯一标识符,通常是一个UUID,确保每个任务都是独一无二的。agentId: 指定由哪个Agent来执行这个任务,比如示例中的main。name: 任务在界面上显示的名称,起个清晰的名字方便管理。enabled: 一个布尔值开关。true表示启用,调度器会正常触发它;false则表示暂时关闭,不会被调度。createdAtMs/updatedAtMs: 分别表示任务创建和最后更新的时间戳,单位是毫秒。
调度计划 schedule
这是定时任务的“大脑”,决定了它何时运行。示例中展示的是最简单的一种:
kind: "every": 表示按固定时间间隔重复触发,而不是用Cron表达式。everyMs: 间隔的毫秒数。比如示例的43200000毫秒,换算一下就是12小时。
当然,OpenClaw的调度器不止这一种玩法,它支持三种主要的调度类型:
| 类型 | 说明 | 示例 |
|---|---|---|
| at | 一次性定时 | “30分钟后提醒我” |
| every | 固定间隔重复 | “每5分钟检查一次” |
| cron | 标准Cron表达式 | 0 9 * * 1-5(工作日每天9点) |
如果想使用更灵活、更强大的Cron表达式,配置可以写成这样:
"schedule": {
"kind": "cron",
"expr": "55 9 3 * *",
"tz": "Asia/Shanghai"
}
字段说明
kind: "cron": 明确使用cron表达式来触发。expr: "55 9 3 * *": 标准的五段式Cron表达式(分 时 日 月 周)。tz: "Asia/Shanghai": 指定解释Cron表达式所使用的时区。这个设置非常重要,能有效避免因容器默认使用UTC时间而造成的执行时间偏差。
会话与唤醒
这部分配置决定了任务执行时的“环境”和“触发时机”:
sessionTarget: "isolated": 表示每次任务运行时,会启动一个独立的“隔离会话”。这样做的好处是,任务执行过程中的所有上下文和状态都被限制在这个独立环境里,不会污染到主对话或其他的任务上下文,非常适合执行一些复杂的、有副作用的操作。wakeMode: "next-heartbeat": 指定唤醒执行的方式。意思是,当触发时间到达后,任务不会立即被拉起,而是要等到“下一次调度器心跳/调度周期tick”时才会执行。这种方式能合并调度检查,减少系统开销,但可能会引入轻微的、几乎可以忽略的延迟。
关于执行模式,通常有两种选择:
- Main Session: 把任务当作一条“用户消息”直接注入到主对话中。效果就像你手动发了一条消息去触发Agent一样。适合简单、快捷的交互。
- Isolated Session: 如上面配置所示,启动一个完全独立的会话来执行任务。这种模式特别适合那些需要联网搜索、调用外部工具、生成复杂报告等耗时较长的复杂任务。独立任务执行完毕后,可以选择将结果汇总汇报回主会话。
执行内容 payload
这里定义了任务被触发后,具体要做什么:
kind: "agentTurn": 表示将一条“消息回合”投递给指定的Agent来执行。这是最常见的一种任务类型。message: 实际发送给Agent的指令文本。在示例中,就是一条执行Bash脚本并传递参数的命令。
需要注意的是,payload.kind也可以是其他类型。如果设置为systemEvent,那么任务触发时只是在OpenClaw界面里显示一条系统消息,而不会触发Agent执行具体操作。
运行状态 state(运行后写回)
这部分字段不是由你配置的,而是任务在每次运行后,由系统自动更新写回的,用于记录任务的历史执行情况:
nextRunAtMs: 根据调度计划计算出的下一次触发时间(毫秒时间戳)。lastRunAtMs: 上一次任务实际被触发的时间。lastStatus: 上一次执行的状态,比如示例中的ok表示成功。lastDurationMs: 上一次任务执行所耗费的时间(毫秒)。
配置方式
那么,怎么创建这些定时任务呢?其实最简单的方式,就是直接和OpenClaw对话。你完全可以用自然语言说,比如“请帮我创建一个每周一早上9点检查系统状态的定时任务”。
OpenClaw在理解你的意图后,会自动在后台生成相应的Cron任务配置,并将数据持久化保存到我们前面提到的/home/node/.openclaw/cron目录中。整个过程非常直观,几乎不需要手动编辑JSON文件。