首页 / Hermes Agent 教程 / 自动化利器:Cron 定时任务

Hermes Agent 教程

自动化利器:Cron 定时任务

本教程共 25 篇 · 第 16 篇 · 更新于 2026-07-26 · 约 20 分钟阅读

Hermes AgentHermes Agent 教程定时任务Cron自动化cronjob脚本模式

16. 自动化利器:Cron 定时任务

本节目标:搞懂 Hermes Agent 的定时任务系统怎么用—从创建任务、调度格式、结果投递,到无 Agent 脚本模式和任务链式编排,学完你能让智能体在未来某个时间主动做事。

到上一章为止,Agent 的工作方式都是「用户说一句,它做一句」。用户不说话,它就闲着。但真实场景里你会说:

  • 「30 分钟后帮我看看部署有没有成功。」
  • 「每 2 小时查一次磁盘,超过 80% 就告警。」
  • 「每个工作日早上 9 点总结昨天的 git log。」

这些需求的共同点:你希望 Agent 在未来某个时间主动做事。这就是定时任务(Cron)要解决的问题。

核心理念:任务即消息

Hermes 的定时任务有个很妙的设计—到期后不是直接执行命令,而是构造一条 MessageEvent,伪装成用户消息送进 Gateway。从 Gateway 往下,所有逻辑完全复用:工具调用、错误恢复、权限检查、上下文压缩,一行都不用重写。

打个比方:定时任务就像一个闹钟,到点了自己替你发一条消息给 Agent。Agent 不知道这是闹钟发的还是你发的,它照常处理、照常回复,结果自动路由回你原来的聊天窗口。

统一的 cronjob 工具

当前最新稳定版 v2026.7.20 把所有定时任务操作收拢进一个 cronjob 工具,用 action 参数区分操作类型,而不是给你一堆 schedule/list/remove 分散工具:

cronjob(action="create", ...)
cronjob(action="list")
cronjob(action="update", job_id="...")
cronjob(action="pause", job_id="...")
cronjob(action="resume", job_id="...")
cronjob(action="run", job_id="...")
cronjob(action="remove", job_id="...")

这意味着你可以直接用自然语言让 Agent 管理任务,不需要碰命令行:

每天早上 9 点查一下 Hacker News 的 AI 新闻,发摘要到我的 Telegram。

Agent 会自动调用 cronjob(action="create", ...) 把任务建好。

三种创建方式

方式一:聊天里用 /cron 命令

/cron add 30m "提醒我检查构建结果"
/cron add "every 2h" "检查服务器状态"
/cron add "every 1h" "总结新的订阅源条目" --skill blogwatcher
/cron add "every 1h" "同时用两个技能并合并结果" --skill blogwatcher --skill maps

方式二:命令行 CLI

hermes cron create "every 2h" "检查服务器状态"
hermes cron create "every 1h" "总结订阅源" --skill blogwatcher
hermes cron create "every 1h" "合并两个技能的结果" \
  --skill blogwatcher \
  --skill maps \
  --name "Skill combo"

方式三:自然语言

直接跟 Agent 说人话,它会自己调 cronjob 工具:

Every morning at 9am, check Hacker News for AI news and send me a summary on Telegram.

调度格式

Hermes 支持四种调度格式,覆盖从「30 分钟后」到「每月第一天」的各种场景:

相对延时(一次性)

30m     -> 30 分钟后执行一次
2h      -> 2 小时后执行一次
1d      -> 1 天后执行一次

这种格式最常用—用户说「30 分钟后提醒我」,直接写 30m 就行,不用算 cron 表达式。

间隔(循环)

every 30m    -> 每 30 分钟
every 2h     -> 每 2 小时
every 1d     -> 每天

Cron 表达式(五字段)

0 9 * * *       -> 每天 9:00
0 9 * * 1-5     -> 工作日 9:00
0 */6 * * *     -> 每 6 小时
30 8 1 * *      -> 每月 1 号 8:30
0 0 * * 0       -> 每周日 0:00
Note

五字段顺序是:分 时 日 月 星期。星期 0 和 7 都是周日。

ISO 时间戳(一次性)

2026-03-15T09:00:00    -> 2026 年 3 月 15 日 9:00 执行一次

重复次数控制

默认情况下,延时和 ISO 时间戳跑一次,间隔和 cron 表达式永久循环。可以用 repeat 覆盖:

cronjob(
    action="create",
    prompt="...",
    schedule="every 2h",
    repeat=5,           # 只跑 5 次就停
)

结果投递

定时任务跑完后,结果往哪发?deliver 参数控制这件事。

投递目标速查

目标示例说明
origin--deliver origin发回创建任务时的聊天(消息平台默认)
local--deliver local只存本地文件(CLI 默认)
telegram--deliver telegramTelegram home 频道
telegram:123456--deliver telegram:123456指定 Telegram 聊天
telegram:-100123:17585--deliver telegram:-100123:17585指定 Telegram 话题(chat_id:thread_id)
discord--deliver discordDiscord home 频道
discord:#engineering--deliver discord:#engineering按频道名指定
feishu--deliver feishu飞书 home 频道
all--deliver all扇出到所有已连接的 home 频道
telegram,discord--deliver telegram,discord指定多个平台
origin,all--deliver origin,allorigin 加上所有其他频道

all 是在触发时动态解析的—你创建任务时还没接 Telegram,后来接上了,下次触发时就会自动发到 Telegram。

[SILENT] 静默标记

监控类任务最怕刷屏。在 prompt 里告诉 Agent:没事就只回 [SILENT]

检查 nginx 是否在运行。如果一切正常,只回复 [SILENT]。否则报告问题。

Hermes 看到 [SILENT] 就不投递消息,但输出仍然存到本地 ~/.hermes/cron/output/ 留档。只有成功运行才能被静默,失败的任务一定会通知你。

可续接任务

默认情况下,定时任务发完消息就结束了—你回复它,它不知道自己之前说过什么。开启 mirror_delivery 后,投递的消息会写入会话历史,你直接回复就能接着聊:

# ~/.hermes/config.yaml
cron:
  mirror_delivery: false    # 设 true 让 cron 投递可续接

行为是「线程优先」的:支持线程的平台(Telegram 话题、Discord/Slack 线程)每次投递开一个独立线程,回复在这个线程里带完整上下文;只支持私信的平台(WhatsApp、Signal)则把摘要写入 origin 私信会话。

调度器怎么工作

后台调度循环

Cron 的执行由 Gateway 守护进程负责。Gateway 每 60 秒 tick 一次,检查有没有到期的任务:

  1. ~/.hermes/cron/jobs.json 加载任务
  2. 检查 next_run_at 是否到了当前时间
  3. 为每个到期任务启动一个全新的 Agent 会话
  4. 可选地注入附加技能
  5. 运行 prompt 到完成
  6. 投递最终响应
  7. 更新运行元数据和下次触发时间

文件锁 ~/.hermes/cron/.tick.lock 防止多个 tick 重叠导致同一个任务被重复执行。

执行历史

每次 cron 尝试都记录在 ~/.hermes/cron/executions.db 里,状态流转是 claimed -> running -> 终态(completed / failed / unknown)。重启后,Hermes 会检查被遗弃的尝试—只有确认原进程确实没了,才标记为 unknownunknown 只是审计记录,不会自动重跑。

查看历史:

hermes cron runs --limit 20         # 最近 20 次执行
hermes cron runs <job-id> --limit 20  # 某个任务的执行历史

安装为系统服务

让 Gateway 长期在后台跑,cron 才能按时触发:

hermes gateway install              # 安装为用户级服务
sudo hermes gateway install --system  # Linux: 开机自启的系统级服务
hermes gateway                      # 或者前台运行

生命周期管理

任务不是建完就只能删了重来,Hermes 提供完整的生命周期操作:

/cron list                    # 列出所有任务
/cron pause <job_id>          # 暂停(保留任务但不调度)
/cron resume <job_id>         # 恢复(重新计算下次触发时间)
/cron run <job_id>            # 立即触发一次(用于测试)
/cron remove <job_id>         # 永久删除
/cron edit <job_id> --schedule "every 4h"   # 改调度
/cron edit <job_id> --prompt "新任务描述"    # 改 prompt
/cron edit <job_id> --skill blogwatcher --skill maps  # 换技能
/cron edit <job_id> --remove-skill blogwatcher        # 移除某个技能
/cron edit <job_id> --clear-skills                    # 清空所有技能
Tip

<job_id> 也可以填任务名称(不区分大小写)。如果你记不住十六进制 ID,用名字更方便。但名字不唯一—如果多个任务同名,命令会拒绝执行并列出候选 ID 让你选。

CLI 也有对应的命令:

hermes cron list
hermes cron pause <job_id_or_name>
hermes cron resume <job_id_or_name>
hermes cron run <job_id_or_name>
hermes cron status              # 查看调度器状态
hermes cron tick                # 手动触发一次 tick

生命周期防护机制

Provider 快照与失败关闭

这是个很重要的安全设计。创建任务时,如果你没显式指定 providermodel,Hermes 会快照当前全局默认的 provider 和 model 到任务上。

为什么这么做?防止你后来把全局默认切到一个付费模型,然后无人值守的 cron 任务默默花你的钱。

如果全局默认后来变了,任务会失败关闭(fail closed):跳过这次运行,不调用推理 API,发一条告警让你手动指定 provider/model:

cronjob action=update job_id=... provider=... model=...
Note

如果你想让任务跟随全局默认,改完全局默认后手动更新任务即可。用 hermes setup --portal 配置的 OAuth 认证对无人值守任务最友好,因为 token 会自动刷新。

禁止递归创建

Warning

Cron 运行的会话里不能创建新的 cron 任务。Hermes 在 cron 执行时禁用 cron 管理工具,防止失控的调度循环。

技能绑定

一个 cron 任务可以绑定零个、一个或多个技能。技能在 prompt 执行前按顺序加载:

单技能

cronjob(
    action="create",
    skill="blogwatcher",
    prompt="检查配置的订阅源,总结新内容。",
    schedule="0 9 * * *",
    name="Morning feeds",
)

多技能

cronjob(
    action="create",
    skills=["blogwatcher", "maps"],
    prompt="找新的本地活动和附近有意思的地方,合并成一份简报。",
    schedule="every 6h",
    name="Local brief",
)

技能按列表顺序加载,prompt 是叠加在技能之上的任务指令。好处是你不用把完整的技能文本塞进 cron prompt 里。

无 Agent 模式:纯脚本任务

不是所有定时任务都需要 LLM 推理。内存告警、磁盘检查、心跳上报—这些脚本本身就能产出最终消息,让 Agent 参与纯属浪费 token。

no_agent=True 创建纯脚本任务:

hermes cron create "every 5m" \
  --no-agent \
  --script memory-watchdog.sh \
  --deliver telegram \
  --name "memory-watchdog"

语义很清晰:

  • 脚本 stdout(去掉首尾空白)直接作为消息投递,一字不改。
  • stdout 为空 -> 静默 tick,不投递。这就是看门狗模式:「没事别说话」。
  • 非零退出或超时 -> 投递错误告警,防止坏掉的看门狗沉默失败。
  • 不消耗任何 token,不碰推理层。
Tip

你甚至可以直接跟 Agent 说「每 5 分钟检查一次内存,超过 85% 就在 Telegram 通知我」,Agent 会自动判断这是看门狗场景,写脚本到 ~/.hermes/scripts/,然后用 no_agent=True 创建任务。

脚本必须放在 ~/.hermes/scripts/ 目录下。.sh / .bash 文件用 /bin/bash 执行,其他文件用当前 Python 解释器执行。

wakeAgent 门控:省 token 的利器

如果你的 cron 任务绑定了预检脚本(script= 参数),脚本可以在运行时决定要不要叫醒 Agent。在 stdout 最后一行输出:

{"wakeAgent": false}

Hermes 就跳过这次 Agent 调用。这对高频轮询(每 1-5 分钟)特别有用—状态没变就不花 token:

#!/usr/bin/env python
# ~/.hermes/scripts/new-rows.py
import json, sqlite3
conn = sqlite3.connect("/home/me/data/app.db")
n = conn.execute(
    "SELECT COUNT(*) FROM messages WHERE ts > strftime('%s','now','-2 hours')"
).fetchone()[0]
if n < 1:
    print(json.dumps({"wakeAgent": False}))   # 没新数据,跳过
else:
    print(json.dumps({"wakeAgent": True, "context": {"new_rows": n}}))

省钱的逻辑是:脚本做机械检查(查数据库、比文件 mtime、看外部标志),只在状态真的变了时才让 Agent 推理。

任务链式编排:context_from

Cron 任务默认在隔离会话里跑,互相不知道对方的输出。但有时候任务 B 恰好需要任务 A 的产出。context_from 参数把这个连接自动接上:

# 任务 1:收集原始数据
cronjob(
    action="create",
    prompt="抓取 Hacker News 上最新的 10 条 AI/ML 新闻,保存到 ~/.hermes/data/briefs/raw.md。",
    schedule="0 7 * * *",
    name="AI News Collector",
)

# 任务 2:筛选排名 -- 自动接收任务 1 的输出作为上下文
cronjob(
    action="create",
    prompt="读取 raw.md,给每条新闻打分,输出前 5 名到 ranked.md。",
    schedule="30 7 * * *",
    context_from="<任务1的ID>",
    name="AI News Triage",
)

# 任务 3:生成推文 -- 自动接收任务 2 的输出作为上下文
cronjob(
    action="create",
    prompt="读取 ranked.md,为每条新闻写 3 条推文草稿,发到 Telegram。",
    schedule="0 8 * * *",
    context_from="<任务2的ID>",
    name="AI News Brief",
)

任务 2 触发时,Hermes 从 ~/.hermes/cron/output/{job1_id}/ 读取任务 1 最近一次成功输出,自动拼在任务 2 的 prompt 前面。链可以任意长,也支持多个上游:

cronjob(
    action="create",
    name="daily-digest",
    schedule="every day 7am",
    context_from=["ai-news-fetch", "github-prs-fetch"],  # 两个上游的输出拼接
    prompt="用上面的输出写每日简报。",
)
Note

链式读取的是最近一次完成的输出,不会等待同一个 tick 里正在运行的上游任务。所以调度时间要错开—任务 1 在 7:00,任务 2 在 7:30,确保任务 1 跑完了任务 2 才触发。

在项目目录里运行

默认情况下,cron 任务不加载任何项目上下文(没有 AGENTS.mdCLAUDE.md),终端和文件工具在 Gateway 启动目录下工作。用 --workdir 指定项目目录:

hermes cron create "every 1d at 09:00" \
  "审查未合并的 PR,总结 CI 状态,发到 #eng" \
  --workdir /home/me/projects/acme

设置 workdir 后:

  • 该目录的 AGENTS.mdCLAUDE.md.cursorrules 会注入系统 prompt。
  • terminalread_filewrite_file 等工具以该目录为工作目录。
  • 路径必须是已存在的绝对路径,相对路径和不存在的目录会被拒绝。
Warning

workdir 的任务在调度 tick 里串行执行,不走并行池。因为 cron worker 通过进程全局的终端状态应用工作目录,两个 workdir 任务同时跑会互相污染 cwd。不带 workdir 的任务仍然并行。

工具集控制

Cron 任务在全新会话里跑,默认获得你在 hermes tools 里为 cron 平台配置的工具集。可以用 enabled_toolsets 做更细的按任务控制:

cronjob(
    action="create",
    name="weekly-news-summary",
    schedule="every sunday 9am",
    enabled_toolsets=["web", "file"],      # 只要 web 和 file,不要 terminal/browser
    prompt="总结本周 AI 新闻:..."
)

这关乎成本控制—给每个「抓新闻」的小任务都带上 browserdelegation 工具集,会让工具 schema 在每次 LLM 调用时膨胀 prompt。

Prompt 必须自包含

这是 cron 任务最重要的规则,我单独拎出来强调:

Warning

Cron 任务在全新的 Agent 会话里运行,没有你当前聊天的任何记忆。Prompt 必须包含 Agent 需要的所有信息。

反面教材:「检查一下那个服务器的问题」—Agent 完全不知道「那个服务器」是哪个。

正确写法:「SSH 到 192.168.1.100,用户名 deploy,运行 systemctl status nginx 检查 nginx 是否在运行,并验证 https://example.com 返回 HTTP 200。」

把 URL、仓库名、格式偏好、投递指令都写进 prompt 里,别指望 Agent 能猜。

配置项汇总

~/.hermes/config.yaml 里可以调整这些 cron 全局行为:

cron:
  wrap_response: false              # false = 投递原始输出,不包头尾
  script_timeout_seconds: 1800      # 预检脚本超时(默认 3600 秒)
  mirror_delivery: false            # true = 投递可续接

wrap_response 默认是 true,投递的消息会带一个头尾:

Cronjob Response: Morning feeds
-------------

<Agent 输出>

Note: The agent cannot see this message, and therefore cannot respond to it.

如果你觉得这个包装多余,设成 false 就只投递原始输出。

脚本超时也可以用环境变量覆盖,优先级是:环境变量 > config.yaml > 3600 秒默认值:

HERMES_CRON_SCRIPT_TIMEOUT=1800

安全

定时任务的 prompt 在创建和更新时会扫描注入和凭据泄露模式。包含隐形 Unicode 技巧、SSH 后门尝试、明显的密钥外泄 payload 的 prompt 会被拦截。

任务存储在 ~/.hermes/cron/jobs.json,输出在 ~/.hermes/cron/output/{job_id}/{timestamp}.md

Tip

管理任务请用 cronjob 工具、hermes cron 命令或 /cron 聊天命令,别直接改 jobs.json。直接编辑可能因为文件写入安全策略被静默拦截,而且没有文件变更验证器的校验。