跳转到内容

定时任务

使用自然语言或 cron 表达式自动调度任务。Hermes 通过统一的 cronjob 工具暴露 cron 管理,使用 action 风格操作而非独立的 schedule/list/remove 工具。

定时任务可以:

  • 调度一次性或循环任务
  • 暂停、恢复、编辑、触发和移除任务
  • 为任务附加零个、一个或多个技能
  • 将结果投递回原始聊天、本地文件或配置的平台目标
  • 在全新的 Agent 会话中以正常静态工具列表运行
  • 在无 Agent 模式下运行——定时运行的脚本,其 stdout 原样投递,零 LLM 参与(见下方无 Agent 模式

所有这些功能都通过 cronjob 工具对 Hermes 本身可用,因此你可以用自然语言创建、暂停、编辑和移除任务——无需 CLI。

Terminal window
/cron add 30m "提醒我检查构建"
/cron add "every 2h" "检查服务器状态"
/cron add "every 1h" "总结新的订阅内容" --skill blogwatcher
/cron add "every 1h" "使用两个技能并合并结果" --skill blogwatcher --skill maps
Terminal window
hermes cron create "every 2h" "Check server status"
hermes cron create "every 1h" "Summarize new feed items" --skill blogwatcher
hermes cron create "every 1h" "Use both skills and combine the result" \
--skill blogwatcher \
--skill maps \
--name "Skill combo"

直接对 Hermes 说:

每天早上9点,检查 Hacker News 上的 AI 新闻并在 Telegram 上给我发摘要。

Hermes 会在内部使用统一的 cronjob 工具。

Cron 任务可以在运行提示之前加载一个或多个技能。

cronjob(
action="create",
skill="blogwatcher",
prompt="Check the configured feeds and summarize anything new.",
schedule="0 9 * * *",
name="Morning feeds",
)

技能按顺序加载。提示成为叠加在这些技能之上的任务指令。

cronjob(
action="create",
skills=["blogwatcher", "maps"],
prompt="Look for new local events and interesting nearby places, then combine them into one short brief.",
schedule="every 6h",
name="Local brief",
)

当你希望定时 Agent 继承可复用的工作流,而无需将完整技能文本塞入 cron 提示时,这很有用。

Cron 任务默认与任何仓库脱离运行——不加载 AGENTS.md、CLAUDE.md 或 .cursorrules,terminal/file/code-exec 工具从网关启动的任何工作目录运行。传递 --workdir(CLI)或 workdir=(工具调用)来改变这一点:

Terminal window
# 独立 CLI
hermes cron create "every 1d at 09:00" \
"Audit open PRs, summarize CI health, and post to #eng" \
--workdir /home/me/projects/acme
# 从聊天中,通过 cronjob 工具
cronjob(
action="create",
schedule="every 1d at 09:00",
workdir="/home/me/projects/acme",
prompt="Audit open PRs, summarize CI health, and post to #eng",
)

设置 workdir 后:

  • 该目录中的 AGENTS.md、CLAUDE.md 和 .cursorrules 注入系统提示
  • terminal、read_file、write_file、patch、search_files 和 execute_code 都使用该目录作为工作目录
  • 路径必须是已存在的绝对目录——相对路径和缺失目录在创建/更新时被拒绝

默认情况下,cron 任务继承创建它的 Hermes profile。传递 --profile <name>(CLI)或 profile=(cronjob 工具)将任务重定向到不同的 profile——调度器解析该 profile 的 HERMES_HOME,在运行期间临时切换到其中,加载其 .env + config.yaml 并在那里执行任务:

Terminal window
hermes cron create "every 1d at 03:00" \
"Tail the security log and flag anomalies" \
--profile night-ops

使用 --profile default 显式指定根 Hermes profile。命名 profile 必须已存在;调度器拒绝即时创建 profile。要清除 profile 绑定,传递空字符串(--profile ""profile="")。

你不需要删除并重建任务来修改它们。

任务引用<job_id> 占位符(以及生命周期操作中)也接受任务的名称(不区分大小写)——当你记住 morning-digest 而不是十六进制 ID 时很方便。精确的 job ID 优先于名称匹配;如果引用不是 ID 且名称匹配多个任务,命令会拒绝并打印候选 ID 供你消歧。

Terminal window
/cron edit <job_id> --schedule "every 4h"
/cron edit <job_id> --prompt "Use the revised task"
/cron edit <job_id> --skill blogwatcher --skill maps
/cron edit <job_id> --remove-skill blogwatcher
/cron edit <job_id> --clear-skills
Terminal window
hermes cron edit <job_id> --schedule "every 4h"
hermes cron edit <job_id> --prompt "Use the revised task"
hermes cron edit <job_id> --skill blogwatcher --skill maps
hermes cron edit <job_id> --add-skill maps
hermes cron edit <job_id> --remove-skill blogwatcher
hermes cron edit <job_id> --clear-skills

注意:

  • 重复的 --skill 替换任务的附加技能列表
  • --add-skill 追加到现有列表而不替换
  • --remove-skill 移除特定附加技能
  • --clear-skills 移除所有附加技能

Cron 任务现在拥有比仅创建/移除更完整的生命周期。

Terminal window
/cron list
/cron pause <job_id>
/cron resume <job_id>
/cron run <job_id>
/cron remove <job_id>
Terminal window
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 remove <job_id_or_name>
hermes cron edit <job_id_or_name> [...flags]
hermes cron status
hermes cron tick

操作说明:

  • pause — 保留任务但停止调度
  • resume — 重新启用任务并计算下次运行时间
  • run — 在下次调度器滴答时触发任务
  • remove — 完全删除
  • edit — 修改调度、提示、profile、投递等

Cron 执行由网关守护进程处理。网关每 60 秒滴答调度器,在隔离的 Agent 会话中运行所有到期任务。

Terminal window
hermes gateway install # 安装为用户服务
sudo hermes gateway install --system # Linux: 启动时系统服务(服务器用)
hermes gateway # 或在前台运行
hermes cron list
hermes cron status

每次滴答时 Hermes:

  1. ~/.hermes/cron/jobs.json 加载任务
  2. 检查 next_run_at 是否在当前时间之前
  3. 为每个到期任务启动全新的 AIAgent 会话
  4. 可选地将一个或多个附加技能注入到新会话中
  5. 运行提示直到完成
  6. 投递最终响应
  7. 更新运行元数据和下次调度时间

~/.hermes/cron/.tick.lock 处的文件锁防止重叠的调度器滴答重复运行同一批任务。

调度任务时,你指定输出去向:

选项说明示例
"origin"回到任务创建处消息平台上的默认值
"local"仅保存到本地文件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 主房间
"email"邮件
"sms"通过 Twilio 的 SMS
"dingtalk"钉钉
"feishu"飞书/Lark
"wecom"企业微信
"weixin"微信
"all"扇出到所有已连接的主频道在触发时解析
"telegram,discord"扇出到特定频道集合逗号分隔列表
"origin,all"投递到原始位置加所有其他连接频道组合任意标记

Agent 的最终响应会自动投递。你不需要在 cron 提示中调用 send_message

如果 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/config.yaml
cron:
script_timeout_seconds: 300 # 5 分钟

或设置 HERMES_CRON_SCRIPT_TIMEOUT 环境变量。解析顺序为:环境变量 → config.yaml → 120 秒默认。

对于不需要 LLM 推理的循环任务——经典看门狗、磁盘/内存告警、心跳、CI Ping——在创建时传递 no_agent=True。调度器按时运行你的脚本并直接投递其 stdout,完全跳过 Agent:

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

语义:

  • 脚本 stdout(修剪后)→ 原样投递为消息
  • 空 stdout → 静默滴答,不投递。这就是看门狗模式:“只在出问题时才说话”
  • 非零退出或超时 → 投递错误告警,确保看门狗不会静默失败
  • {"wakeAgent": false} 在最后一行 → 静默滴答
  • 无 Token 消耗、无模型、无提供商回退——任务从不触碰推理层

.sh / .bash 文件在 /bin/bash 下运行;其他文件在当前 Python 解释器下运行。脚本必须存放在 ~/.hermes/scripts/ 中。

cronjob 工具的 schema 直接向 Hermes 暴露 no_agent,所以你可以在聊天中描述一个看门狗,让 Agent 来配置:

每 5 分钟检查一次,如果 RAM 超过 85% 就在 Telegram 通知我。

Hermes 会通过 write_file 将检查脚本写入 ~/.hermes/scripts/,然后调用:

cronjob(action="create", schedule="every 5m",
script="memory-watchdog.sh", no_agent=True,
deliver="telegram", name="memory-watchdog")

当消息内容完全由脚本决定时(看门狗、阈值告警、心跳),它会自动选择 no_agent=True

Cron 任务在隔离会话中运行,没有之前运行的记忆。但有时一个任务的输出正是下一个任务需要的。context_from 参数自动建立这种连接——任务 B 的提示在运行时将任务 A 的最近输出作为上下文前置。

# 任务 1:收集原始数据
cronjob(
action="create",
prompt="Fetch the top 10 AI/ML stories from Hacker News. Save them to ~/.hermes/data/briefs/raw.md in markdown format with title, URL, and score.",
schedule="0 7 * * *",
name="AI News Collector",
)
# 任务 2:筛选 — 接收任务 1 的输出作为上下文
# 从 cronjob(action="list") 获取任务 1 的 ID
cronjob(
action="create",
prompt="Read ~/.hermes/data/briefs/raw.md. Score each story 1–10 for engagement potential and novelty. Output the top 5 to ~/.hermes/data/briefs/ranked.md.",
schedule="30 7 * * *",
context_from="<job1_id>",
name="AI News Triage",
)
# 任务 3:发布 — 接收任务 2 的输出作为上下文
cronjob(
action="create",
prompt="Read ~/.hermes/data/briefs/ranked.md. Write 3 tweet drafts (hook + body + hashtags). Deliver to telegram:7976161601.",
schedule="0 8 * * *",
context_from="<job2_id>",
name="AI News Brief",
)

工作原理:

  • 当任务 2 触发时,Hermes 从 ~/.hermes/cron/output/{job1_id}/*.md 读取任务 1 的最近输出
  • 该输出自动前置到任务 2 的提示中
  • 任务 2 不需要硬编码”读取这个文件”——它将内容作为上下文接收
  • 链条可以是任意长度:任务 1 → 任务 2 → 任务 3 → …

context_from 接受的格式:

格式示例
单个任务 ID(字符串)context_from="a1b2c3d4"
多个任务 ID(列表)context_from=["job_a", "job_b"]
表达式含义
30m30 分钟后运行一次
2h2 小时后运行一次
1d1 天后运行一次
表达式含义
every 2h每 2 小时
every 1d at 09:00每天 9:00
0 9 * * *标准 cron:每天 9:00
0 9 * * 1标准 cron:每周一 9:00

Cron 任务继承你配置的后备提供商和凭据池轮换。如果主 API Key 被限速或提供商返回错误,cron Agent 可以:

  • 如果你配置了 fallback_providers(或旧的 fallback_model),回退到备用提供商
  • 轮换到同一提供商凭据池中的下一个凭据

这意味着高频率或在高峰期运行的 cron 任务更有弹性——单个被限速的 Key 不会让整个运行失败。

# 在 ~/.hermes/config.yaml 中
cron:
wrap_response: false # 投递时不包装响应头尾
script_timeout_seconds: 300 # 脚本超时(秒)