向量检索不是记忆:Agent 记忆的三层进化

导读:给 Agent 接上向量数据库不等于它就有了记忆。真正的 Agent 持久化记忆系统是一个包含写入路径、维护策略和检索管线的完整子系统------它经历三个阶段:先把原始交互"存下来"(Storage),再学会"归纳和压缩"(Reflection),最终做到"从经验中自主学习"(Experience)。这篇文章展开每一层的核心原理、工程困境和关键代码实现。

2023 年 4 月,斯坦福和 Google 的论文《Generative Agents》(arXiv:2304.03442)让 25 个 AI 角色在一个虚拟小镇里活了起来------它们聊天、散步、策划派对。这篇论文的核心不是模型,而是一套叫"记忆流"(memory stream)的机制:每个 Agent 把自己看到、听到、想到的一切写成自然语言记录,然后按"相关性 × 时效 × 重要性"的三维分数在需要时检索。

三年过去,"Agent 记忆"已经从一个论文概念变成了一个有自己 benchmark 体系、按架构分化的框架生态和一个被量化的 benchmark-to-production 精度鸿沟的独立工程领域。

但一个根本性的混淆始终存在:很多人以为记忆就是检索。给 Agent 接上向量数据库,把对话历史 embed 进去,query 的时候拉回 top-k 相似片段------然后困惑地发现 Agent 还是记不住你是谁。

向量检索本质上还是 RAG,不是真正意义上的记忆。两者的区别不在技术,在生命周期------检索是单向的读操作,记忆有写入路径。


CoALA:一张地图

用一个统一的概念框架来定位三层进化。2023 年普林斯顿团队提出的 CoALA(Cognitive Architectures for Language Agents,arXiv:2309.02427)是目前被引用最广的 Agent 认知架构论文。它将 Agent 记忆分为四种类型:

工作记忆 (working memory):当前任务的临时草稿纸------子目标、中间计算结果、进行中的状态。存于上下文窗口,任务结束即丢弃。•情景记忆 (episodic memory):带时间戳的事件记录------"上次用户问这个问题时,我给了这个答案,他说不对"。追加式日志,按相关性 + 时效检索。•语义记忆 (semantic memory):去上下文化的事实------"用户住在杭州""团队的技术栈是 Python + PostgreSQL"。存于向量数据库或知识图谱,跨会话持久。•程序性记忆(procedural memory):怎么做的知识------技能、策略、工具使用模式。常驻于系统提示词、Agent 代码、微调权重中。

这四种记忆不是学术分类游戏。它们对应三种不同的存储介质、三种不同的生命周期和三种不同的检索策略。CoALA 的价值在于给后续一切工程方案提供了一张地图------你加的任何一个"记忆功能",都能在这张地图上找到它属于哪个象限。而大多数"给 Agent 加了记忆"的实现,只碰了语义记忆这一种,忽略了其他三种。


1. Storage:为什么向量检索不是记忆

一个写入路径包含三件事:提取、去重、冲突解决。 三个环节,少了任何一个都会影响记忆质量。

以 Mem0 的生产架构(ECAI 2025, arXiv:2504.19413)为例。当用户说"我把后端从 Django 迁移到 FastAPI 了",写入管线是这样的:

1提取 :LLM 从原始消息中提取结构化事实------{"fact": "用户后端框架: FastAPI", "source": "message_42", "timestamp": "2026-07-20"}。这不是简单的文本切片,而是一次语义级别的重写。2去重与冲突检测 :新事实"FastAPI"与旧事实"Django"语义冲突。系统识别后不删除旧记录,而是将新记录标记为 supersedes: old_record_id。3存储 + 嵌入:事实本体存入结构化表,embedding 向量存入向量索引,两者通过 fact_id 关联。

Supersedes 关系是记忆系统的一等公民。 追加式存储(append-only)如果没有 superseeds,旧事实永远不会被"更正",只会被"叠加"。当 Agent 同时检索到"用户后端是 Django"和"用户后端是 FastAPI"时,它需要自己判断哪个更新------而这恰好是 LLM 不擅长的事(它没有内置的时间推理能力)。

python 复制代码
from uuid import uuid4
from datetime import datetime, timezone

class MemoryStore:
    """带 supersedes 链条的简化记忆存储"""

    def __init__(self):
        self.facts: list[dict] = []  # 事实表
        # 注意:self.embeddings 仅作示意,生产中使用外部向量索引
        self.embeddings = []         # 向量索引(简化)

    def add(self, fact: str, source: str) -> str:
        """写入:冲突检测 → 生成 ID → 存储"""
        # 冲突检测:对同类事实做语义相似度匹配(stub: 实际使用 LLM)
        conflict = self._find_conflict(fact)
        fact_id = str(uuid4())
        self.facts.append({
            "id": fact_id,
            "fact": fact,
            "source": source,
            "supersedes": conflict["id"] if conflict else None,
            "timestamp": datetime.now(timezone.utc)
        })
        # 嵌入并存入向量索引(stub: 实际使用 embedding model)
        self.embeddings.append({"id": fact_id, "vec": self._embed(fact)})
        return fact_id

    def retrieve(self, query: str, top_k: int = 5) -> list[dict]:
        """读取:混合检索 → supersedes 过滤 → 返回活跃事实"""
        # 第一步:多路检索(stub: 向量 + 关键词)
        vector_results = self._vector_search(query, top_k * 2)
        bm25_results = self._bm25_search(query, top_k * 2)
        # 第二步:RRF 融合(Reciprocal Rank Fusion, stub)
        merged = self._rrf_merge(vector_results, bm25_results)
        # 第三步:过滤被 supersede 的记录
        # 收集所有被其他记录指向的 ID(链式 supersedes 自动处理)
        superseded_ids = {f["supersedes"] for f in self.facts if f["supersedes"]}
        valid = [r for r in merged if r["id"] not in superseded_ids]
        return valid[:top_k]

即使旧事实的向量相似度高于新事实,检索结果也不会同时返回矛盾的两条。supersedes 链条在检索阶段就完成了"一致性过滤"------不需要 LLM 自己判断哪个版本更新。

但纯向量检索很难做到精确匹配。 当用户问"我的 API key 是什么",向量相似度检索可能返回"API 配置相关"的一大堆内容,但漏掉包含实际 key 的那条记录。生产中常见的解决方案是双路检索:BM25(关键词匹配)+ 稠密向量(语义匹配),再用 RRF 或 cross-encoder 重排序。Elasticsearch Labs 用这套方案在生产环境跑出了 R@10 0.89 的召回率。


2. Reflection:OS 隐喻下的上下文管理

存下来是第一步。第二步是决定什么该留在上下文里、什么该移到长期存储------并且让 Agent 自己管理这件事。

MemGPT(arXiv:2310.08560,现已更名为 Letta)是这套思路的开创者。它把 LLM 的上下文窗口类比为操作系统的 RAM------容量有限,程序只能访问当前驻留在"内存"里的数据。需要时,操作系统把数据从"磁盘"分页到"内存";不需要时,swap 出去。

翻译成 Agent 的语境:

主上下文(RAM):系统提示词 + 工作记忆 + 最近的对话历史(FIFO 队列)。容量 = 模型的 context window。

外部存储(Disk):归档记忆(Archival Memory,存于向量数据库,按语义检索)+ 回忆存储(Recall Storage,完整对话历史的可搜索存档)。

Agent 通过 function calls 自主管理"分页" ------决定什么时候把当前上下文里的关键信息写入归档、什么时候从归档检索相关记忆回来。这个决策不是写死在代码里的"每 10 轮做一次摘要",而是 Agent 在运行时自己判断的。

python 复制代码
class MemGPTAgent:
    """精简版 MemGPT 的记忆管理循环(一次只处理一个 tool call)"""

    def __init__(self, model, archival_store, max_context_tokens=4096):
        self.model = model
        self.archival = archival_store    # 归档记忆(向量数据库)
        self.recall: list[str] = []       # 回忆存储(完整对话记录)
        self.core_memory = {"persona": "", "human": ""}  # 核心记忆块
        self.max_tokens = max_context_tokens

    def step(self, user_message: str) -> str:
        """单步交互:组装上下文 → LLM 推理 → 解析 tool call → 返回"""
        context = self._assemble_context(user_message)
        response = self.model.chat(context, tools=[
            self._tool_archival_insert,   # 写入归档
            self._tool_archival_search,   # 检索归档
            self._tool_core_memory_edit,  # 编辑核心记忆
        ])
        # 解析 tool calls:本示例假定一次只触发一个 tool call
        for tool_call in response.tool_calls:
            if tool_call.name == "archival_insert":
                self.archival.insert(tool_call.args["content"])
            elif tool_call.name == "archival_search":
                results = self.archival.search(tool_call.args["query"])
                context = self._inject_results(context, results)
                # 重新推理(带检索结果,不再允许进一步 tool call)
                response = self.model.chat(context, tools=[])
                break  # 一次只处理一个 tool call
        return response.text

    def _assemble_context(self, user_msg: str) -> str:
        """组装上下文 = 系统指令 + 核心记忆 + 近期对话 + 用户消息"""
        tokens = self.max_tokens
        system = f"你是 {self.core_memory['persona']}。用户: {self.core_memory['human']}"
        tokens -= self._count(system)  # stub: 实际使用 tokenizer 计数
        # 从最近的 N 条对话中取,直到填满 token 预算的 70%
        # 预留 30% 给 user_msg 和模型输出
        history = []
        for msg in reversed(self.recall[-20:]):  # 20 为回顾轮数上限
            n = self._count(msg)
            if tokens - n < self.max_tokens * 0.3:
                break
            history.insert(0, msg)
            tokens -= n
        return "\n".join([system] + history + [user_msg])

这个架构的核心洞察是:上下文窗口本质上是一块 Agent 主动管理的工作内存,而非单纯的读入通道。 压缩不是被动的"窗口满了怎么办",而是 Agent 决策循环的一部分------它决定留什么、存什么、什么时候该召回。

但压缩有代价。 2026 年 7 月的一篇综述(Colaco & Lahjouji, arXiv:2607.08032)将 Agent 记忆系统的所有压缩方案统一到了一个框架下:rate-distortion tradeoff。简单来说------压缩就是丢掉信息,丢多少信息换多少 token 节省,是一个不可调和的取舍。

可逆压缩(分页时保留原始副本、时序知识图谱的双时态追踪)保真度最高,但 token 节省有限。不可逆压缩(摘要式压缩、定期归并旧记忆)token 节省明显,但每次压缩丢 5-10% 细节,十轮后的累积漂移足以让 Agent 的行为约束偏离原始设定------"传话游戏"效应。

微软的 ACON(arXiv:2510.00615)用蒸馏策略取得了 26-54% 的 token 压缩,同时保留 95% 以上的任务性能。但它的前提是:压缩器本身是一个更强的模型,需要用教师模型的知识蒸馏训练------这恰恰说明压缩不是一个纯工程优化问题,它依赖模型本身的认知能力。


3. Experience:记忆的自主进化

前两层解决的是"怎么存"和"怎么管"。但 2026 年最活跃的前沿方向在第三层:让 Agent 自己学会怎么记忆。

ACL 2026 的 AgeMem(Agentic Memory, arXiv:2601.01885)把记忆操作抽象为 Agent 的工具调用------六种操作:ADD(写入长期记忆)、UPDATE(更新)、DELETE(删除)、RETRIEVE(检索)、SUMMARY(摘要)、FILTER(过滤)。关键是,它不是手工写规则决定什么时候用哪种操作,而是用三阶段渐进式 RL 训练:

1基础交互 :Agent 在简单对话中学习基本记忆操作的时机2干扰过滤 :训练数据加入大量无关信息,Agent 学会区分"该记的"和"该忽略的"3任务执行:在需要跨会话记忆的长周期任务上优化决策

结果是,AgeMem 在 5 个长周期 benchmark 上超过了测试中所有手工规则的基线方案。

但 RL 训练的记忆管理有一个隐含假设:Agent 可以犯错。 在生产系统中,一条错误删除、一次错误覆盖的代价可能很高------用户偏好被清空、关键上下文被覆盖。

2026 年 3 月的 SSGM 框架(Stability- and Safety-Governed Memory, arXiv:2603.11768)正是针对这个问题:它把记忆分为可变轨道和不可变轨道,Agent 可以自由编辑可变轨道的内容(偏好、学习到的策略),但不可变轨道(系统约束、安全规则)只有验证过的更新才能写入。双轨设计不是阻止自主进化,而是给进化加了护栏。

Letta 在 2026 年 6 月发布的"Memory Models"走了另一条路:不是通过 RL 训练参数,而是通过 sleep-time compute 做持续学习。Agent 在空闲时段自己回顾对话日志、整合新知识、修改核心记忆块------不改变模型权重,只改变上下文。

这和 CoALA 的程序性记忆形成互补:前者在 token 空间做学习,后者在参数空间做学习。目前来看,token 空间的路径更安全、更可审计、也更可控。


生产数据:benchmark 到现实的距离

Benchmark 数字和现实生产环境之间存在差距。2026 年 5 月 RankSquire 的 Memory Fidelity Curve 给出了一条基于单一案例的拟合曲线(尚未独立验证):

scss 复制代码
生产精度 ≈ Benchmark 分数 - (0.22 × 陈旧率) - (0.15 × log₁₀(实体数))

以 Mem0 v0.8.2 为例:LongMemEval benchmark 上 93.4 分,但在 RankSquire 的 50K 会话生产模拟中,有效精度降至 49.0%,44.4 个百分点的鸿沟。

这个差距主要来自三个因素:

实体膨胀 :一个用户 100 条记忆中,只有 20 条还"活跃",其余 80 条是被 supersede 的旧版、过期偏好、一次性的上下文。向量检索无法自动区分。•时效衰减 :生产环境的写入频率远高于 benchmark。benchmark 假设每条记忆都被正确索引和更新,但生产中写入管线有延迟、有失败、有不完整的提取。•检索噪声:向量检索在 1000 条记忆量级上表现良好,到 50K 条时"最近邻"开始混入语义相关但时序无关的内容。

核心结论不是"这些系统不行",而是"当前的 benchmark 测的与实际生产需要之间存在着结构性错位"

LoCoMo 和 LongMemEval 的评估方式(注入固定事实 → 固定间隔查询 → 统计精度)覆盖了记忆的"读取"面,但没有覆盖"写入"面的质量------提取准确率、冲突解决率、时间推理能力、遗忘合规性。MemoryArena(ICML 2026)和 MemBench 正在补这个缺口,但目前这些新 benchmark 还没有成为行业标准。


还没解决的问题

前文梳理了三层进化(Storage、Reflection、Experience),每一层都有明确的工程方案和学术基础。但三层串起来后,压缩和检索之间存在结构性冲突

压缩要求你做摘要(用更少的 token 表达同样的信息)。但摘要本身是丢失精度的。当检索依赖的不是原始记录而是压缩后的摘要时,每次检索都在一个已经失真的空间里做搜索。然后新一轮写入又在上一轮检索的失真结果上做提取------这是两个 lossy 步骤的串联,误差会被放大而不是抵消。

MemGPT 用原始副本 + 摘要并存的方案部分缓解了这个问题------检索时用摘要做快速过滤,需要精确内容时回退到原始记录。但这意味着存储成本线性增长。生产系统迟早要面对"删不删"的问题。

而"删不删"的答案,目前看是"不删"。Abhishek Chauhan 在 2026 年的生产实践总结中提出 No-Delete Principle:情景记忆不做自动删除,只做压缩摘要;原始记录保留为审计源;只有用户显式请求删除时才硬删除。这本质上是把"要不要记住"的决策权从系统交还给用户------系统负责检索优化和存储管理,但不替用户删除。

这大概就是 Agent 记忆系统的现状:原理清晰了,工程方案有了,benchmark 也在追赶,但"什么是该记住的、什么是该忘掉的"这个问题,最终还是要人来做决定。

参考来源

  • CoALA 论文 --- Sumers et al., "Cognitive Architectures for Language Agents" (arXiv:2309.02427, 2023)
  • MemGPT 论文 --- Packer et al., "MemGPT: Towards LLMs as Operating Systems" (arXiv:2310.08560, 2023)
  • Generative Agents 论文 --- Park et al., "Generative Agents: Interactive Simulacra of Human Behavior" (arXiv:2304.03442, 2023)
  • Zep/Graphiti --- "A Temporal Knowledge Graph Architecture for Agent Memory" (arXiv:2501.13956, 2025)
  • AgeMem --- "Agentic Memory: Learning Unified Long-Term and Short-Term Memory Management" (arXiv:2601.01885, 2026)
  • SSGM --- "Governing Evolving Memory in LLM Agents" (arXiv:2603.11768, 2026)
  • 压缩 rate-distortion 综述 --- Colaco & Lahjouji, "What to Keep, What to Forget: A Rate--Distortion View of Memory Compaction" (arXiv:2607.08032, 2026)
  • ACON --- "Optimizing Context Compression for Long-horizon LLM Agents" (arXiv:2510.00615, 2025)
  • 记忆综述 --- "Memory in the Age of AI Agents" (arXiv:2512.13564, 2025)
  • Mem0 --- "Building Production-Ready AI Agents with Scalable Long-Term Memory" (arXiv:2504.19413, 2025)
  • 生产架构 --- Abhishek Chauhan, "Production Agent Memory: Compaction, Decay, and the Observation Engine" --- dev.to (2026)
  • Benchmark-to-Production Gap --- RankSquire Memory Fidelity Curve --- ranksquire.com (May 2026)
  • Elasticsearch Labs --- "Agent Memory on Elasticsearch: Hybrid Retrieval and DLS" --- elastic.co (2026)
  • Letta --- docs.letta.com/
  • Mem0 --- docs.mem0.ai/
  • LangChain Memory --- docs.langchain.com/oss/python/...
相关推荐
大模型码小白1 小时前
Java 部署:Jenkins Pipeline 构建 Java 项目(自动化)
java·人工智能·python·机器学习
paeamecium1 小时前
【PAT甲级真题】- Kuchiguse (20)
数据结构·c++·python·算法·pat考试·pat
思考着亮2 小时前
11.Rag
agent
不一样的少年_2 小时前
原来 AI Agent 的核心循环这么简单:手搓一个 Agent Loop
前端·后端·agent
满怀冰雪2 小时前
09-使用 paddle.nn 构建第一个多层感知机
python·深度学习·神经网络·paddle
Bigger2 小时前
🔥每天最难的问题不是做饭,而是今天到底吃什么——我做了「烟火食间」
前端·人工智能·agent
李燚2 小时前
ReAct Agent 源码拆解:Eino 如何把 Graph 变成 Agent(第64篇-E50)
ai·agent·react·graph·multi-agent·aiagent·eino
CoovallyAIHub2 小时前
当制造业遇上 AI 智能体:Coco 把工艺知识留在了工厂里
llm·agent
小大宇3 小时前
python sqlalchemy 案例
开发语言·python