乐于分享
好东西不私藏

Hermes Agent 源码-Curator 编排器与后台自治

Hermes Agent 源码-Curator 编排器与后台自治

Hermes Agent 源码解析

第 17 讲:Curator 编排器与后台自治

基于 Hermes Agent v0.16.0 源码 · 2026-06-30

一、从"对话机器人"到"自治Agent"的跃迁

前 16 讲我们逐层拆解了 Hermes 的核心运行时——从架构概览、对话循环、工具系统、插件、Provider 抽象、记忆与技能、网关适配、上下文压缩、MCP 集成、多模态、ACP 协议、CLI 交互、认证配置、预算控制、System Prompt 构建到工具执行。这些构成了 Hermes 的"前台能力"

但 Hermes 的真正野心不止于此。它要成为一个24/7 运行的自治 Agent——即使你没有打开终端,它也在后台默默工作:管理技能库、执行定时任务、在每次对话后自我学习。这一讲我们深入 Hermes 的"后台自治系统",核心组件包括:

📦 本讲核心文件

agent/curator.py(1835 行)— 技能编排器主逻辑

agent/curator_backup.py(695 行)— 快照与回滚

agent/background_review.py(608 行)— 每轮对话后的后台审查

cron/scheduler.py(2213 行)— 定时任务调度器

cron/jobs.py(1212 行)— 任务存储与管理

hermes_cli/curator.py(598 行)— CLI 子命令

二、Curator 编排器:技能库的"自治管家"

随着使用时间的增长,Agent 会创建大量技能——有些重复、有些过时、有些可以合并。Curator 就是为了解决这个问题而生的后台技能维护编排器。它不依赖 cron 守护进程,而是采用空闲触发机制:当 Agent 空闲超过 min_idle_hours(默认 2 小时),且距上次 Curator 运行超过 interval_hours(默认 7 天),就会自动启动一次审查。

Curator 的核心职责:

🔹 自动状态转换:基于技能活跃度将技能标记为 active/stale/archived

🔹 LLM 审查:派生一个 forked AIAgent 进行大规模技能合并与归档

🔹 快照与回滚:每次审查前自动备份,支持一键回滚

🔹 状态持久化:将运行状态写入 .curator_state 文件

三、技能生命周期状态机

Curator 维护一个三态生命周期,转换完全基于时间戳计算,不需要 LLM 参与:

🟢 Active(活跃)

⬇️ 超过 stale_after_days(默认 30 天)无活动

🟡 Stale(陈旧)

⬇️ 超过 archive_after_days(默认 90 天)无活动

🔴 Archived(归档)

关键规则:

🔹 只处理 agent-created 技能:内置技能和 Hub 安装的技能不受影响(除非开启 prune_builtins

🔹 从不自动删除:归档 = 移动到 .archive/,随时可恢复

🔹 Pinned 技能免疫:被用户标记为 pinned 的技能跳过所有自动转换

🔹 使用辅助客户端:不触碰主会话的 prompt cache,独立运行

状态机代码在 apply_automatic_transitions() 中,是一个纯函数——遍历所有候选技能,基于 last_activity_atcreated_at 时间戳做出决定:

def apply_automatic_transitions(now=None) -> Dict[str, int]:
    """遍历所有候选技能,基于活跃度时间戳转换状态。"""
    stale_cutoff = now - timedelta(days=stale_after_days)
    archive_cutoff = now - timedelta(days=archive_after_days)
    
    for row in agent_created_report():
        if row.get("pinned"):
            continue  # Pinned 技能跳过
        
        anchor = last_activity or created_at or now
        
        if anchor <= archive_cutoff:
            archive_skill(name)          # → Archived
        elif anchor <= stale_cutoff:
            set_state(name, STATE_STALE) # → Stale
        elif current == STATE_STALE:
            set_state(name, STATE_ACTIVE)  # 重新活跃

四、LLM 审查:伞形技能合并引擎

自动状态转换只是"粗筛"。真正的智能审查由 run_curator_review() 触发——它派生一个forked AIAgent,使用专门的 CURATOR_REVIEW_PROMPT(约 1400 词)指导 LLM 执行大规模技能整合。

这个 Prompt 的核心哲学是"伞形技能"(Umbrella Skills)

"目标形状是 CLASS-LEVEL 技能,每个都有丰富的 SKILL.md
和 references/ 目录存储会话级细节。
而不是数百个窄技能,每个只记录一个特定 bug ——
那是技能库的失败,不是特性。"

审查器识别前缀簇(Prefix Clusters)——共享首词或领域关键词的技能组,然后将它们合并为伞形技能。三种合并策略:

策略 适用场景 操作
A. 合并入现有伞 簇中已有足够宽泛的技能 patch 伞形 SKILL.md 添加子章节,归档兄弟技能
B. 创建新伞形技能 没有成员足够宽泛 skill_manage create 新伞,归档被吸收的窄技能
C. 降阶为支持文件 有价值但过于窄化的内容 移入 references/、templates/、scripts/ 子目录

五、三重信号融合:去伪存真的分类机制

如何区分一个被归档的技能是"被合并进了伞形技能"还是"纯粹被清理了"?Curator 使用三重信号融合机制:

信号 1:Model-declared absorbed_into

skill_manage delete 时的 absorbed_into 参数(最高优先级)

⬇️ 模型在删除瞬间声明意图

信号 2:YAML 结构化摘要

LLM 最终输出中的 consolidations/prunings 列表

⬇️ 模型自述的分类结果

信号 3:工具调用审计

扫描 skill_manage 调用的 file_path/content 字段

_reconcile_classification() 函数按优先级融合这三个信号,处理各种矛盾场景(如模型声称合并但目标不存在 = 幻觉,回退到工具调用审计)。

六、快照与回滚系统

每次 Curator 运行前,curator_backup.py 自动创建技能树的 tar.gz 快照,存储在 ~/.hermes/skills/.curator_backups/ 下。快照包含:

🔹 所有 SKILL.md 及其子目录(scripts/、references/、templates/、assets/)

🔹 .usage.json(使用遥测数据)

🔹 .archive/(已归档技能,保证回滚完整性)

🔹 .curator_state(运行状态指针)

🔹 jobs.json(Cron 任务引用,回滚时自动修复技能链接)

回滚本身也是可撤销的——回滚前会先对当前状态创建快照,形成无限可回退链。默认保留最近 5 个快照,通过 curator.backup.keep 配置。

七、Cron 调度器:定时任务的"引擎"

Hermes 的后台自治能力很大程度上依赖 Cron 调度器。Gateway 每 60 秒调用一次 tick(),检查并执行到期任务。调度器架构非常精密:

① tick() 入口

文件锁确保全局唯一执行

⬇️ get_due_jobs() 筛选到期任务

② 预推进 next_run_at

at-most-once 语义保证

⬇️ 分区:有 workdir → 串行池 / 无 workdir → 并行池

③ 双线程池执行

_sequential_pool (1 worker) + _parallel_pool (N workers)

⬇️ _process_job():执行 → 保存 → 投递 → 标记

④ 后置清理

MCP 孤儿进程回收 + 旧输出清理

串行 vs 并行分区逻辑

# workdir 任务修改 os.environ["TERMINAL_CWD"]
# 这是进程全局状态,必须串行执行
sequential_jobs = [j for j in due_jobs 
                   if (j.get("workdir") or "").strip()]
parallel_jobs = [j for j in due_jobs 
                 if not (j.get("workdir") or "").strip()]

三种任务类型

类型 说明 LLM 参与
no_agent 纯脚本执行,直接输出 stdout ❌ 无
wakeAgent 门控 先运行脚本,脚本决定是否需要 LLM 🔀 条件
完整 Agent 派生 AIAgent 执行完整对话循环 ✅ 是

八、后台审查:每轮对话后的自我学习

background_review.py 实现了 Hermes 的"自我改进循环"。每次对话结束后,AIAgent.run_conversation 可能调用 spawn_background_review,派生一个后台线程审查本轮对话:

对话结束

⬇️ spawn_background_review()

派生 forked AIAgent

继承父 Agent 运行时(provider/model/credentials)

⬇️ 工具白名单:仅 memory + skills

审查 Prompt 执行

_MEMORY_REVIEW_PROMPT / _SKILL_REVIEW_PROMPT

⬇️ 更新 MEMORY.md / SKILL.md

返回动作摘要给用户

"Memory updated" / "Skill patched"

关键设计细节:

🔹 继承父运行时:fork 使用相同的 provider/model/credentials,命中相同的 prefix cache

🔹 工具白名单:仅允许 memory 和 skill 管理工具,其他工具运行时拒绝

🔹 跳过外部记忆skip_memory=True 防止 fork 污染外部记忆插件(honcho/mem0 等)

🔹 禁用压缩:审查需要完整上下文,压缩会丢失细节

🔹 危险命令自动拒绝:安装非交互式审批回调,防止死锁

九、后台自治全景图

将 Curator、Cron 调度器、后台审查三个组件放在一起,就构成了 Hermes 完整的后台自治系统

🔄 后台自治循环

前台路径:用户对话 → AIAgent 循环 → 后台审查 → 更新记忆/技能

定时路径:Gateway tick(60s) → 筛选到期任务 → 双线程池执行 → 投递结果

空闲路径:Agent 空闲 2h + 距上次审查 7d → Curator 审查 → 合并/归档技能

三个路径共享同一个辅助客户端auxiliary_client.py),确保不干扰主会话的 prompt cache。辅助客户端有独立的 provider 解析链(主 provider → OpenRouter → Nous Portal → Custom → Anthropic),保证后台任务始终有可用的 LLM 后端。

十、安全与隔离

后台自治系统有多层安全机制:

安全层 机制 保护范围
工具集隔离 Cron 任务禁用 cronjob/messaging/clarify 防止递归调度/交互
Prompt 注入扫描 _build_job_prompt 扫描最终 Prompt 技能内容中的注入载荷
文件锁 fcntl/msvcrt 跨平台文件锁 防止多实例重复执行
路径逃逸防护 _job_output_dir 拒绝 .. / 绝对路径 输出目录沙箱
危险命令拒绝 _bg_review_auto_deny 回调 后台审查中的危险操作

十一、CLI 管理命令

hermes curator 提供完整的 CLI 管理接口:

hermes curator status          # 查看 Curator 状态和技能统计
hermes curator run             # 立即触发一次审查
hermes curator run --dry-run   # 预览模式(只报告不修改)
hermes curator pause/resume    # 暂停/恢复自动审查
hermes curator pin <name>     # 保护指定技能不被自动归档
hermes curator unpin <name>   # 取消保护
hermes curator restore <name> # 恢复已归档的技能
hermes curator archive <name> # 手动归档技能
hermes curator prune --days N  # 批量归档闲置 N 天的技能
hermes curator backup          # 手动创建快照
hermes curator rollback        # 回滚到指定快照

十二、配置参数一览

配置项 默认值 说明
curator.enabled true 是否启用 Curator
curator.interval_hours 168 (7天) 审查间隔
curator.min_idle_hours 2 最小空闲时间
curator.stale_after_days 30 标记为陈旧的阈值
curator.archive_after_days 90 归档阈值
curator.prune_builtins true 是否清理内置技能
curator.backup.keep 5 保留快照数
cron.max_parallel_jobs 无限制 并行任务数上限

十三、总结与展望

Curator 编排器和后台自治系统让 Hermes 从一个"对话机器人"进化为一个能自我维护、自我学习的自治 Agent。关键设计原则:

🔹 空闲触发:不占用对话资源,只在 Agent 空闲时运行

🔹 伞形合并:将碎片化技能整合为类级别的知识库

🔹 三重信号:模型声明 + YAML 摘要 + 工具审计,去伪存真

🔹 快照回滚:每次操作可撤销,无限回退链

🔹 纵深防御:工具集隔离 + Prompt 注入扫描 + 文件锁 + 路径沙箱

🔹 双线程池:串行/并行分区,workdir 任务不互相污染

下一讲我们将深入 TUI、Dashboard 与桌面应用,看看 Hermes 如何通过多种前端形态触达用户。

📚 系列导航

← 第 16 讲:Hermes Agent 源码-Tool Executor 深度解析与安全护栏

→ 第 18 讲:TUI、Dashboard 与桌面应用

关注公众号获取更多技术干货 📖

🔗 源码:https://github.com/NousResearch/hermes-agent