跳到主要内容
Agent Memory

第3章:Memory 存储底座 —— Vector / Graph / KV / Hybrid

向量库、图数据库、KV、全文索引的选型与组合,Mem0/Zep 的 Hybrid 架构,Graphiti 的 bi-temporal 模型详解

Vector Store Graph DB Hybrid Bi-temporal Graphiti

Memory 在物理上落到哪里?这一章把”存储底座”这件事讲透——单一介质都有短板:vector store 找不到”上次和谁聊过类似问题”,graph DB 写入慢、检索语义模糊,KV / 全文各有适用场景。真正生产级的 Memory 系统几乎都是 Hybrid——vector + graph + KV + 时间索引,每一层各自承担一类查询。本章详解每种底座的能力边界、Mem0 / Zep 的 Hybrid 架构,以及 Graphiti 的 bi-temporal 模型。

📑 目录


1. 单一介质的能力边界

介质擅长不擅长典型查询
Vector Store语义相似度、模糊匹配多跳关系、精确属性”和’退款流程’相似的记忆”
Graph DB多跳推理、关系网络全文模糊检索、低延迟点查”用户 A 的朋友的朋友买过什么”
KV Store精确属性 lookup模糊查询、relation”user:123 的当前地址”
全文(BM25)关键词搜索、长尾覆盖同义/语义、多模态”包含’订阅’关键词的对话”
关系型(SQL)结构化、事务、聚合高维语义”今天创建了多少条 memory”

🌟 核心结论:没有银弹。生产 Memory 系统几乎一定是 hybrid——选择哪几种、怎么组合,就是设计的核心权衡。


2. Vector Store 选型

Vector store 是 Memory 系统最常见的底座,负责”语义相似的 memory 召回”。主流选型对比:

数据库类型优点缺点推荐场景
Qdrant独立服务Rust 高性能、payload 强 filter、production ready自托管运维Mem0 默认后端
Weaviate独立服务内置混合检索、模块化资源占用大需要 hybrid search
Milvus独立服务大规模集群部署复杂亿级 vector
Chromaembedded / 服务轻量、开发友好大规模性能弱原型 / 单机
pgvectorPostgreSQL 扩展直接复用 RDBMS、事务、SQL大规模 ANN 性能弱已有 PostgreSQL 栈
FAISSFacebook 出品、单机最快无服务、无 metadata filter离线检索
LanceDBembeddedcolumnar 存储、分析友好较新生态数据分析场景

2.1 关键能力对比

                     Qdrant  Weaviate  Milvus  Chroma  pgvector
Hybrid (vec + BM25)    ✓       ✓                       (需配合 tsvector)
Payload Filter         ✓✓      ✓✓        ✓     ✓        ✓
Sharding               ✓       ✓        ✓✓     ✗        ✗
HNSW / IVF             ✓       ✓        ✓✓     HNSW   HNSW/IVF
Updates / Soft Delete  ✓       ✓        ✓     ✓        ✓
Schema-less Payload    ✓       ✓        partial ✓      ✗
Time Travel(snapshot) ✓       ✓        ✓     ✗        (Postgres MVCC)

2.2 给 Memory 用 vector store 的关键技巧

Payload 必带字段(以 Qdrant 为例):

{
    "user_id": "u_123",         # 强制 filter,隔离用户
    "memory_type": "semantic",  # 类型分桶
    "created_at": 1714838400,   # 时间索引
    "valid_from": 1714838400,
    "valid_to": null,           # bi-temporal valid 区间
    "importance": 0.85,         # 用于 retrieval scoring
    "source_msg_id": "m_456",   # 溯源
    "embedding_model": "text-embedding-3-large",
}

索引策略:

# Qdrant 创建带 filter 索引的 collection
client.create_collection(
    collection_name="agent_memory",
    vectors_config=VectorParams(size=1536, distance=Distance.COSINE),
    hnsw_config=HnswConfigDiff(m=32, ef_construct=200),
)
client.create_payload_index("agent_memory", "user_id", "keyword")  # filter 加速
client.create_payload_index("agent_memory", "created_at", "integer")

避坑:绝不要忘了 user_id filter——一旦漏了,会把别人的 memory 召回到当前用户,既是 bug 也是合规事故。


3. Graph DB:何时它是必需品

Vector store 解决”模糊找一条相似 memory”,Graph DB 解决”沿着关系链找答案”。两个场景必须用图:

3.1 多跳推理

Q: 我老板的助理上次推荐的那家咖啡店在哪?

需要的查询路径:

user → boss → assistant → recommended → cafe → location

向量库根本无法表达”沿着多跳关系走”——这是 Graph DB 的本职工作。

3.2 复杂事实演化

“用户 A 在公司 X 工作 → 跳槽到 Y → 又跳槽到 Z”

事实之间有 supersede 关系,Graph 可以画出来:

A --works_at[t_valid: 2020-2022]--> X
A --works_at[t_valid: 2022-2024]--> Y
A --works_at[t_valid: 2024-now]--> Z

向量库存得了原始三句话,但回答”A 现在在哪”需要时间感知逻辑——硬塞给 LLM 容易出错。

3.3 主流 Graph DB 选型

数据库类型优点Memory 场景适配
Neo4j老牌 native graph成熟、Cypher 表达力强Zep / Graphiti 默认后端
Kùzuembedded、columnar单机超快、列存单 agent 单进程
TigerGraph分布式大规模、企业级集团级 multi-agent
NebulaGraph国产分布式大规模、中文社区国内大集群
NetworkXPython 库极轻量原型、< 万节点
Memgraphin-memory流式、TimescaleDB 集成实时图

🍎 生产首选:Neo4j——Zep 和 Graphiti 都基于它,生态成熟。


4. KV / 文件 / 全文(BM25)的角色

4.1 KV Store

适合”精确字段”——不模糊、不推理、毫秒级响应:

redis.set(f"user:{uid}:address", "上海浦东张江...")
redis.hset(f"user:{uid}:profile", mapping={"age": 30, "vip": "gold"})

Letta 的 core memory 就是 KV 风格——用 LLM 自己生成的 string 直接放在 prompt 里(human / persona blocks)。

4.2 文件 / 文档存储

适合大块内容:对话原文、上传的图片/PDF:

# Mem0 的对话原文存 S3,vector 只存 embedding 和 metadata
s3.put_object(Bucket="agent-memory-raw", Key=f"conv/{conv_id}.json", Body=...)

4.3 全文(BM25)

向量召回擅长”语义”,BM25 擅长”关键词”——两者互补:

查询向量召回BM25
”我的猫不吃东西”召回养宠物相关召回带”猫”的精确条
”SKU-001 的退货”弱(SKU 没语义)强(精确匹配)
“用户感觉很难过”

Hybrid 检索的标配:vector_score × α + bm25_score × (1-α),通常 α=0.6-0.7。


5. Hybrid 架构:Mem0 与 Zep 的设计

5.1 Mem0 的三层 Store

Mem0 把 Memory 拆成三个独立的 store,各自承担不同查询:

┌──────────────────────────────────────────────────────┐
│                     Memory API                        │
└──────────────────────────────────────────────────────┘
              ↓             ↓             ↓
       ┌──────────┐  ┌──────────┐  ┌──────────┐
       │ Vector   │  │ Graph    │  │ KV / SQL │
       │ (Qdrant) │  │ (Neo4j)  │  │ (SQLite) │
       └──────────┘  └──────────┘  └──────────┘
       语义相似度    关系/多跳     精确属性

写入时同时写入三层,检索时按 query 类型分发或并行查询后融合。

from mem0 import Memory
config = {
    "vector_store": {"provider": "qdrant", "config": {...}},
    "graph_store":  {"provider": "neo4j",  "config": {...}},  # 可选
    "llm":          {"provider": "openai", "config": {...}},
    "embedder":     {"provider": "openai", "config": {...}},
}
memory = Memory.from_config(config)
memory.add("我对乳糖不耐受", user_id="u123")
results = memory.search("早餐推荐", user_id="u123")

5.2 Zep 的架构

Zep 围绕单一 KG(Graphiti)展开,vector 是 KG 节点的属性而非独立 store:

┌──────────────────────────────────────────────────────┐
│                    Zep Memory API                     │
└──────────────────────────────────────────────────────┘

              ┌───────────────────┐
              │     Graphiti      │
              │   (Neo4j-based)   │
              │                   │
              │  Nodes(Entity)    │
              │  Edges(Relation)  │
              │  + embeddings     │
              │  + bi-temporal    │
              └───────────────────┘

每个 node 和 edge 都带 vector embedding,既支持图遍历也支持向量检索——结构上更”原生 KG”,代价是查询复杂度高。

5.3 选 Hybrid 的两种风格

风格代表哲学
Multi-store(各自专精)Mem0不同存储各司其职,通过应用层融合
Single-store with hybrid indexZep / Graphiti一套 KG 解决所有问题,内嵌 vector

Multi-store 灵活但应用层逻辑重;Single-store 一致性强但 KG 表达不出的查询要绕路。


6. Bi-temporal 模型:Graphiti 详解

6.1 为什么需要 Bi-temporal

考虑场景:“我去年住在上海,今年搬到了北京。”

  • Temporal 1(事实有效时间):住址在 2024 全年 = 上海,2025 至今 = 北京
  • Temporal 2(系统记录时间):这条信息是今天(2025-05-04)才被告诉系统的

如果只记一个时间,会出现两类错误:

  • 只记”事实何时为真”:回答不了”系统什么时候知道这个事实的”——审计时无法重现历史决策
  • 只记”系统何时记录”:回答不了”用户当时实际在哪”——会错误地把今天才知道的事实回填到去年

6.2 Graphiti 的四时间戳模型

每条 edge 带 4 个时间戳:

字段含义
t_valid事实开始为真的时间(用户去年搬来上海)
t_invalid事实失效的时间(去年底搬走)
t_created系统记录该事实的时间(今天告诉 Agent)
t_expired系统将其标记为废止的时间(今天发现旧地址过期)

形象示意:

事实时间轴(t_valid / t_invalid)
  2024-01 ─────────────── 2024-12

                        ↓ 用户搬家
  2025-01 ─────────────── now
                        
系统记录时间轴(t_created / t_expired)
                                          2025-05-04 系统第一次知道两件事

                            "去年住上海" t_created ──── ?
                            "今年住北京" t_created ──── ?

6.3 查询能力

有了 4 时间戳,可以回答:

查询SQL/Cypher 思路
”用户当前住址”WHERE t_invalid IS NULL
”用户 2024-06 时住址”WHERE t_valid <= 2024-06 AND (t_invalid IS NULL OR t_invalid > 2024-06)
”系统在 2025-04-01 时认为用户住哪”WHERE t_created <= 2025-04-01 AND (t_expired IS NULL OR t_expired > 2025-04-01)
”事实有冲突时谁覆盖谁”按 t_valid 排序,新的覆盖旧的;t_expired 标记废止

Zep 论文(arXiv 2501.13956)实测:开 bi-temporal vs 不开,准确率提升 18.5%,延迟降 90%(因为不再需要 LLM 二次推理”哪条是当前的”)。

6.4 工程实现要点

# Graphiti 添加事实(自动检测时间)
from graphiti_core import Graphiti
graphiti = Graphiti("bolt://localhost:7687", "neo4j", "password")

await graphiti.add_episode(
    name="user_move",
    episode_body="用户在 2025 年 1 月搬到北京",
    source_description="客服对话",
    reference_time=datetime(2025, 5, 4, 14, 30),  # t_created
)
# Graphiti 内部:
#   1. LLM 抽取 entities + relations
#   2. 检测与已有 fact 冲突("住上海"还在 valid)
#   3. 把"住上海"的 t_invalid 设为 2025-01,t_expired 设为 now
#   4. 新增"住北京",t_valid=2025-01,t_invalid=NULL

7. 选型决策树

你的应用核心查询是什么?

├─ "找语义相似的 memory"(占比 > 80%)
│   └─ Vector Store(Qdrant / pgvector)
│       └─ 加 BM25 → Hybrid 检索

├─ "沿着关系网络多跳推理"
│   └─ Graph DB(Neo4j)
│       └─ 需要时间一致性 → Graphiti(bi-temporal)

├─ "精确属性 lookup"(用户档案、状态)
│   └─ KV(Redis) / SQL(Postgres)

├─ "复杂混合"(都用)
│   ├─ 自己集成 → Mem0 multi-store
│   └─ 全托管 → Zep / Letta

└─ "原型阶段,先跑通"
    └─ Chroma + JSON / SQLite + 偶尔 Redis

🌟 生产经验:90% 的团队 Phase 1 都是 vector-only(Mem0 默认即可),Phase 2 加 KG(用户开始问多跳问题),Phase 3 加 bi-temporal(数据演化频繁,审计需求强)。


✅ 自我检验清单

  • 介质边界:能列出 vector / graph / KV / 全文各自的擅长/不擅长场景,各举一例
  • Vector store 选型:能根据”自托管 vs 云、规模、是否需要 hybrid”给出推荐
  • Payload 字段:能默写 Memory 系统中 Qdrant payload 至少 6 个必带字段
  • Graph DB 必要性:能给出 2 个具体场景”vector store 解决不了、必须用图”的例子
  • Hybrid 两种风格:能对比 Mem0 multi-store 和 Zep single-store 的优劣
  • Bi-temporal 必要性:能讲清”为什么 t_valid 和 t_created 必须独立”
  • Graphiti 4 时间戳:能默写四个字段的含义和典型用途
  • 冲突处理:能解释 Graphiti 如何处理”用户搬家”这类事实演化
  • 决策树:面对 3 个不同业务,能给出存储底座方案

📚 参考资料

Vector Store

Graph DB

Hybrid 与 Bi-temporal

检索方法

  • BM25 + 向量混合检索:Pinecone / Weaviate 文档均有详解
  • Lost in the Middle (Liu et al., 2023):arXiv 2307.03172