长期记忆存储介质选型:向量 / 结构化 / 图谱
难度:中级 分类:Memory & State
简短回答
Agent 长期记忆有三种主要存储介质,各擅其长:(1) 向量记忆(Embedding + 向量数据库)擅长语义相似度检索,基于"意思相近"找到相关记忆,适合非结构化文本和模糊查询;(2) 结构化记忆(SQL / NoSQL)擅长精确属性查询,适合用户画像、偏好、配置等键值化数据;(3) 知识图谱(Neo4j + Graphiti 等)擅长关系推理与时间推理——支持多跳推理(A→B→C)、时间有效性追踪(事实何时变更)、实体关系网络,回答"A 的上级是谁?""这个信息何时变化?"等结构化问题。代表性的 Graphiti(Zep 出品)采用双时间线模型(事件时间 + 入库时间)+ 混合检索(语义 Embedding + 关键词 BM25 + 图遍历)。生产系统推荐混合架构——向量存对话历史和非结构化内容,图谱存事实、实体和关系,结构化存属性配置。Mem0 的混合方案实现了 26% 准确率提升和 90%+ token 成本降低。
详细解析
三种介质的核心区别
#JxRTNbivxGhL text { white-space: pre; }#JxRTNbivxGhL .fb0 { stroke: var(--N900-FG, 0.15); }#JxRTNbivxGhL .fb0 {stroke: rgba(31,35,41, 0.15)}#JxRTNbivxGhL .fx0 { stroke-miterlimit: 10; }#JxRTNbivxGhL .fx0 {stroke-miterlimit: 10}#JxRTNbivxGhL .fa1 { fill: rgb(187, 191, 196); }#JxRTNbivxGhL .fa1 {fill: rgb(187, 191, 196)}#JxRTNbivxGhL .fx1 { font: 400 12px MonospacedNumber, LarkHackSafariFont, LarkEmojiFont, LarkChineseQuote, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Tahoma, "PingFang SC", "Microsoft Yahei", Arial, "Hiragino Sans GB", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; }#JxRTNbivxGhL .fx1 {font-style: normal}#JxRTNbivxGhL .fx2 { text-anchor: middle; }#JxRTNbivxGhL .fx2 {text-anchor: middle}#JxRTNbivxGhL .fx3 { dominant-baseline: central; }#JxRTNbivxGhL .fx3 {dominant-baseline: central}#JxRTNbivxGhL .fx4 { stroke: none; }#JxRTNbivxGhL .fx4 {stroke: none}#JxRTNbivxGhL .fc0 { fill: var(--text-caption); }#JxRTNbivxGhL .fc0 {fill: #646a73}#JxRTNbivxGhL .fa2 { fill: var(--text-link-disabled); }#JxRTNbivxGhL .fa2 {fill: rgb(187, 191, 196)}#JxRTNbivxGhL .fx5 { font: 400 13px MonospacedNumber, LarkHackSafariFont, LarkEmojiFont, LarkChineseQuote, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Tahoma, "PingFang SC", "Microsoft Yahei", Arial, "Hiragino Sans GB", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; }#JxRTNbivxGhL .fx5 {font-style: normal}#JxRTNbivxGhL .fx6 { text-anchor: start; }#JxRTNbivxGhL .fx6 {text-anchor: start}#JxRTNbivxGhL .fx7 { dominant-baseline: alphabetic; }#JxRTNbivxGhL .fx7 {dominant-baseline: alphabetic}#JxRTNbivxGhL .fc1 { fill: rgb(0, 0, 0); }#JxRTNbivxGhL .fc1 {fill: rgb(0, 0, 0)}#JxRTNbivxGhL .fb1 { fill: var(--N900-FG, 0.15); }#JxRTNbivxGhL .fb1 {fill: rgba(31,35,41, 0.15)}#JxRTNbivxGhL .fb2 { fill: rgb(209, 217, 224); }#JxRTNbivxGhL .fb2 {fill: rgb(209, 217, 224)}#JxRTNbivxGhL .fa3 { fill: var(--bg-body); }#JxRTNbivxGhL .fa3 {fill: #fff}ABCD1234介质向量结构化(SQL/NoSQL)知识图谱数据结构高维浮点向量表/键值/文档节点 + 边 + 属性查询能力语义相似度精确字段查询多跳推理 + 时间推理典型场景对话历史、文档检索用户画像、偏好、配置实体关系、事实追踪
向量记忆(Vector / Embedding-based)
代码块
Python
# 向量记忆的工作原理
class VectorMemory:
def store(self, text: str):
embedding = embed_model.encode(text) # 文本 → 向量
self.index.add(embedding, metadata={"text": text})
def retrieve(self, query: str, top_k=5):
query_vec = embed_model.encode(query)
# 基于余弦相似度找最近邻
results = self.index.search(query_vec, top_k)
return results
# 示例
memory.store("用户是 Python 开发者,偏好 FastAPI")
memory.store("上次讨论了数据库优化,使用了 PostgreSQL")
results = memory.retrieve("推荐一个 Web 框架")
# → 返回语义相似的记忆条目
优势:
•
语义理解:基于含义而非关键词匹配
•
灵活性:不需要预定义 Schema
•
对数级延迟:HNSW 索引在百万级数据上仍保持毫秒级检索
劣势:
•
基于相似度而非真正理解——语义相近但含义不同的内容可能被错误检索
•
不支持关系推理("A 的上级是 B,B 的上级是谁?")
•
不支持时间推理("上周和这周的偏好有什么变化?")
结构化记忆(SQL / NoSQL)
代码块
Python
# 结构化记忆:直接用关系数据库或键值存储
class StructuredMemory:
"""适合用户画像、偏好等键值化数据"""
def __init__(self, db):
self.db = db # PostgreSQL / DynamoDB / Redis
def update_profile(self, user_id, updates: dict):
# 例如:{"language": "Python", "framework": "FastAPI"}
self.db.upsert("user_profile", user_id, updates)
def get_profile(self, user_id):
优势:
•
精确查询:按字段名直接获取
•
强一致性:ACID 事务支持
•
低延迟:主键查询 ms 级
•
易于审计和迁移
劣势:
•
不支持模糊匹配("偏好类似 FastAPI 的框架" 无法用 SQL 直接表达)
•
Schema 演化成本高
•
不擅长关系推理
知识图谱记忆(Knowledge Graph)
为什么用知识图谱做记忆?
代码块
Plain Text
向量记忆的局限:
"张三在公司A工作" → embedding → 存入向量数据库
"张三跳槽到公司B" → embedding → 存入向量数据库
查询"张三在哪工作?" → 两条记忆都被检索出来,LLM 无法判断哪个是最新的
知识图谱的优势:
(张三) --[works_at, valid: 2024-01 ~ 2025-05]--> (公司A)
(张三) --[works_at, valid: 2025-06 ~ now]-------> (公司B)
查询"张三现在在哪?" → 图遍历直接找到当前有效的边 → 公司B
知识图谱记忆架构
图谱记忆:交互抽取入图(节点/边/双时间线),查询走图遍历 + 语义 + BM25 三路合流回灌上下文。
基础三元组接口
代码块
Python
# 知识图谱记忆
class GraphMemory:
def store(self, subject, predicate, object, valid_from=None, valid_to=None):
# 存储三元组 + 时间有效性
self.graph.add_edge(
subject, object,
relation=predicate,
valid_from=valid_from or datetime.now(),
valid_to=valid_to # None = 当前有效
)
def query(self, query: str):
# 支持图遍历和多跳查询
return self.graph.traverse(query)
Graphiti 框架详解
代码块
Python
from graphiti_core import Graphiti
from graphiti_core.nodes import EpisodeType
# 初始化 Graphiti(连接 Neo4j)
graphiti = Graphiti(
neo4j_uri="bolt://localhost:7687",
neo4j_user="neo4j",
neo4j_password="password"
)
双时间线模型(Bi-temporal)
代码块
Python
# 时间线 T:事件实际发生的时间
# 时间线 T':数据被系统记录的时间
class BiTemporalFact:
subject: str # 主体
predicate: str # 关系
object: str # 客体
event_time: datetime # T: 事件发生时间
ingestion_time: datetime # T': 系统记录时间
valid_from: datetime # 生效时间
valid_to: datetime | None # 失效时间(None=当前有效)
# 示例
fact1 = BiTemporalFact(
subject="张三", predicate="works_at", object="公司A",
event_time="2024-01-15", # 实际入职时间
ingestion_time="2024-03-01", # 我们获知这个信息的时间
valid_from="2024-01-15", valid_to="2025-05-31"
混合检索(Hybrid Retrieval)
Graphiti 的检索结合三路信号:
代码块
Python
class HybridRetriever:
"""三路混合检索"""
async def search(self, query: str, top_k: int = 10):
# 路径 1:语义向量搜索
semantic_results = await self.vector_index.search(
embed(query), top_k=top_k
)
⚠️ 延迟实测:Graphiti 检索阶段不调用 LLM,但实测中位数延迟 2-3 秒(受图查询深度、Neo4j 网络开销影响),并非常被引用的 "300ms" 数字。生产中需要做延迟测试并设置 SLA。
多 Agent 共享图谱
代码块
Python
# 知识图谱作为多 Agent 的共享记忆
class SharedKGMemory:
"""多 Agent 共享的知识图谱"""
def __init__(self, graph_db):
self.graph = graph_db
async def agent_update(self, agent_id, fact):
"""任何 Agent 的更新对所有 Agent 可见"""
fact["updated_by"] = agent_id
fact["updated_at"] = datetime.now()
# 检查冲突
existing = await self.graph.find_conflicting(fact)
if existing:
何时选择哪种?
混合架构(推荐)
代码块
Python
class HybridMemory:
def __init__(self):
self.vector_store = ChromaDB() # 语义检索
self.knowledge_graph = Neo4j() # 关系推理
self.profile_store = PostgreSQL() # 结构化属性
async def store(self, interaction):
# 1. 对话历史 → 向量存储
self.vector_store.add(interaction["text"])
# 2. 提取实体和关系 → 知识图谱
entities = extract_entities(interaction["text"])
for entity in entities:
self.knowledge_graph.upsert(entity)
Mem0 的混合方案正是这个思路:用向量存储做语义记忆,用图数据库做关系追踪,统一的 API 对上层透明。
性能对比数据
代码块
Plain Text
Mem0 混合方案 vs 纯全上下文方案:
- 准确率:+26%
- P95 延迟:-91%
- Token 成本:-90%+
Agent Memory 框架横评(2026)
上文讨论的向量 / 结构化 / 图谱是存储介质层面的选型。到了 2026 年——业界称之为"Agent Memory 元年"——记忆从一个"附属功能"升级为独立的工程学科,涌现出专门做记忆管理的框架层产品。理解这些框架的本质,是把上文介质选型"产品化封装"的结果。四大主流框架:Mem0 / Zep / LangMem / Letta。
四大框架速览
记忆抽象:类型与结构
四个框架对"记忆是什么"有截然不同的抽象——这是理解它们差异的钥匙:
代码块
Plain Text
Mem0:扁平事实卡片(最细粒度,最易上手)
{user_id, memory: "用户偏好 FastAPI"} → 向量 + 图
一句话区分:
•
Mem0 把记忆抽象成"原子事实",粒度最细,接入最快
•
Zep 把记忆抽象成"带时间戳的图结构",最强于关系推理与时间推理(见上文 Graphiti 详解)
•
LangMem 借鉴认知科学的三分类,强调"不同类型的记忆走不同的写入/检索策略"
•
Letta 把记忆抽象成"计算机内存层级",核心是 Agent 自己通过 function call 翻页(page in/out)管理记忆
抽取 - 更新 - 检索:机制横评
记忆系统有三个核心环节,四个框架做法迥异。
写入时抽取(Extract)
代码块
Python
# Mem0:LLM 驱动的事实抽取(用户无感)
m.add("我之前用 Django,最近切到 FastAPI 了", user_id="alice")
# 内部:LLM 提取"用户从 Django 切到 FastAPI"
# → 与旧记忆比对 → 判定为冲突 → 覆盖旧事实
# Zep:LLM 驱动的实体/关系抽取 + 双时间线
await graphiti.add_episode(episode_body="张三跳槽到公司B", ...)
# 内部:提取实体(张三/公司B) + 关系(works_at)
# → 旧边(张三→公司A) valid_to=now → 新边 valid_from=now
# LangMem:抽取策略由开发者在 LangGraph 节点里定义(最灵活)
# Letta:Agent 自主抽取(self-edit)
# Agent 调用 core_memory_append / core_memory_replace 工具自己维护
冲突与更新(Update)
检索(Retrieve)
代码块
Plain Text
Mem0: 向量相似度 + 图遍历(混合) → 返回扁平事实列表
Zep: 向量 + BM25 + 图遍历 → RRF 融合 → 返回带时间的事实
LangMem:可插拔 Store,检索策略由开发者定(语义/关键词/元数据过滤)
Letta: core memory 常驻 → 需要时 Agent 主动调 archival_memory_search 拉取
💡 最重要的范式差异:Mem0 / Zep / LangMem 都是系统在用户无感的情况下自动管理记忆(更像数据库);而 Letta 走 Agent 自主管理(self-editing memory) 路线——记忆操作本身是 Agent 可调用的工具,Agent 像人一样"决定记什么、忘什么"(更像大脑)。关于记忆的遗忘与更新机制,详见 #048 — 记忆的遗忘与更新机制。
自研 vs 框架:取舍清单
经验法则:
•
MVP / 中小流量 → 直接上框架(Mem0 最快,Zep 最强推理,LangMem 最贴合 LangGraph 栈,Letta 适合需要 Agent 自治的场景)
•
大流量 / 强数据主权 / 特殊记忆结构 → 自研,借鉴框架的设计(双时间线、三分类、self-edit);框架可用来做原型验证
这一取舍逻辑与框架选型的通用判断一致,参见 #043 — 持久化记忆 中"何时该上专用记忆层"的讨论。
对接生产级记忆系统的选型建议
记忆框架按需求五路选型:召回选 Mem0,关系/时间推理选 Zep,Agent 自治选 Letta,共享选 Zep,严苛生产自研。
🔗 与本文存储介质选型的关系:本节的框架,本质是把上文"向量 + 结构化 + 图谱"的混合架构产品化封装——Mem0 = 向量 + 图;Zep = 图谱 + 混合检索;LangMem = 可插拔后端;Letta = 块存储 + 向量。选框架 = 选一套预先组装好的存储介质组合 + 抽取/检索策略。理解了上文的介质取舍(参见 记忆类型总览 #040),框架选型就是"买现成组装车还是自己攒"的问题。
A-MEM:Zettelkasten 方法的记忆系统
代码块
Python
# A-MEM 将 Zettelkasten 卡片笔记法应用于 Agent 记忆
class AMemNote:
"""结构化记忆笔记"""
content: str # 原子化的知识点
context: str # 上下文描述
keywords: list[str] # 关键词标签
links: list[str] # 与其他笔记的关联
created_at: datetime
access_count: int
# 记忆笔记之间形成互联的知识网络
# 类似人脑的联想记忆——从一个记忆可以"联想"到相关记忆
设计挑战与解决方案
常见误区 / 面试追问
误区:"向量数据库能解决所有记忆需求" — 向量检索基于相似度,不是真正的"理解"。它无法回答"A 和 B 是什么关系?"或"这个信息是什么时候变的?"等需要结构化推理的问题。
误区:"知识图谱太复杂,不值得用" — 对于简单应用确实如此。但当 Agent 需要追踪实体间关系、处理矛盾信息、或做时间推理时,知识图谱的价值会迅速超过构建成本。Graphiti 等框架已大幅降低了使用门槛。
误区:"知识图谱就是把所有信息都存成三元组" — 不是所有信息都适合图谱。对话历史、非结构化文档适合向量存储;用户属性配置适合结构化存储。图谱应该只存储实体、关系和关键事实。混合架构是最佳实践。
误区:"构建知识图谱必须预定义完整 Schema" — Graphiti 等现代框架支持 learned ontology——从数据中自动学习 Schema,同时支持预定义的 prescribed ontology 约束关键关系。
追问:"向量检索返回了语义相似但实际无关的内容怎么办?" — 两层解决:(1) 加 Reranker 对检索结果做精排;(2) 结合结构化元数据过滤(如按用户 ID、时间范围、类别筛选后再做语义检索)。
追问:"混合架构的一致性如何保证?" — 同一条信息在向量存储和图谱中需要同步更新。实践中用事务性写入或最终一致性。删除操作尤其要注意——向量存储和图谱中都要清理。
追问:"图谱记忆的实体提取准确率不够怎么办?" — 三层保障:(1) 用专门的 NER 模型做初步提取;(2) LLM 做上下文理解和关系推理;(3) 对关键事实做人工审核或交叉验证。
追问:"图谱和 RAG 的关系是什么?" — GraphRAG 是两者的结合——用知识图谱增强 RAG 检索。传统 RAG 只做文档级语义检索,GraphRAG 可以在检索后利用图结构做关系推理和多跳问答,提供更准确和完整的答案。