定时任务(Cron)#
使用自然语言或 cron 表达式调度自动运行的任务。Hermes 通过单一 cronjob 工具暴露 cron 管理能力,采用动作式操作,而非分散的 schedule/list/remove 工具。Cron 当前能做什么#
在全新的 agent 会话中运行,使用正常的静态工具列表
以无 agent 模式运行——按计划执行脚本,其 stdout 原样投递,零 LLM 参与(参见下方无 agent 模式章节) 所有这些功能均可通过 cronjob 工具由 Hermes 自身使用,因此你可以用自然语言创建、暂停、编辑和删除任务——无需 CLI。Cron 运行的会话不能递归创建更多 cron 任务。Hermes 在 cron 执行内部禁用了 cron 管理工具,以防止失控的调度循环。
创建定时任务#
在聊天中使用 /cron#
从独立 CLI#
通过自然对话#
Every morning at 9am, check Hacker News for AI news and send me a summary on Telegram.
Hermes 会在内部使用统一的 cronjob 工具。附带 skill 的 cron 任务#
Cron 任务可以在运行 prompt(提示词)之前加载一个或多个 skill。单个 skill#
多个 skill#
Skill 按顺序加载。Prompt 作为任务指令叠加在这些 skill 之上。当你希望定时 agent 继承可复用的工作流,而不必将完整的 skill 文本塞入 cron prompt 本身时,这非常有用。在指定项目目录中运行任务#
Cron 任务默认与任何代码仓库脱离运行——不加载 AGENTS.md、CLAUDE.md 或 .cursorrules,终端/文件/代码执行工具从 gateway 启动时的工作目录运行。传入 --workdir(CLI)或 workdir=(工具调用)可更改此行为:该目录中的 AGENTS.md、CLAUDE.md 和 .cursorrules 会被注入系统 prompt(发现顺序与交互式 CLI 相同)
terminal、read_file、write_file、patch、search_files 和 execute_code 均以该目录为工作目录(通过 TERMINAL_CWD)
路径必须是已存在的绝对目录——相对路径和不存在的目录在创建/更新时会被拒绝
编辑时传入 --workdir ""(或工具中的 workdir="")可清除该设置并恢复原有行为
设置了 workdir 的任务在调度器 tick 时串行运行,而非在并行池中运行。这是有意为之——TERMINAL_CWD 是进程全局变量,两个 workdir 任务同时运行会互相破坏各自的 cwd。无 workdir 的任务仍像以前一样并行运行。
在指定 profile 中运行 cron 任务#
默认情况下,cron 任务继承创建它的 gateway/CLI 所属的 Hermes profile。传入 --profile <name>(CLI)或 profile=(cronjob 工具)可将任务重定向到不同的 profile——调度器会解析该 profile 的 HERMES_HOME,在运行期间临时切换到该 profile,加载其 .env 和 config.yaml,并在其中执行任务:使用 --profile default 可显式固定到根 Hermes profile。指定的 profile 必须已存在;调度器不会动态创建 profile。在 cron edit 时清除 profile 固定,传入空字符串(--profile "" 或 profile="")——任务将恢复在调度器当前所在的 profile 中运行。如果固定的 profile 后来被删除,调度器会记录警告并回退到在当前 profile 中运行该任务,而不是崩溃——因此过期的 profile 引用不会卡住任务。设置了 profile 的任务也串行运行,原因与 workdir 固定任务相同:切换 HERMES_HOME 是进程全局变更,两个 profile 固定任务并行运行会产生竞争。未固定的任务仍在正常并行池中运行。
编辑任务#
下方(以及生命周期操作中)的 <job_id> 占位符也接受任务名称(不区分大小写)——当你记得 morning-digest 但不记得十六进制 ID 时很方便。精确的任务 ID 优先于名称匹配;如果引用不是 ID 且名称匹配到多个任务,命令会拒绝执行并打印候选 ID 供你消歧义。 独立 CLI#
重复使用 --skill 会替换任务已附加的 skill 列表
--remove-skill 删除指定的已附加 skill
--clear-skills 删除所有已附加的 skill
生命周期操作#
Cron 任务现在拥有比创建/删除更完整的生命周期。独立 CLI#
工作原理#
Cron 执行由 gateway 守护进程处理。 Gateway 每 60 秒 tick 一次调度器,在隔离的 agent 会话中运行到期的任务。Gateway 调度器行为#
1.
从 ~/.hermes/cron/jobs.json 加载任务
4.
可选地将一个或多个已附加的 skill 注入该新会话
~/.hermes/cron/.tick.lock 处的文件锁防止重叠的调度器 tick 重复运行同一批任务。投递选项#
| 选项 | 说明 | 示例 |
|---|
"origin" | 回传到任务创建的来源 | 消息平台上的默认值 |
"local" | 仅保存到本地文件(~/.hermes/cron/output/) | CLI 上的默认值 |
"telegram" | Telegram 主频道 | 使用 TELEGRAM_HOME_CHANNEL |
"telegram:123456" | 按 ID 指定的 Telegram 会话 | 直接投递 |
"telegram:-100123:17585" | 指定 Telegram 话题 | chat_id:thread_id 格式 |
"discord" | Discord 主频道 | 使用 DISCORD_HOME_CHANNEL |
"discord:#engineering" | 按频道名指定的 Discord 频道 | 按频道名 |
"slack" | Slack 主频道 | |
"whatsapp" | WhatsApp 主账号 | |
"signal" | Signal | |
"matrix" | Matrix 主房间 | |
"mattermost" | Mattermost 主频道 | |
"email" | 邮件 | |
"sms" | 通过 Twilio 发送 SMS | |
"homeassistant" | Home Assistant | |
"dingtalk" | 钉钉 | |
"feishu" | 飞书/Lark | |
"wecom" | 企业微信 | |
"weixin" | 微信(WeChat) | |
"bluebubbles" | BlueBubbles(iMessage) | |
"qqbot" | QQ Bot(腾讯 QQ) | |
"all" | 扇出到所有已连接的主频道 | 触发时解析 |
"telegram,discord" | 扇出到指定的一组频道 | 逗号分隔列表 |
"origin,all" | 投递到来源加上所有其他已连接频道 | 可组合任意 token |
Agent 的最终响应会自动投递,无需在 cron prompt 中调用 send_message。路由意图(all)#
all 让你将一个 cron 任务发送到所有已配置的消息频道,无需逐一列举名称。它在触发时解析,因此在你配置 TELEGRAM_HOME_CHANNEL 之前创建的任务,会在下次 tick 时自动纳入 Telegram。语义:all 展开为所有已配置主频道的平台。零个也没问题;任务只是没有投递目标,并在上游记录为投递失败。all 可与显式目标组合。origin,all 投递到来源会话加上所有其他已连接的主频道,按 (platform, chat_id, thread_id) 去重。Telegram cron 话题(TELEGRAM_CRON_THREAD_ID)#
启用 Telegram 话题模式后,根 DM 被保留为系统大厅——发送到那里的回复会被拒绝并附带大厅提示,reply_to_message_id 会被丢弃,因此你无法回复落在主聊天中的 cron 消息。1.
在 Telegram 中打开机器人 DM,创建一个名为 Cron 的话题。长按话题标题 → 复制链接;末尾的整数即为该话题的 message_thread_id。
2.
在 .env 中设置 TELEGRAM_CRON_THREAD_ID=<该 id>。
这仅适用于 cron 投递。TELEGRAM_HOME_CHANNEL_THREAD_ID(用于其他地方,如重启通知)不受影响。显式的 deliver="telegram:chat_id:thread_id" 目标仍优先于环境变量。对 cron 消息的回复现在会进入已有的话题会话,你可以直接在其中操作。响应包装#
默认情况下,投递的 cron 输出会带有页眉和页脚,以便接收方知道这来自定时任务:Cronjob Response: Morning feeds
-------------
<agent output here>
Note: The agent cannot see this message, and therefore cannot respond to it.
若要投递不带包装的原始 agent 输出,将 cron.wrap_response 设为 false:静默抑制#
如果 agent 的最终响应以 [SILENT] 开头,投递将被完全抑制。输出仍会保存到本地以供审计(位于 ~/.hermes/cron/output/),但不会向投递目标发送任何消息。Check if nginx is running. If everything is healthy, respond with only [SILENT].
Otherwise, report the issue.
失败的任务无论 [SILENT] 标记如何都会投递——只有成功的运行才能被静默。脚本超时#
预运行脚本(通过 script 参数附加)的默认超时为 120 秒。如果你的脚本需要更长时间——例如,包含随机延迟以避免类机器人的时序模式——可以增加此值:或设置 HERMES_CRON_SCRIPT_TIMEOUT 环境变量。解析顺序为:环境变量 → config.yaml → 默认 120 秒。无 agent 模式(纯脚本任务)#
对于不需要 LLM 推理的周期性任务——经典的看门狗、磁盘/内存告警、心跳、CI ping——在创建时传入 no_agent=True。调度器按计划运行你的脚本,并直接投递其 stdout,完全跳过 agent:脚本 stdout(去除首尾空白)→ 原样作为消息投递。
stdout 为空 → 静默 tick,不投递。这是看门狗模式:"只在出现问题时才说话"。
非零退出或超时 → 投递错误告警,确保损坏的看门狗不会静默失败。
最后一行输出 {"wakeAgent": false} → 静默 tick(与 LLM 任务使用相同的门控)。
无 token、无模型、无 provider 回退——任务永远不会触及推理层。
.sh/.bash 文件在 /bin/bash 下运行 ;其他文件在当前 Python 解释器(sys.executable)下运行。脚本必须位于 ~/.hermes/scripts/(与预运行脚本门控相同的沙箱规则)。Agent 为你设置这些#
cronjob 工具的 schema 直接向 Hermes 暴露了 no_agent,因此你可以在聊天中描述一个看门狗,让 agent 来配置它:Ping me on Telegram if RAM is over 85%, every 5 minutes.
Hermes 会通过 write_file 将检查脚本写入 ~/.hermes/scripts/,然后调用:当消息内容完全由脚本决定时(看门狗、阈值告警、心跳),它会自动选择 no_agent=True。同一工具也让 agent 可以暂停、恢复、编辑和删除任务——整个生命周期都通过聊天驱动,无需任何人接触 CLI。通过 context_from 串联任务#
Cron 任务在隔离的会话中运行,不保留之前运行的记忆。但有时一个任务的输出恰好是下一个任务所需的输入。context_from 参数自动建立这种连接——任务 B 的 prompt 在运行时会将任务 A 的最新输出作为上下文前置。任务 2 触发时,Hermes 从 ~/.hermes/cron/output/{job1_id}/*.md 读取任务 1 的最新输出
任务 2 无需硬编码"读取此文件"——它以上下文形式接收内容
链可以是任意长度:任务 1 → 任务 2 → 任务 3 → …
| 格式 | 示例 |
|---|
| 单个任务 ID(字符串) | context_from="a1b2c3d4" |
| 多个任务 ID(列表) | context_from=["job_a", "job_b"] |
多阶段流水线(收集 → 过滤 → 格式化 → 投递)
Provider 恢复#
Cron 任务继承你配置的回退 provider 和凭证池轮换。如果主 API key 被限速或 provider 返回错误,cron agent 可以:回退到备用 provider,前提是你在 config.yaml 中配置了 fallback_providers(或旧版 fallback_model)
轮换到下一个凭证,即同一 provider 的凭证池中的下一个 这意味着高频运行或在高峰时段运行的 cron 任务更具弹性——单个被限速的 key 不会导致整次运行失败。调度格式#
Agent 的最终响应会自动投递——你无需在 cron prompt 中为同一目标包含 send_message。如果 cron 运行调用了 send_message 且目标与调度器已投递的目标完全相同,Hermes 会跳过该重复发送,并告知模型将面向用户的内容放在最终响应中。仅对额外或不同的目标使用 send_message。相对延迟(一次性)#
30m → 30 分钟后运行一次
2h → 2 小时后运行一次
1d → 1 天后运行一次
间隔(周期性)#
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 → 每周日午夜
ISO 时间戳#
2026-03-15T09:00:00 → 2026 年 3 月 15 日上午 9:00 一次性运行
重复行为#
| 调度类型 | 默认重复次数 | 行为 |
|---|
一次性(30m、时间戳) | 1 | 运行一次 |
间隔(every 2h) | 永久 | 运行直到删除 |
| Cron 表达式 | 永久 | 运行直到删除 |
以编程方式管理任务#
对于 update,传入 skills=[] 可删除所有已附加的 skill。Cron 任务可用的工具集#
Cron 在全新的 agent 会话中运行每个任务,不附加任何聊天平台。默认情况下,cron agent 获得你在 hermes tools 中为 cron 平台配置的工具集——不是 CLI 默认值,也不是 所有工具。通过 cronjob.create(或通过 cronjob.update 对现有任务)上的 enabled_toolsets 字段可进行更精细的单任务控制:cronjob(action="create", name="weekly-news-summary",
schedule="every sunday 9am",
enabled_toolsets=["web", "file"], # 仅 web + file,无 terminal/browser 等
prompt="Summarize this week's AI news: ...")
当任务上设置了 enabled_toolsets 时,它优先生效;否则 hermes tools 的 cron 平台配置生效;否则 Hermes 回退到内置默认值。这对成本控制很重要:在每个小型"获取新闻"任务中携带 moa、browser、delegation 会在每次 LLM 调用时膨胀工具 schema prompt。完全跳过 agent:wakeAgent#
如果你的 cron 任务附加了预检脚本(通过 script=),脚本可以在运行时决定 Hermes 是否应该调用 agent。在 stdout 最后一行输出如下格式:……cron 将完全跳过本次 tick 的 agent 运行。适用于高频轮询(每 1–5 分钟),只在状态实际发生变化时才需要唤醒 LLM——否则你会为一遍遍的零内容 agent 轮次付费。省略 wakeAgent 时,默认为 true(照常唤醒 agent)。实用方案:低成本预运行门控#
wakeAgent 门控提供了一种零成本的方式,用于决定定时任务是否应该消耗任何 LLM token。三种模式覆盖了大多数使用场景。文件变更门控——仅在被监视文件自上次成功 tick 以来有新内容时运行。调度器记录每个任务的 last_run_at;将其与文件的 mtime 比较。cronjob(action="create", name="process-feed",
schedule="every 30m",
script="feed-changed.sh",
prompt="A new ~/data/feed.json has landed. Summarize what changed.")
外部标志门控——仅在其他进程发出就绪信号时运行(例如,部署 hook 落下一个文件,CI 任务在状态存储中设置一个值)。cronjob(action="create", name="nightly-analysis",
schedule="0 9 * * *",
script="flag-ready.sh",
prompt="Run the nightly analysis over today's batch.")
SQL 计数门控——仅在你自己的数据库中有新行需要处理时运行。脚本还可以通过 context 将计数传递给 agent,让 agent 无需重新查询就知道数据量。cronjob(action="create", name="summarize-new-msgs",
schedule="every 2h",
script="new-rows.py",
prompt="Summarize the new messages from the last 2 hours.")
同样的模式适用于任何可以从脚本查询的数据源——Postgres、HTTP API、你自己的状态存储——无需将 SQL 求值器内置到 cron 子系统中。Hermes 自身的 ~/.hermes/state.db 是内部 schema,会在版本间变更。不要从预运行门控中查询它——指向你自己的数据库或 feed。
致谢:此方案集由 @iankar8 在 #2654 中的探索所启发,该 PR 提议将 sql/file/command 触发器作为并行机制添加。script + wakeAgent 门控已以零成本覆盖了所有三种情况,因此该工作以文档形式落地。串联任务:context_from#
Cron 任务可以通过在 context_from 中列出其他任务的名称(或 ID)来消费这些任务最近一次成功运行的输出:cronjob(action="create", name="daily-digest",
schedule="every day 7am",
context_from=["ai-news-fetch", "github-prs-fetch"],
prompt="Write the daily digest using the outputs above.")
被引用任务最近一次完成的输出会作为上下文注入到本次运行的 prompt 之上。每个上游条目必须是有效的任务 ID 或名称(参见 cronjob action="list")。注意:串联读取的是最近一次完成的输出——它不会等待同一 tick 中正在运行的上游任务。任务存储#
任务存储在 ~/.hermes/cron/jobs.json。任务运行的输出保存到 ~/.hermes/cron/output/{job_id}/{timestamp}.md。任务可能将 model 和 provider 存储为 null。省略这些字段时,Hermes 在执行时从全局配置中解析它们。只有设置了单任务覆盖时,这些字段才会出现在任务记录中。存储使用原子文件写入,因此中断的写入不会留下部分写入的任务文件。自包含的 prompt 仍然重要#
Cron 任务在完全全新的 agent 会话中运行。Prompt 必须包含 agent 所需的一切,除非已由附加的 skill 提供。
错误: "Check on that server issue"正确: "SSH into server 192.168.1.100 as user 'deploy', check if nginx is running with 'systemctl status nginx', and verify https://example.com returns HTTP 200."安全性#
定时任务的 prompt 在创建和更新时会扫描 prompt 注入和凭证外泄模式。包含不可见 Unicode 技巧、SSH 后门尝试或明显的密钥外泄载荷的 prompt 会被拦截。