长期记忆
🎯 学习目标
- 区分短期对话缓冲、摘要记忆与向量长期记忆
- 设计 Memory 写入策略(何时记、记什么、谁可读)
- 识别 Memory 污染与 Context Collapse 风险
- 将 Memory 与 RAG 的职责边界说清楚
引言
RAG 解决「组织知识」;Memory 解决「这个用户之前说过什么、偏好是什么」。二者互补但易混:把整库当 Memory 或把闲聊全写入长期记忆都会导致检索与隐私灾难。本节建立 Memory 分层与治理框架。
章节正文
第 1 步:三层 Memory 模型
| 层级 | 存储 | 生命周期 | 典型内容 |
|---|---|---|---|
| 短期 | 消息数组 / Redis | 单会话 | 最近 N 轮对话 |
| 摘要 | DB 文本 | 跨会话 | 用户目标、已确认事实 |
| 长期向量 | Vector + metadata | 用户级 | 偏好、历史案例 embedding |
短期靠窗口与滑动;摘要靠定期 compression;长期靠「是否值得记」分类器。
第 2 步:ChatMessageHistory 与滑动窗口
python
class SessionMemory:
def __init__(self, max_turns=20):
self.messages = []
def add(self, role, content):
self.messages.append({"role": role, "content": content})
if len(self.messages) > max_turns * 2:
self.messages = self.messages[-max_turns * 2 :]超过窗口时触发摘要回填(见 5.5),而非无声丢弃。
第 3 步:何时写入长期记忆
python
def should_persist_memory(turn) -> bool:
# 示例规则:用户显式「记住」、或提取的稳定偏好
if "请记住" in turn.user_text:
return True
if turn.extracted_facts and turn.user_confirmed:
return True
return False默认不把工具返回的大段 JSON、RAG chunk 写入用户长期记忆——应存 session 或丢弃。
第 4 步:向量记忆检索
长期记忆按 user_id partition,检索时 filter:
python
hits = memory_store.search(embed_query(question), filter={"user_id": uid}, top_k=3)注入 Prompt 时标注「来自历史会话,可能过时」,并与 RAG 企业知识区分来源。
第 5 步:失效模式与治理
- Memory 污染:错误事实被记住 → 提供「忘记/更正」入口,版本化 memory entry
- 与 RAG 冲突:用户口述与文档矛盾 → Prompt 优先可信文档,memory 仅作偏好
- 隐私:PII 不入长期记忆或加密;合规删除 propagate 到向量库
- Context Collapse:摘要过度 → 保留关键 fact 列表结构化存储
动手练习
- 实现 SessionMemory 滑动窗口 + 超窗触发摘要的伪代码。
- 写 should_persist_memory 规则 5 条,区分应记与不应记内容。
- 设计 memory entry schema:id、user_id、text、created_at、supersedes。
- 模拟 RAG 与 Memory 冲突:文档说 A,用户历史说 B,写 Prompt 优先级策略。
- 列出 GDPR 删除用户数据时需触达的存储(消息、摘要、向量)。
常见问题
Q:Memory 和 RAG 都要向量库吗?
可以同一引擎不同 collection,但 schema 与权限不同。RAG 多租户只读企业知识;Memory 强 user_id 隔离。勿混索引。
Q:摘要记忆会不会丢细节?
会。关键数字、日期、已确认约束应进 structured fact store;摘要只叙叙事脉络。
Q:Multi-Agent 如何共享 Memory?
通过共享 fact store 或黑板,而非拼接全部对话。各 Agent 窗口独立,只同步精炼结论(见 5.7)。
本节小结
Memory 分短期、摘要、长期三层;写入要 selective,与 RAG 分责;治理删除、更正与冲突优先级。好 Memory 让用户感到「被记住」,而不是「被监视」。