找回密码
 立即注册
首页 业界区 业界 让Agent越来越"懂你":长期记忆的原理与工程实现 ...

让Agent越来越"懂你":长期记忆的原理与工程实现

旌磅箱 昨天 22:35
引言

从本质上来说,AI Agent 仍然是一种工具。但与软件工程领域中的其他工具不同,我们对 AI Agent 怀有更高、也更复杂的期待。这种期待源于 AI 能力的加持——我们希望它不仅仅提升效率,而是真正解放生产力。
在这一愿景下,"长期记忆"成为关键能力之一。它的意义在于,让 Agent 不再只是被动执行指令,而是能够在持续交互中逐渐变得更智能、更贴近使用者,甚至越来越"懂我"。
在《AI Agent 的记忆系统:从必要性到工程实践》一文中,我们已经系统阐述了 Agent 记忆的分类及其作用。本文则进一步聚焦其中的长期记忆能力,尝试从工程实践的角度展开具体落地方案。
在正式展开之前,先简单回顾一下长期记忆与短期记忆的区别。
短期记忆:存储会话中产生的各类消息,包括用户输入、模型回复、工具调用及其结果等。这些消息直接参与模型推理,实时更新。当消息累积导致上下文窗口超出限制时,需要通过压缩、卸载、摘要等上下文工程策略进行处理。
长期记忆:Agent 在多次交互与会话之间持久化存储信息的系统。它相当于智能体的"个人档案"或"知识库",用于保留需要跨会话记忆的关键信息,如用户偏好、重要事实、从历史会话中积累的经验与知识等。其核心目标是实现个性化持续学习
Agent 长期记忆的工程实现,核心可以归结为两个基本问题:

  • 如何维护长期记忆 —— 包括长期记忆的新增、更新与删除;
  • 如何使用长期记忆 —— 即在实际推理与决策过程中,如何高效、准确地检索与注入相关记忆。
围绕这两个问题,我们需要首先明确长期记忆的存储载体。在当前主流的工程实践中,长期记忆通常依赖两类基础设施:VectorStore(向量数据库)GraphStore(图数据库)。接下来,我们将从这两种载体的特性与适用场景出发,逐步展开长期记忆的工程化实现方式。
以下内容我们将结合mem0进行说明,github地址:https://github.com/mem0ai/mem0
一、长期记忆的载体

1.1 VectorStore:语义检索的基石

向量数据库是当前长期记忆最主流的存储载体。其核心思路是:将文本信息通过 Embedding 模型转换为高维向量,存储在专门的向量索引结构中。检索时,将查询语句同样转换为向量,通过余弦相似度或内积等度量方法,找出语义最相近的记忆条目。
这种方式的优势非常直接——语义相似即可命中。即便用户的表述与存储时的措辞完全不同,只要语义接近,向量检索就能找到对应的记忆。例如,Agent 存储了"用户不喜欢辣食",当下次对话中用户问到餐厅推荐时,即便没有提及"辣",Agent 也能从向量空间中召回这条偏好。
常见的 VectorStore 选型包括:
方案特点适用场景Chroma轻量级,易集成,支持本地持久化本地开发、小规模部署Qdrant高性能,支持丰富的过滤条件生产环境,记忆量较大Pinecone全托管,开箱即用快速原型、云端部署pgvectorPostgreSQL 扩展,与关系数据融合已有 PG 栈的团队Faiss极致性能,适合批量检索离线分析、大规模召回向量数据库的局限同样明显:它擅长处理孤立事实,但对于实体间关系的表达能力较弱。"张三是李四的上司"和"李四喜欢咖啡"对向量数据库而言没有本质差异,它无法自然地表达和利用实体之间的关联结构。这正是图数据库要解决的问题。
1.2 GraphStore:关系知识的天然载体

图数据库将知识以节点(Node)和边(Edge) 的形式组织,节点代表实体(人、地点、事件等),边代表实体间的关系。这种结构天然适合表达复杂的关联信息。
以 mem0 的 Graph Memory 实现为例,当 Agent 处理"Alice 在 GraphConf 2025 上认识了 Bob"这条信息时:

  • 向量数据库:将这句话嵌入为一个向量,语义检索时可以找到它;
  • 图数据库:提取出 Alice、Bob、GraphConf 2025 三个节点,以及 MET_AT 关系边,形成结构化的知识图谱。
后续当用户问"Alice 认识哪些人?"时,图数据库可以直接沿着边遍历,精确返回所有相关节点,而不依赖语义相似度的模糊召回。
mem0 的 Graph Memory 架构如下:
  1. Conversation → Extraction LLM → VectorStore (embeddings)
  2.                               ↓
  3.                            GraphStore (nodes & edges)
  4. Query → VectorSearch → Candidate Memories
  5.                      → Graph traversal → Contextual Relations
复制代码
两者并行工作:向量检索负责语义召回,图遍历补充关系上下文,最终合并返回给 Agent。
GraphStore 的接入示例(以 Neo4j 为例):
  1. from mem0 import Memory
  2. config = {
  3.     "graph_store": {
  4.         "provider": "neo4j",
  5.         "config": {
  6.             "url": "neo4j+s://<your-instance>.databases.neo4j.io",
  7.             "username": "neo4j",
  8.             "password": "your-password",
  9.         }
  10.     }
  11. }
  12. memory = Memory.from_config(config)
复制代码
目前 mem0 支持的图数据库后端包括 Neo4j、Memgraph、Amazon Neptune 和 Kuzu(嵌入式),可以按部署环境灵活选择。
1.3 两者的核心差异

维度VectorStoreGraphStore存储单元文本片段 + 向量节点 + 边 + 属性检索方式语义相似度搜索关系遍历 + 模式匹配擅长场景偏好、事实、陈述性知识人物关系、事件脉络、多跳推理部署复杂度低中高典型问题类型"用户喜欢什么?""Alice 认识谁?谁参加了哪个会议?"
值得注意的是,以上仅是工程上常见的实现方式,实际应用中可能因具体需求而异。在我们此前对 OpenClaw 的分析《OpenClaw 入门指南:从原理到实战》就发现,其长期记忆是以本地 MEMORY.md 文档的形式存储的——简单直接。这告诉我们,不应拘泥于形式,而应根据实际需要合理评估,有时候最朴素的方案反而是最合适的。
二、如何维护长期记忆?

确定了存储载体,接下来的核心问题是:如何让记忆库保持准确、及时、不冗余
长期记忆的维护本质上是一个持续的 CRUD 过程:新信息到来时新增,信息发生变化时更新,矛盾信息出现时删除。听起来简单,但在实际 Agent 交互中,判断"这条信息是新增还是更新"并不容易。
1.png

2.1 维护策略:规则 + LLM vs 纯 LLM

目前主流的维护策略有两种路径:
路径一:规则 + LLM 判断
设定一些明确的规则,例如"情感倾向明显的表达优先存储"、"重要度评分超过阈值才触发写入",在规则过滤之后再交由 LLM 做最终判断。这种方式可控性强,适合有明确记忆策略的产品场景。
路径二:完全交给 LLM
直接将当前对话和已有记忆库交给 LLM,让它自行判断应该 ADD、UPDATE、DELETE 还是 NONE。这种方式更灵活,能处理更复杂的边界情况。
我个人更倾向于后者。既然我们选择了使用 AI,就应该充分信任 AI 的判断力。在《AI Agent 的记忆系统:从必要性到工程实践》一文中对 Cursor 记忆系统的解析中也发现,Cursor 正是采用了这种"完全交给 LLM"的方式。
mem0 将这种灵活性通过 Custom Update Memory Prompt 开放给开发者,允许用户自定义 LLM 的决策逻辑。
2.2 记忆的新增(ADD)

当会话产生了新的、尚未存储的信息时,触发 ADD 操作。mem0 的信息提取流程分为两步:

  • 信息抽取:通过 LLM 从对话中提取关键事实、偏好、决策等信息片段;
  • 冲突检测:与现有记忆进行比对,确认是否为全新信息。
  1. from mem0 import Memory
  2. m = Memory()
  3. messages = [
  4.     {"role": "user", "content": "我计划下个月去东京旅行。"},
  5.     {"role": "assistant", "content": "好的,我会记住这个计划,方便后续提供相关建议。"}
  6. ]
  7. result = m.add(messages, user_id="alice")
复制代码
执行完毕后,mem0 会自动从对话中抽取"用户计划下个月去东京旅行"这一事实,写入向量存储,同时(如果启用了图存储)在图中创建 Alice → PLANS_TO_VISIT → Tokyo 的关系边。
infer=True 是默认行为,也是推荐做法——让 LLM 决定哪些信息值得存储,而不是盲目地把所有对话都塞进记忆库。
2.3 记忆的更新(UPDATE)

更新是维护中最复杂的操作。当新信息与已有记忆描述的是同一件事,但内容有所变化时,需要更新而非新增。
以 mem0 的 Custom Update Memory Prompt 为例,它通过结构化的 LLM 决策来处理这个问题:
  1. UPDATE_MEMORY_PROMPT = """你是一个智能记忆管理器,负责控制系统的记忆。
  2. 你可以执行四种操作:(1) 新增记忆 (2) 更新记忆 (3) 删除记忆 (4) 不做变更
  3. 比较新获取的事实与现有记忆。对于每条新事实,决定是否:
  4. - ADD:作为新条目添加
  5. - UPDATE:更新现有记忆条目
  6. - DELETE:删除现有记忆条目  
  7. - NONE:不做变更(事实已存在或不相关)
  8. """
复制代码
更新场景示例:
  1. 旧记忆:[{"id": "0", "text": "用户喜欢奶酪披萨"}]
  2. 新事实:["用户喜欢鸡肉披萨和奶酪披萨"]
  3. 决策结果:
  4. {
  5.   "memory": [{
  6.     "id": "0",
  7.     "text": "用户喜欢鸡肉披萨和奶酪披萨",
  8.     "event": "UPDATE",
  9.     "old_memory": "用户喜欢奶酪披萨"
  10.   }]
  11. }
复制代码
有两个细节值得关注:保留原始 ID,确保历史追溯链路不断;记录 old_memory,方便审计与 Debug。
2.4 记忆的删除(DELETE)

当新信息与已有记忆产生直接矛盾时,触发 DELETE。这是记忆库保持"当前真实"的关键机制。
  1. 旧记忆:[{"id": "1", "text": "用户喜欢奶酪披萨"}]
  2. 新事实:["用户不喜欢奶酪披萨"]
  3. 决策结果:
  4. {
  5.   "memory": [{
  6.     "id": "1",
  7.     "text": "用户喜欢奶酪披萨",
  8.     "event": "DELETE"
  9.   }]
  10. }
复制代码
矛盾信息不应被"更新"掩盖,而应被删除——因为这不是一次"说法变化",而是一次"事实反转"。LLM 需要能准确区分这两种情形。
三、如何使用长期记忆?

维护好的记忆库,只有在被有效利用时才体现价值。"如何使用长期记忆"的本质,是在正确的时机,把正确的记忆注入到 LLM 的上下文中
2.png

3.1 检索流程全貌

mem0 的检索流水线如下:
  1. 用户查询
  2.    ↓
  3. 查询改写(Query Rewrite)
  4.    ↓
  5. 向量检索(Vector Search)
  6.    ↓
  7. 重排序(Reranking)
  8.    ↓
  9. 注入上下文
复制代码
每一步都在为最终结果的精准度做贡献,不能随意省略。
3.2 为什么需要查询改写(Query Rewrite)?

用户在对话中的表达往往不适合直接作为检索 Query。以一个具体例子说明:
用户问:"帮我推荐一家今晚吃饭的地方。"
这句话直接丢给向量检索,能命中什么?最可能返回的是"用户曾经去过某家餐厅"或"用户提到过晚饭"——而真正有用的记忆是"用户不吃辣"、"用户偏好日式料理"、"用户对花生过敏"。
查询改写的作用,就是将用户的原始表达转化为更适合记忆检索的语义查询。例如将上述问题扩展为:"用户的饮食偏好"、"用户的食物禁忌"、"用户过往的餐厅偏好",分别独立检索,再合并结果。
这一步通常由一个轻量的 LLM 调用完成,成本可控,但对检索质量的提升非常显著。
3.3 为什么需要重排序(Reranking)?

向量检索基于余弦相似度(最常见),返回的是"语义最近"的结果,但"语义最近"≠"最相关"。
举一个典型的反例。用户问:"我学过哪些编程语言?",记忆库中存储了以下四条:

  • A "用户认为学好编程最重要的是多实践"
  • B "用户大学读的是计算机专业,辅修数学"
  • C "用户掌握 Python、Java 和 Go,目前主力使用 Go"
  • D "用户不喜欢前端开发"
向量检索按余弦相似度排序后,假设结果可能是:A(0.88)> B(0.85)> C(0.79)> D(0.74)。
原因并不难理解:A 里"学好编程"和 B 里"计算机专业"与查询中"学过"和"编程语言"的向量表示高度重叠,而 C 虽然是这道题的直接答案,但措辞不同,语义向量反而排第三。
如果此时 limit=2,用到的记忆就只有 A 和 B,真正的答案 C 被截断丢掉了。
Reranking 的作用正在于此:它在向量召回之后引入另一个模型(通常是交叉编码器 Cross-Encoder),将查询和每条记忆作为一对输入,整体评估其语义相关性,而不是分别计算各自的向量再做比较。经过精排,C 会被推到第一位,A 和 B 反而被压后。
Reranking 前后的排序对比:
排名向量检索(召回阶段)Reranking(精排后)1A - "学好编程最重要的是多实践"C - "掌握 Python、Java 和 Go"2B - "大学读计算机专业"B - "大学读计算机专业"3C - "掌握 Python、Java 和 Go"D - "不喜欢前端开发"4D - "不喜欢前端开发"A - "学好编程最重要的是多实践"在 mem0 中,开启 Reranking 只需一个参数:
  1. results = m.search(
  2.     "推荐一部今晚适合看的电影",
  3.     user_id="alice",
  4.     rerank=True,
  5.     limit=5
  6. )
复制代码
两阶段检索(向量召回 + Reranking 精排)是当前 RAG 领域的主流范式,在记忆检索场景同样适用,也是 mem0 官方推荐的使用方式。
3.4 其他优化策略

除了改写和 Reranking,还有几种常见的检索优化手段:
① 相关度阈值过滤(Threshold Filtering)
只返回相似度分数超过阈值的记忆条目,避免将不相关的记忆注入上下文,干扰 LLM 推理。
  1. results = m.search(
  2.     "用户的饮食偏好",
  3.     user_id="alice",
  4.     threshold=0.7  # 只返回相似度 > 0.7 的记忆
  5. )
复制代码
② 元数据过滤(Metadata Filtering)
为记忆条目附加结构化元数据,检索时通过过滤条件缩小候选范围,提高精度同时降低延迟。
  1. # 写入时携带元数据
  2. m.add(messages, user_id="alice", metadata={"category": "food_preference"})
  3. # 检索时按类别过滤
  4. results = m.search(
  5.     "用户喜欢吃什么",
  6.     user_id="alice",
  7.     filters={"categories": {"contains": "food_preference"}}
  8. )
复制代码
③ 图关系增强(Graph-Enhanced Retrieval)
对于涉及实体关系的查询,启用图存储可以在向量召回的基础上补充关系上下文,使 Agent 对复杂关联有更完整的认知。
  1. results = m.search(
  2.     "Alice 认识哪些人?",
  3.     user_id="demo-user",
  4.     enable_graph=True
  5. )
  6. # results["results"] 包含向量召回的记忆
  7. # results["relations"] 包含图数据库返回的关系信息
复制代码
④ 多路检索合并(Multi-Query Retrieval)
对一个用户请求生成多个不同角度的检索 Query,分别独立检索后合并去重。这在用户问题较模糊或涉及多个维度时特别有效。
以"帮我安排下周的学习计划"为例,这是一个需要综合多维度上下文才能回答好的问题。如果只用原始问题检索,很可能只能命中"用户提到过学习"之类的泛化记忆。
正确做法是先将其拆解为多个语义明确的子查询:
  1. from mem0 import Memory
  2. m = Memory()
  3. user_query = "帮我安排下周的学习计划"
  4. # 将原始问题拆解为多个子查询,覆盖不同维度
  5. sub_queries = [
  6.     "用户的学习目标和方向",
  7.     "用户每天的可用时间和作息习惯",
  8.     "用户目前的学习进度",
  9.     "用户偏好的学习方式",
  10. ]
  11. # 分别检索,去重合并
  12. all_memories = []
  13. seen_ids = set()
  14. for q in sub_queries:
  15.     results = m.search(q, user_id="alice", limit=3)
  16.     for mem in results["results"]:
  17.         if mem["id"] not in seen_ids:
  18.             all_memories.append(mem)
  19.             seen_ids.add(mem["id"])
  20. # all_memories 包含从四个角度召回、去重后的完整上下文
复制代码
四路检索分别可能命中:

  • 子查询1 → "用户目标是通过 AWS 认证考试"、"用户想深入学习分布式系统"
  • 子查询2 → "用户工作日晚上有 1-2 小时空余"、"用户周末可以学习半天"
  • 子查询3 → "用户已经看完了第 3 章,卡在第 4 章的实验上"
  • 子查询4 → "用户更喜欢边做项目边学,不喜欢纯看书"
将这些记忆合并注入上下文,Agent 才能给出真正贴合用户实际情况的学习计划,而不是完全自由发挥、脱离实际需要的结果。
结语

长期记忆是 AI Agent 从"工具"走向"伙伴"的关键一步。
本文围绕长期记忆的两个核心工程问题展开:存储载体的选择(VectorStore vs GraphStore),以及记忆的维护(ADD/UPDATE/DELETE)与检索(改写 + 向量召回 + Reranking)。
核心逻辑就是:把合适的信息存在合适的地方,在合适的时机以合适的方式取出来。这句话说起来平平无奇,但其中每一个步,都是工程上多次实践后的结晶。
在如何维护长期记忆这个问题上,我更倾向于信任 LLM 的判断——无论是记忆维护时的 ADD/UPDATE/DELETE 决策,还是检索时的查询改写,LLM 的灵活性远超硬编码规则。当然,"信任 AI"并不意味着放任不管,有审计、有监控、可干预,才能使我们设计出一个更安全、可靠的Agent。
Agent 的“懂你”,从来不是一蹴而就的。
它不是某个模型参数的瞬间跃迁,而是一次次交互之后,一条条记忆沉淀下来的结果。
而我们作为工程师,要做的并不是赋予它“理解”的幻觉,而是为这种长期积累的过程搭建稳固的地基——
让记忆可存储、可演化、可调用,
让“越来越懂你”不再只是愿景,而成为一种可以被工程化实现的能力。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册