该笔记篇主要是博主在设计自己Agent项目定时功能时的笔记篇,主要是学习,参照hermes成熟的cron设计体系,如何支持任务高度定制,风险管控,权限审批,消息溯源机制等等一系列cron功能设计的细节点,通过深入学习hermes的设计,可以深入理解他cron功能设计的精妙点,同时结尾附上博主自己项目的该功能的优化点以及设计思路方案,参考文档hermes定时功能深入讲解
1.hermes的cron是可以支持自然语言描述任务,不用手写cron表达式啥的,比如cron表达式,once表达式,相对延迟(30m,50h)之类的,对于任务时间的定义方式比较多
| 类型 | 说明 | 触发类型 | 写法示例 | 行为说明 |
|---|---|---|---|---|
| 相对延迟(one-shot) | 多久之后跑一次 | 一次性 | 30m / 2h / 1d |
从任务创建时刻开始倒计时;跑完任务直接消失,不会重复Hermes Age... |
| 固定间隔(interval) | 每隔多久循环跑 | 周期性 | every 30m / every 2h |
从创建时间起,每间隔固定时长执行;不是对齐整点 |
| 标准 Cron 表达式 | 传统 5 位 cron | 周期性 | 0 9 * * * |
分 时 日 月 周;对齐日历时间(每天 9 点),适合定点排班Hermes Age... |
| ISO 时间戳 | 指定一个精确时刻跑 | 一次性 | 2026-11-01T14:30:00 |
到这个绝对时间点执行一次,执行完任务销毁Hermes Age... |
2.hermes-定时任务本身以JSON对象表达,落盘持久化,比如任务的定义,描述等存放在~/.hermes/cron/jobs.json,任务的输出产物存放在~/.hermes/cron/output/{job_id}/{timestamp}.md
- 目标:服务重启、多进程并发访问的时候,定时任务不会丢失,调度可以持续跑
3.- 定时任务存储严格按profile隔离
- 每个Hermes Profile拥有独立的
HERMES_HOME目录 - 🛡️ 安全价值:不同profile之间,凭证、配置不会互相泄露
-核心作用:确保不同作用域范围的cron任务不会相互干扰,破坏安全性
4.Hermes 在读写 jobs.json (定时任务文件)时做了两层锁设计:跨进程锁 + 进程内递归锁,并且增加锁超时机制,防止死锁、多进程并发篡改定时任务。
👉 两层锁组合逻辑:
- 跨进程文件锁:拦住别的进程来改文件
- . 进程内递归锁:拦住本进程内部多个线程同时进入读写逻辑
表格
| 锁类型 | 作用域 | 锁类型 | 解决问题 |
|---|---|---|---|
| 跨进程文件锁 | 机器内所有 Hermes 进程 | 文件排他锁(flock /fcntl) | 不同进程同时打开、写入 jobs.json,文件撕裂 |
| 进程内递归锁 | 当前单个进程内部 | 递归互斥锁(ReentrantLock) | 同一进程多个线程(多个协程 / 处理入口)同时调用读写;支持同一个线程重复加锁不阻塞 |
| 锁超时 | 两层锁都带超时 | 超时控制 | 防止某个线程 / 进程拿到锁后异常卡住不放锁,全局卡死调度 |
| 以一次要修改 jobs.json(新增 / 更新 / 删除任务)为例: |
- 线程尝试获取【进程内递归锁】,带超时
- ✅ 获取成功:进入下一步
- ❌ 超时失败:直接抛锁获取超时异常,终止本次操作,不碰文件
- 在已经持有进程内递归锁的前提下,尝试获取【跨进程文件排他锁】,带超时
- ✅ 获取文件锁成功:现在拿到双层锁保护,可以打开 jobs.json
- ❌ 文件锁超时失败:释放递归锁,抛出异常,退出
- 安全执行文件逻辑:读取 json / 内存修改 / 序列化写回磁盘(一般会先写临时文件,rename 原子替换 jobs.json,进一步防文件损坏)
- 释放顺序:反向释放
1)先释放跨进程文件锁
2)再释放进程内递归锁(递归计数减一,计数到 0 才真正释放)
读操作:看实现,Hermes 这里文件锁一般也是排他锁(读写都加排他),简化模型;也有方案读共享写排他,Hermes 为简单稳定,读写统一排他。
5.Hermes 的 Cron 调度器运行在 gateway 进程内部,后台定时触发 tick 心跳扫描到期任务;通过tick全局锁 + Job认领+心跳保活整套机制,保证多进程场景下同一个定时任务不会被重复并发执行。
Claim(认领)就是:多个进程一起抢同一个到期定时任务,其中某一个进程成功给这个任务打上「我正在处理它」的标记,宣告所有权;拿到标记的进程负责执行,其他进程看到标记就跳过这个任务。
任务续命机制(heartbeat refresh):心跳线程相当于定时"打卡续命",任务活着就不断延长认领有效期;一旦进程挂掉打卡停止,认领自动失效留给别的实例接管。
核心作用是长定时任务执行期间可以不断续期,因为hermes每个任务都有租期,任务到期后没有续期,当前任务就会有可能被其他线程抢走,类似于看门狗机制(watch dog)
6.hermes通过run_job方法启动定时任务,任务以及执行结果是存放在磁盘文件中,cron的执行过程,也就是cron的Agent的执行过程是存放在sqlite中,具体流程如下
调用 run_job()
↓为本次任务新建专属 SessionDB SQLite连接(带10s锁等待超时)
↓启动Agent工作流执行
↓任务快要结束、会话关闭前,写入唯一session标题存入数据库
↓关闭会话、释放数据库资源
好处如下:
- 稳定性兜底(SQLite超时)
SQLite锁阻塞是非常经典坑,Agent场景任务多很容易并发抢库,10s超时防止线程卡死,保护整个cron tick调度循环。 - 会话隔离(dedicated连接)
每次定时任务独立数据库会话,任务之间数据库操作互相隔离,减少互相干扰。 - 可追溯可审计
每一次定时任务运行都打上唯一会话标识,后续可以回溯完整Agent执行记录,适合生产环境排查Agent执行异常。
7.Hermes Cron 的 pin(固定绑定):创建定时任务的时候,可以硬性指定这个任务只能跑在某一组 provider(模型服务商) + model(模型名称) ;不绑定的任务,则会跟随全局配置动态选择模型,支持服务商故障自动切换。
简单说:就是可以为每个cron绑定自己的任务模型,用来执行cron任务
8.Hermes Cron 里面分为两大类定时任务:Agent Jobs(AI智能体任务) 和 No‑Agent Jobs(纯脚本任务,不用大模型)
- Agent Jobs(智能体任务)- 走 Hermes 完整的 AIAgent 核心工作流流水线- 会调用大模型(LLM),可以加载、使用各种Skill工具- 你写的prompt交给Agent,LLM自主思考、调用工具、完成复杂任务
- ✅特点:需要模型,Agent自动规划执行 例子:每周定时让AI读取文档、分析数据、自动生成业务周报
- No‑Agent Jobs(无Agent脚本任务) - 通过底层函数 _run_job_script() 直接运行独立脚本
- ❌完全不调用LLM,不消耗Token- 脚本运行后的标准输出(stdout)直接捕获保存
- ✅特点:可以把任意代码作为定时任务跑 例子:定时运行python脚本备份文件、定时shell脚本拉取服务器监控指标
9.Hermes Cron 任务跑完之后,运行结果可以投递到3种不同目标位置:本地文件、原始对话会话、第三方消息平台,整套投递由网关适配器负责路由分发,同时做安全校验防止异常。
(1)本地文件形式:- 路径: ~/.hermes/cron/output/{job_id}/{timestamp}.md
(2)原始会话形式:自动把结果推送回最开始创建这条定时任务的那一条对话线程,- 底层依靠 _resolve_origin() 解析任务里面记录的来源会话信息,用户可以直接在原来聊天对话里面收到定时任务结果,上下文连贯,立刻看到输出
(3)第三方平台投递:通过网关平台适配器 + 凭据池,支持将定时任务推送到第三方的消息平台,比如X,telegram,飞书等等平台,hermes本地支持大量第三方消息平台配置
简单点理解:hermes已经内置好了对应平台的Api对接,当cron执行完后会调用该平台api推送消息,hermes就是做了平台与ai直接的通信机制,底层继续下面两套机制
- Gateway进程(网关)
网关是长驻后台进程,所有第三方平台消息收发都由gateway统一接管,cron投递能力复用这套网关基础设施
启动网关之后,各个平台适配器加载、连接IM服务,监听收到的消息,同时对外发送任务结果 - Platform Adapter 平台适配器
每个IM平台单独一套adapter(telegram.py、matrix.py),封装该平台独有的SDK、API调用逻辑
- 收消息:适配器接收平台消息,转成Hermes内部统一会话格式交给Agent
- 发消息(cron投递):把cron任务输出,转换成对应平台消息格式调用API发送
10.Hermes Cron 内置一套完整监控、诊断、故障巡检工具链,可以通过 CLI 查看任务状态、识别逾期任务,还提供 cron doctor 一键体检,用来排查配置、脚本、投递、调度存活等各类问题。
命令: hermes cron list
- 功能:列出全部 cron 任务,展示任务状态标记:
- enabled :启用
- paused :暂停
- completed :一次性任务已执行完毕- 下次调度执行时间
11.因为 cron 定时任务是无人值守自动运行,Hermes 专门增加了一套安全防护机制,分为两套扫描函数,拦截提示词注入、机密外泄、敏感文件读取等高危行为,同时针对 Skill 拼接场景做策略折中,兼顾安全性与业务灵活性。
_scan_cron_prompt() 函数负责扫描,命中可疑恶意模式直接拒绝任务执行:
- 提示词劫持指令
ignore previous instructions 、 disregard your rules 这类试图让Agent抛弃原有系统规则的注入指令
2. 窃取机密并外发使用 curl / wget 携带认证请求头,向不可信外部主机上传、盗取内部密钥 - 读取系统敏感文件尝试读取 .env 、 .netrc 、 /etc/sudoers 这类存放账号密码、权限配置的敏感文件
通过他,可以在执行cron前拦截一些风险提示词的问题啥的,保证定时任务可以正常执行
_scan_cron_prompt() 底层是硬编码正则 / 硬编码危险关键词模式(黑名单)做匹配,属于基于规则的扫描,不是大模型语义扫描
_scan_cron_skill_assembled():专门给多个 Skill 自动拼接生成的提示词用的一套"宽松版安全扫描器",用来减少正常业务场景误拦截,但依然拦住提示词注入攻击。
简单说他就是专门审查skills,当ai启动时往系统提示词加载一堆skills,他会宽松审查skills,查找skills是否出现风险的提示词,指令啥的,但是策略比较宽松,也是为了进一步保证后台cron执行时安全可靠
12.hermes的cron功能本质就是在后台启动一个cron的agent,去帮我们执行任务,进行任务汇报等等,所以对于agent弹出的工具审批啥的,我们也是需要进行处理,让后台agent可以稳定运行
核心痛点:交互式聊天时遇到高危工具/Shell,可以弹窗等待人工审批;Cron是无人值守后台跑,没人在线点同意,必须一套独立的审批策略,配置项在 config.yaml 的 approvals.cron_mode
Cron专属两大审批模式(最核心)
approvals:
cron_mode: deny # 可选 deny / approve
1.deny (默认推荐,出厂默认)
- 当Agent在cron执行过程调用被标记为危险的工具、高危Shell命令时 → 直接拒绝执行,任务记录失败,不会卡住等待人工
- ✅ 安全优先,适合绝大多数后台定时任务,防止无人值守场景自动执行高危操作
2. approve (全自动放行模式)- 遇到高危命令自动审批通过,无需人工干预,实现完整自动化跑Agent工作流
- ⚠️ 高风险,只有充分评估任务Prompt、Skill边界之后才打开
Hermes 里所有工具调用有一套安全标签:
- 工具 / Shell 脚本可以被标记为 高危(需要人工审批)
- 正常交互式会话(人手动发起任务):遇到高危调用 → 阻塞、等待人点审批 / 拒绝,卡在那里等输入
但是!定时 Cron 任务是无人值守后台跑的 ,没人在线审批,所以单独分出 cron_mode 来定义「没人的时候遇到高危调用怎么办」。
表格
| cron_mode | 行为 | 阻塞? | 结果记录 | 适用场景 |
|---|---|---|---|---|
deny(出厂默认推荐) |
命中高危工具 / 高危 Shell → 自动拒绝 | ❌ 不阻塞,直接拒绝 | 任务标记失败,写入日志,不再继续往下执行 | 绝大多数后台定时任务,安全兜底,防止定时任务偷偷执行高危操作 |
approve |
命中高危工具 / 高危 Shell → 自动放行、自动审批通过 | ❌ 不阻塞,直接执行 | 任务继续正常跑,流程走完 | 可控、经过审计的自动化流水线;风险极高 |
下面需要讲解一下他的approve模式:
先理清边界:approve 到底放开了什么,没放开什么
| 项目 | approve 是否放开 |
|---|---|
| 高危工具人工审批卡点 | ✅ 放开(自动同意) |
| Skill / 工具本身的入参校验、白名单 | ❌ 不放开 |
| Shell 命令语法黑名单、命令过滤规则 | ❌ 不放开 |
| 操作系统账号权限(uid、文件 DAC、sudo 权限) | ❌ 不放开 |
| 资源限额(CPU / 时长 / 内存 / 输出大小) | ❌ 不放开 |
| 任务沙箱隔离(如果开启沙箱) | ❌ 不放开 |
| 日志审计、调用快照记录 | ❌ 不放开(甚至会强制加强) |
| 其他独立的全局安全策略 | ❌ 不放开 |
通俗例子:
一个 Shell 工具标记为高危,正常交互式要等人工审批;
cron_mode=approve:不用等人,直接放行这个工具调用;
但是:如果这个 Shell 内置黑名单禁止
rm -rf /,或者运行账号根本没有 root,这条命令照样跑失败。
总结一下:放开approve权限不代表所有工具审批都同意,像claudecode,codex等都是有内置的指令黑名单,白名单啥的,即使放开了shell,但是shell如果命中指令黑名单,也是直接拦截不执行的,这一点需要清楚
13.定时工具博主总结的不同设计以及可优化点:
借助像claudecode,codex等agent的定时功能设计,我们其实还可以对cron功能设计更加精确化以及安全性管控,具体实施如下:
(1)可以对每个cron暴露指定的工具,不同工具集的cron具备不同能力,比如我们暴露只读工具,那么cronAgent在执行时只能做汇报类,暴露可写工具,那么就可以执行写操作,对于暴露了subagent子代里等工具,cronAgent就可以成为真实的agent,能够帮我们像真实Agent一样自动化处理复杂任务。通过对每个agent暴露不同工具集可以进行cron任务的个性化设计
(2)对于cron的审批机制,我们就可以融入codex的审批机制,将cron的审批分为用户审批与自动审批,
用户审批模式下,cron的特定权限审批(高风险指令)可以交给用户,用户代替审批,审批完后cron继续执行。
自动审批下,可以实现真正后台cron,有自动化审批agent收集cron上报的审批请求,自己审核后决定是否批准或拒绝
下面以博主自己的Agent项目讲解:
我们的每个任务可以进行个性化工具配置,提供基础任务(只读),工作区任务(暴露可写工具)与自定义工具组(自己定制cron可使用工具)

博主是将自己的Agent所有可调用的任务分为一组一组,成为能力组,勾选文件读取与检索,那么就会自动封装所有只读工具暴露给cron,勾选shell,cron就可以使用shell一类的所有指令,高度化自定义工具可以极大提升cron灵活性
同时我们将审批方式分为自动审批与人工审批,人工审批用于用户在使用Agent是可以手动审批cron权限,复用我们主Agent的权限系统,不新建第二套,同时极大提升我们对于cron的执行可控性,自动审批用户用户后台执行cron任务,让自动化审批助手去完成审批,对于自动审批功能的设计,可以学习codex的权限机制


至此,hermes的cron功能已经大体上梳理了一边,总结一下,hermes对于cron功能的实现还是挺完整的,不仅可以实现多种任务时间解析格式,多平台投递等任务设计与传递,还做了agent权限把控,cron风险管控,执行溯源机制,并发下的锁机制以及文件存储机制等多种防控措施,确保cron能够安全稳定,遇到意外情况也可以回复任务,继续执行等,如果自身项目有定时任务的需求,还是非常值得人学习的