编程 Hermes Agent 深度拆解:当AI Agent学会「记住」——从三层记忆架构、自进化闭环到多平台网关的工程全貌(2026)

2026-07-19 10:44:18 +0800 CST views 13

Hermes Agent 深度拆解:当AI Agent学会「记住」——从三层记忆架构、自进化闭环到多平台网关的工程全貌(2026)

写在前面

2026年的开源AI Agent领域,正在经历一场从「调用模型」到「管理记忆」的关键范式转移。

过去两年,大多数AI Agent框架的核心叙事是「prompt engineering」——怎么写好system prompt,怎么设计tool calling,怎么拼接ReAct循环。但当我们真正把这些框架跑起来,用到第二周、第三周,就会发现一个根本性的问题:它们不记得任何事。每次对话都是白板,每次任务都要重新解释上下文,所谓的「Agent」不过是披着自主外衣的「一次性API调用器」。

Hermes Agent的出现,正是对这个问题的一次正面回应。由知名AI研究实验室Nous Research于2026年2月开源的这个项目,GitHub Stars在发布后短短数月便突破21.6万,成为当年增长最快的开源AI项目之一。但star数字背后的真正创新,不是它接了多少模型,而是它解决了一个更根本的问题:怎样让一个AI Agent真正拥有持续学习和进化的能力

本文将从工程视角,对Hermes Agent的核心架构进行深度拆解:三层记忆系统是如何协同工作的?自进化闭环的五环节是怎么跑起来的?多平台网关和Skill系统如何配合实现「越用越聪明」?以及——作为程序员,我们能从它的设计里学到什么。


一、背景:从「工具调用器」到「有记忆的伙伴」

1.1 AI Agent框架的现状与瓶颈

在深入Hermes Agent之前,我们先梳理一下2026年AI Agent框架的整体格局。

当前主流的Agent框架可以分为三类:

第一类:工具调用型(Tool Calling)

以LangChain、AutoGPT为代表,核心架构是「LLM + 工具列表 + ReAct循环」。模型负责决策何时调用工具,工具列表提供外部能力。这类框架的优点是简单直接,缺点是完全没有持久化能力——每次对话都是独立上下文,无法积累经验。

# 典型的ReAct循环
while not task_complete:
    thought = llm.think(task, history)
    action = llm.decide_action(thought, tools)
    result = execute(action)
    history.append((thought, action, result))

这种模式的问题在于:当用户第二天回来时,Agent已经完全不记得昨天做过什么、卡在哪里、哪些方案行不通。

第二类:多Agent协作型(Multi-Agent)

以CrewAI、AutoGen为代表,通过多个专门化的Agent协作完成任务。这类框架解决了「分工」问题,但同样没有解决「记忆」问题。每个Agent的上下文窗口是有限的,历史信息必须靠外部存储。

第三类:持久化记忆型(Memory-Aware)

这就是Hermes Agent所在的赛道。它的核心假设是:一个真正有用的AI Agent,必须能够跨会话积累知识,从经验中学习,并将学到的内容固化为可复用的技能。

Hermes Agent的官方slogan是「The agent that grows with you」——与你一起成长的Agent。这个定位直接点出了它的核心价值:不是一次性执行任务的工具,而是能够持续学习、变得越来越懂你的数字伙伴

1.2 Nous Research是谁?

Hermes Agent出自Nous Research之手,这家实验室在AI开源社区有着良好声誉。他们此前开源过Hermes系列大模型(Hermes-3-70B、Nominal-8B等)以及Psyche系列推理模型,在LoRA微调社区有大量使用者。Nous Research的特点是:重视开源、重视实际部署体验、不搞过度营销。

这与某些「先放论文再看代码」的实验室形成鲜明对比。Hermes Agent从一开始就是完整可运行的项目,MIT协议,代码质量较高,文档相对完善。

1.3 数字对比:增长最快的Agent项目

  • GitHub Stars: 21.6万+(持续增长中)
  • Commits: 超过7,690次
  • Contributors: 895人
  • 技术栈: Python 88.5% + TypeScript 8.1%
  • 内置Skills: 118个bundled + 12个optional
  • 支持的模型Provider: 33个(OpenAI、Anthropic、OpenRouter、千问、GLM、Kimi等)
  • 支持的平台: Telegram、Discord、Slack、WhatsApp、Signal、Email等12+平台

这些数字背后的工程量是巨大的:一个支持多平台、多模型、具备自进化能力的Agent系统,远不是「给LLM套个壳」那么简单。


二、核心架构:三层记忆系统详解

Hermes Agent最核心的创新,在于它的三层记忆架构(Three-Layer Memory System)。这不是简单的「把对话历史存下来」,而是一套经过精心设计的层次化信息管理机制,每一层解决不同的问题,对应认知科学中不同类型的记忆。

2.1 为什么需要三层?单层记忆的问题

在讨论三层架构之前,我们先理解单层记忆的局限性。

方案一:把所有历史都塞进上下文(Naive Context Injection)

这是最简单的方案,但问题显而易见:

  • API成本爆炸式增长:每轮对话都在往context里追加历史,token费用翻倍
  • 模型性能下降:上下文越长,模型的「近期关注」能力越弱
  • 检索效率为零:想找某次特定对话,只能顺序扫描

方案二:只存最近N条消息(Fixed Window)

解决了成本问题,但丢失了长期知识。上个月解决过的bug,今天又遇到了,还是要从零分析。

方案三:外部向量数据库(Vector Retrieval)

这是当前的主流方案,ChromaDB、Pinecone、Milvus都可以用。但向量检索的问题是:相似度匹配不等于精确召回。「上次那个关于数据库优化的讨论」可能因为措辞不同而检索不到。

Hermes Agent的三层架构,正是对以上三种方案局限性的逐一破解。

2.2 Layer 1:内置记忆(Built-in Memory)

最底层,也是每次对话必定激活的记忆层。

两个核心文件

~/.hermes/memories/
├── MEMORY.md      # Agent个人笔记(~2,200字符上限)
└── USER.md        # 用户画像(~1,375字符上限)

这两个Markdown文件的特点是:始终加载,始终可用。在每次会话开始时,它们会被注入到system prompt的最前面,模型可以立即访问。

<!-- MEMORY.md 示例 -->
# MEMORY.md - Agent Personal Notes

## 环境配置
- 工作目录:~/projects/myapp
- 常用语言:Python 3.11, TypeScript
- 包管理器:uv(首选), npm

## 使用约定
- 重大操作前必须确认
- 数据库修改使用trash替代rm
- commit message遵循Conventional Commits规范

## 技术发现
- 发现了uv比pip快3-5倍的包安装速度
- PostGIS在处理地理空间查询时比纯SQL效率高很多
<!-- USER.md 示例 -->
# USER.md - About Your Human

- **Name:** Alex
- **What to call them:** Alex
- **Timezone:** Asia/Shanghai
- **Preferred Language:** 中文

## Context
- 全栈工程师,主要做Web开发和数据管道
- 喜欢简洁直接的代码风格
- 对性能优化有强烈兴趣
- 在Mac上开发,Linux服务器部署

为什么是字符上限,而不是token上限?

这是一个非常精明的设计决策。用字符数而非token数作为上限,意味着:

  • 开发者可以直观地估算文件大小(不用考虑模型的tokenize规则)
  • 在不同模型间切换时,上限行为是一致的
  • 当模型换用不同的tokenizer时,不会突然「爆仓」

为什么选择Markdown格式?

Markdown是程序员最熟悉的纯文本格式,有几个关键优势:

  • 可以用版本控制(git)管理记忆的演化历史
  • 模型可以直接读取和修改,不需要序列化/反序列化
  • 可以轻松实现diff对比,看Agent的记忆发生了什么变化
  • 可以通过简单的文本操作实现记忆的「压缩」和「遗忘」

触发条件:所有会话都会写入这两个文件,但写入时机经过精心设计——不是在每次工具调用后写入,而是在会话结束、任务完成后才写入。这避免了「边工作边改记忆」导致的上下文混乱。

2.3 Layer 2:外部记忆提供者(External Memory Providers)

第二层是可插拔的外部记忆后端,这是Hermes Agent区别于其他框架的核心差异化设计。

内置记忆(Layer 1)的问题是容量有限,且只能存储「事实性知识」。当我们需要:

  • 大规模语义搜索
  • 跨用户的团队记忆共享
  • 结构化的知识图谱
  • 复杂的时间序列查询

就需要外部记忆系统来承接。Hermes Agent设计了统一的Provider接口,目前支持8个外部后端:

Provider特点适用场景
HonchoNous Research自研,专为Agent优化默认选择,个人使用
Holographic向量+图数据库混合复杂关系推理
Mem0专注于AI Agent记忆的服务需要API服务架构
Hindsight基于对话的自动总结长程任务追踪
OpenViking开源替代,私有部署数据主权要求
RetainDB结构化SQL存储需要精确查询
ByteRover字节跳动内部开源版大规模团队
Supermemory链接收集+记忆融合信息收集型任务

关键设计约束单Provider激活原则

虽然可以安装多个Provider,但同时只允许激活一个。这个设计避免了记忆冲突(同一信息在两个Provider里不一致)和性能浪费(不需要同时写两份记忆)。

# 典型的Provider配置
# ~/.hermes/config.toml
[memory]
provider = "honcho"  # 当前激活的Provider

[memory.honcho]
storage_path = "~/.hermes/honcho_db"
enable_semantic_search = true

这个Provider模式的设计非常值得学习:它用接口抽象替代了具体实现,让记忆系统可以随着用户需求升级而迁移数据,而不需要重写业务代码。

第三层是按需触发的历史会话检索,基于SQLite + FTS5全文索引实现。

这是解决「历史回溯」问题的终极方案:当用户问「上次我们讨论的那个数据库方案是什么来着」,Agent需要能够精确找到那次对话,而不是靠模糊的向量相似度碰运气。

FTS5全文索引的工作原理

-- 会话历史表
CREATE TABLE sessions (
    id INTEGER PRIMARY KEY,
    session_id TEXT,
    created_at TIMESTAMP,
    summary TEXT
);

-- FTS5虚拟表
CREATE VIRTUAL TABLE sessions_fts USING fts5(
    session_id,
    summary,
    content='sessions',
    content_rowid='id'
);

-- 精确检索示例
SELECT session_id, summary, created_at
FROM sessions s
JOIN sessions_fts fts ON s.id = fts.rowid
WHERE sessions_fts MATCH 'docker postgresql 性能优化'
ORDER BY rank
LIMIT 5;

这个检索粒度可以精确到「某次会话中的某个工具调用」,而不仅仅是「某次会话的主题」。

三层协同工作流

用户提问
    ↓
Layer 1: 读取 MEMORY.md + USER.md → 获取Agent自身知识 + 用户偏好
    ↓
Layer 2: 查询外部Provider → 获取长期语义记忆
    ↓
Layer 3: FTS5检索 → 找到相关的历史会话
    ↓
三者合并 → 构建完整上下文 → LLM推理

这个协同流程的设计哲学是:每一层解决不同粒度的问题,层与层之间通过清晰的接口隔离,而不是一股脑儿地塞给模型。


三、自进化闭环:五环节实现「越用越聪明」

Hermes Agent的灵魂,不仅在于它能「记住」,更在于它能「学习」。自进化闭环(Self-Improving Loop)是它的第二项核心创新,也是最接近「通用人工智能」特性的部分。

3.1 闭环的五环节

自进化闭环包含五个环节,形成一个持续运转的飞轮:

完成任务
    ↓
策划记忆(Memory Planning)
    ↓
创建Skill(Skill Creation)
    ↓
Skill自改进(Skill Refinement)
    ↓
FTS5召回(Session Search)
    ↓
用户建模(User Profiling)
    ↓
(下一轮任务 → 回到策划记忆)

环节一:策划记忆(Memory Planning)

触发时机:每次任务完成后。

系统会评估:「这次任务中,哪些信息值得长期记住?」不是所有对话内容都值得记忆——Agent需要学会「判断什么重要」。

判断标准包括:

  • 任务是否涉及多个工具调用(5次以上触发技能提取)
  • 是否有异常处理逻辑值得记录
  • 是否发现了新的环境配置或依赖关系
  • 用户是否有特别的偏好反馈
# 伪代码:记忆策划逻辑
def should_remember(task_result):
    if task_result.tool_call_count > 5:
        return True  # 复杂任务,值得提取模式
    if task_result.error_count > 0:
        return True  # 错误处理,值得记录
    if "preference" in task_result.user_feedback:
        return True  # 偏好反馈,需要更新USER.md
    if task_result.novel_discovery:
        return True  # 新发现,值得加入MEMORY.md
    return False

这个评估过程本身由LLM驱动——让模型自己判断什么是重要的,这也是为什么它能适应不同用户的不同关注点。

环节二:创建Skill(Skill Creation)

当策划记忆判断某类任务「值得提取模式」时,Agent会自动从这次经验中生成一个Skill文件。

Skill文件是Hermes Agent知识复用的基本单元,采用Markdown格式,包含:

# Skill: docker-postgres-performance-check
description: |
  Docker + PostgreSQL 性能检查与优化诊断流程。
  适用于数据库响应慢、连接池耗尽等场景。
triggers:
  - "数据库慢"
  - "postgres性能"
  - "连接池"
  - "docker postgres"
steps:
  - name: check_slow_queries
    command: |
      docker exec <container> psql -U postgres -c \
        "SELECT pid, duration, query FROM pg_stat_activity WHERE state='active' AND duration > interval '5 seconds';"
    description: 检查超过5秒的慢查询
  
  - name: check_connection_count
    command: |
      docker exec <container> psql -U postgres -c \
        "SELECT count(*) FROM pg_stat_activity;"
    description: 检查当前连接数
  
  - name: check_buffer_hit
    command: |
      docker exec <container> psql -U postgres -c \
        "SELECT sum(heap_blks_read) as read, sum(heap_blks_hit) as hit, \
        round(100 * sum(heap_blks_hit) / sum(heap_blks_hit + heap_blks_read), 2) as ratio \
        FROM pg_statio_user_tables;"
    description: 检查缓存命中率
context:
  - PostgreSQL Docker容器需要开启pg_stat_statements扩展
  - 连接数超过max_connections的80%需要告警
examples:
  - input: "docker里的postgres跑得很慢"
    output: 执行上述三个检查命令,给出分析报告

这个Skill文件的价值在于:它把一次具体的经验,转化成了可复用的模板。下次遇到类似问题,Agent不需要重新摸索,可以直接调用这个Skill。

环节三:Skill自改进(Skill Refinement)

这是整个闭环中最「智能」的部分。

当一个现有Skill在某次执行中失败时(用户反馈结果不好,或者执行过程中出错),系统不会简单地跳过这个Skill,而是会分析失败原因,并更新Skill文件。

# 伪代码:Skill自改进逻辑
def refine_skill(failed_skill, task_result):
    analysis = llm.analyze(
        f"Skill '{failed_skill.name}' 失败了。"
        f"失败原因:{task_result.error_message}。"
        f"原始Skill内容:{failed_skill.content}。"
        f"请分析失败原因并提出修改建议。"
    )
    
    # 分析结果可能包括:
    # 1. 更新trigger条件(更精确地匹配场景)
    # 2. 修改step顺序或参数
    # 3. 添加新的错误处理分支
    # 4. 更新context信息(边界条件)
    
    if analysis.should_update:
        skill.update(analysis.suggested_changes)
        skill.add_learning_note(analysis.reasoning)

这种机制的效果是:Agent不仅「做事」,而且「反思做事的方式」。一个被使用了100次的Skill,会比初次生成时更加健壮和精准。

环节四:FTS5召回(Session Search)

当Agent遇到新任务时,会先搜索历史会话,找找看之前是否处理过类似的问题。

这个召回机制是「按需」的,不是每次都搜——只有当当前任务触发了某种「熟悉感」时才会启动。比如用户提到了一个项目名、一个bug id、一个文件路径,Agent会自动检查历史中是否有相关内容。

# 召回逻辑示例
async def retrieve_relevant_context(current_task):
    # 提取当前任务中的关键实体
    entities = extract_entities(current_task)
    
    # 逐个检索
    for entity in entities:
        results = await session_search(entity)
        if results:
            return results  # 找到匹配,返回上下文
    
    # 没有直接匹配,尝试语义搜索
    semantic_results = await semantic_search(current_task)
    return semantic_results

环节五:用户建模(User Profiling)

最后一环是用户偏好的持续建模。

Hermes Agent会从对话中学习用户的:

  • 技术栈和经验水平(据此调整解释深度)
  • 沟通风格(简洁型还是详细型)
  • 决策偏好(快速试错 vs 充分论证)
  • 工作节奏(活跃时段、任务类型模式)

这些信息最终写入USER.md,形成Agent对用户的「心智模型」。当新会话开始时,这个模型会指导Agent的行为策略——比如面对专家用户会直接给方案,面对新手用户会给出更多解释。

3.2 认知科学的对应关系

如果我们把自进化闭环与认知科学中的记忆类型对应起来,会发现设计者显然借鉴了认知科学的框架:

认知科学概念Hermes Agent实现功能
程序性记忆(Procedural)Skill文件怎么做事的流程知识
语义记忆(Semantic)MEMORY.md事实性、概念性知识
情景记忆(Episodic)SQLite会话历史具体经历的时间序列
工作记忆(Working)当前会话上下文当前任务所需的即时信息

这种对应关系不是偶然的——设计者显然在构建一个仿生记忆系统,用计算的方式实现人类记忆的层级化特性。


四、Skill系统:知识复用的工程实践

4.1 Skill的分类

Hermes Agent内置了118个bundled Skills和12个optional Skills,覆盖了日常开发中最常见的场景:

Shell与系统管理类

  • 文件操作、进程管理、日志分析
  • Docker容器管理、Kubernetes操作
  • Git高级操作(rebase、bisect、stash管理)

编程开发类

  • 代码审查(Code Review)
  • 测试用例生成
  • 调试辅助(性能分析、内存泄漏检测)
  • API文档生成

数据处理类

  • SQL查询优化
  • CSV/JSON数据处理
  • 数据库迁移
  • 数据可视化脚本生成

AI与模型类

  • Prompt优化
  • 模型输出评估
  • RAG pipeline构建
  • Fine-tuning数据集准备

4.2 Skill的加载与调用

当Agent处理用户请求时,会经历一个「Skill匹配」的过程:

# Skill匹配逻辑
async def find_relevant_skills(user_message):
    # 1. 精确触发词匹配
    exact_matches = []
    for skill in all_skills:
        for trigger in skill.triggers:
            if trigger.lower() in user_message.lower():
                exact_matches.append(skill)
                break
    
    # 2. 如果没有精确匹配,用语义搜索
    if not exact_matches:
        semantic_matches = await semantic_skill_search(user_message)
        return semantic_matches
    
    # 3. 返回匹配结果(按相关性排序)
    return rank_by_relevance(exact_matches, user_message)

4.3 Skill的编写规范

Skill的编写质量直接决定了Agent能否正确复用。这里有几个关键设计原则:

描述要具体,不要泛泛而谈

# ❌ 不好的描述
description: "数据库优化相关操作"

# ✅ 好的描述
description: |
  PostgreSQL慢查询分析与优化流程。
  适用场景:查询响应超过1秒、EXPLAIN ANALYZE显示Seq Scan过多、
  索引失效或缺失、连接池耗尽等问题。

Step要有原子性
每个step应该是独立的、可单独执行的。一个step不要做太多事情,这样当某个step失败时,可以精准定位问题。

examples是关键的质量门控
examples字段用具体输入输出对来验证Skill的有效性。在Skill自动生成后,系统会运行这些examples,确保输出符合预期。

4.4 技能库的位置与管理

~/.hermes/skills/           # 用户自定义技能
├── docker-postgres-performance.md
├── api-error-debugger.md
└── git-conflict-resolver.md

~/.hermes/bundled_skills/   # 内置技能(不要直接修改)
├── shell/
├── docker/
├── git/
└── ...

这是一个非常实用的分离设计:内置Skill在更新版本时会覆盖,用户自定义Skill存放在独立目录,不会被覆盖。这种「可扩展的内核」模式,是优秀框架的共同特征。


五、多平台网关:消息路由的架构设计

5.1 网关的整体架构

Hermes Agent的多平台支持是通过一个统一的**消息网关(Gateway)**实现的,而不是为每个平台单独写适配代码。

用户消息(来自任意平台)
    ↓
Gateway Router(统一路由层)
    ↓
消息格式化(统一格式转换)
    ↓
Agent Core(核心处理逻辑)
    ↓
响应格式化(适配目标平台)
    ↓
各平台适配器(Platform Adapters)
    ↓
平台原生消息(Slack/Discord/Telegram...)

这个设计的核心思想是:把「跨平台」的问题,从「每个平台各写一套」,变成「写一个核心 + 多个轻量适配器」

5.2 支持的平台

目前支持的平台包括:

即时通讯类

  • Telegram(最推荐,API友好)
  • Discord(适合社区Bot场景)
  • Slack(企业场景)
  • WhatsApp
  • Signal

邮件与通知类

  • Email(SMTP/IMAP集成)
  • 系统通知(OS原生通知)

命令行与开发类

  • CLI(终端直接交互)
  • Web Dashboard(浏览器界面)

5.3 平台适配器的实现

每个平台适配器只需要处理两件事:

  1. 接收:把平台原生的消息格式 → 统一格式
  2. 发送:把统一响应格式 → 平台原生格式
# 统一消息格式
class UnifiedMessage:
    sender: str           # 发送者标识
    content: str          # 文本内容
    attachments: list     # 附件列表
    timestamp: datetime   # 时间戳
    channel: str          # 频道/群组
    platform: str         # 来源平台
    
# Telegram适配器示例
class TelegramAdapter:
    def receive(self, update) -> UnifiedMessage:
        return UnifiedMessage(
            sender=update.message.from_user.id,
            content=update.message.text,
            attachments=self.extract_attachments(update.message),
            timestamp=update.message.date,
            channel=str(update.message.chat.id),
            platform="telegram"
        )
    
    def send(self, unified: UnifiedMessage, chat_id: str):
        self.bot.send_message(
            chat_id=chat_id,
            text=unified.content,
            parse_mode="Markdown"
        )

5.4 多平台同时在线

Hermes Agent支持同时连接多个平台,并且能够根据不同平台的特性调整交互策略:

  • Telegram:支持Markdown格式,可以发送图片、文件、按钮
  • Discord:支持Embed、Slash Commands
  • Slack:支持Block Kit UI、线程回复
  • Email:纯文本优先,附件处理为独立消息

这种「同一Agent,不同面孔」的体验,是它区别于单平台Bot的关键。


六、33个模型Provider:多模型架构

6.1 Provider设计

Hermes Agent支持33个模型Provider,这是通过一个统一的Provider接口实现的:

class ModelProvider(ABC):
    @abstractmethod
    async def complete(self, messages: list[Message]) -> str:
        """给定对话历史,返回模型补全"""
        pass
    
    @abstractmethod
    async def count_tokens(self, text: str) -> int:
        """计算token数量(用于上下文管理)"""
        pass
    
    @abstractmethod
    def get_max_context(self) -> int:
        """返回该模型的最大上下文长度"""
        pass

这个接口设计非常干净,隐藏了各Provider之间的差异(API格式、认证方式、错误处理等),让核心逻辑可以完全不关心「用的是什么模型」。

6.2 主流Provider一览

Provider说明
OpenAIGPT-4o、GPT-4o-mini等
AnthropicClaude 3.5 Sonnet、Claude 3 Opus
GoogleGemini 1.5/2.0系列
OpenRouter聚合200+模型,统一API
千问(Qwen)Qwen3-Max、Qwen3-72B等
智谱(GLM)GLM-5.2(开源榜第一)
KimiK2.5、K3(2.8万亿参数)
DeepSeekDeepSeek-V3
GrokGrok-3
NousNous原生模型系列

6.3 模型选择的策略

有了这么多Provider,怎么选择?这涉及到一个在实际部署中非常重要的问题:性能与成本的平衡

# 模型选择策略
class ModelSelector:
    def select(self, task: Task) -> ModelProvider:
        if task.type == "quick_clarification":
            return self.fast_cheap  # 简单问答,用便宜的模型
        elif task.type == "code_generation":
            return self.code_specialist  # 写代码,用擅长代码的模型
        elif task.type == "complex_reasoning":
            return self.reasoning_powerhouse  # 复杂推理,用最强模型
        elif task.cost_budget == "low":
            return self.open_source  # 预算有限,用开源模型
        
        return self.default  # 默认选择

这个策略模式让不同任务可以使用最合适的模型,而不是「一刀切」地全部用最强模型(成本高)或全部用最便宜模型(效果差)。


七、深度代码实战:构建一个自定义记忆Provider

为了更好地理解Hermes Agent的架构,我们来实现一个自定义的外部记忆Provider。这个例子展示如何在不修改核心代码的情况下扩展记忆系统。

7.1 Provider接口定义

# providers/base.py
from abc import ABC, abstractmethod
from typing import Optional
from dataclasses import dataclass

@dataclass
class MemoryEntry:
    id: str
    content: str
    created_at: float
    tags: list[str]
    access_count: int = 0

class MemoryProvider(ABC):
    """外部记忆Provider的统一接口"""
    
    @abstractmethod
    async def store(self, entry: MemoryEntry) -> bool:
        """存储一条记忆"""
        pass
    
    @abstractmethod
    async def retrieve(
        self, 
        query: str, 
        top_k: int = 5
    ) -> list[MemoryEntry]:
        """基于文本检索记忆"""
        pass
    
    @abstractmethod
    async def update_access(self, entry_id: str) -> None:
        """更新访问计数(用于热度排序)"""
        pass
    
    @abstractmethod
    async def compress(self, max_entries: int) -> int:
        """压缩记忆,保留最重要的N条"""
        pass

7.2 实现一个基于SQLite的简单Provider

# providers/simple_sqlite_provider.py
import sqlite3
import json
from pathlib import Path
from typing import Optional
from providers.base import MemoryProvider, MemoryEntry

class SimpleSQLiteProvider(MemoryProvider):
    """基于SQLite的简单记忆Provider"""
    
    def __init__(self, db_path: str = "~/.hermes/memories.db"):
        self.db_path = Path(db_path).expanduser()
        self._init_db()
    
    def _init_db(self):
        self.conn = sqlite3.connect(str(self.db_path))
        self.conn.execute("""
            CREATE TABLE IF NOT EXISTS memories (
                id TEXT PRIMARY KEY,
                content TEXT NOT NULL,
                created_at REAL NOT NULL,
                tags TEXT,
                access_count INTEGER DEFAULT 0
            )
        """)
        # 创建全文搜索索引
        self.conn.execute("""
            CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts 
            USING fts5(id, content, content='memories', content_rowid='rowid')
        """)
        self.conn.commit()
    
    async def store(self, entry: MemoryEntry) -> bool:
        try:
            self.conn.execute("""
                INSERT OR REPLACE INTO memories 
                (id, content, created_at, tags, access_count)
                VALUES (?, ?, ?, ?, ?)
            """, (
                entry.id,
                entry.content,
                entry.created_at,
                json.dumps(entry.tags),
                entry.access_count
            ))
            # 同步更新FTS索引
            self.conn.execute("""
                INSERT INTO memories_fts(id, content)
                VALUES (?, ?)
            """, (entry.id, entry.content))
            self.conn.commit()
            return True
        except Exception as e:
            print(f"存储记忆失败: {e}")
            return False
    
    async def retrieve(
        self, 
        query: str, 
        top_k: int = 5
    ) -> list[MemoryEntry]:
        cursor = self.conn.execute("""
            SELECT m.id, m.content, m.created_at, m.tags, m.access_count,
                   bm25(memories_fts) as rank
            FROM memories m
            JOIN memories_fts fts ON m.id = fts.id
            WHERE memories_fts MATCH ?
            ORDER BY rank
            LIMIT ?
        """, (query, top_k))
        
        results = []
        for row in cursor:
            results.append(MemoryEntry(
                id=row[0],
                content=row[1],
                created_at=row[2],
                tags=json.loads(row[3]) if row[3] else [],
                access_count=row[4]
            ))
        return results
    
    async def update_access(self, entry_id: str) -> None:
        self.conn.execute("""
            UPDATE memories SET access_count = access_count + 1
            WHERE id = ?
        """, (entry_id,))
        self.conn.commit()
    
    async def compress(self, max_entries: int) -> int:
        """基于访问频率和新鲜度的混合压缩策略"""
        cursor = self.conn.execute("""
            SELECT id FROM memories
            ORDER BY 
                access_count * 0.3 + 
                (created_at / 1e9) * 0.7
            DESC
            LIMIT -1 OFFSET ?
        """, (max_entries,))
        
        to_delete = [row[0] for row in cursor]
        for entry_id in to_delete:
            self.conn.execute("DELETE FROM memories WHERE id = ?", (entry_id,))
        self.conn.commit()
        return len(to_delete)

7.3 在配置中启用自定义Provider

# ~/.hermes/config.toml
[memory]
provider = "custom_sqlite"  # 使用我们的自定义Provider

[memory.custom_sqlite]
db_path = "~/.hermes/my_memories.db"
max_entries = 500  # 最多保留500条记忆

这个例子展示了Hermes Agent Provider模式的力量:在不修改一行核心代码的情况下,我们用约100行代码实现了一个完全自定义的记忆系统。如果你需要把记忆存储在PostgreSQL中、MongoDB中,甚至云对象存储中,只需要实现同一个接口即可。


八、生产级部署:从安装到调优

8.1 安装流程

Hermes Agent的安装设计得非常友好,一行命令搞定:

curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash

安装脚本会自动处理:

  • Python 3.11+ 环境(如果没有)
  • uv包管理器(Nous推荐的新一代Python包管理工具)
  • Node.js环境(用于Web Dashboard)
  • 项目依赖的数据库、API客户端等

安装完成后,初始化配置:

hermes setup

这个命令会引导你:

  1. 选择默认模型Provider
  2. 配置API Key
  3. 选择初始启用的平台(Telegram/Discord等)
  4. 设置工作目录和记忆存储路径

8.2 Docker部署(推荐生产环境)

# Dockerfile
FROM python:3.11-slim

WORKDIR /app
COPY . /app

# 安装uv(比pip快5倍)
RUN pip install uv

# 安装依赖
RUN uv sync --frozen

# 非root用户运行
RUN useradd -m hermes
USER hermes

CMD ["python", "-m", "hermes_agent", "start"]
# docker-compose.yml
services:
  hermes:
    build: .
    volumes:
      - ./config:/home/hermes/.hermes
      - hermes-data:/home/hermes/.hermes/data
    environment:
      - HERMES_DEFAULT_MODEL=openai/gpt-4o
      - HERMES_LOG_LEVEL=info
    restart: unless-stopped
    ports:
      - "3000:3000"  # Web Dashboard

volumes:
  hermes-data:

8.3 性能调优

上下文窗口管理

# 控制每次加载的记忆量,避免context溢出
MAX_MEMORY_TOKENS = 8000  # 记忆占用最多8000 tokens
MAX_HISTORY_TURNS = 20    # 最近20轮对话

def build_context(memory_entries, session_history):
    # 按优先级排序:记忆 > 最近对话 > 相关历史
    context_parts = []
    remaining_tokens = MAX_MEMORY_TOKENS
    
    # 先加记忆
    for entry in memory_entries:
        if remaining_tokens <= 0:
            break
        tokens = estimate_tokens(entry.content)
        if tokens <= remaining_tokens:
            context_parts.append(entry)
            remaining_tokens -= tokens
    
    # 再加最近对话
    for turn in session_history[-MAX_HISTORY_TURNS:]:
        if remaining_tokens <= 0:
            break
        tokens = estimate_tokens(turn.content)
        if tokens <= remaining_tokens:
            context_parts.append(turn)
            remaining_tokens -= tokens
    
    return context_parts

冻结快照(Frozen Snapshot)优化

这是防止API成本翻倍的技巧。当前的记忆系统需要把MEMORY.md和USER.md注入到每次请求的system prompt中。但每次对话,这些内容其实是不变的——只有用户的新消息在变化。

# LLM API前缀缓存优化
# 大多数LLM API服务(OpenAI、Anthropic等)会缓存相同的system prompt前缀
# 如果每次请求的system prompt开头部分完全相同,缓存命中后这部分不收费

# 所以关键设计是:system prompt的结构要稳定
SYSTEM_PROMPT_TEMPLATE = """{memory_section}
{user_profile_section}
{skill_section}
---
当前任务:{current_task}
"""

这样,包含了记忆内容的{memory_section}{user_profile_section}部分,会被API服务缓存。只有不断变化的任务描述部分会被计费。


九、与其他框架的横向对比

9.1 与OpenClaw的对比

这是一个有意思的对比,因为OpenClaw正是运行当前会话的框架。两者虽然都是Agent框架,但设计哲学有显著差异:

维度Hermes AgentOpenClaw
核心焦点记忆与自进化工具链与任务执行
记忆系统三层架构(内置+外部+会话)MEMORY.md单文件 + 会话历史
自进化能力完整闭环(策划→创建→改进)依赖外部Skill机制
多模型支持33个Provider模型路由
平台支持12+消息平台主要桌面端
开源协议MIT闭源/私有部署
语言侧重Python(88%)TypeScript/Node.js
记忆格式Markdown + SQLiteMarkdown文件

Hermes Agent在「记忆持久化」上更深入,OpenClaw在「工具链集成」上更强大。两者不是非此即彼的关系——实际上,已经有开发者尝试在OpenClaw中集成Hermes Agent的记忆能力。

9.2 与LangChain Agents的对比

LangChain的Agent(如ReAct、Plan-and-Execute)没有内置的持久化记忆系统,这是它被Hermes Agent「弯道超车」的主要原因。LangChain的优点是工具生态丰富,但作为个人助理使用时,每次都是「白板」启动,用户体验远不如Hermes Agent。


十、局限性与挑战

任何技术都有其局限性,Hermes Agent也不例外。诚实地面对这些局限,才能真正判断它是否适合自己的场景。

10.1 记忆膨胀问题

随着使用时间增长,MEMORY.md会逐渐逼近字符上限。当前的压缩策略(基于LRU或访问频率)可能会在压缩时丢失一些重要的长期记忆。这是一个开放的研究问题:如何判断一段记忆「是否仍然重要」?

10.2 技能冲突

当多个Skill对同一场景都有处理逻辑时,Agent需要做出选择。当前版本使用「后注册的Skill优先」策略,但在复杂场景下,这可能导致非预期行为。

10.3 幻觉风险

Skill自改进环节中,LLM对自己失败的「反思分析」可能产生幻觉——把不相关的原因当成失败根因,然后生成错误的修复方案。如何在这个环节引入验证机制(让Agent在新场景中实际测试改进后的Skill)是一个未解决的问题。

10.4 隐私考量

记忆系统天然涉及隐私问题。当Agent记住了你的密码、工作习惯、私人偏好时,这些数据的安全性完全取决于本地存储的保护程度。对于处理敏感信息的企业用户来说,这需要额外的安全加固。


十一、总结:记忆即智能

回望AI Agent的发展历程,我们经历了:

  • 第一阶段:纯prompt工程——prompt写得好,Agent就表现好
  • 第二阶段:工具调用工程——给Agent装备更多工具
  • 第三阶段:工作流编排——多步骤任务的流程设计

而Hermes Agent指向的方向,是第四阶段:记忆驱动的持续学习

真正的智能不在于单次任务的完成度,而在于能否从经验中学习并进化。当一个Agent能够在多次任务中积累知识、把成功经验固化为技能、从失败教训中更新策略,它才真正从「工具」变成了「伙伴」。

三层记忆架构让信息有了清晰的层次,自进化五环节让知识有了流动的方向,Provider模式让系统有了扩展的弹性。这些设计组合在一起,构成了一个在工程上扎实、在概念上清晰的系统。

更重要的是,Hermes Agent的开源性质,意味着这些设计思想可以被整个社区检验、改进和复用。当一个框架的核心思想成为公共知识,行业的整体水平就会向前推进。

这不是终点,而是一个新起点的开始。


关于作者:本文基于Hermes Agent开源项目(MIT License)的源码分析和公开文档撰写,所有代码示例均为基于接口设计的示例代码,非直接摘自源码。

参考资源

推荐文章

Elasticsearch 聚合和分析
2024-11-19 06:44:08 +0800 CST
XSS攻击是什么?
2024-11-19 02:10:07 +0800 CST
php curl并发代码
2024-11-18 01:45:03 +0800 CST
15 个 JavaScript 性能优化技巧
2024-11-19 07:52:10 +0800 CST
程序员茄子在线接单