b1babo
timeline
keywords
articles
targets
projects
about
b1babo

2026 All Rights Reserved.

  • 关于本站
  • 所有文章
  • 站点地图
  • RSS Feed

Powered by Next.js & Trilium

  • HippoRAG:让 AI 像人类一样 记忆 和 回忆

HippoRAG:让 AI 像人类一样 记忆 和 回忆

b1babo
2026年1月17日
2026年1月17日
RAGGraph

HippoRAG:让 AI 像人类一样"记忆"和"回忆"

GitHub - OSU-NLP-Group/HippoRAG: [NeurIPS'24] HippoRAG is a novel RAG framework inspired by human long-term memory that enables LLMs to continuously integrate knowledge across external documents. RAG + Knowledge Graphs + Personalized PageRank.

 

先问个问题

假设你有一个包含 1000 篇 Wikipedia 文章的知识库,我问你:

"Thomas Sudhof 和 Karl Deisseroth 有什么共同点?"

你会怎么找?

传统 RAG 的做法:

问题 → 向量检索 → 找到最相似的 5 篇文章 → 答案

问题:如果答案散落在多篇文章里,或者需要推理连接,传统方法就失效了。

HippoRAG 的做法:

问题 → 找到关键实体 → 在知识图谱上"游走" → 连接多篇文章 → 答案

这更像人类的思考过程:先提取关键人名,再回忆他们各自的信息,最后找出联系。

核心想法:模仿人类记忆

HippoRAG 的假设是:人类记忆不是一张大表格,而是一张网。

你听到 "George Rankin"
    ↓
大脑激活相关节点: politician, government, elected
    ↓
沿着记忆网络扩散
    ↓
想起更多相关信息

HippoRAG 用知识图谱 + 向量检索模拟这个过程。

Part 1:把文档"存"进大脑 (Index)

一个例子

假设你有一段话:

"Erik Hort's birthplace is Montebello. Montebello is a part of Rockland County."

HippoRAG 会把它拆解成:

1. 拆成 chunk(语义完整的段落)
2. 提取三元组(谁什么关系谁)
   - ("Erik Hort", "birthplace", "Montebello")
   - ("Montebello", "part of", "Rockland County")
3. 构建图谱
   Erik Hort ──birthplace──→ Montebello ──part of──→ Rockland County
4. 给每个节点打上向量标签(方便后续查找)

完整流程

原始文档
   │
   ├─→ Chunk 1: "Erik Hort's birthplace..."
   │       │
   │       ├─→ 提取三元组 → ("Erik Hort", "birthplace", "Montebello")
   │       │
   │       └─→ 存入 Chunk Embedding Store (向量)
   │
   ├─→ Chunk 2: "Montebello is a part of..."
   │       │
   │       ├─→ 提取三元组 → ("Montebello", "part of", "Rockland County")
   │       │
   │       └─→ 存入 Chunk Embedding Store (向量)
   │
   └─→ 构建知识图谱
         │
         ├─→ 实体节点: Erik Hort, Montebello, Rockland County
         ├─→ 文档节点: Chunk 1, Chunk 2
         ├─→ 事实边: birthplace, part of
         └─→ 同义边: politician ↔ political figure (通过向量相似度发现)

为什么需要同义边?

因为同一个概念可能有不同说法:

用户问: "What political figures..."
知识图谱里有: "politician"

没有同义边 → 找不到
有同义边 → political figure → politician → 找到了!

Index 步骤总结

Part 2:从大脑中"回忆" (Retrieve)

一个完整的检索过程

假设问题是:

"What county is Erik Hort's birthplace in?"

Step 1: 问题向量化

"What county is Erik Hort's birthplace in?"
    ↓
用 instruction 引导编码
"Given a question, retrieve relevant triplet facts..."
    ↓
得到一个向量,专门用来匹配三元组

为什么需要 instruction?

同一个问题,不同用途需要不同向量:

# 用于匹配事实
query_to_fact_vector = encode(query, "retrieve relevant triplet facts...")

# 用于匹配文档
query_to_passage_vector = encode(query, "retrieve relevant documents...")

# 两个向量方向不同,分别优化用于匹配不同目标

Step 2: 向量检索找到候选事实

问题向量 · 所有事实向量 = 相似度分数

("Erik Hort", "birthplace", "Montebello")        → 0.87 ✓
("Montebello", "part of", "Rockland County")     → 0.65 ✓
("Cinderella", "attended", "ball")               → 0.12 ✗
("George Rankin", "is a", "politician")          → 0.21 ✗

初筛可能返回 50-100 个候选事实。

Step 3: LLM 精炼 (Recognition Memory)

向量检索可能有噪声,让 LLM 再筛一遍:

输入:
  问题: "What county is Erik Hort's birthplace in?"
  候选事实: [初筛的 50 个三元组]

输出:
  精选事实: [最相关的 5 个三元组]
得到:
("Erik Hort", "birthplace", "Montebello")
("Montebello", "part of", "Rockland County")

这一步叫 Recognition Memory(识别记忆),模拟人类从多个记忆片段中识别出最相关的。

Step 4: 在图上"游走" (PPR)

这是 HippoRAG 的魔法时刻 —— Personalized PageRank 算法结合静态图结构和动态查询相关性,给所有节点打分。

PPR 的核心:两种权重的协作
┌─────────────────────────────────────────────────────────────────┐
│                    静态图 + 动态查询                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  静态图 (Index 阶段构建,可复用):                                │
│    ┌────────────────────────────────────────┐                 │
│    │ 节点 + 边,每条边有固定权重              │                 │
│    │                                         │                 │
│    │   Erik ──w=1.0──→ Montebello ──w=1.0──→ Rockland        │
│    │     │               │               │                    │
│    │   w=1.0          w=1.0           w=1.0                   │
│    │     ↓               ↓               ↓                    │
│    │  Chunk1         Chunk2          Chunk3                  │
│    └────────────────────────────────────────┘                 │
│                                                                 │
│  动态查询 (Retrieve 阶段):                                       │
│    ┌────────────────────────────────────────┐                 │
│    │ 查询与每条 fact 边的相关度:              │                 │
│    │   (Erik, birthplace, Montebello) → 0.87 │                 │
│    │   (Montebello, part_of, Rockland) → 0.65│                 │
│    │                                         │                 │
│    │ 这些相关度 → 节点初始权重                │                 │
│    └────────────────────────────────────────┘                 │
│                                                                 │
│  PPR 结合两者遍历图:                                             │
│    初始权重 + 边权重 → 传播 → 所有节点分数                       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
完整流程

1. 准备静态图结构

Index 阶段已构建好的知识图谱:

        fact edge (w=1.0)      fact edge (w=1.0)
    Erik ─────────────────→ Montebello ─────────────→ Rockland
      │                           │                       │
    passage edge               passage edge           passage edge
    (w=1.0)                    (w=1.0)                (w=1.0)
      │                           │                       │
      ↓                           ↓                       ↓
   Chunk1                      Chunk2                  Chunk3

边权重含义:
  - fact edge: 三元组出现的次数 (语义关系的强度)
  - passage edge: 固定 1.0 (包含关系)

2. 计算查询相关度

查询: "Erik Hort 的出生地在哪个县?"

向量化 → 匹配 fact_embedding_store:

Fact 1: ("Erik Hort", "birthplace", "Montebello")
  向量相似度 = 0.87
  → 这是查询与这条 fact 边的相关度

Fact 2: ("Montebello", "part of", "Rockland County")
  向量相似度 = 0.65
  → 这是查询与这条 fact 边的相关度

注意: 这些分数表示"这条边与查询有多相关"

3. 转换为节点初始权重

将 fact 边的相关度转换为节点的初始权重:

Fact 1 相关度 0.87:
  → Erik Hort 权重 += 0.87
  → Montebello 权重 += 0.87

Fact 2 相关度 0.65:
  → Montebello 权重 += 0.65
  → Rockland County 权重 += 0.65

初始权重向量 (reset):
  Erik Hort:       0.87
  Montebello:      1.52 (0.87 + 0.65)
  Rockland County: 0.65
  George Rankin:   0.00 (无关)

图上状态:
    Erik (0.87) ───────→ Montebello (1.52) ───────→ Rockland (0.65)
      │                       │                        │
      ↓                       ↓                        ↓
   Chunk1 (0)              Chunk2 (0)                Chunk3 (0)

4. PPR 迭代传播

迭代公式:
  p_{t+1} = (1 - α) × reset + α × p_t × M

  其中:
    p_t = 当前所有节点的权重
    reset = 初始权重 (固定不变,来自 fact 相关度)
    α = damping 系数 (0.5,表示 50% 传播,50% 重置)
    M = 转移矩阵 (包含边权重)

关键理解:
  - reset: 表示"查询从哪里开始" (动态,每次查询不同)
  - M: 表示"节点间的连接强度" (静态,图结构)
  - PPR: 结合两者,让权重沿图边流动

迭代过程:

初始 (t=0): 从相关实体开始
  Erik: 0.87, Monte: 1.52, Rockland: 0.65
  Chunks: 都是 0

迭代 1: 权重沿边传播
  Erik (0.87) ──w=1.0──→ Montebello: 新增 0.87 × 1.0 = 0.87
  Monte (1.52) ──w=1.0──→ Erik: 新增 1.52 × 1.0 = 1.52
  Monte (1.52) ──w=1.0──→ Rockland: 新增 1.52 × 1.0 = 1.52
  各实体 ──w=1.0──→ 对应 Chunk: passage edge 传播

  应用 damping 和 reset:
    新权重 = 0.5 × reset + 0.5 × 传播后权重

  Erik:    0.5×0.87 + 0.5×传播权重 ≈ 1.20
  Monte:   0.5×1.52 + 0.5×传播权重 ≈ 2.10
  Rockland: 0.5×0.65 + 0.5×传播权重 ≈ 1.09
  Chunk1:  0 + 0.5×Erik的传播 ≈ 0.6
  Chunk2:  0 + 0.5×Montebello的传播 ≈ 1.0

迭代 2-10: 继续传播,权重重新分配...

收敛 (t=n):
  Erik:     1.5
  Monte:    2.3
  Rockland: 1.8
  Chunk1:   2.1 (包含 Erik)
  Chunk2:   2.5 (包含 Montebello,最高分!)
  Chunk3:   1.3 (包含 Rockland)
关键概念澄清

Q1: Fact 相似度是边权重吗?

不是!它们是两个不同概念:

边权重 (静态):
  - 在 Index 阶段确定
  - 表示"节点间连接的强度"
  - 由三元组出现次数决定
  - 存储在图中,每次查询复用

Fact 相似度 (动态):
  - 在 Retrieve 阶段计算
  - 表示"这条边与查询的相关度"
  - 由向量相似度决定
  - 转换为节点初始权重

为什么分开?
  - 边权重固定 → 图可以复用
  - Fact 相似度动态 → 每次查询不同

Q2: 为什么不直接用 Chunk 包含的实体分数累加?

直接累加: Chunk 分数 = Σ(包含实体分数)
  → 假设实体相互独立
  → 丢失实体间的关系

PPR 传播: Chunk 分数 = 图传播后的分数
  → 利用图结构
  → 实体相互影响
  → 支持多跳推理

示例:
  查询: "Thomas 和 Karl 的共同点?"

  直接累加:
    Chunk A (包含 Thomas) ≠ Chunk B (包含 Karl)
    → 找不到关系 ✗

  PPR:
    Thomas → Stanford ← Karl
    → 通过共同连接找到相关 Chunk ✓

Q3: PPR 如何做多跳推理?

传统检索 (向量匹配):
  Query → 向量相似度 → 直接匹配的文档
  → 找不到两跳之外的信息

PPR (图传播):
  Query → fact 相关度 → 初始权重
  → 沿图边传播
  → Erik → Montebello → Rockland
  → Rockland 获得权重 (两跳!)
  → 包含 Rockland 的 Chunk 获得分数
  → 找到了两跳之外的文档 ✓

本质: 权重可以沿着图边"流"到多跳之外的节点

Q4: 为什么会收敛?

1. Damping: 每次迭代 50% 权重重置
   → 不会无限累积

2. 有限图: 节点数固定
   → 权重分配空间有限

3. 数学保证: PPR = 计算矩阵特征向量
   → 必然收敛

实际停止:
  - 变化 < 0.000001
  - 或达到最大迭代 (100-500次)
最终输出
文档节点排序:
  Chunk 2 "Montebello is part of Rockland County"  → 2.5
  Chunk 1 "Erik Hort's birthplace is Montebello"   → 2.1
  Chunk 3 "Rockland County is in New York"         → 1.3

返回 top-k 文档给 LLM 生成最终答案

Step 5: 返回排序后的文档

1. "Montebello is a part of Rockland County" (score: 0.92)
2. "Erik Hort's birthplace is Montebello" (score: 0.88)
3. "Rockland County is in New York" (score: 0.45)
4. "George Rankin is a politician" (score: 0.12)

完整召回链路

问题
  │
  ↓ 向量化 (query_to_fact)
问题向量
  │
  ↓ 匹配 fact_embedding_store
候选事实 (50-100个)
  │
  ↓ LLM 重排序
关键事实 (5-10个)
  │
  ↓ 提取实体
实体节点 (带权重)
  │
  ↓ 图上 PPR 传播
文档排序
  │
  ↓
最终答案

争议:这么复杂值得吗?

支持方

优势说明
多跳推理Erik Hort → Montebello → Rockland County(两跳)
语义连接通过同义边连接不同表述 (politician ↔ political figure)
结构化知识图比纯向量更能表达关系
测试表现在 MuSiQue、HotpotQA 等多跳问答基准上领先

反对方

质疑说明
复杂度高6-7 个步骤,每个都可能出错
依赖 LLMOpenIE 和重排序都要调用 LLM,慢且贵
收益有限简单问答和普通 RAG 差距不大
过度设计很多场景下 DPR 就够用

我的看法

HippoRAG 的价值取决于场景:

简单问答: "George Rankin 的职业是什么?"
  → 普通 RAG 够用,快且便宜

多跳推理: "Erik Hort 出生地属于哪个县?"
  → HippoRAG 有优势,能跨文档推理

实时系统: 需要 <100ms 响应
  → HippoRAG 太复杂,可能不适合

离线分析: 可以容忍几秒延迟
  → HippoRAG 可以考虑

兜底机制

HippoRAG 作者也意识到复杂性的问题,代码中有兜底:

# 如果 LLM 重排序后没有相关事实,退回到简单 DPR
if len(top_k_facts) == 0:
    sorted_doc_ids = self.dense_passage_retrieval(query)

这说明:复杂的图检索是锦上添花,不是必需品。

本质:还是表格匹配?

本质上就是维护了一个表格,通过几个字段进行映射搜索匹配

HippoRAG 本质上就是:

chunk_table
┌─────────────┬──────────┬────────────────┐
│ chunk_hash  │ chunk    │ chunk_embedding │
├─────────────┼──────────┼────────────────┤
│ hash_001    │ "Erik..."│ [0.1, 0.2...]  │
└─────────────┴──────────┴────────────────┘

entity_table
┌─────────────┬─────────┬──────────────────┐
│ entity_hash │ entity  │ entity_embedding │
├─────────────┼─────────┼──────────────────┤
│ hash_e001   │ "Erik.."│ [0.3, 0.1...]   │
└─────────────┴─────────┴──────────────────┘

fact_table
┌─────────────┬───────────────────┬────────────────┐
│ fact_hash   │ fact (三元组)     │ fact_embedding │
├─────────────┼───────────────────┼────────────────┤
│ hash_f001   │ (Erik, birthplace │ [0.5, 0.8...]  │
│             │  , Montebello)    │                │
└─────────────┴───────────────────┴────────────────┘

graph
┌─────────┬─────────┬────────┐
│ node_1  │ node_2  │ weight │
├─────────┼─────────┼────────┤
│ Erik    │Montebello│ 1.0   │
└─────────┴─────────┴────────┘

但关键在于:

  1. 图结构 让这些表之间可以"游走"
  2. PPR 算法 让相关性可以传播
  3. 同义边 让相似实体可以互通

所以不只是简单的表连接,而是带权重的图推理。

总结:你应该用 HippoRAG 吗?

适合用的场景

  • ✓ 需要多跳推理的问答系统
  • ✓ 知识密集型领域(医疗、法律、科研)
  • ✓ 对准确度要求高于延迟
  • ✓ 有预算调用 LLM
  • ✓ 问题类型复杂(需要连接多个信息点)

不适合用的场景

  • ✗ 简单的文档问答
  • ✗ 实时响应要求高
  • ✗ 预算有限
  • ✗ 问题类型单一
  • ✗ 数据规模小

建议

  1. 先试简单方案: 从普通 RAG/DPR 开始
  2. 评估瓶颈: 如果发现多跳推理是瓶颈,再考虑 HippoRAG
  3. A/B 测试: 在你的数据上对比 HippoRAG 和基线方法
  4. 成本分析: 计算 LLM 调用成本是否值得提升的准确度

写在最后

HippoRAG 代表了一个方向:让 AI 更像人类思考。它把向量检索的"直觉"和知识图谱的"推理"结合起来,虽然复杂,但确实在多跳问答上展示了潜力。

至于是否值得在你的项目中使用?取决于你的问题有多复杂,以及你愿意为这多出来的准确性付出多少代价。

但无论如何,HippoRAG 提供了一个很好的思路:不要只盯着向量相似度,有时候结构和关系更重要。

 

本页目录

  • HippoRAG:让 AI 像人类一样"记忆"和"回忆"
  • 先问个问题
  • 核心想法:模仿人类记忆
  • Part 1:把文档"存"进大脑 (Index)
    • 一个例子
    • 完整流程
    • 为什么需要同义边?
    • Index 步骤总结
  • Part 2:从大脑中"回忆" (Retrieve)
    • 一个完整的检索过程
      • Step 1: 问题向量化
      • Step 2: 向量检索找到候选事实
      • Step 3: LLM 精炼 (Recognition Memory)
      • Step 4: 在图上"游走" (PPR)
        • PPR 的核心:两种权重的协作
        • 完整流程
        • 关键概念澄清
        • 最终输出
      • Step 5: 返回排序后的文档
  • 完整召回链路
  • 争议:这么复杂值得吗?
    • 支持方
    • 反对方
    • 我的看法
    • 兜底机制
  • 本质:还是表格匹配?
  • 总结:你应该用 HippoRAG 吗?
    • 适合用的场景
    • 不适合用的场景
    • 建议
  • 写在最后

评论