Skip to main content

Cron 内部机制

Cron 子系统提供定时任务执行功能——从简单的单次延迟到支持技能注入和跨平台交付的周期性 cron 表达式任务。

核心文件

文件用途
cron/jobs.py作业模型、存储及对 jobs.json 的原子读写操作
cron/scheduler.py调度器循环 —— 到期作业检测、执行与重复追踪
tools/cronjob_tools.py面向模型的 cronjob 工具注册与处理器
gateway/run.py网关集成 —— 在长期运行循环中触发 cron 计时
hermes_cli/cron.pyCLI hermes cron 子命令

调度模型

支持四种调度格式:

格式示例行为
相对延迟30m, 2h, 1d单次执行,指定时间后触发
间隔every 2h, every 30m周期性执行,按固定间隔重复触发
Cron 表达式0 9 * * *标准 5 字段 cron 语法(分钟、小时、日、月、星期几)
ISO 时间戳2025-01-15T09:00:00单次执行,在精确时间触发

面向模型的接口是一个单一的 cronjob 工具,采用动作式操作:create, list, update, pause, resume, run, remove

作业存储

作业存储于 ~/.hermes/cron/jobs.json,采用原子写入语义(先写入临时文件,再重命名)。每个作业记录包含:

{
"id": "job_abc123",
"name": "Daily briefing",
"prompt": "Summarize today's AI news and funding rounds",
"schedule": "0 9 * * *",
"skills": ["ai-funding-daily-report"],
"deliver": "telegram:-1001234567890",
"repeat": null,
"state": "scheduled",
"next_run": "2025-01-16T09:00:00Z",
"run_count": 42,
"created_at": "2025-01-01T00:00:00Z",
"model": null,
"provider": null,
"script": null
}

作业生命周期状态

状态含义
scheduled激活状态,将在下次预定时间触发
paused暂停状态 —— 不会触发,直到恢复
completed重复次数耗尽或已执行过的单次任务
running当前正在执行(瞬态状态)

向后兼容性

旧版作业可能仅包含一个 skill 字段,而非 skills 数组。调度器在加载时会进行标准化处理——将单个 skill 提升为 skills: [skill]

调度器运行时

计时周期

调度器以周期性计时运行(默认:每 60 秒一次):

tick()
1. Acquire scheduler lock (prevents overlapping ticks)
2. Load all jobs from jobs.json
3. Filter to due jobs (next_run <= now AND state == "scheduled")
4. For each due job:
a. Set state to "running"
b. Create fresh AIAgent session (no conversation history)
c. Load attached skills in order (injected as user messages)
d. Run the job prompt through the agent
e. Deliver the response to the configured target
f. Update run_count, compute next_run
g. If repeat count exhausted → state = "completed"
h. Otherwise → state = "scheduled"
5. Write updated jobs back to jobs.json
6. Release scheduler lock

网关集成

在网关模式下,调度器计时被整合进网关的主事件循环。网关在其定期维护周期中调用 scheduler.tick(),该过程与消息处理并行运行。

在 CLI 模式下,cron 任务仅在运行 hermes cron 命令时或处于活跃 CLI 会话期间才会触发。

新会话隔离

每个 cron 任务都在一个完全独立的代理会话中运行:

  • 无上一次运行的对话历史
  • 不保留对之前 cron 执行的记忆(除非显式持久化至内存/文件)
  • 提示必须自包含——cron 任务无法提出澄清问题
  • cronjob 工具集被禁用(防止递归)

技能驱动的任务

一个 cron 任务可通过 skills 字段附加一个或多个技能。在执行时:

  1. 技能按指定顺序加载
  2. 每个技能的 SKILL.md 内容作为上下文注入
  3. 任务提示被追加为任务指令
  4. 代理处理合并后的技能上下文 + 提示

这使得可复用、经过测试的工作流无需将完整指令粘贴到 cron 提示中。例如:

Create a daily funding report → attach "ai-funding-daily-report" skill

脚本驱动的任务

任务也可通过 script 字段附加一个 Python 脚本。脚本在每次代理回合前运行,其标准输出作为上下文注入提示中。这支持数据采集和变更检测模式:

# ~/.hermes/scripts/check_competitors.py
import requests, json
# Fetch competitor release notes, diff against last run
# Print summary to stdout — agent analyzes and reports

脚本超时默认为 120 秒。_get_script_timeout() 通过三层链式机制解析该限制:

  1. 模块级覆盖 —— _SCRIPT_TIMEOUT(用于测试/猴子补丁)。仅当其不同于默认值时使用。
  2. 环境变量 —— HERMES_CRON_SCRIPT_TIMEOUT
  3. 配置文件 —— cron.script_timeout_seconds 中的 config.yaml(通过 load_config() 读取)
  4. 默认值 —— 120 秒

提供商恢复机制

run_job() 将用户配置的备用提供商和凭证池传递给 AIAgent 实例:

  • 备用提供商 —— 从 fallback_providers(列表)或 fallback_model(旧式字典)读取 config.yaml,匹配网关的 _load_fallback_model() 模式。作为 fallback_model= 传入 AIAgent.__init__,后者将两种格式统一为备用链。
  • 凭证池 —— 通过 load_pool(provider)agent.credential_pool 加载,使用解析出的运行时提供商名称。仅当池中有凭证时才传递(pool.has_credentials())。实现同一提供商在遭遇 429 或限流错误时的密钥轮换。

这与网关行为一致——否则 cron 代理在限流时会失败且不会尝试恢复。

交付模型

Cron 任务结果可交付至任意支持的平台:

目标语法示例
原始聊天origin交付至任务创建的聊天
本地文件local保存至 ~/.hermes/cron/output/
Telegramtelegramtelegram:<chat_id>telegram:-1001234567890
Discorddiscorddiscord:#channeldiscord:#engineering
Slackslack交付至 Slack 主频道
WhatsAppwhatsapp交付至 WhatsApp 主会话
Signalsignal交付至 Signal
Matrixmatrix交付至 Matrix 主房间
Mattermostmattermost交付至 Mattermost 主频道
邮件email通过邮件交付
短信sms通过短信交付
Home Assistanthomeassistant交付至 HA 对话
DingTalkdingtalk交付至钉钉
Feishufeishu交付至飞书
WeComwecom交付至企业微信
Weixinweixin交付至微信(WeChat)
BlueBubblesbluebubbles通过 BlueBubbles 交付至 iMessage

对于 Telegram 讨论组,请使用格式 telegram:<chat_id>:<thread_id>(例如 telegram:-1001234567890:17585)。

响应包装

默认情况下(cron.wrap_response: true),cron 交付内容会被包裹:

  • 包含作业名称和任务标识的标题
  • 注明代理无法在对话中看到已交付消息的页脚

若在 cron 响应中使用 [SILENT] 前缀,则完全禁用交付——适用于仅需写入文件或执行副作用的任务。

会话隔离

Cron 交付不会镜像到网关会话的对话历史中。它们仅存在于 cron 任务自身的会话中。这可防止目标聊天对话中的消息交替违规。

递归防护

Cron 运行会话禁用了 cronjob 工具集。这可防止:

  • 定时任务创建新的 cron 任务
  • 可能导致 token 使用量爆炸的递归调度
  • 任务内部意外修改任务调度

锁定机制

调度器使用基于文件的锁定,防止多个重叠的计时周期重复执行同一组到期任务。这在网关模式下尤为重要,因为如果前一个计时周期耗时超过计时间隔,多个维护周期可能发生重叠。

CLI 接口

hermes cron CLI 提供直接的作业管理功能:

hermes cron list                    # Show all jobs
hermes cron create # Interactive job creation (alias: add)
hermes cron edit <job_id> # Edit job configuration
hermes cron pause <job_id> # Pause a running job
hermes cron resume <job_id> # Resume a paused job
hermes cron run <job_id> # Trigger immediate execution
hermes cron remove <job_id> # Delete a job

相关文档