编程 hermes-agent 深度拆解:当 AI Agent 决定记住你是谁——一个 130K Star 的开源框架如何用 Personal Memory 和闭环学习重新定义自进化 Agent 的终极形态

2026-08-04 21:45:58 +0800 CST views 5

hermes-agent 深度拆解:当 AI Agent 决定「记住你是谁」——一个 130K Star 的开源框架如何用 Personal Memory 和闭环学习重新定义自进化 Agent 的终极形态

引言:Agent 不该每次都失忆

2026 年,AI Agent 已经从概念走向生产。但你有没有遇到过这种尴尬:昨天刚告诉 Agent 你的代码规范、项目偏好、沟通习惯,今天它又全忘了?每次对话都像第一次见面,你需要重新铺垫上下文、重申需求、重复纠正。

这不是 Agent 的 bug,而是架构层面的根本缺陷。

绝大多数 Agent 框架的思路是:让 LLM 更好地调用工具。Nous Research 的 hermes-agent 走了一条完全不同的路:让 Agent 越用越聪明

2026 年 2 月发布,两个月内 GitHub Star 冲到 130K,贡献者超过 240 人。hermes-agent 用代码证明了一件事:AI Agent 可以拥有长期记忆、可以自我学习、可以真正「认识」你。

本文从源码出发,把 hermes-agent 的架构拆开来看。不是功能介绍,而是分析它为什么这样设计,以及这些设计决策背后的取舍。


一、项目定位:从「工具调用器」到「自进化智能体」

1.1 传统 Agent 的困境

市面上大部分 Agent 框架(LangChain、CrewAI 等)的核心抽象是:Agent = LLM + Tools。LLM 负责理解意图,Tools 负责执行操作。这个模型在单次任务中表现不错,但一旦涉及多轮交互、长期项目跟进,就暴露了致命短板:

  • 无记忆:每次对话都是白纸一张,Agent 不认识你
  • 无学习:同样的错误反复犯,不会从历史中吸取教训
  • 无个性:所有用户拿到的是同一个 Agent,没有差异化

hermes-agent 的官方定位是 self-improving AI agent——自进化 AI Agent。这个定位直接决定了整个架构的设计取舍。

1.2 闭环学习:五阶段循环

hermes-agent 的核心创新是把「学习」做成了原生能力,不是事后打补丁,而是从 Day 1 就融入了核心架构。

完整的闭环学习包含五个环节:

完成任务 → 策划记忆 → 创建 Skill → Skill 自改进 → FTS5 召回 → 用户建模 → (循环)

具体来说:

  1. 策划记忆:任务完成后,Agent 自主判断什么值得记住
  2. 创建 Skill:识别重复模式,自动生成 Markdown 格式的 Skill 文件
  3. Skill 自改进:现有 Skill 失败时自动优化
  4. FTS5 召回:按需检索历史对话(SQLite 全文索引)
  5. 用户建模:从行为推断偏好

这套机制对应认知科学的三种记忆类型:

记忆类型hermes-agent 实现存储位置
情景记忆会话历史 + FTS5 索引SQLite
语义记忆MEMORY.md 持久事实文件系统
程序性记忆Skill 文件~/.hermes/skills/

二、核心架构:源码级拆解

2.1 目录结构与职责划分

先看目录结构,快速理解系统的职责划分:

hermes-agent/
├── run_agent.py           # Agent 主循环核心
├── tools/                 # 40+ 工具实现
├── toolsets.py            # Toolset 组合系统
├── agent/
│   ├── prompt_builder.py  # System Prompt 组装器
│   ├── memory_manager.py  # 记忆管理(双 Provider)
│   ├── context_engine.py  # 上下文引擎(可插拔)
│   └── context_compressor.py  # 轨迹压缩器
├── hermes_state.py        # SQLite 状态存储 + FTS5
├── gateway/run.py         # 多平台消息网关
├── skills/                # 内置 Skill 库(24 个分类)
├── plugins/               # 插件系统
├── hermes_cli/            # CLI 接口(51 个模块)
└── environments/          # 执行环境后端

几个关键的设计决策:

  • Agent 循环和工具执行在同一进程:通过 run_agent.py 中的 AIAgent 类管理整个生命周期,没有用微服务
  • 工具自注册模式:每个工具文件在模块级别调用 registry.register() 声明自己,不需要中心化的注册表
  • 上下文压缩可插拔ContextEngine 是抽象基类,可以替换实现
  • 状态存储用 SQLite + WAL:单文件数据库,支持多读者 + 单写者
  • MCP 双向支持:既能作为 MCP 客户端连接外部工具服务器,也能作为 MCP 服务端被 Cursor、VS Code 等接入

2.2 Profile 系统:多实例隔离

v0.6.0 引入了多实例 Profile,每个 Profile 拥有独立的:

  • 配置
  • 记忆库
  • 会话历史
  • Skill 集合
  • 工具权限

这意味着你可以在同一台机器上跑多个独立的 Agent 实例,互不干扰。比如一个用于工作项目,一个用于个人自动化。


三、Agent 循环深度拆解

run_agent.py 中的 AIAgent 类是整个系统的心脏。它管理对话流、工具执行、响应处理的全流程。

3.1 基本循环

Agent 循环的基本流程:

接收用户消息
→ 构建请求(含 system prompt + 记忆 + 上下文)
→ 调用 LLM
→ 解析响应
→ 如果包含工具调用则执行工具
→ 把工具结果加入上下文
→ 再次调用 LLM
→ 直到 LLM 不再请求工具调用
→ 返回最终响应

3.2 迭代预算控制

Agent 循环不是无限运行的。IterationBudget 类实现了一个线程安全的迭代计数器:

class IterationBudget:
    """线程安全的迭代预算控制"""
    def __init__(self, max_iterations: int):
        self._max = max_iterations
        self._count = 0
        self._lock = threading.RLock()
    
    def decrement(self) -> bool:
        """减少预算,返回 True 表示还有余量"""
        with self._lock:
            if self._count >= self._max:
                return False
            self._count += 1
            return True
    
    @property
    def exhausted(self) -> bool:
        with self._lock:
            return self._count >= self._max

预算配置:

  • 父 Agent 默认 90 次迭代
  • 子 Agent 默认 50 次迭代

这个设计解决了一个很实际的问题:Agent 有时候会陷入死循环,反复调用同一个工具。迭代预算到了就强制停止,防止 token 烧穿。

3.3 并行工具执行

_should_parallelize_tool_batch() 方法把工具分成三类:

分类策略典型工具
_NEVER_PARALLEL_TOOLS永远不并行clarify(需要用户交互)
_PARALLEL_SAFE_TOOLS只读安全,可并行web_search, read_file
_PATH_SCOPED_TOOLS路径隔离,条件并行read_file, write_file, patch

最大并发工作线程数硬编码为 8(_MAX_TOOL_WORKERS)。

这个设计说明团队对 Agent 的实际使用场景做过深入思考。比如 web_searchread_file 都是纯读操作,并行执行完全安全。但 write_filepatch 操作同一文件时就有冲突风险,所以需要路径隔离检查。

3.4 中断与转向机制

Agent 在执行过程中可能会收到用户的新指令。hermes-agent 的处理方式是 _interrupt_requested + _pending_steer 双标志位设计:

  • 不打断当前正在执行的工具:等工具批次完成后再处理
  • 注入转向指令:把用户的新需求作为 _pending_steer 注入下一轮对话

这个选择很务实。如果强行中断正在执行的 write_file,可能导致文件写了一半。等工具批次完成再转向,保证了操作的原子性。


四、工具系统:自注册 + Toolset 组合

4.1 ToolRegistry 自注册模式

工具系统的核心在 tools/registry.py。它采用了一个很优雅的自注册模式:

# tools/registry.py 中的 ToolEntry 结构(简化)
class ToolEntry:
    name: str           # 工具名
    toolset: str        # 所属工具集
    schema: dict        # JSON Schema
    handler: callable   # 处理函数
    check_fn: callable  # 检查函数
    requires_env: bool  # 是否需要执行环境
    is_async: bool      # 是否异步
    description: str    # 描述
    emoji: str          # 显示图标

每个工具文件在模块级别调用 registry.register(),声明自己的 schema、handler、toolset 归属。注册表用 threading.RLock() 保证线程安全,读取时做快照。

发现机制也很有意思:discover_builtin_tools() 通过 AST 分析检测哪些 .py 文件包含 registry.register() 调用,不需要手动维护工具列表。

4.2 Toolset 组合系统

toolsets.py 实现了一个组合式工具集系统。Toolset 之间可以互相 includes,支持递归解析。

核心工具列表 _HERMES_CORE_TOOLS 包含 63 个工具,所有平台共享:

类别工具举例
Webweb_search, web_extract
终端terminal, process
文件read_file, write_file, patch, search_files
视觉vision_analyze, image_generate
技能skills_list, skill_view, skill_manage
浏览器browser_navigate 等 11 个浏览器工具
规划todo, memory
代码执行execute_code, delegate_task
定时任务cronjob
其他send_message, Home Assistant, session_search, clarify

平台特定 toolset 如 hermes-clihermes-telegramhermes-discord 等有 20 多个。hermes-gateway 是所有平台工具的并集。

这种组合式设计的好处是:新接入一个平台时,只需要定义该平台特有的工具,然后 includes 核心工具集就行。比如新增一个飞书适配器,只需要定义飞书的消息发送、消息格式化等平台特有工具,然后引用 hermes-gateway 的基础工具集。

4.3 MCP 集成

hermes-agent 既支持作为 MCP 客户端连接外部 6,000+ 应用,也能作为 MCP 服务端被 Cursor、VS Code 等 IDE 接入。MCP 工具通过动态注册机制集成到 ToolRegistry 中。

# MCP 工具动态注册示例
async def register_mcp_tools(server_config: dict):
    """连接 MCP 服务器并注册其工具"""
    client = MCPClient(server_config)
    tools = await client.list_tools()
    for tool in tools:
        registry.register(
            name=f"mcp_{server_config['name']}_{tool.name}",
            toolset="mcp",
            schema=tool.input_schema,
            handler=client.call_tool,
            description=tool.description
        )

五、记忆与学习闭环:架构的核心差异

这是 hermes-agent 和其他 Agent 框架拉开差距的地方。

5.1 双文件记忆架构

agent/memory_manager.py 中的 MemoryManager 采用双 Provider 架构:一个内置 Provider(管理 MEMORY.mdUSER.md)+ 最多一个外部 Provider(如 Honcho)。

两个记忆文件的分工很明确:

  • MEMORY.md:Agent 的个人笔记。记录环境事实、项目约定、工具的使用技巧
  • USER.md:Agent 对用户的认知。记录偏好、沟通风格、期望
# 记忆注入示例
memory_context = f"""
<memory-context>
{memory_manager.get_snapshot()}
</memory-context>
"""
# 注入到 system prompt 中

记忆条目之间用 §(section sign)分隔。用一个不太常见的字符做分隔符,降低了和正文内容冲突的概率。

5.2 冻结快照模式

这个设计解决了一个很实际的问题:系统提示的稳定性

会话开始时,MemoryManager 把当前的记忆内容作为快照注入系统提示。之后整个会话期间,系统提示保持不变——中途写入的记忆只更新磁盘文件,不刷新系统提示。

# 冻结快照模式的实现思路
class MemoryManager:
    def start_session(self) -> str:
        """会话开始时获取记忆快照"""
        snapshot = self._load_memory_snapshot()
        # 快照注入后,整个会话期间不再刷新
        self._session_snapshot = snapshot
        return snapshot
    
    def update_memory(self, key: str, value: str):
        """中途更新只写磁盘,不刷新会话快照"""
        self._write_to_disk(key, value)
        # 不更新 self._session_snapshot

这样做的好处是保持 前缀缓存有效。如果每次记忆更新都刷新系统提示,KV cache 就会失效,每次请求都要重新计算前面所有 token 的 KV 对。冻结快照让前缀缓存命中率大大提高。

这是一个典型的读写分离思路:读路径(会话开始时的快照注入)和写路径(中途更新磁盘文件)完全解耦。代价是 Agent 在一个会话内对记忆的修改,需要等到下一个会话才能被读取。对于大多数使用场景来说,这个延迟是可以接受的。

5.3 Skill 系统

Skill 文件存放在 ~/.hermes/skills/ 目录下,是标准的 Markdown 文件。

Skill 的创建和改进流程:

  1. Agent 完成一个复杂任务后,自动识别是否包含可复用的模式
  2. 如果有,生成 Markdown 格式的 Skill 文件,包含触发条件、执行步骤、注意事项
  3. 使用 Skill 时如果发现问题(过时、报错),立即 patch 更新

PromptBuilder 还实现了两级缓存(LRU + 磁盘快照)来加速 Skill 索引的读取。

5.4 会话搜索:FTS5 全文检索

hermes_state.py 中的 SessionDB 基于 SQLite FTS5 实现跨所有会话消息的全文搜索。

搜索流程:

用户提问 → Agent 判断是否需要历史信息
→ 调用 session_search(FTS5 召回)
→ LLM 总结召回结果
→ 结合 MEMORY.md/USER.md 的快照
→ 形成完整的记忆上下文

这是一个多级检索的设计,不同类型的记忆走不同的通道。

# FTS5 搜索示例
class SessionDB:
    def search(self, query: str, limit: int = 10) -> list[dict]:
        """全文搜索历史会话"""
        sql = """
        SELECT messages.*, rank
        FROM messages
        JOIN messages_fts ON messages.id = messages_fts.rowid
        WHERE messages_fts MATCH ?
        ORDER BY rank
        LIMIT ?
        """
        return self.conn.execute(sql, (query, limit)).fetchall()

六、上下文管理:可插拔压缩引擎

6.1 ContextEngine 抽象架构

agent/context_engine.py 定义了抽象基类 ContextEngineagent/context_compressor.py 提供了默认实现 ContextCompressor

可插拔设计意味着你可以实现自己的上下文管理策略,比如基于 RAG 的检索增强、基于重要性评分的选择性保留等。

为什么要把上下文管理做成可插拔?因为不同使用场景对上下文的处理策略差异很大:

  • 编程场景:需要保留完整的代码变更历史
  • 对话场景:需要保留情感上下文
  • 数据分析场景:需要保留中间结果

一个通用的压缩策略很难同时满足这些需求。

6.2 压缩策略详解

ContextCompressor 的压缩策略:

参数默认值作用
threshold_percent0.75上下文使用率超过 75% 时触发压缩
protect_first_n3保护前 3 条消息(通常是 system prompt + 用户初始请求)
protect_last_n6保护后 6 条消息(最近的对话上下文)

压缩的具体流程:

  1. 保护头部和尾部:前 N 条和后 M 条消息不动
  2. 中间部分用辅助模型总结:不是简单截断,而是让另一个 LLM 生成结构化摘要
  3. 总结模板:包含已解决的问题、待处理的事项、活跃任务
  4. 工具输出裁剪:对工具返回的长文本做前置过滤,减少总结的 token 消耗
  5. 按比例分配 token 预算:中间部分的每段对话按比例分配总结的 token 预算

6.3 安全扫描

agent/prompt_builder.py_scan_context_content() 检测注入攻击。

上下文文件的优先级是 .hermes.md > AGENTS.md > CLAUDE.md > .cursorrules。安全扫描会检查这些文件中是否包含恶意指令,防止 prompt injection。


七、子 Agent 委托机制

tools/delegate_tool.py 实现了子 Agent 委托机制,允许主 Agent 生成独立的子 Agent 实例来并行处理任务。

7.1 隔离设计

子 Agent 的隔离做得比较彻底:

  • 无父历史:子 Agent 看不到父 Agent 的对话历史
  • 独立终端会话:子 Agent 有自己的终端环境
  • 工具限制DELEGATE_BLOCKED_TOOLS 列表禁止子 Agent 使用特定工具

被禁止的工具包括:

  • 递归委托(防止子 Agent 再生孙 Agent)
  • 用户交互类工具(子 Agent 不应该直接和用户对话)
  • 记忆写入工具(防止子 Agent 污染主 Agent 的记忆)

7.2 并行与深度控制

# 子 Agent 关键配置
MAX_DEPTH = 1       # 默认扁平,最多可配到 3
MAX_CONCURRENT = 3  # 最多 3 个并行子 Agent

并行执行通过 ThreadPoolExecutor 实现。多个子任务可以同时运行。

深度控制有两层含义:

  • 委托深度_delegate_depth 跟踪委托链的长度,默认 MAX_DEPTH=1 意味着子 Agent 不能再委托
  • 角色模式leaf(叶节点,只能执行)或 orchestrator(编排者,可以再次委托)

说实话,子 Agent 并发最多 3 个这个限制,社区里有人吐槽过。对于复杂的并行场景,3 个子 Agent 确实不够用。但从架构安全的角度看,限制并发数可以避免资源爆炸——每个 Agent 都有自己的终端会话和上下文,3 个就已经意味着 3 倍的资源消耗。


八、多平台网关架构

gateway/run.py 实现了一个统一的消息网关,单一进程处理所有平台消息。

8.1 支持的平台

网关支持 17+ 个平台:

Telegram、Discord、Slack、WhatsApp、Signal、Email、SMS、Home Assistant、Mattermost、Matrix、DingTalk(钉钉)、Feishu(飞书)、WeChat(微信)、WeCom(企业微信)、QQ、BlueBubbles、Webhook。

8.2 Agent 缓存策略

网关用 LRU 缓存 + 空闲 TTL 淘汰来管理 Agent 实例:

  • 缓存容量:128 个 Agent 实例
  • 空闲淘汰:1 小时无活动自动清理

这个设计是为了平衡内存使用和响应速度。Agent 实例占用内存不小(需要维护对话历史、工具状态等),但每次创建新实例开销也大。LRU + TTL 是一个合理的折中。

8.3 跨平台会话

同一个 Agent 可以同时服务多个平台——你可以在 Telegram 上开始一个对话,然后在 Discord 上继续。不同平台的消息格式差异(Telegram 的 Markdown V2、Discord 的 Embed、Slack 的 Block Kit)在网关层统一处理,Agent 核心不需要关心这些细节。


九、状态持久化:SQLite + FTS5

9.1 技术选型

  • SQLite + WAL 模式:支持多读者 + 单写者,适合网关多平台并发场景
  • FTS5 全文搜索:跨所有会话消息的快速文本搜索
  • Schema 版本控制:当前 v8,自动迁移

9.2 写竞争处理

SQLite 在高并发写入时容易出现锁竞争。SessionDB 的处理方式是随机抖动重试:15 次重试,每次等待 20-150ms 的随机时间。这个随机抖动可以避免多个写请求同时重试导致的护航效应(convoy effect)。

# 写竞争处理示例
async def _write_with_retry(self, sql: str, params: tuple) -> None:
    """带随机抖动的写重试"""
    for attempt in range(15):
        try:
            self.conn.execute(sql, params)
            self.conn.commit()
            return
        except sqlite3.OperationalError as e:
            if "locked" in str(e):
                delay = random.uniform(0.02, 0.15)
                await asyncio.sleep(delay)
            else:
                raise
    raise RuntimeError("SQLite write failed after 15 retries")

9.3 本地优先

所有数据存储在本地 ~/.hermes/ 目录,没有云端同步。搬家的时候拷贝目录就行。这种本地优先的设计在隐私敏感场景下是个加分项——你的对话历史和 Agent 记忆不会上传到任何云服务。

从选型上看,SQLite 在这里是一个恰到好处的选择。PostgreSQL 太重了,需要单独的数据库服务;纯文件存储又缺乏查询能力。SQLite + FTS5 提供了全文搜索、事务支持、WAL 并发,同时保持单文件部署的简洁性。


十、性能与部署

10.1 资源占用

  • 不跑本地 LLM 的情况下,Agent 进程本身占用不到 500MB
  • 官方宣称可以在 $5/月的 VPS 上运行
  • 这个数据从架构上看是合理的——SQLite 很轻,Agent 实例的主要开销在对话历史的内存占用上

10.2 模型支持

支持的模型后端:

  • Nous Portal
  • OpenRouter(200+ 模型)
  • NVIDIA NIM
  • OpenAI GPT-5.x
  • Anthropic Claude Opus 4.6
  • Google Gemini 3.1 Pro
  • DeepSeek
  • Hugging Face(20+ 开放模型)
  • Ollama(本地模型)

切换模型只需一条 hermes model 命令,不需要改代码。

10.3 执行环境

支持多种执行环境后端:

  • local(本地)
  • Docker
  • SSH
  • Daytona
  • Singularity
  • Modal

十一、架构评价:做得好的与需要关注的

做得好的地方

  1. 闭环学习是原生能力:从 PromptBuilder 的 MEMORY_GUIDANCE 到 MemoryManager 的双文件架构,再到 SessionDB 的 FTS5 搜索,整套学习闭环在架构层面是贯通的
  2. 工具自注册 + Toolset 组合:很灵活,新接入平台只需定义差异部分
  3. 上下文压缩的可插拔架构:给未来的优化留下了空间
  4. 迭代预算 + 中断机制:体现了对 Agent 实际运行问题的深入理解
  5. Profile 系统:多实例隔离,实用性很强

需要关注的局限

  1. 子 Agent 并发上限 3 个:复杂并行场景受限
  2. 项目迭代节奏很快:不到 3 周发布了 4 个大版本,稳定性需要观察
  3. Windows 原生不支持:必须通过 WSL2
  4. 冻结快照的代价:一个会话内对记忆的修改,需要等到下一个会话才能被读取

十二、与其他框架的对比

维度hermes-agentLangChainCrewAIAutoGen
核心理念自进化 Agent工具编排多角色协作多 Agent 对话
记忆系统三层记忆 + FTS5基础记忆有限记忆对话历史
学习能力闭环自学习有限
平台支持17+ 平台API 调用API 调用API 调用
部署复杂度低(单进程)
Star 数130K100K+30K+40K+

十三、总结与展望

hermes-agent 最大的贡献是把 Mitchell Hashimoto 提出的 Harness Engineering 五大组件(指令层、约束层、反馈层、记忆层、编排层)做成了产品级的内建能力。这在 Agent 框架领域是比较少见的。

从架构设计上看,hermes-agent 证明了一件事:AI Agent 可以不只是「工具调用器」,它可以是一个真正的「长期伙伴」

个人记忆、Skill 自进化、闭环学习——这些能力让 Agent 从「每次对话都失忆」变成了「越用越懂你」。这不是炫技,而是解决了一个真实的用户痛点。

如果你在做 Agent 相关的架构设计,hermes-agent 的闭环学习和上下文管理部分值得细看。开源生态里能做到这个完成度的 Agent 框架,目前确实不多。

未来值得关注的方向:

  • Skill 生态的标准化:hermes-agent 已经内置了 Skill 系统,但跨框架的 Skill 复用还没有标准
  • 记忆的跨会话一致性:冻结快照解决了前缀缓存问题,但也带来了延迟,如何平衡是下一个课题
  • 多 Agent 协作的扩展:当前 3 个子 Agent 的限制可能需要放开

项目地址:https://github.com/NousResearch/hermes-agent

技术栈:Python 3.11+ | SQLite + FTS5 | MIT License

推荐文章

全栈利器 H3 框架来了!
2025-07-07 17:48:01 +0800 CST
Rust 并发执行异步操作
2024-11-19 08:16:42 +0800 CST
Go 单元测试
2024-11-18 19:21:56 +0800 CST
程序员茄子在线接单