高级 RAG 与记忆架构:从向量检索到 GraphRAG 与双层记忆系统

本文是「Agent 基础与工程」系列专栏的第 16 篇。专栏总览参见:《Agent 基础认知与工程架构全景》。

在多数 Agent 原型中,知识检索通常采用经典的“切片 向量化 余弦相似度匹配 拼接提示词”的简单 RAG 流水线。但在支撑具备复杂推理链的生产级 Agent 时,这种扁平的向量检索会暴露四个系统性死穴:

  1. 切片孤立(Chunk Isolation):文档切块后丢失了章节层级与主谓语指代(如切片内包含“该公司第三季度营收下滑”,脱离全文后模型无法获知“该公司”指代谁);
  2. 多跳关系断裂(Multi-hop Disconnect):面对“排查负责人所属业务线的核心链路依赖”等复合问题,单一向量查询无法穿透多个跨实体的依赖跳数;
  3. 宏观理解失效(Global Blindness):面对“该系统今年在稳定性建设上有哪些核心突破”等全局归纳性提问,自底向上的单点切片无法拼凑出全局全景;
  4. 高相关低信噪(Relevance Utility):向量空间计算出的高相似度切片,往往充满重复的样板代码或冗余声明,严重稀释模型的有效注意力。

为了给智能体提供高精度、具备拓扑连通性的事实支撑,RAG 架构必须向上下文增强切片、知识图谱(GraphRAG)与双层记忆系统深度演进。


进阶范式一:Contextual Retrieval(上下文增强切片)

传统切片直接将切分后的文本块转为向量,导致切片丢失了父文档的宏观语义。

工程解法:在离线切片预处理阶段,利用轻量模型为每一个文本块补充一段 50 ~ 100 字的全局上下文摘要前缀(Context Prefix),并将该前缀与原切片拼合后再计算 Embedding:

在原切片前注入文档主题与上下文定位后,向量空间能准确捕捉代词的真实语义,检索召回率可获得显著提升,且完全不增加在线检索的耗时。


进阶范式二:GraphRAG(知识图谱驱动的拓扑检索)

面对复杂的实体关联与多跳推导任务,基于图结构的 GraphRAG 能够提供传统向量检索完全不具备的关系穿透能力:

  • Global Search(全局搜索):直接在预先聚类并生成的“社群摘要(Community Summaries)”上进行并行扫描,专为解决“这篇文档的核心主旨是什么”等宏观归纳问题,彻底避免扫描数千个局部碎片的庞大开销;
  • Local Search(局部图扩散):从用户问题中识别核心实体种子(Seed Entities),在知识图谱中沿关联边向外跳跃 1 到 2 度,把关联的实体属性与相互依赖结构整包拉取,直接解决多跳推导问题。

进阶范式三:混合检索(Hybrid Search)与 RRF 融合

生产级 RAG 绝不单独依赖密集向量(Dense Vector),必须与稀疏检索(Sparse BM25)建立双路并发召回,并通过**倒数排名融合(Reciprocal Rank Fusion, RRF)**进行无量纲分值对齐:

其中 为检索器集合(向量检索与 BM25 检索), 为文档 在检索器 中的名次, 为平滑常数(工业界通常取 )。

混合检索彻底解决了向量检索对缩写、专有名词、错误码(如 ERR_0x9941)不敏感的问题,同时保全了语义泛化能力。


生产级 RRF 混合检索算法实现

以下展示使用原生 Python 实现的具备名次对齐与融合重排的 RRF 算法核心实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
from collections import defaultdict
from typing import List, Dict, Any

def reciprocal_rank_fusion(
vector_rankings: List[Dict[str, Any]],
bm25_rankings: List[Dict[str, Any]],
k: int = 60,
top_n: int = 5
) -> List[Dict[str, Any]]:
"""
RRF 倒数排名融合算法:
将向量检索与关键词检索的不同量纲分数归一化为相对名次加权分
"""
rrf_scores = defaultdict(float)
doc_metadata = {}

# 1. 统计向量检索排名
for rank, doc in enumerate(vector_rankings):
doc_id = doc["id"]
rrf_scores[doc_id] += 1.0 / (k + (rank + 1))
doc_metadata[doc_id] = doc

# 2. 统计 BM25 检索排名
for rank, doc in enumerate(bm25_rankings):
doc_id = doc["id"]
rrf_scores[doc_id] += 1.0 / (k + (rank + 1))
if doc_id not in doc_metadata:
doc_metadata[doc_id] = doc

# 3. 按融合得分降序排序
sorted_items = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)

# 4. 组装 Top-N 最终交付
fused_results = []
for doc_id, score in sorted_items[:top_n]:
res = dict(doc_metadata[doc_id])
res["rrf_score"] = round(score, 5)
fused_results.append(res)

return fused_results

if __name__ == "__main__":
# 模拟两路检索器的召回结果
vector_res = [
{"id": "doc_101", "content": "集群水平扩容通常通过调整 replicas 副本数实现"},
{"id": "doc_102", "content": "容器调度遵循反亲和性规则配置"}
]
bm25_res = [
{"id": "doc_103", "content": "集群扩容命令: kubectl scale deployment --replicas=5"},
{"id": "doc_101", "content": "集群水平扩容通常通过调整 replicas 副本数实现"}
]

fused = reciprocal_rank_fusion(vector_res, bm25_res, k=60, top_n=2)
print("--- 混合检索融合输出结果 ---")
for item in fused:
print(f"[{item['id']}] 得分: {item['rrf_score']} -> 内容: {item['content']}")

系列导航与参考