memory机制至关重要,一起探究一下hermes的记忆是如何管理的
hermes的记忆类型?
分内部记忆和外部记忆
- 内部记忆
静态文件,memory.md、user.md两个文件,大小有限制,没有检索能力,初始化一次性加载,注入到prompt prefix cache,
- 外部记忆
第三方服务,honcho、mem0等,可以支持语义检索、向量检索、用户画像构建,每次用户输入 都会检查是否可以读入、可写,注入到 user message 尾部
什么时候读入记忆?
内部记忆:每次初始化时,只加载一次
外部记忆:
- 每次用户输入 都会check一下是否需要读入(当然,这个检索频率是可以配置的);
- 外部记忆支持很多种,honcho和mem0等,但是同时只能支持一次(不然多个记忆provider互相打架);
- 优点是,可支持跨会话持久化(换设备也能访问,确保不会丢失)、语义检索、用户画像、团队多用户使用等(根据用户id存储对应记忆)
- 初始阶段,provider的静态memory也会放进 system prompt
工作流程(以 Honcho 为例)
每轮对话开始
↓
memory_manager.on_turn_start() # 通知 provider 新 turn 开始
↓
memory_manager.prefetch_all(query) # 用当前用户消息做语义检索
↓
honcho.search(query) → 返回相关记忆片段
↓
结果注入到 user message 尾部(不破坏 prompt cache)
↓
模型生成回复
↓
memory_manager.sync_turn(user_msg, assistant_response) # 异步写入对话
外部memory装载时,每次查询都是同步的吗?这耗时是怎样的呢?会不会影响响应速度呢?
不一定会,因为有预热以及缓存,存在阻塞的情况如下
完整的时序图
┌─────────────────────────────────────────────────────────┐
│ Session 初始化 │
│ manager.prefetch_context() + 预热 dialectic (后台线程) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────────────────┐
│ Turn 1 │
│ │
│ prefetch() 被调用 │
│ ↓ │
│ Base Context: 首次同步获取 (缓存到 _base_context_cache) │
│ ↓ │
│ Dialectic: 检查预热结果是否已就绪 │
│ - 就绪 → 直接使用 │
│ - 未就绪 → 启动后台线程 + join(timeout=8s) ← 唯一可能阻塞点 │
│ ↓ │
│ 返回结果 → 注入 user message │
│ │
│ Turn 结束 → queue_prefetch() → 后台线程预取 Turn 2 的 dialectic │
└─────────────────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────────────────┐
│ Turn 2+ │
│ │
│ prefetch() 被调用 │
│ ↓ │
│ Base Context: 从缓存读取 (_base_context_cache) │
│ - 后台按 context_cadence 刷新缓存 │
│ ↓ │
│ Dialectic: 从缓存读取 (_prefetch_result) │
│ - 上一 turn 的 queue_prefetch 已预取好了 │
│ - 不阻塞,直接返回 │
│ ↓ │
│ 返回结果 → 注入 user message │
│ │
│ Turn 结束 → queue_prefetch() → 后台线程预取下一 turn │
└─────────────────────────────────────────────────────────────────────────────┘
即turn1可能会存在8s的阻塞,不过前置有预期,应该还好;
后续的turn都是从缓存里取,
外部记忆 检索频率控制机制
主流程是每个 turn 都会触发检索,但实际检索行为由 provider 控制
honcho 实现有两层
- Base Context 用户画像 + peer card
context_cadence 控制(默认每 turn)
- Dialectic Supplement 语义检索 + 推理
dialectic_cadence 控制(默认每 turn)
什么时候写入记忆?
内部记忆
每隔一段时间(用户输入10次)提醒 llm 做一下检查,看看是否要写入记忆,模型调用 memory 工具,写入内容,等下次启动时生效
review 只是提醒模型检查,模型可以选择不写、写一点、写很多
外部记忆
每次用户输入结束后自动触发,后台任务执行
存了什么东西?
如何评估是否对任务有用
解决了读取记忆、写入记忆,这些对任务完成是否有帮组呢?
如何验证呢?
出了问题如何优化呢?
如果基准跑分、红队攻击等手段发现有问题,我们有哪些手动解决对应的问题呢?
memory机制至关重要,一起探究一下hermes的记忆是如何管理的
hermes的记忆类型?
分内部记忆和外部记忆
静态文件,memory.md、user.md两个文件,大小有限制,没有检索能力,初始化一次性加载,注入到prompt prefix cache,
第三方服务,honcho、mem0等,可以支持语义检索、向量检索、用户画像构建,每次用户输入 都会检查是否可以读入、可写,注入到 user message 尾部
什么时候读入记忆?
内部记忆:每次初始化时,只加载一次
外部记忆:
工作流程(以 Honcho 为例)
外部memory装载时,每次查询都是同步的吗?这耗时是怎样的呢?会不会影响响应速度呢?
不一定会,因为有预热以及缓存,存在阻塞的情况如下
完整的时序图
即turn1可能会存在8s的阻塞,不过前置有预期,应该还好;
后续的turn都是从缓存里取,
外部记忆 检索频率控制机制
主流程是每个 turn 都会触发检索,但实际检索行为由 provider 控制
honcho 实现有两层
context_cadence控制(默认每 turn)dialectic_cadence控制(默认每 turn)什么时候写入记忆?
内部记忆
每隔一段时间(用户输入10次)提醒 llm 做一下检查,看看是否要写入记忆,模型调用 memory 工具,写入内容,等下次启动时生效
review 只是提醒模型检查,模型可以选择不写、写一点、写很多
外部记忆
每次用户输入结束后自动触发,后台任务执行
存了什么东西?
内部记忆
外部记忆
记录的是用户的输入和llm的返回,不包含工具调用
如何评估是否对任务有用
解决了读取记忆、写入记忆,这些对任务完成是否有帮组呢?
如何验证呢?
出了问题如何优化呢?
如果基准跑分、红队攻击等手段发现有问题,我们有哪些手动解决对应的问题呢?