AI Agent 的“记忆“与“进化“:从被动存储到闭环学习的技术路径
以 Hermes Agent 的 MemoryProvider 体系与 OpenClaw 的 memory-core 插件为案例,探讨 Agent 持久化记忆和自我改进的不同技术方案。
一、引言
一个 AI Agent 如果没有记忆,每次对话都是从零开始。用户需要反复解释自己的偏好、项目背景和工作习惯。这不仅效率低下,也让 Agent 无法真正成为"个人助手"。
记忆系统是 AI Agent 从"工具"进化为"助手"的关键分水岭。但"记忆"本身是一个模糊的概念——它可以是简单的键值存储,也可以是复杂的语义检索系统;可以是被动的"用户写入、Agent 读取",也可以是主动的"Agent 自主决定记什么、忘什么"。
Hermes Agent 和 OpenClaw 在记忆系统上的设计差异,恰好代表了这个光谱的两端。本文将从架构设计、生命周期管理、主动学习机制三个层面展开分析。
二、记忆架构:Provider 模式 vs 插件模式
2.1 Hermes Agent 的 MemoryProvider 体系
Hermes Agent 的记忆系统由两个核心类构成:MemoryProvider(抽象基类)和 MemoryManager(管理器)。
MemoryProvider 定义了记忆提供者的完整生命周期接口:
class MemoryProvider(ABC):
"""记忆提供者抽象基类"""
@abstractmethod
def name(self) -> str: ...
@abstractmethod
def is_available(self) -> bool: ...
@abstractmethod
def initialize(self, session_id: str, **kwargs) -> None: ...
def system_prompt_block(self) -> str: ...
def prefetch(self, query: str, *, session_id: str = "") -> str: ...
def queue_prefetch(self, query: str, *, session_id: str = "") -> None: ...
def sync_turn(self, user_content: str, assistant_content: str,
*, session_id: str = "") -> None: ...
@abstractmethod
def get_tool_schemas(self) -> List[Dict[str, Any]]: ...
def handle_tool_call(self, tool_name: str, args: Dict, **kwargs) -> str: ...
def shutdown(self) -> None: ...
# 可选钩子
def on_turn_start(self, turn_number: int, message: str, **kwargs) -> None: ...
def on_session_end(self, messages: List[Dict]) -> None: ...
def on_pre_compress(self, messages: List[Dict]) -> str: ...
def on_memory_write(self, action: str, target: str, content: str) -> None: ...
def on_delegation(self, task: str, result: str, *,
child_session_id: str = "", **kwargs) -> None: ...
这个接口设计有几个值得注意的特点:
双层架构: 内建记忆(BuiltinMemoryProvider,基于 MEMORY.md 和 USER.md 文件)始终活跃且不可移除。外部提供者(Honcho、Hindsight、Mem0 等)是附加层,一次只能激活一个,以避免工具 schema 膨胀和后端冲突。
预取机制: prefetch() 和 queue_prefetch() 构成了一个两阶段检索管线。queue_prefetch() 在每轮对话结束后触发后台检索,结果缓存起来;prefetch() 在下一轮 API 调用前返回缓存的结果。这种设计将检索延迟从关键路径上移除。
压缩感知: on_pre_compress() 钩子在上下文压缩发生前被调用,允许记忆提供者从即将被丢弃的消息中提取关键信息。返回的文本会被注入压缩摘要提示词,确保压缩器保留记忆相关的洞察。
委派感知: on_delegation() 钩子在父 Agent 收到子 Agent 的结果时被调用。父 Agent 的记忆提供者可以观察到委派的任务和结果,但子 Agent 本身不运行记忆系统(skip_memory=True),避免子 Agent 的临时上下文污染长期记忆。
2.2 MemoryManager 的编排逻辑
MemoryManager 负责编排多个 MemoryProvider 的生命周期:
class MemoryManager:
def __init__(self):
self._providers: List[MemoryProvider] = []
self._external_count = 0
def add_provider(self, provider: MemoryProvider) -> None:
"""添加提供者,强制执行"最多一个外部提供者"规则"""
if provider.name != "builtin":
if self._external_count >= 1:
raise ValueError("Only one external memory provider allowed")
self._external_count += 1
self._providers.append(provider)
def build_system_prompt(self) -> str:
"""聚合所有提供者的系统提示词片段"""
blocks = [p.system_prompt_block() for p in self._providers]
return "\n".join(b for b in blocks if b)
def prefetch_all(self, query: str, *, session_id: str = "") -> str:
"""并行预取所有提供者的上下文"""
results = [p.prefetch(query, session_id=session_id) for p in self._providers]
return "\n".join(r for r in results if r)
def sync_all(self, user_content: str, assistant_content: str,
*, session_id: str = "") -> None:
"""同步所有提供者的轮次数据"""
for p in self._providers:
p.sync_turn(user_content, assistant_content, session_id=session_id)
def on_turn_start(self, turn_number: int, message: str, **kwargs) -> None:
"""通知所有提供者新轮次开始"""
for p in self._providers:
p.on_turn_start(turn_number, message, **kwargs)
def on_session_end(self, messages: List[Dict]) -> None:
"""通知所有提供者会话结束"""
for p in self._providers:
p.on_session_end(messages)
Manager 的设计遵循"广播通知"模式:每个生命周期事件都广播给所有已注册的提供者,由提供者自行决定是否响应。
2.3 OpenClaw 的 memory-core 插件
OpenClaw 的记忆系统以插件形式实现。核心记忆插件 memory-core 提供了基于文件的记忆存储和搜索能力:
// extensions/memory-core/index.ts
export default definePluginEntry({
id: "memory-core",
name: "Memory (Core)",
description: "File-backed memory search tools and CLI",
kind: "memory",
register(api) {
// 注册嵌入向量提供者
registerBuiltInMemoryEmbeddingProviders(api);
// 注册"做梦"机制(短期记忆提升为长期记忆)
registerShortTermPromotionDreaming(api);
// 注册记忆能力
api.registerMemoryCapability({
promptBuilder: buildPromptSection,
flushPlanResolver: buildMemoryFlushPlan,
runtime: memoryRuntime,
publicArtifacts: { listArtifacts: listMemoryCorePublicArtifacts },
});
// 注册工具
api.registerTool(
(ctx) => createMemorySearchTool({ config: ctx.config, agentSessionKey: ctx.sessionKey }),
{ names: ["memory_search"] }
);
api.registerTool(
(ctx) => createMemoryGetTool({ config: ctx.config, agentSessionKey: ctx.sessionKey }),
{ names: ["memory_get"] }
);
},
});
OpenClaw 还有一个独立的 active-memory 插件(约 2000 行),实现了更高级的主动回忆机制:在每轮对话前,使用一个子 Agent 分析对话上下文,从记忆库中检索相关信息并注入系统提示词。
active-memory 的核心流程:
async function maybeResolveActiveRecall(params) {
// 1. 检查是否启用、是否为交互式会话
if (!isEnabledForAgent(...) || !isEligibleInteractiveSession(...)) return;
// 2. 构建查询(从最近几轮对话中提取)
const query = buildQuery({ recentTurns, sessionKey, ... });
// 3. 检查缓存
const cached = getCachedResult(cacheKey);
if (cached) return cached;
// 4. 运行回忆子 Agent
const result = await runRecallSubagent({ query, model, ... });
// 5. 缓存结果
setCachedResult(cacheKey, result, ttlMs);
return result;
}
2.4 架构对比
| 维度 | Hermes Agent | OpenClaw |
|---|---|---|
| 记忆存储 | MEMORY.md + USER.md(Markdown 文件) | 文件 + 嵌入向量索引 |
| 检索方式 | FTS5 全文搜索 + LLM 摘要 | 嵌入向量语义搜索 + 子 Agent 回忆 |
| 提供者模型 | 抽象基类 + Manager 编排 | 插件注册 + Gateway 调度 |
| 外部后端 | Honcho、Hindsight、Mem0(可选) | 插件式(memory-wiki、memory-lancedb) |
| 工具暴露 | Provider 自带工具 schema | 插件注册 memory_search、memory_get |
| 多提供者 | 内建 + 最多 1 个外部 | 插件槽位(一次一个记忆插件) |
三、闭环学习:Hermes Agent 的差异化能力
Hermes Agent 的记忆系统不仅仅是"存储和检索",它构建了一个完整的闭环学习系统。这是与 OpenClaw 最根本的差异。
3.1 Nudge 机制:后台 Agent 自主复盘对话
Hermes Agent 实现了一套基于计数器的"nudge"机制,其核心思路是:在用户对话结束后,在后台 fork 一个独立的 Agent 实例来回顾对话,自主决定是否将有价值的信息持久化为记忆或技能。 整个过程对用户完全透明,不会干扰正常对话流程。
计数器累积与阈值触发:
Agent 内部维护两个独立的计数器:
self._memory_nudge_interval = 10 # 每 10 轮用户对话触发一次记忆回顾
self._skill_nudge_interval = 10 # 每 10 次工具调用迭代触发一次技能回顾
self._turns_since_memory = 0 # 距上次使用 memory 工具的用户轮数
self._iters_since_skill = 0 # 距上次使用 skill_manage 工具的迭代数
每轮用户对话 _turns_since_memory 加 1,每次工具调用迭代 _iters_since_skill 加 1。当 Agent 在正常对话中主动使用了 memory 或 skill_manage 工具时,对应计数器归零——这意味着如果 Agent 已经在自发地管理记忆和技能,系统就不会多此一举地触发回顾。
当计数器达到阈值时,系统标记 _should_review_memory 或 _should_review_skills 为 True,但不会立即执行任何操作。
后台 Review Agent:
真正的回顾发生在当前轮对话完成之后。_spawn_background_review() 方法在一个 daemon 线程中创建一个全新的 AIAgent 实例:
def _spawn_background_review(self, messages_snapshot, review_memory=False, review_skills=False):
"""在后台线程中 fork 一个独立 Agent 来回顾对话"""
def _run_review():
review_agent = AIAgent(
model=self.model,
max_iterations=8, # 最多 8 次工具调用
quiet_mode=True, # 静默模式,不产生用户可见输出
)
# 共享记忆存储,但禁用 nudge(避免无限递归)
review_agent._memory_store = self._memory_store
review_agent._memory_nudge_interval = 0
review_agent._skill_nudge_interval = 0
# 将当前对话历史快照 + review prompt 传给后台 Agent
review_agent.run_conversation(
user_message=prompt,
conversation_history=messages_snapshot,
)
t = threading.Thread(target=_run_review, daemon=True, name="bg-review")
t.start()
后台 Agent 收到的 review prompt 根据触发类型不同而不同。记忆回顾的 prompt 为:
“Review the conversation above and consider saving to memory if appropriate. Focus on: 1) Has the user revealed things about themselves — their persona, desires, preferences, or personal details worth remembering? 2) Has the user expressed expectations about how you should behave, their work style, or ways they want you to operate? If something stands out, save it using the memory tool. If nothing is worth saving, just say ‘Nothing to save.’ and stop.”
技能回顾的 prompt 为:
“Review the conversation above and consider saving or updating a skill if appropriate. Focus on: was a non-trivial approach used to complete a task that required trial and error, or changing course due to experiential findings along the way? If a relevant skill already exists, update it with what you learned. Otherwise, create a new skill if the approach is reusable.”
后台 Agent 拥有与主 Agent 相同的工具集(memory、skill_manage),可以自主决定是否调用这些工具。如果它确实保存了记忆或创建了技能,主 Agent 会在终端打印一条简短通知(如 💾 Memory updated · User profile updated),让用户知道发生了什么。
设计要点:
这种"后台 fork + 独立决策"的设计有几个关键优势:
- 不干扰对话:回顾在响应交付后才启动,不占用用户等待时间
- 不污染上下文:review prompt 不会注入主对话历史,不影响后续对话的 prompt 缓存
- 自主判断:后台 Agent 看到完整对话上下文后自行决定是否值得保存,而非机械地执行保存操作
- 防递归:后台 Agent 的 nudge interval 被设为 0,避免回顾 Agent 再触发回顾
3.2 技能自创建与自改进
Nudge 机制中的技能回顾(skill review)是 Hermes Agent 技能自创建能力的核心驱动力。
当后台 Agent 判断对话中包含了"经过试错才找到的非平凡方法"时,它会调用 skill_manage 工具创建一个新的 SKILL.md 文件,包含触发条件、步骤说明和验证方法,保存到 ~/.hermes/skills/ 目录。下次遇到类似任务时,Agent 可以直接加载这个 Skill,而不需要重新推理整个解决方案。
同样的机制也驱动技能的自改进:如果后台 Agent 发现对话中使用了某个已有 Skill 但遇到了边界情况或更优的方法,它可以更新该 Skill 的内容。这形成了一个正反馈循环:
任务执行 → 后台回顾 → 创建/更新 Skill → 下次执行更好 → 后台回顾 → ...
3.3 会话搜索:跨会话的长期回忆
Hermes Agent 使用 SQLite + FTS5 全文搜索引擎存储所有会话历史(hermes_state.py)。Agent 可以通过 session_search 工具搜索过去的对话:
# 搜索过去的会话
session_search(query="how did we set up the Docker deployment last month")
搜索结果经过 LLM 摘要后返回,提供跨会话的上下文连续性。这意味着即使会话被重置,Agent 仍然可以"回忆"过去的对话内容。
3.4 用户建模:Honcho 集成
Hermes Agent 集成了 Honcho(一个辩证式用户建模框架)作为可选的外部记忆提供者。Honcho 不仅存储事实性记忆,还构建用户的偏好模型、沟通风格和工作习惯的动态表示。
这种集成通过 MemoryProvider 接口实现,Honcho 作为一个外部提供者注册到 MemoryManager,与内建记忆并行工作。
四、上下文压缩:记忆的"遗忘"机制
长对话不可避免地会超出 LLM 的上下文窗口。如何在压缩上下文的同时保留关键信息,是记忆系统的另一个重要维度。
4.1 Hermes Agent 的 ContextCompressor
Hermes Agent 的 ContextCompressor(agent/context_compressor.py)实现了一套精细的压缩策略:
触发条件: 当 prompt tokens 超过上下文窗口的配置阈值时触发压缩。
压缩流程:
- 工具结果裁剪(
_prune_old_tool_results()):首先裁剪旧的工具调用结果,保留最近 N 轮的完整结果 - 摘要预算计算(
_compute_summary_budget()):根据待压缩的轮次数量计算摘要的 token 预算 - 记忆提取(
on_pre_compress钩子):在压缩前通知记忆提供者提取关键信息 - LLM 摘要生成(
_generate_summary()):使用辅助 LLM 生成对话摘要 - 边界对齐(
_align_boundary_forward/backward()):确保压缩边界不会切断工具调用对(assistant tool_call + tool result 必须成对保留或丢弃) - 消息替换:用摘要消息替换被压缩的轮次
def compress(self, messages, current_tokens=None, focus_topic=None):
"""压缩对话历史"""
# 1. 保护最近 N 轮不被压缩
protected_tail = messages[-self.protect_last_n:]
compressible = messages[:-self.protect_last_n]
# 2. 通知记忆提供者
memory_insights = self._notify_pre_compress(compressible)
# 3. 生成摘要
summary = self._generate_summary(compressible, focus_topic)
# 4. 构建压缩后的消息列表
compressed = [summary_message(summary)] + protected_tail
return compressed
工具调用对完整性保护是一个容易被忽视但至关重要的细节。如果压缩边界恰好落在一个 assistant 的 tool_call 消息和对应的 tool result 消息之间,会导致 API 调用失败(OpenAI 和 Anthropic 都要求 tool_call 和 tool result 成对出现)。_sanitize_tool_pairs() 方法专门处理这种情况。
4.2 OpenClaw 的会话修剪
OpenClaw 的上下文管理采用了"session pruning"策略,在 Gateway 层面处理。当会话长度接近模型的上下文限制时,Gateway 会触发修剪:
- 移除最早的轮次
- 保留系统提示词和最近的对话
- 可选地生成摘要替代被移除的内容
OpenClaw 的 active-memory 插件在修剪前会尝试将重要信息提取到长期记忆中,但这个过程是由独立的子 Agent 驱动的,而非像 Hermes 那样通过 on_pre_compress 钩子与压缩器紧密集成。
五、记忆的生命周期管理
5.1 初始化阶段
Hermes Agent 的 MemoryProvider.initialize() 接收丰富的上下文参数:
def initialize(self, session_id: str, **kwargs) -> None:
"""
kwargs 包含:
- hermes_home: 活跃的 HERMES_HOME 目录(支持多 profile)
- platform: "cli", "telegram", "discord", "cron" 等
- agent_context: "primary", "subagent", "cron", "flush"
- agent_identity: Profile 名称
- parent_session_id: 子 Agent 的父会话 ID
- user_id: 平台用户标识
"""
agent_context 参数尤其重要:当上下文为 "cron" 或 "flush" 时,记忆提供者应该跳过写入操作,避免 Cron 任务的系统提示词污染用户记忆。
5.2 会话结束阶段
on_session_end() 在会话真正结束时被调用(CLI 退出、/reset 命令、Gateway 会话过期),而非每轮对话后。这个时机适合做端到端的事实提取和摘要。
Hermes Agent 的 Gateway 在会话重置前会调用 _flush_memories_for_session(),确保记忆被持久化:
def _flush_memories_for_session(self, session_key, agent, reason="reset"):
"""会话重置前刷新记忆"""
if agent and agent.memory_manager:
messages = agent.get_conversation_history()
agent.memory_manager.on_session_end(messages)
5.3 配置与设置
Hermes Agent 的 MemoryProvider 还提供了 get_config_schema() 和 save_config() 方法,支持通过 hermes memory setup 命令交互式配置记忆提供者。每个配置字段可以声明为 secret(存入 .env)或普通配置(存入 YAML)。
六、两种路线的本质差异
回顾两个项目的记忆系统设计,可以看到一个根本性的哲学差异:
OpenClaw 的记忆是"被动的基础设施"。 它提供了存储、检索和搜索的能力,但记忆的写入主要由用户或 Agent 的显式工具调用驱动。active-memory 插件增加了主动检索能力,但记忆的创建和维护仍然是被动的。
Hermes Agent 的记忆是"主动的学习系统"。 通过 nudge 机制(后台 fork Agent 自主复盘对话)、技能自创建与自改进、压缩前提取等设计,系统主动推动 Agent 积累和改进知识。记忆不仅是"存储过去",更是"改进未来"。
这种差异可以用一个类比来理解:OpenClaw 的记忆像一个笔记本——你写什么它记什么,你搜什么它找什么。Hermes Agent 的记忆像一个学习日志——系统会在对话结束后自动复盘,把有价值的经验提炼为持久化的记忆和可复用的技能,还会在下次遇到类似问题时主动提供参考。
七、工程启示
从这两个项目的实践中,可以提炼出 AI Agent 记忆系统设计的几个关键原则:
- 分层存储:内建记忆(快速、简单)+ 外部后端(语义检索、用户建模)并行工作,而非二选一
- 预取管线:将检索延迟从关键路径上移除,使用后台预取 + 缓存模式
- 压缩感知:记忆系统必须与上下文压缩器协同工作,在信息丢失前提取关键内容
- 上下文隔离:子 Agent、Cron 任务等非主要上下文不应写入用户记忆
- 主动学习:从"用户驱动写入"进化到"系统驱动积累",是记忆系统从"功能"到"智能"的关键跃迁。Hermes Agent 的后台复盘模式(fork 独立 Agent 回顾对话、自主决定是否持久化)提供了一种不干扰用户体验的实现路径
本文基于 Hermes Agent v0.9.0(run_agent.py、agent/memory_provider.py、agent/memory_manager.py、agent/context_compressor.py)和 OpenClaw v2026.4.14-beta.1(extensions/memory-core/、extensions/active-memory/)的开源代码分析撰写。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)