Hermes 闭环自学习系统 — 深度解析

面向新人:本文从零拆解 Hermes Agent 的闭环自学习机制,覆盖每一步的触发条件、执行流程、关键代码位置和设计哲学。配合 Mermaid 流程图理解更佳。


一句话概述

Hermes 在每次对话轮次结束后,自动 fork 一个独立 agent 线程,复盘刚刚完成的任务,主动判断:这段对话中是否出现了值得记住的"用户特性"(记忆系统)和"可复用的工作模式"(技能系统),然后将它们持久化。几轮之后,一个定期运行的"管家"会审查所有 agent 自创的技能,合并碎片、归档过期内容,保持技能库始终处于"类级别"的高质量结构。

这不是传统的"用户手动存备忘"或"被动日志",而是 agent 主动判断、主动沉淀、主动维护 的完整闭环。


为什么需要闭环自学习?


系统全景图


三大子系统逐一拆解

4.1 每轮后背景审查 (Background Review)

触发时机

不是每轮都触发,而是基于计数器的"nudge"机制:

两个计数器独立运作------可能同一轮同时触发 memory 和 skill 审查。

执行流程(逐步骤)

Step 1: 计数器累加

复制代码
# agent/turn_context.py:210-215
agent._turns_since_memory += 1
if agent._turns_since_memory >= agent._memory_nudge_interval:
    should_review_memory = True
    agent._turns_since_memory = 0

# agent/conversation_loop.py:518-520
if (agent._skill_nudge_interval > 0
        and "skill_manage" in agent.valid_tool_names):
    agent._iters_since_skill += 1

Step 2: 响应交付后才触发

复制代码
# agent/turn_finalizer.py:374-397
# 先判断 skill nudge 是否达到阈值
if (agent._skill_nudge_interval > 0
        and agent._iters_since_skill >= agent._skill_nudge_interval
        and "skill_manage" in agent.valid_tool_names):
    _should_review_skills = True
    agent._iters_since_skill = 0

# 响应已经交付,再后台审查------不与用户任务争抢模型注意力
if final_response and not interrupted and (_should_review_memory or _should_review_skills):
    agent._spawn_background_review(
        messages_snapshot=list(messages),
        review_memory=_should_review_memory,
        review_skills=_should_review_skills,
    )

Step 3: 选择审查 Prompt

复制代码
# agent/background_review.py:580-587
# 根据触发条件选择对应 prompt
if review_memory and review_skills:
    prompt = _COMBINED_REVIEW_PROMPT      # 同时审查记忆和技能
elif review_memory:
    prompt = _MEMORY_REVIEW_PROMPT         # 仅审查记忆
else:
    prompt = _SKILL_REVIEW_PROMPT          # 仅审查技能

Step 4: Fork 独立 AIAgent

这是设计最精巧的环节:

复制代码
# agent/background_review.py:400-425
review_agent = AIAgent(
    model=agent.model,                    # 继承父 agent 的模型
    provider=agent.provider,              # 继承 provider
    base_url=parent_runtime["base_url"],   # 继承 base_url
    api_key=parent_runtime["api_key"],     # 继承 api_key
    max_iterations=16,                     # 最多 16 次迭代
    quiet_mode=True,                       # 静默模式
    skip_memory=True,                      # 不访问外部记忆后端
)

# 关键:继承缓存 promptsystem prompt,保证 Anthropic cache 命中
review_agent._cached_system_prompt = agent._cached_system_prompt

# 关键:关闭压缩------审查需要完整上下文
review_agent.compression_enabled = False

# 关键:关闭递归 nudge------fork 不再触发内层 fork
review_agent._memory_nudge_interval = 0
review_agent._skill_nudge_interval = 0

Step 5: 工具白名单限制

复制代码
# agent/background_review.py:460-474
# 只允许 memory 和 skill 相关工具
review_whitelist = {
    t["function"]["name"]
    for t in get_tool_definitions(
        enabled_toolsets=["memory", "skills"],
        quiet_mode=True,
    )
}
set_thread_tool_whitelist(review_whitelist)
# 其他工具运行时拒绝,例如终端、浏览器等

Step 6: 运行审查

复制代码
# agent/background_review.py:475-481
review_agent.run_conversation(
    user_message=prompt + "\n\n" + "只能使用 memory 和 skill 管理工具",
    conversation_history=messages_snapshot,  # 完整对话快照作为上下文
)

Step 7: 提取动作摘要

复制代码
# agent/background_review.py:505-510
actions = summarize_background_review_actions(
    review_messages,
    messages_snapshot,  # 排除对话中已有的旧工具结果
)
# 例如: actions = ["Memory updated", "skill 'python-debugging' updated"]

Step 8: 通知用户

复制代码
  💾 Self-improvement review: Memory updated · skill 'python-debugging' updated
记忆审查 Prompt 的核心意图
复制代码
审查对话并考虑保存到记忆中。

关注点:
1. 用户是否透露了关于自身的信息------个性、愿望、偏好、个人细节?
2. 用户是否表达了对你行为的期望、工作风格、操作方式?

如果有值得记住的,使用 memory 工具保存。
如果没什么值得保存,说 'Nothing to save.' 然后停止。
技能审查 Prompt 的核心意图(更复杂)
复制代码
审查对话并更新技能库。要主动------大多数会话至少产生一个技能更新。

信号(任何一个就值得操作):
  • 用户纠正了你的风格、语调、格式、详细程度
  • 用户纠正了你的工作流、方法或步骤顺序
  • 出现了非平凡的技术、修复、变通方案、调试路径或工具使用模式
  • 本会话中加载/查阅的技能被证明是错误的、缺少步骤或过时的

操作优先级:
  1. 更新"当前加载的"技能(通过 /skill-name 或 skill_view 加载的)
  2. 更新一个已有的 umbrella skill
  3. 在已有 umbrella 下添加 references/templates/scripts 支持文件
  4. 创建一个新的类级别 umbrella skill

绝不要捕捉为 skill 的:
  ✗ 环境相关的失败(缺少二进制、未配置凭据等)
  ✗ 关于工具的负面断言("浏览器工具坏了"------修好后 agent 会自我设限数月)
  ✗ 已解决的瞬态错误
  ✗ 一次性任务叙述("总结今天的市场"不是一类工作)

4.2 记忆管理器 (Memory Manager)

agent/memory_manager.py 是整个记忆系统的统一入口,替代了早期分散在各处的记忆代码。

架构
记忆上下文隔离

记忆内容通过 <memory-context> 标签注入 system prompt,并用 [System note] 标记:

复制代码
<memory-context>
[System note: The following is recalled memory context, 
NOT new user input. Treat as authoritative reference data --- 
this is the agent's persistent memory and should inform all responses.]

用户偏好简洁的代码输出,不喜欢冗长的解释...
用户的工作环境是 Windows + PowerShell...

</memory-context>

StreamingContextScrubber 确保即使流式输出被分割,这些标记也不会泄露到 UI 中。

核心约束
  • 只有一个外部 provider:注册第二个会被拒绝并警告,防止工具 schema 膨胀和记忆后端冲突。

  • 内置 provider 永远存在~/.hermes/memory/MEMORY.mdUSER.md 不可移除。

  • 核心工具名保护:memory provider 注册的工具如果与核心工具重名,会被拒绝(核心工具永远获胜)。


4.3 技能管家 (Curator)

agent/curator.py 不是 cron daemon,而是 空闲触发 的后台任务:

两阶段设计

阶段 1:自动状态转换(纯函数,无 LLM 开销)

复制代码
# agent/curator.py:250-300
for row in agent_created_report():
    if row.get("pinned"):
        continue  # pinned 技能永不自动转换
    
    anchor = last_activity or created_at
    
    if anchor <= archive_cutoff:      # > 90 天无活动
        archive_skill(name)            # 移到 .archive/,可恢复
    elif anchor <= stale_cutoff:      # > 30 天无活动
        set_state(name, STATE_STALE)   # 标记为陈旧
    elif anchor > stale_cutoff and current == STATE_STALE:
        set_state(name, STATE_ACTIVE)  # 重新激活

阶段 2:LLM 驱动审查

Curator 的审查 prompt 非常强调"类级别"和"umbrella 合并":

  • 扫描所有 agent 自创 skill

  • 识别前缀聚类 (如 gateway-*python-*

  • 对每个聚类:合并为 umbrella skill 或降级为子文件

  • 目标:把数百个碎片 skill 变成几十个高质量类级别 skill

Curator 的铁律
复制代码
1. 绝不触碰 bundled / hub-installed 技能
2. 绝不删除------最大破坏性操作是归档(可恢复)
3. 绝不触碰 pinned=yes 的技能
4. 不以 use_count 低为理由拒绝合并------计数器是新功能,大多是 0
5. "每个 skill 有不同的触发器"不是拒绝合并的理由

数据流全链路

将以上所有子系统串联起来,一图看清完整数据流:


设计哲学

1. 主动而非被动 (Proactive, not Passive)

传统的记忆系统是"用户说存我才存"。Hermes 的核心理念是agent 应该自己判断什么值得沉淀

每轮对话结束后,不是简单地记录日志,而是让一个独立的 agent 以"第三视角"复盘:

  • 有没有出现新的用户偏好?

  • 有没有出现值得复用的技术模式?

  • 用户是否纠正了我?

如果没有,坦然说 Nothing to save.;如果有,主动写入。

2. 非阻塞、零感知 (Non-blocking, Zero-perception)

后台审查在 daemon 线程中运行,响应先交付给用户,审查后发生

复制代码
# agent/turn_finalizer.py:391
# Background memory/skill review --- runs AFTER the response is delivered
# so it never competes with the user's task for model attention.

用户不会感知到审查的发生------只有在完成后看到一行简洁的 💾 Self-improvement review: ...

3. 低成本复用 (Cost-Efficient Reuse)

Fork 的子 agent 通过以下手段最大化 API 调用的性价比:

4. 分层职责 (Layered Responsibility)

三个子系统各司其职,互不越界:

复制代码
Background Review  →  微观、高频  →  每 10 轮触发  →  从单轮对话中提取信号
Memory Manager     →  基础设施    →  每轮必调用    →  管理记忆的存储/召回
Curator            →  宏观、低频  →  每 7 天触发   →  从技能库整体视角做合并/归

5. 安全不可妥协 (Safety Non-negotiable)

复制代码
# 后台审查 fork 的审批回调
def _bg_review_auto_deny(command, description, **kwargs):
    return "deny"  # 危险命令一律拒绝,不会向用户弹窗

审查 agent 只被允许使用 memory 和 skill 工具------即使模型幻觉要执行 rm -rf /,运行时也会被拒绝。

6. 渐进式、可逆 (Gradual & Reversible)

7. 类级别知识结构 (Class-Level Knowledge Structure)

Curator 的核心哲学是:"一个包含 100 个碎片技能的库是失败的"。目标是一个图书馆式的类级别结构

复制代码
❌ 错误的技能结构(碎片化):
  - debug-python-importerror-20240501
  - debug-python-modulenotfound-20240503
  - fix-python-path-issue
  - python-venv-setup-workaround

✅ 正确的技能结构(类级别 umbrella):
  - python-debugging/
    ├── SKILL.md           # 类级别:Python 调试方法论
    ├── references/
    │   ├── import-errors.md       # 会话特有的错误排查记录
    │   └── venv-issues.md         # 虚拟环境问题与修复
    ├── templates/
    │   └── debug-script.py        # 可复用的调试模板
    └── scripts/
        └── check-pyenv.sh         # 环境检查脚本

8. 隔离而非耦合 (Isolation over Coupling)

背景审查 fork 与主 agent 之间的隔离边界非常清晰:

  • 独立 AIAgent 实例:不共享 conversation loop 状态

  • 独立的 stdout/stderr:重定向到 /dev/null

  • 不触碰外部记忆后端skip_memory=True

  • 不参与上下文压缩compression_enabled=False

  • 不触发递归 nudge_memory_nudge_interval=0, _skill_nudge_interval=0

唯一共享的是凭据缓存 system prompt------前者是必需品,后者是为了省钱。


关键源码索引


本文基于 Hermes Agent 源码分析生成。