跳转到内容

03 - 记忆与用户建模

学习目标

本章聚焦 Hermes Agent 的记忆管理系统。你将了解:

  • MemoryManager 如何编排内置记忆 + 外部 provider
  • FTS5 全文搜索如何实现跨会话记忆召回
  • Honcho 方言建模如何构建用户画像
  • 记忆 nudge 机制如何确保定期回顾
  • 网关模式下的计数器重建问题

前置知识

本章涉及记忆系统的通用原理,建议先阅读:

下文直接聚焦 Hermes 的具体实现。


项目实践

MemoryManager 编排器

agent/memory_manager.py(约 610 行)是记忆系统的核心编排器:

class MemoryManager:
def add_provider(provider) # 只允许一个外部 provider
def prefetch_all(query) -> str # 获取所有记忆
def sync_all(user_content, assistant_content) # 同步记忆
def handle_tool_call(tool_name, args) -> str
def on_turn_start(turn_count, msg) # 通知 turn 开始

MemoryProvider 抽象基类(agent/memory_provider.py)定义生命周期:

initialize → system_prompt_block → prefetch → sync_turn
→ get_tool_schemas → handle_tool_call → shutdown

可选钩子:on_turn_starton_session_endon_session_switchon_pre_compresson_memory_writeon_delegation

FTS5 全文搜索 + LLM 摘要

Hermes 使用 SQLite 的 FTS5 扩展实现跨会话记忆搜索:

  1. 所有历史会话的对话内容索引到 FTS5 虚拟表
  2. 用户查询时:FTS5 检索匹配的消息
  3. LLM 对检索结果生成摘要
  4. 摘要注入到当前 turn 的上下文

设计优势:无需引入外部向量数据库,仅用 SQLite 即可实现”按关键词找到历史,按 LLM 理解提炼”的两阶段检索。对于 Agent 场景中的精确引用(文件路径、函数名、错误信息),FTS5 比向量检索更准确。

Honcho 方言用户建模

Hermes 集成了 Honcho 作为用户建模后端:

  • 方言建模(dialectic modeling):通过与用户的辩证交互构建用户画像
  • 不直接存储用户的原始陈述,而是提取”触发条件 → 偏好行为”的原子化规则
  • 跨会话持久化:用户换新对话时仍能召回历史画像

与普通记忆的区别:普通记忆存储”发生了什么”,Honcho 建模”用户是谁/偏好什么”。

记忆 Nudge 机制

agent._turns_since_memory += 1
if agent._turns_since_memory >= agent._memory_nudge_interval:
_should_review_memory = True
agent._turns_since_memory = 0

每 N 轮对话(可配置),Agent 自动触发一次记忆回顾。回顾内容:检查现有记忆是否仍然准确,是否有新的偏好需要记录。

网关模式下的计数器重建:Gateway 每次收到消息都创建新的 AIAgent 实例,因此 _turns_since_memory_user_turn_count 每轮从 0 开始。Hermes 通过扫描历史消息中的 user 角色数量来重建:

prior_user_turns = sum(1 for m in conversation_history if m.get("role") == "user")
agent._turns_since_memory = prior_user_turns % agent._memory_nudge_interval

% 运算保留了原始的 1-in-N 节奏,而不是在恢复时立即触发回顾(这会让用户惊讶)。

插件化记忆后端

当前支持的插件后端:honcho、mem0、supermemory、byterover、hindsight、holographic、openviking、retaindb。同一时间只允许一个外部 provider 活跃。

CLI 命令:仅当前活跃的记忆 provider 的 CLI 命令被暴露到 hermes --help,禁用的 provider 不污染帮助文本。


问题与规避

网关模式下的记忆重复调用

问题prefetch_all() 如果在每个工具调用迭代都执行,会导致 N 倍延迟和成本(10 个工具调用 = 10 次 prefetch)。

对策:Hermes 在进入工具循环前调用一次 prefetch_all() 并缓存结果:

_ext_prefetch_cache = agent._memory_manager.prefetch_all(_query) or ""
# 在循环中复用缓存,不在每个 tool iteration 重新调用

记忆注入破坏前缀缓存

问题:如果将记忆内容注入系统提示,会导致系统提示每轮不同,破坏 Anthropic prefix cache。

对策:Hermes 通过 build_memory_context_block() 将记忆注入用户消息而非系统提示。系统提示保持字节级稳定。

Nudge 间隔选择不当

问题:间隔太短导致频繁回顾干扰对话;间隔太长导致记忆落后于对话。

对策:默认值通过经验调优,用户可通过 memory.nudge_interval 配置调整。对于高频用户,建议调大间隔。


设计取舍

FTS5 vs 向量数据库

方案优势代价
FTS5(Hermes)零额外依赖、精确匹配强、SQLite 内完成无语义相似度检索
向量数据库语义检索、模糊匹配额外服务依赖、嵌入成本

Hermes 选择 FTS5 因为 Agent 对话中的关键信息(代码、路径、错误信息)通常需要精确匹配而非语义相似度。LLM 摘要层提供了理解能力,弥补了 FTS5 缺乏语义检索的不足。

单外部 Provider 限制

方案优势代价
单 Provider(Hermes)避免记忆冲突、组合策略简单无法同时使用多种记忆后端
多 Provider灵活组合需要解决冲突、去重、优先级

Hermes 限制同一时间只允许一个外部 provider 活跃,避免多个记忆后端返回矛盾信息时的组合复杂性。


参考来源