短期记忆只能装下当下。Agent 真正的「阅历」——历史对话、用户偏好、领域知识、过往经验——必须存在短期记忆之外的地方。这就是长期记忆,它让 Agent 从「金鱼」变成「有阅历的助手」。这一节讲清长期记忆的载体(向量数据库)、索引(Embedding)、选型与写入机制。
如果说短期记忆是「工作台」,长期记忆就是「档案柜」:
| 维度 | 短期记忆(工作台) | 长期记忆(档案柜) |
|---|---|---|
| 容量 | 有限(Token 窗口) | 近似无限 |
| 速度 | 极快(已在上下文) | 慢(需检索) |
| 持久 | 单次会话 | 跨会话/跨任务 |
| 访问 | LLM 直接看 | 必须先检索再拼进 Prompt |
长期记忆的核心动作是「存进档案 → 按需检索 → 拼回工作台」:
💡 长期记忆 ≠ 数据库:传统数据库靠「精确匹配」查(如
WHERE id=123),长期记忆靠「语义相似」查(如「我之前问过类似的吗」)。这种语义检索靠 Embedding + 向量数据库实现,是 Agent 记忆区别于传统存储的根本特征。
要让计算机做「语义相似」检索,第一步是把文本变成向量——这个过程叫 Embedding。
Embedding 把任意长度的文本,映射成一个定长的数值向量(如 768 维或 1536 维浮点数)。
"苹果 2024 营收 $391B" → [0.21, -0.45, 0.88, ..., 0.12] (768 维) "Apple FY2024 revenue" → [0.19, -0.42, 0.91, ..., 0.10] (相似) "今天天气真好" → [-0.33, 0.67, 0.05, ..., -0.88] (不相似)
关键性质:语义相近的文本,向量也相近。
两个向量的「相似度」通常用余弦相似度或点积度量。余弦相似度公式:
查询: "苹果营收" 候选 A: "Apple revenue 2024" sim = 0.92 ← 最相关 候选 B: "苹果的营养价值" sim = 0.71 ← 一词多义干扰 候选 C: "今天的股市行情" sim = 0.35 ← 不相关
⚠️ Embedding 的多义词问题:「苹果」既可是公司也可是水果。Embedding 把两者混在一个向量里,会让「苹果营收」误匹配到「苹果营养」。对抗多义词干扰,靠查询改写(5.3 节)或混合检索(关键词+语义)。
| 模型类型 | 代表 | 特点 |
|---|---|---|
| 通用英文 | OpenAI text-embedding-3、Cohere | 英文强、多语言支持 |
| 中文优化 | BGE、M3E、Ernie | 中文场景更准 |
| 多语言 | E5、BGE-M3 | 跨语言检索 |
| 领域定制 | 自己微调 | 专业领域(医疗、法律) |
选型考虑:语言、领域、维度、速度、成本。生产推荐先用通用模型验证,再按需换领域模型。
文本变成向量后,需要专门存储和检索——这就是向量数据库(Vector Database)。
| 能力 | 说明 |
|---|---|
| 存储 | 存向量 + 原文 + 元数据 |
| 检索 | 给一个查询向量,返回最相似的 Top-K |
| 过滤 | 按元数据预过滤(如「只看用户 A 的记忆」) |
| 更新/删除 | 增量维护记忆 |
朴素检索是「线性扫描」——把查询向量与库里每个向量算相似度,排序取 Top-K。但库里几百万向量时,线性扫描太慢。生产向量库用**近似最近邻(ANN)**算法:
| 算法 | 思想 | 代表实现 |
|---|---|---|
| HNSW | 分层小世界图 | 大多数主流库的默认 |
| IVF | 倒排索引 | Llama Index、Faiss |
| PQ | 乘积量化压缩 | 大规模场景 |
ANN 牺牲少量精度(可能漏掉极少数真正相似项),换取数量级的速度提升。
| 向量库 | 类型 | 特点 | 适用 |
|---|---|---|---|
| Chroma | 嵌入式 | 轻量、零配置、纯 Python | 原型、单机、小规模 |
| Pinecone | 云托管 | 全托管、易扩展、收费 | 生产、不愿运维 |
| Weaviate | 自托管 | 功能全、支持混合检索 | 生产、需定制 |
| Qdrant | 自托管 | Rust 实现、高性能 | 高吞吐生产 |
| Milvus | 自托管 | 分布式、超大规模 | 亿级向量 |
| pgvector | Postgres 扩展 | 复用现有 PG | 已有 PG 的项目 |
| Faiss | 库 | 仅算法、无服务 | 自己造轮子 |
💡 新手选型建议:Chroma 起步 → Qdrant/Weaviate 进生产。Chroma 零配置适合原型验证;当规模或可靠性要求上来,迁到 Qdrant 或 Weaviate。Pinecone 适合「不想运维、付费换省心」的团队。
把什么写进长期记忆、什么时候写,是 5.2 节的核心问题。
第 2.2 节已强调:不是所有观察都值得写进长期记忆。全量写入会让库膨胀且充满噪声。
| 写入策略 | 触发 | 内容 |
|---|---|---|
| 全量写入 | 每步 | 所有 Thought/Action/Observation |
| 里程碑写入 | 任务关键节点 | 完成的子目标、关键决策 |
| 偏好写入 | 检测到偏好变化 | 「用户喜欢简洁回答」 |
| 事实写入 | 关键事实更新 | 「公司 A 营收已发布」 |
| 反思写入 | 任务失败/成功后 | 经验笔记(Reflexion) |
| 该写 | 不该写 |
|---|---|
| 用户长期偏好 | 临时闲聊 |
| 关键事实与决策 | 中间步骤细节 |
| 任务的成功/失败经验 | 重复的过程数据 |
| 跨会话有用的信息 | 仅本次任务相关的临时态 |
按类型分库,每库不同的写入与检索策略,是大型 Agent 的常见做法。
写入时会遇到两个问题:重复写入与信息更新。
同一个事实可能被多次写入(如每次对话都确认「用户时区 UTC+8」)。
| 去重方式 | 做法 |
|---|---|
| 完全去重 | 文本完全相同则不写 |
| 语义去重 | 语义相似度高则合并 |
| 键去重 | 用元数据键(如 user_id+key)判重 |
新事实可能与旧事实矛盾(如「用户时区是 UTC+8」→ 后来「用户搬到 UTC-5」)。
| 处理 | 做法 |
|---|---|
| 新覆旧 | 直接覆盖(适用于时效数据) |
| 版本保留 | 都存,按时间排序 |
| 标记失效 | 旧的标记 invalid 但不删 |
| 冲突告警 | 触发人工确认 |
⚠️ 不处理矛盾 = 记忆出错:一个同时存「用户在 UTC+8」和「用户在 UTC-5」的 Agent,下次决策时会迷惑。矛盾消解是长期记忆的必修课——通常用「新覆旧 + 关键变更触发确认」组合。
把短期与长期记忆串起来用,就是下一节 RAG 的核心。先给个全景预告:
这个「查询 → 向量化 → 检索 → 拼上下文 → 推理 → 写回」的闭环,就是 RAG 的完整流水线,5.3 节会详细展开。
| 反模式 | 表现 | 后果 | 正确做法 |
|---|---|---|---|
| 全量写入 | 每步都存 | 库膨胀+噪声多 | 分类型分级写入 |
| 不检索只用 | 长期记忆存了不用 | 等于没存 | 每轮按需检索 |
| 不分库 | 偏好/事实/经验混存 | 检索互相干扰 | 按类型分库 |
| 不去重 | 同一事实写 N 次 | 浪费空间+排名失真 | 语义/键去重 |
| 不更新 | 旧事实不消解 | 决策矛盾 | 新覆旧+版本管理 |
| 过早大规模 | 一开始就上 Milvus | 运维成本高 | Chroma 起步按需升级 |
💡 长期记忆的设计核心是「克制」:不是「能存多少存多少」,而是「该存的存、该查的查、该忘的忘」。一个塞满但从不被检索的记忆库,比一个精简但高频使用的库更没用。
下一节《5.3 RAG 检索增强生成》将把短期与长期记忆串成完整流水线,讨论从朴素 RAG 到 Advanced RAG 的演进。