三层记忆:先分清职责,再谈存储
Agent 记忆系统借鉴认知科学框架,通常划分为短期记忆、工作记忆和长期记忆。三者的核心差异不在"存什么",而在生命周期和访问路径。
短期记忆对应当前会话的上下文窗口,直接可用、信息完整,但受 token 上限约束,会话结束即丢失。工作记忆是任务执行期间临时维护的状态,比如多步推理的中间结果、待办列表、当前进度,任务完成即丢弃。长期记忆跨会话持久化,存用户偏好、历史决策、学习到的知识,容量理论上无上限,但每次调用都要经过检索。
关键认知:三者不是三个独立模块,而是一条信息管道。短期记忆中的信息经筛选压缩后写入长期记忆,长期记忆在需要时被检索进工作记忆参与任务执行。设计记忆系统,本质是设计这条管道的流转规则。
短期记忆的工程实现:滑动窗口 + 摘要
短期记忆管理的核心矛盾是上下文窗口有限。GPT-4 的 128K tokens 在长对话下依然会溢出。常见处理方式有三种:滑动窗口保留最近 N 条、摘要压缩将早期对话浓缩、选择性保留只留关键信息。
一个可落地的组合策略是滑动窗口叠加摘要:维护完整消息列表,当消息数超过阈值时,将最旧的若干条压缩为摘要,从列表中移除原文。获取上下文时,摘要作为前缀,拼接剩余消息。
class ShortTermMemory:
def __init__(self, max_messages=50, token_limit=8000):
self.messages = []
self.max_messages = max_messages
self.summary = ""
def add_message(self, role, content):
self.messages.append({"role": role, "content": content})
self._evict_if_needed()
def _evict_if_needed(self):
if len(self.messages) > self.max_messages:
old_msgs = self.messages[:10]
self.summary = self._compress(old_msgs)
self.messages = self.messages[10:]
工作记忆则建议采用结构化格式而非自然语言。以代码编辑 Agent 为例,重构大文件时需要维护当前函数名、已完成修改点、待修改位置、依赖关系图。用字典或栈结构存储,Agent 可以精确更新和查询字段,避免自然语言歧义。
长期记忆存储选型:向量、KV、图
长期记忆的存储设计是记忆系统最核心的工程决策,直接决定写入速度、检索效率和可维护性。主流方案有三种,各有明确的适用边界。
向量数据库是语义检索的首选。文本经 Embedding 模型转为高维向量,检索时计算相似度找 Top-K。优势是模糊匹配——查询词与存储内容字面不同,语义相近就能命中。适合对话历史、知识库这类非结构化内容。写入需计算向量,速度中等。
KV 存储适合精确匹配场景。用户偏好(语言=中文、回答风格=简洁)、事实性知识(用户生日)、配置信息,不需要语义检索,只需精确查找。读写速度最快,扩展性极好。
图数据库以节点和边组织信息,节点代表实体或事件,边代表关系。核心优势是捕捉记忆间的关联,支持多跳推理。比如记住"用户在去年圣诞节提到女儿小美",可表示为用户节点 --has_child--> 小美节点,用户节点 --mentioned_at--> 圣诞节事件节点。查询家庭成员时通过图遍历直接命中。写入需维护关系,速度较慢。
实际系统通常三者结合:向量库负责语义召回,KV 负责精确查找,图库负责关系推理,由统一的记忆管理器协调。
检索排序:不能只看相似度
向量检索的一个常见误区是只按余弦相似度排序。工程上更合理的做法是综合三个因子:语义相似度、重要度、时间衰减。
final_score = sim * 0.7 + mem.importance * 0.2 + recency_bonus * 0.1
时间衰减因子可以用线性函数实现,比如 30 天内从 1 衰减到 0。这样既保证语义相关,又让近期记忆和重要记忆获得加权,避免检索结果被大量陈旧但语义相近的记忆淹没。
记忆压缩:三个层次与质量评估
记忆数据量随运行时间线性增长,压缩是控制存储成本和检索效率的必要手段。压缩分三个层次:
- 消息级压缩:针对单条消息去除冗余,如 500 字调试日志压缩为"执行命令 X,报错 Y,通过 Z 修复",在写入前实时完成。
- 会话级压缩:将数十条消息浓缩为结构化摘要,会话结束时批量执行,用 LLM 生成高质量摘要。
- 跨会话压缩:将多次会话摘要再次合并,形成用户级知识沉淀,频率低但跨度大。
压缩质量看三个指标:信息保留率(关键事实点在摘要中的覆盖率,一般要求不低于 80%)、压缩比(合理范围 5:1 到 20:1)、检索影响度(压缩后召回率和准确率不应显著下降)。
触发时机需要权衡:太频繁增加 LLM 调用开销,太晚导致短期记忆溢出。常用触发条件包括消息数阈值(如超过 50 条)、token 阈值(接近上下文窗口 80%)、时间阈值(如每 6 小时)、事件驱动(会话结束)。实际系统通常组合使用。
遗忘机制:让资源集中在高价值记忆
不加区分地保留所有信息,会导致存储成本失控,检索时大量无关记忆干扰真正有用的内容。遗忘策略有五种典型类型:
| 策略 | 触发条件 | 适用场景 |
|---|---|---|
| 时间驱动 | TTL 到期 | 临时信息、日志 |
| 访问频率驱动 | 长期未访问 | 对话历史、知识片段 |
| 重要度驱动 | 评分低于阈值 | 所有类型记忆 |
| 冲突驱动 | 检测到矛盾信息 | 事实性记忆 |
| 容量驱动 | 超过存储上限 | 存储受限场景 |
时间驱动是最基础的策略,每条记忆带 TTL,日常闲聊可能 7 天,用户偏好 365 天,关键决策记录永久保留。访问频率驱动借鉴 LRU 思路,维护 last_accessed 字段定期清理。冲突驱动处理信息更新,比如用户先说住上海、后说搬到北京,旧记忆应更新。
工程上建议组合使用:TTL 做兜底,重要度做豁免(高重要度记忆不受 TTL 限制),冲突检测做一致性维护。
状态管理框架的落地取舍
记忆系统的状态管理有两种路径:用 LangGraph 这类框架,或自研状态机。
框架的优势是提供现成的状态图抽象、检查点机制和持久化接口,适合流程相对标准化的场景。代价是灵活性受限,复杂记忆逻辑可能需要绕过框架抽象。
自研的优势是完全可控,记忆的写入、检索、压缩、遗忘可以按业务需求精确编排。代价是需要自己处理状态持久化、并发一致性和故障恢复。
一个务实的判断标准:如果 Agent 的任务流程可以用有限状态机清晰描述,且记忆逻辑不复杂,用框架能显著降低开发成本;如果记忆策略需要频繁调整、涉及多种存储的协同,自研的长期维护成本可能更低。
无论哪种路径,核心都是把三层记忆的流转规则显式化——什么信息在什么时机从短期进入长期,什么条件下被检索回工作记忆,什么条件下被压缩或遗忘。规则清晰,系统才可维护。