Nous Research 推出的 Hermes Agent 是 2026 年增长最快的开源 AI Agent,其核心差异化能力是内置学习循环(Learning Loop)——它能从经验中自动创建技能、在使用中自我改进、主动持久化知识,实现跨 Session 的能力累积。本文从源码层面深度拆解其原理。
架构总览:学习循环的四阶段
Hermes Agent 的学习循环遵循 Observe → Distill → Reuse → Refine 四个阶段,运行在 agent loop 的主循环之上:
User Message
│
▼
┌─────────────────────────────────┐
│ Agent Loop │
│ (run_conversation) │
│ │
│ while budget_remaining: │
│ response = LLM.call(...) │
│ if tool_calls: │
│ execute tools │
│ append results │
│ else: │
│ return response │
│ │
│ ┌─────────────────────────┐ │
│ │ Self-Evaluation │ │
│ │ Checkpoint │ │
│ │ (每 15 次 tool call) │ │
│ └─────────┬───────────────┘ │
│ ▼ │
│ ┌─────────────────────────┐ │
│ │ Skill Creation / Update │ │
│ │ Memory Nudge │ │
│ └─────────────────────────┘ │
└─────────────────────────────────┘
核心洞察:这不是模型权重层面的训练,而是结构化的经验记录与检索系统。LLM 的权重从未被改变,改变的是围绕 LLM 的”知识层”——提示词、技能文档、记忆文件。
主线 Agent Loop
Hermes 的核心驱动在 run_agent.py 中的 AIAgent 类(约 13,700 行)。其主循环逻辑如下:
def run_conversation(self): while (api_call_count < self.max_iterations and self.iteration_budget.remaining > 0) \ or self._budget_grace_call: if self._interrupt_requested: break # 1. 构建系统提示(含 skills 索引、记忆) system_prompt = self._build_system_prompt() # 2. 调用 LLM(支持多种 provider) response = client.chat.completions.create( model=model, messages=messages, tools=tool_schemas ) # 3. 解析响应 if response.tool_calls: for tool_call in response.tool_calls: result = handle_function_call( tool_call.name, tool_call.args, task_id ) messages.append(tool_result_message(result)) # ★ 自评价检查点 if self._should_evaluate(): self._learning_checkpoint(messages) api_call_count += 1 else: self._flush_memory() return response.content
关键设计:
self.iteration_budget:追踪父子 agent 的预算消耗,防止无限循环_should_evaluate():基于 tool call 计数判断是否需要触发学习检查点_flush_memory():在上下文丢失前将关键信息写入持久化文件
技能系统:学习循环的核心载体
SKILL.md 格式
技能以 Markdown 文件存储在 ~/.hermes/skills/ 下,通过 YAML frontmatter 声明元数据:
--- name: code-review description: 执行标准代码审查工作流 version: 1.2.0 platforms: [macos, linux] metadata: hermes: tags: [code-review, github, ci] related_skills: [github-pr-workflow] config: - key: review.strictness description: 审查严格程度 default: "medium" --- # 代码审查技能 ## 触发条件 当用户说"审查这个 PR"时自动加载。 ## 步骤 1. 读取 PR 的变更文件列表 2. 对每个文件运行 linter 检查 3. 生成变更摘要报告 ## 已知陷阱 - 超过 500 行变更的 PR,先要求作者拆分
渐进式加载
prompt_builder.py 实现了三层缓存加载策略:
| 层级 | 内容 | Token 开销 | 触发时机 |
|---|---|---|---|
| L0 | 技能名称 + 描述索引 | ~3K(总) | Session 启动(自动注入 system prompt) |
| L1 | 完整 SKILL.md 内容 | 按需 | Agent 调用 skill_view(name) |
| L2 | 附属文件(references/templates/scripts) | 按需 | Agent 调用 skill_view(name, file_path) |
def build_skills_system_prompt(available_tools=None): """构建紧凑的技能索引,注入 system prompt""" index_lines = [] for category in sorted(skills_by_category): for name, desc in sorted(...): if desc: index_lines.append(f" - {name}: {desc}") result = ( "## Skills (mandatory)\n" "Before replying, scan the skills below. " "If a skill matches your task, you MUST load it. " "<available_skills>\n" + "\n".join(index_lines) + "\n</available_skills>\n" ) return result
Agent 自管理技能:skill_manage 工具
这是学习循环的关键工具——Agent 可以在运行时创建、更新、删除自己的技能文件:
def skill_manage(action, name, content=None, category=None, old_string=None, new_string=None, replace_all=False, file_path=None, file_content=None): if action == "create": return _create_skill(name, content, category) elif action == "patch": return _patch_skill(name, old_string, new_string, replace_all, file_path) elif action == "edit": return _edit_skill(name, content) elif action == "delete": return _delete_skill(name) elif action == "write_file": return _write_file(name, file_path, file_content) elif action == "remove_file": return _remove_file(name, file_path)
_patch_skill 的模糊匹配引擎
def _patch_skill(name, old_string, new_string, replace_all=False, file_path=None): content = target.read_text(encoding="utf-8") # 8 策略模糊匹配引擎 # 处理:空白标准化、缩进差异、块锚点匹配 from tools.fuzzy_match import fuzzy_find_and_replace new_content, match_count, _strategy, match_error = \ fuzzy_find_and_replace(content, old_string, new_string, replace_all) if match_error: preview = content[:500] + ("..." if len(content) > 500 else "") return {"success": False, "error": match_error, "file_preview": preview} err = _validate_content_size(new_content) if err: return {"success": False, "error": err} _atomic_write_text(target, new_content) # 原子写入,失败回滚 return {"success": True, "message": f"Patched skill '{name}' ({match_count} replacements)."}
_create_skill 完整流程
def _create_skill(name, content, category=None): # 1. 解析 YAML frontmatter frontmatter, body = _parse_frontmatter(content) skill_name = frontmatter.get("name", name) # 2. 安全检查 if _detect_injection(content): return {"success": False, "error": "注入检测失败"} err = _validate_content_size(content) if err: return {"success": False, "error": err} # 3. 确定目标路径 skill_dir = SKILLS_DIR / category / skill_name if category else SKILLS_DIR / skill_name skill_dir.mkdir(parents=True, exist_ok=True) # 4. 原子写入 _atomic_write_text(skill_dir / "SKILL.md", content) return {"success": True, "message": f"Skill '{skill_name}' created."}
System Prompt 中的技能引导
# agent/prompt_builder.py SKILLS_GUIDANCE = ( "After completing a complex task (5+ tool calls), fixing a tricky error, " "or discovering a non-trivial workflow, save the approach as a " "skill with skill_manage so you can reuse it next time.\n" "When using a skill and finding it outdated, incomplete, or wrong, " "patch it immediately with skill_manage(action='patch') — " "don't wait to be asked. " "Skills that aren't maintained become liabilities." )
这不是硬编码规则,而是通过 System Prompt 引导 LLM 自主决策:
- 5+ 次 tool call → 判定为”复杂任务”
- 成功完成 + 发现更好方法 + 用户纠正 → 分别是创建/更新/修正技能的触发条件
这意味着学习能力来自于 LLM 自身的推理能力,而非预设规则。
技能生命周期管理:Curator
Curator 是 Hermes 的”技能管家”,自动管理技能的完整生命周期:
class SkillCurator: def run(self): usage = self._load_usage_stats() # ~/.hermes/skills/.usage.json for skill in self._get_agent_skills(): state = usage.get(skill.name, {}) if self._is_stale(skill, state): self._archive_skill(skill) # 30 天未使用 → 归档 elif self._is_frequently_used(skill, state): self._pin_skill(skill) # 高频使用 → 固定 if self._needs_review(skill, state): review = self._llm_review_skill(skill) if review.suggested_improvements: self._apply_review(skill, review) def _archive_skill(self, skill): """移到 .archive/ 目录,永不删除""" shutil.move(str(skill.dir), str(SKILLS_DIR / ".archive" / skill.name))
Curator 的不变量:
- 仅操作
created_by: "agent"的技能,内置 + Hub 安装技能不受影响 - 永不删除,最多归档到
.archive/ - Pinned 技能豁免所有自动操作
记忆系统:跨 Session 知识持久化
提示记忆:MEMORY.md + USER.md
class MemoryManager: def flush(self, conversation_history): insights = self._extract_insights(conversation_history) # MEMORY.md:工作相关的记忆 with open(HERMES_HOME / "MEMORY.md", "a") as f: for insight in insights.work_memories: f.write(f"- {insight}\n") # USER.md:用户画像 with open(HERMES_HOME / "USER.md", "a") as f: for insight in insights.user_insights: f.write(f"- {insight}\n")
情节记忆:SQLite FTS5
-- 10ms 内跨 10,000+ 文档检索 CREATE VIRTUAL TABLE session_history USING fts5( content, metadata, tokenize='porter' ); SELECT snippet(session_history, 1, '<b>', '</b>', '...', 32) FROM session_history WHERE session_history MATCH ? ORDER BY rank LIMIT 5;
Memory Nudge
每 10 次交互或会话结束时,Hermes 会问自己:”这次对话中有哪些值得记住的信息?”
| Nudge 类型 | 触发条件 | 目的 |
|---|---|---|
| 会话结束 | 对话关闭 | 总结关键收获 |
| 模式检测 | 3+ 次类似请求 | 持久化偏好 |
| 用户声明 | “记住这个” | 立即存储 |
| 周期检查 | 每 10 轮交互 | 检查有价值信息 |
完整的技能自改进循环
第一次请求:”帮我审查这个 PR”
# 1. Agent 收到请求 # 2. 扫描 skills_list → 没有匹配技能 → 从头推理 # 3. 执行 7 次 tool call(gh pr view, gh pr diff, lint...) # 4. 成功完成 ✓ # 5. ★ 自评价检查点触发(7 > 5) # 6. Agent 判断这是可复用的工作流 skill_manage(action='create', name='code-review', content=...) # → 文件写入 ~/.hermes/skills/code-review/SKILL.md
第二次请求:”再审查 PR #58″
# 1. 扫描 skills_list → 发现 code-review 技能匹配 skill_view("code-review") # → 加载完整 SKILL.md # 2. 按步骤执行,遇到新问题:PR 包含二进制文件 # 3. 完成审查后,自动修补技能: skill_manage(action='patch', name='code-review', old_string="1. 读取 PR 的变更文件列表", new_string="1. 读取 PR 的变更文件列表\n" "2. 过滤二进制文件,仅审查文本文件") # → 技能从 5 步变成了 6 步
第 N 次:技能成熟
经过 20-30 次使用后,技能文档已从简单的指令集演化为经过实战锤炼的操作手册:
| 指标 | 第 1 周 | 第 6 周 |
|---|---|---|
| 每次审查的 tool call 数 | 25 | 8-10 |
| 错误率 | 高(经常遗漏步骤) | 低(边界情况已被覆盖) |
| 需要的人工干预 | 频繁 | 几乎不需要 |
RL 强化学习管道:Atropos 集成
class AtroposRLPipeline: def train_from_trajectories(self, trajectories_dir): trajectories = self._load_trajectories(trajectories_dir) compressed = trajectory_compressor.compress(trajectories) for trajectory in compressed: reward = self._compute_reward(trajectory) # RLHF: 用户反馈作为奖励信号 # DPO: 偏好对比训练 self._training_step(trajectory, reward) self._export_for_finetuning(compressed) # ShareGPT 格式
但需注意:RL 管道是可选的、离线的。日常学习循环不需要权重更新,在用户使用过程中实时发生。
“自改进”的真实含义
| 维度 | Hermes 的学习 | 传统 ML 训练 |
|---|---|---|
| 作用对象 | 提示词、技能文档、记忆文件 | 模型权重 |
| 范围 | 特定用户的工作流 | 全局能力 |
| 频率 | 实时(每次任务后) | 周期性(训练阶段) |
| 存储 | 文件系统(明文 Markdown) | 模型参数(二进制) |
| 可解释性 | 完全透明(可读可编辑) | 黑盒 |
| 回滚 | 删文件或 git revert | 重新训练 |
结论:Hermes 的”自改进”不是模型变聪明了,而是围绕模型的辅助层——过程性记忆(Skills)和陈述性记忆(MEMORY.md/USER.md)在持续累积经验。但这恰恰是实用层面最重要的改进:一个更了解你工作流的 Agent,比一个参数更多的通用模型更有价值。
极简学习循环演示代码
"""极简版学习循环实现(原理演示)""" from pathlib import Path SKILLS_DIR = Path.home() / ".demo-skills" TOOL_CALL_THRESHOLD = 3 class LearningAgent: def __init__(self): self.tool_call_count = 0 self.conversation_history = [] SKILLS_DIR.mkdir(exist_ok=True) def run(self, user_input): self.conversation_history.append( {"role": "user", "content": user_input} ) response = self._call_llm(self._build_prompt()) if response.get("tool_calls"): for tc in response["tool_calls"]: result = self._execute_tool(tc) self.conversation_history.append( {"role": "tool", "content": result} ) self.tool_call_count += 1 if self.tool_call_count >= TOOL_CALL_THRESHOLD: self._learning_checkpoint() # ★ 自评价 return self.run(user_input) else: return response["content"] def _learning_checkpoint(self): steps = self._extract_steps() if not steps: return task_type = self._classify_task(steps) existing = self._find_skill(task_type) if existing: new_steps = self._find_new_steps(existing, steps) if new_steps: self._patch_skill(existing, new_steps) print(f" → 技能 '{task_type}' 已更新 (+{len(new_steps)} steps)") else: self._create_skill(task_type, steps) print(f" → 新技能 '{task_type}' 已创建 ({len(steps)} steps)") def _create_skill(self, name, steps): content = f"""--- name: {name} description: 自动创建的技能 --- # {name} ## 步骤 """ for i, step in enumerate(steps, 1): content += f"{i}. {step}\n" skill_dir = SKILLS_DIR / name skill_dir.mkdir(exist_ok=True) (skill_dir / "SKILL.md").write_text(content)
# 运行示例 agent = LearningAgent() agent.run("帮我设置 CI/CD 流水线") # → 执行 5 次 tool call → 自评价触发 → 创建技能 'ci-cd-pipeline' (8 steps) agent.run("再设置一个前端项目的 CI/CD") # → 技能匹配!加载 ci-cd-pipeline 技能 # → 发现缺少 npm install 步骤 → 自动 patch # → 技能更新为 9 steps
总结
| 机制 | 技术实现 | 学习效果 |
|---|---|---|
| 技能创建 | 将复杂任务的执行轨迹抽象为 SKILL.md | 从”不知道怎么做”到”有标准方法” |
| 技能修补 | 模糊匹配引擎 patch 技能文件 | 从”有标准方法”到”方法越来越完善” |
| 技能渐进加载 | L0 索引 + L1 内容 + L2 附属文件 | 数百技能不增加 token 开销 |
| 记忆持久化 | MEMORY.md / USER.md + SQLite FTS5 | 跨 Session 知识不丢失 |
| Curator 生命周期 | 自动归档 + LLM 审查 | 技能库保持健康 |
| Honcho 用户建模 | 辩证主义演进式用户画像 | Agent 越来越了解你 |
| Atropos RL | 轨迹压缩 + DPO/RLHF 训练 | 可选深度优化模型行为 |
Hermes 的学习循环本质上是将 LLM 的推理能力与文件系统的持久性结合:LLM 负责判断”什么值得学”和”如何改进”,文件系统负责”记住”和”检索”。这种架构让 Agent 在使用中不断累积领域知识,从第 1 天的通用助手,进化为第 30 天的专属工作伙伴。
正如 Nous Research 所说:”这不是一个更聪明的模型,这是一个更聪明的包装器。”
The LLM is a replaceable component; the real engineering work happens in the layers around it.
