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_start、on_session_end、on_session_switch、on_pre_compress、on_memory_write、on_delegation。
FTS5 全文搜索 + LLM 摘要
Hermes 使用 SQLite 的 FTS5 扩展实现跨会话记忆搜索:
- 所有历史会话的对话内容索引到 FTS5 虚拟表
- 用户查询时:FTS5 检索匹配的消息
- LLM 对检索结果生成摘要
- 摘要注入到当前 turn 的上下文
设计优势:无需引入外部向量数据库,仅用 SQLite 即可实现”按关键词找到历史,按 LLM 理解提炼”的两阶段检索。对于 Agent 场景中的精确引用(文件路径、函数名、错误信息),FTS5 比向量检索更准确。
Honcho 方言用户建模
Hermes 集成了 Honcho 作为用户建模后端:
- 方言建模(dialectic modeling):通过与用户的辩证交互构建用户画像
- 不直接存储用户的原始陈述,而是提取”触发条件 → 偏好行为”的原子化规则
- 跨会话持久化:用户换新对话时仍能召回历史画像
与普通记忆的区别:普通记忆存储”发生了什么”,Honcho 建模”用户是谁/偏好什么”。
记忆 Nudge 机制
agent._turns_since_memory += 1if 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 活跃,避免多个记忆后端返回矛盾信息时的组合复杂性。
参考来源
- 源码验证:
agent/memory_manager.py、agent/memory_provider.py、hermes_state.py - 公开验证:Honcho - plastic-labs/honcho