9. 检索增强生成高级架构:GraphRAG 与结构化知识检索
阅读导航:我们从传统 RAG 的"天花板"说起,层层剥开 GraphRAG 的索引与检索内核。每节只解决一个认知疑点:先建立直觉(toy example),再看工业实现,最后给你可落地的伪代码。如果你熟悉 RNN 的展开结构和全连接层的矩阵乘法,那么这里的图遍历和稀疏运算只是老朋友换了身衣服。
9.1 为什么我们需要一张"图"?
传统 RAG 把文档切成块、压成向量,放进一个巨大的平面仓库里。当我们问"乔布斯和马斯克在创业早期的共同投资人有哪些"时,系统会检索与查询向量"最相似"的文本块。但问题来了:答案可能分散在 A 文档的第 3 段、B 文档的第 7 段和 C 文档的脚注里------传统向量检索没有"关系"的概念,它只能回答"这段文字像不像我的问题",却回答不了"A 和 B 是如何通过 C 连接起来的"。
别急,这正是 GraphRAG 诞生的原因。微软研究院在 2024 年提出的 GraphRAG 框架(Edge et al., 2024)不再把知识看作孤立的向量点,而是构建一张实体-关系-社区的三层网络。想象我们把整本百科全书铺在地上,用红线把相关的人、地点、概念连起来,再把紧密相连的红线捆成一束------每一束就是一个"社区"。查询时,我们不再是"在仓库里找相似的箱子",而是"沿着红线走路,甚至直接读取整束线的摘要"。
现在我们已经了解了动机,接下来看看这张网是如何从原始文本中生长出来的。
9.1.1 多粒度图构建:实体抽取、社区检测、层次化图谱索引实现
第一步:从"一句话"到"一张小图"
让我们从一个 toy example 开始,建立直觉。
假设我们只有一段短文:
"Samsung launched the Galaxy S25 in 2024, competing directly with Apple's iPhone 16. The Galaxy S25 is powered by the Qualcomm Snapdragon 8 Elite chip."
如果是传统 RAG,这段文字会被压缩成一个 768 维的向量。但在 GraphRAG 的索引阶段,我们会用 LLM 做一次"阅读理解",抽取出:
- 实体(Entities):Samsung, Galaxy S25, Apple, iPhone 16, Qualcomm, Snapdragon 8 Elite
- 关系(Relationships):Samsung --[launched]--> Galaxy S25;Galaxy S25 --[competes with]--> iPhone 16;Galaxy S25 --[powered by]--> Snapdragon 8 Elite
如果画成图,会是这样:六个圆球(节点),五根带箭头的线(有向边)。每个圆球旁边还贴着一张小纸条,上面写着描述(如"Samsung: a multinational electronics corporation")。
在真实工业流程中,整份语料库会先被切成 TextUnits (默认约 300 tokens)。每个 TextUnit 独立过一遍 LLM,抽取出子图(subgraph)。这些子图像拼图一样被合并:如果两个子图都提到了"Samsung",我们就把它们的描述合并,并去重。最终得到一张统一知识图。
Indexing Pipeline
Raw Corpus
TextUnit1 ... TextUnitn
LLM Entityextraction
Subgraph1 ... Subgraphn
Graphmerge &
Deduplication
Unified KG
听起来抽象对吧?这里的核心洞察是:LLM 不只是压缩器,还是结构提取器。传统 NER(命名实体识别)需要预定义实体类型和关系模板,换领域就要重新训练;而 LLM 的 zero-shot 能力让它可以从法律文档里读出"原告-被告-案由",也可以从医学病历里读出"患者-诊断-治疗方案",无需改一行代码。
第二步:社区检测------把大图切成"主题束"
现在我们已经有了包含百万节点的大图。如果查询时要在整张图上盲目游走,那比向量检索还慢。怎么办?
我们引入社区检测(Community Detection) 。想象你手里拿着一把剪刀,沿着图中"连接最稀疏"的地方剪下去,把图分成一簇簇紧密相连的"社区"。在 GraphRAG 中,微软使用的是 Leiden 算法 (Traag et al., 2019),它是 Louvain 算法的改进版,支持层次化聚类------也就是说,社区内部还可以继续细分出子社区。
Hierarchical Communities
Global Corpus
CommunityL2: Smartphone Ecosystem
CommunityL2: Semiconductor Supply Chain
CommunityL1: Flagship Devices
CommunityL1: Market Competition
CommunityL1: Chip Design
CommunityL0: Galaxy S25 Node Cluster
CommunityL0: iPhone 16 Node Cluster
Leiden 算法保证两个性质:
- 互斥性:每个节点只属于一个社区(在单层划分中)。
- 完备性:没有节点被遗漏。
算法在图上递归运行:先做一次粗粒度划分(Level 2),然后在每个社区内部再做细粒度划分(Level 1、Level 0)。这就像一个公司先分成事业部,再分成部门,最后分成小组。
第三步:自下而上生成社区摘要
社区切好了,但检索时直接返回一万个节点给 LLM,上下文窗口会爆炸。所以 GraphRAG 在索引阶段就做了另一件事:让 LLM 为每个社区写一份"摘要报告"。
具体怎么做?对于每个社区,系统把社区内的实体列表、关系列表(按 combined degree 排序,优先保留高度数边)和可选的声明(claims)打包成 CSV 表格,喂给 LLM,让它生成一段自然语言摘要。这个过程是自下而上的:先为 Level 0 的小组写摘要,再把 Level 0 的摘要作为输入,生成 Level 1 的部门摘要,以此类推。
Bottom-up Summarization
L0 Summary:
Galaxy S25 Cluster
L1 Summary:
Flagship Devices
L0 Summary:
iPhone 16 Cluster
L0 Summary:
Snapdragon Cluster
L1 Summary:
Chip Design
L2 Summary:
Smartphone Ecosystem
现在我们已经了解了多粒度图构建的全貌:原始文本 → 实体/关系抽取 → 子图合并 → Leiden 社区检测 → 层次化摘要。这套索引流程虽然前置计算重(需要大量 LLM API 调用),但它把"理解"的工作从查询时搬到了索引时,查询时只需要读取预计算的摘要和子图,响应速度反而更快。
这在实际使用中意味着什么? 如果你的场景是离线构建一次、在线查询千万次(如企业知识库、法规库),那么重索引轻查询的架构是非常划算的。但如果你的文档每天都在剧烈变化,就需要权衡索引成本。
9.1.2 基于 PageRank 的图检索算法:Personalized PageRank 在 GPU 上的并行实现
从"全局重要性"到"个性化相关性"
好,现在我们已经有一张带社区摘要的图了。假设用户问:"与 Qualcomm 关系密切且对移动芯片行业影响最大的三家公司是哪些?"
如果只用向量检索,系统会返回提到"Qualcomm"的文本块,但无法衡量"影响力"。在图的世界里,这个问题等价于:从 Qualcomm 节点出发,哪些节点最容易被随机游走到达?
这就是 PageRank 的直觉。经典 PageRank 把互联网看作一张有向图,链接就是投票,重要的页面被更多重要页面链接。但经典 PageRank 是"全局"的------它只回答"整个图中谁最重要"。我们需要的是 Personalized PageRank(PPR):把随机游走的" teleport "概率全部集中在查询相关的种子节点上(比如 Qualcomm),计算从种子出发的稳态分布。
听起来抽象对吧?想象你在一个社交舞会上找"最有影响力的人"。经典 PageRank 相当于站在门口看全场;PPR 相当于你直接走到 Qualcomm 身边,只听它认识的人、以及它认识的人认识的人......这样传播开去。离 Qualcomm 越近、连接路径越短、中间节点越重要的公司,排名越高。
Toy Example:三节点图上的 PPR
让我们把公式直觉化。假设图只有三个节点:A(Qualcomm)、B(Samsung)、C(Apple)。边是:A→B(供应芯片),B→C(竞争关系)。
PPR 的迭代公式(用结构化伪代码风格描述)是:
Algorithm: PersonalizedPageRank
Input: Graph G(V, E), seed set S, damping factor alpha, tolerance epsilon
Output: PPR vector pi
Initialize pi[v] <- 0 for all v in V
For each s in S do
pi[s] <- 1 / |S|
End
repeat
pi_old <- pi
For each v in V do
pi[v] <- (1 - alpha) * Sum_{u in InNeighbors(v)} pi_old[u] / OutDegree(u)
If v in S then
pi[v] <- pi[v] + alpha / |S|
End
End
until Norm_1(pi - pi_old) < epsilon
return pi
在继续之前,让我们看看这个迭代在干什么。每次迭代,节点的"分数"沿着入边从邻居流进来,再按出度平分出去。同时,种子节点会额外获得一个 teleport 注入(alpha / |S|)。最终稳态时,pi 向量就代表了从种子出发的随机游走概率分布。
GPU 并行实现:稀疏图上的 Power Iteration
在真实规模下,图的节点数可能是百万级,边数可能是千万级,迭代次数可能是上百次。CPU 上的双重循环显然扛不住。我们需要 GPU。
但图数据极度稀疏:每个节点只连接极少数邻居。如果用稠密矩阵存储邻接关系,99% 的内存都会浪费。工业界的标准做法是 CSR(Compressed Sparse Row) 格式:三个数组 values、col_indices、row_ptr,分别存储非零边权、目标节点索引、每行起始偏移。
CSR Representation on GPU
Row Pointer
Array row_ptr
GPU Global Memory
Column Index
Array col_idx
Edge Weight
Array val
Warp-based SpMV
Kernel
PPR Vector
pinew
GPU 并行的核心算子是 稀疏矩阵-向量乘法(SpMV) :pi_new = A^T * pi_old。在 CUDA 中,一个 warp(32 线程)通常协作处理一个节点的所有入边邻居。
Algorithm: GPUPersonalizedPageRank
Input: CSR graph G, seed array S, alpha, epsilon, max_iter
Output: device array pi
// Host side initialization
Allocate pi, pi_old on device
cudaMemset pi <- 0
For each s in S do
pi[s] <- 1.0 / |S| // Uniform teleport distribution
End
// Device kernel: Warp-based SpMV for PPR iteration
Kernel SpMV_PPR:
tid <- blockIdx.x * blockDim.x + threadIdx.x
warp_id <- tid / 32
lane_id <- tid % 32
For each node v assigned to warp_id do
sum <- 0.0
start <- row_ptr[v]
end <- row_ptr[v + 1]
// Cooperative loading of neighbors within warp
For nbr_idx from start to end - 1 step 32 do
idx <- nbr_idx + lane_id
If idx < end then
u <- col_idx[idx]
w <- val[idx]
contrib <- pi_old[u] / out_degree[u]
sum <- sum + w * contrib // Warp shuffle reduction omitted for brevity
End
End
// Damping and teleport injection
pi_new[v] <- (1 - alpha) * sum
If v in S then
pi_new[v] <- pi_new[v] + alpha / |S|
End
End
End Kernel
// Iteration loop on host
iter <- 0
repeat
pi_old <- pi
Launch SpMV_PPR <<<grid, block>>>(...)
cudaDeviceSynchronize
diff <- L1Norm(pi, pi_old) // Thrust::reduce
iter <- iter + 1
until diff < epsilon or iter >= max_iter
return pi
这里有几个工程细节值得注意:
- Out-degree 预计算:我们在索引阶段就把每个节点的出度算好存下来,避免查询时重复统计。
- Warp 级协作:一个 warp 处理一个节点,而不是一个线程处理一个节点。这是因为图节点的度分布极度不均衡(幂律分布),warp 内的线程可以协作加载高度数节点的长邻居列表,减少线程发散(thread divergence)。
- Early termination:如果某次迭代后 L1 范数变化小于 1e-6,可以提前结束。真实图上通常 20-50 次迭代即可收敛。
这在训练/实际使用中意味着什么? PPR 不是离线算一次就完事的------在查询时,种子集合 S 是动态抽取的(用户查询中的实体)。因此我们需要毫秒级 的 PPR 计算。GPU 加速可以把百万节点图的 PPR 计算从 CPU 上的数秒压到几十毫秒,满足在线 serving 的延迟要求。如果你的部署环境没有 GPU,也可以考虑 Forward Push / Reverse Push 近似算法(Andersen et al., 2006),用局部探索代替全局迭代,牺牲少量精度换取 CPU 友好性。
9.1.3 知识图谱增强的查询重写:多跳推理路径规划与剪枝
查询为什么需要"重写"?
我们已经有了图和 PPR 分数,但用户的问题往往不会直接对应图中的节点名。用户问:"那家给三星最新旗舰机提供芯片的公司,它的创始人是谁?"
直接拿这句话去做向量检索,可能命中"三星""旗舰机""芯片"相关的文本块,但无法自动完成多跳推理:芯片公司 → 创始人。更麻烦的是,图中的路径可能有很多条:
- Galaxy S25 → powered by → Snapdragon 8 Elite → manufactured by → Qualcomm → founded by → Irwin Jacobs
- Galaxy S25 → launched by → Samsung → invested in → Qualcomm → founded by → Irwin Jacobs
第一条路径更短、更直接;第二条绕了个弯。我们需要规划 路径,并且剪枝掉不相关的分支。
别急,这就是知识图谱增强的查询重写的核心任务:把自然语言问题转化为图上的结构化推理计划,并在探索过程中剪掉噪声路径。
三阶段路径探索与剪枝
最近的研究(Paths-over-Graph, PoG, OpenReview 2025)提出了一套优雅的三阶段框架。我们把它拆解开来。
阶段一:查询实体链接与种子扩展
首先,用 LLM 从问题中抽取显式实体("三星""芯片"),映射到图中的节点 ID。同时,LLM 生成隐式扩展------比如"芯片"可能映射到"SoC""Processor""Snapdragon"等同义词节点。
Query Rewriting Stage
User Query
Explicit Entities:
Samsung, Chip
Implicit Expansion:
SoC, Processor, Snapdragon
Seed Set S
Run PPR from S
Top-k High-Score Nodes
阶段二:Beam Search 路径探索
现在我们从种子节点出发,在图上做有宽度限制的深度探索。这很像 RNN 的展开:每一步我们都维护一个候选路径集合(beam),只保留得分最高的 B 条路径继续扩展。
路径得分怎么算?它是三个因子的加权积:
-
PPR 相关性:路径终点在 PPR 分布中的概率质量。
-
语义连贯性:用 SBERT 编码路径上的关系序列,看它与问题语义的匹配度。
-
长度惩罚:避免无限绕圈,每多一跳就乘一个衰减系数 gamma(通常 0.8-0.9)。
Algorithm: MultiHopPathPlanning
Input: Query q, KG G, seed set S, beam width B, max hops H, gamma
Output: Ranked path list P// Stage 1: Entity linking and implicit expansion E <- ExtractEntities(q) using LLM E_imp <- ExpandSynonyms(E) using domain ontology S <- E union E_imp // Stage 2: Personalized PageRank for relevance prior pi <- PersonalizedPageRank(G, S, alpha=0.15) // Stage 3: Beam search with three-factor scoring Beam <- { (s, score=1.0, path=[s]) | s in S } For hop from 1 to H do Candidates <- empty set For each (node, score, path) in Beam do For each neighbor v of node in G do // Compute path extension score ppr_score <- pi[v] rel_emb <- SBERT( ConcatenateRelations(path -> v) ) q_emb <- SBERT(q) sem_score <- CosineSimilarity(rel_emb, q_emb) length_penalty <- gamma ^ hop new_score <- score * (w1 * ppr_score + w2 * sem_score) * length_penalty new_path <- path ++ [v] Candidates <- Candidates union { (v, new_score, new_path) } End End // Pruning: retain only top-B candidates Beam <- TopK(Candidates, B, key=score) End // Stage 4: Deduplication and reranking P <- DeduplicatePaths(Beam) by end-node and relation sequence P <- Sort(P, descending by score) return P
阶段三:三因子剪枝(PoG 的核心创新)
上面的 Beam Search 已经剪掉了很多分支,但在大规模 KG 上,B=5、H=3 仍然可能产生数百条候选路径。PoG 提出了更激进的三阶段剪枝:
-
图结构剪枝:在扩展前,先检查邻居节点的度。如果某个邻居是"超级枢纽"(比如"美国"这种连接到一切实体的节点),直接丢弃,因为它缺乏区分度。
-
LLM 提示剪枝:把当前路径和候选扩展写成自然语言,问 LLM"这个扩展方向与问题相关吗?是/否"。这是一个轻量级二分类判断,虽然每次调用 LLM 有成本,但可以大幅减少下游计算。
-
SBERT 语义剪枝:对剩余候选,用预训练语言模型做快速语义打分,只保留与查询余弦相似度大于阈值 tau 的路径。
Algorithm: ThreeStagePruning
Input: Candidate paths C, query q, threshold tau
Output: Pruned paths C_primeC_prime <- C // Prune 1: Hub filtering by graph structure For each path p in C_prime do last_node <- Last(p) If Degree(last_node) > hub_threshold then C_prime <- C_prime \ { p } End End // Prune 2: LLM binary relevance check For each path p in C_prime do prompt <- "Does path '" + Textualize(p) + "' help answer '" + q + "'? Answer Yes or No only." answer <- LLM(prompt, temperature=0.0) If answer == "No" then C_prime <- C_prime \ { p } End End // Prune 3: SBERT semantic threshold q_emb <- SBERT(q) For each path p in C_prime do p_emb <- SBERT( Textualize(p) ) If CosineSimilarity(q_emb, p_emb) < tau then C_prime <- C_prime \ { p } End End return C_prime
现在我们已经了解了查询重写与多跳路径规划的全流程。它的本质是把模糊的自然语言问题 翻译成图上的定向搜索任务 ,并用三层筛子(结构、LLM、语义)过滤噪声。这在实际使用中意味着:你的 RAG 系统不再只是"找相似的段落",而是真正具备了跨文档推理的能力------比如从"产品名"跳到"供应商"再跳到"创始人"。
9.1.4 轻量级 GraphRAG 优化:双级检索(全局-局部)与关系无向图设计
微软 GraphRAG 太重了吗?
前面的架构很强大,但我们得诚实:微软原版 GraphRAG 的索引成本极高。根据社区实测,处理 100 万 tokens 的语料可能需要数千次 LLM 调用,光是构建社区摘要和实体解析就要烧掉几十美元 API 费用,耗时数小时。对于需要每小时更新一次的知识库(如新闻聚合、股票舆情),这根本不可接受。
别急,2024 下半年到 2025 年,学术界和开源社区提出了一系列轻量级 GraphRAG 方案,核心思想是:保留图的结构性优势,但把构建和检索成本压到可接受的范围 。其中最具代表性的是 LightRAG (Guo et al., 2024)和 E2GraphRAG(2025)。
双级检索:全局摘要 vs 局部细节
我们先解决检索策略的问题。微软 GraphRAG 区分了两种查询模式:
- Global Search:回答宏观问题("这份年报的主要风险点有哪些?"),依赖高层社区摘要。
- Local Search:回答微观问题("乔布斯在 1985 年做了什么?"),依赖实体邻居遍历和原始文本块。
但原版实现需要预先定义查询类型,或者额外训练一个分类器。轻量级方案的做法更聪明:让查询本身携带层级信号。
Dual-Level Retrieval
User Query
Keywordextract
LLM / Regex
High-level Keywords
themes, risks, trends
Low-level Keywords
entities, events, products
Global Index:
Community Summaries
Local Index:
Entity Neighbors + Chunks
Fusion &
Reranking
Final Answer
LLM Generation
具体怎么做?LightRAG 在索引阶段为每个实体和关系提取关键词(keywords)。查询进来时,系统先抽取出查询中的关键词,判断哪些是"抽象词"(如"趋势""影响"),哪些是"具体词"(如"iPhone 16""马斯克")。抽象词走全局检索(社区摘要),具体词走局部检索(实体子图+原文块)。最后把两路结果融合,丢给 LLM 生成答案。
这就像一个图书馆:有人问你"这层楼有哪些类型的书",管理员直接看分类目录(全局);有人问你"《百年孤独》第 3 章讲了什么",管理员去书架拿书(局部)。双级检索就是同时维护分类目录和书架定位系统。
关系无向图设计:为什么"去掉箭头"反而更好?
在微软原版 GraphRAG 中,关系是有向的(Samsung launched Galaxy S25)。有向图语义精确,但带来两个问题:
- 索引成本:LLM 需要判断方向,抽取错误率更高,需要更多"gleaning"(二次抽取)轮次来修正。
- 检索脆弱性:如果用户问"Galaxy S25 是谁发布的?",系统需要反向遍历"launched by"关系;但如果抽取时只存了"launched"方向,就会漏答。
轻量级方案(如 E2GraphRAG)提出:在检索层把图视为无向图。索引时仍然让 LLM 抽取有向关系,但存入图数据库时,把每条有向边拆成两条无向边(或者存储时保留方向,但遍历时不区分方向)。配合 SpaCy 等传统 NLP 工具做实体抽取,替代部分 LLM 调用,索引速度可以提升 10 倍以上。
Undirected Retrieval Graph
Samsung
launched
Galaxy S25
competes with
iPhone 16
powered by
Snapdragon 8 Elite
manufactured by
等等,这不会丢失语义吗?确实会。但实践发现,在 RAG 场景下,召回率比精确的方向性更重要。因为 LLM 在生成最终答案时,会重新阅读检索回来的关系描述文本(如"Samsung launched Galaxy S25"),它自己就能理解方向。图检索层的任务是"把相关素材找全",而不是"替 LLM 做语义推理"。
自适应检索模式选择
E2GraphRAG 进一步提出自适应路由:不需要人工区分全局/局部查询,而是根据查询关键词的分布自动选择检索深度。
Algorithm: AdaptiveDualRetrieval
Input: Query q, GlobalIndex G_idx, LocalIndex L_idx, threshold theta
Output: Context ctx
// Step 1: Keyword extraction and classification
K <- ExtractKeywords(q)
K_high <- Filter(K, type=abstract) // e.g., themes, risks, overview
K_low <- Filter(K, type=concrete) // e.g., named entities, products
// Step 2: Adaptive mode selection
If |K_high| / |K| > theta then
mode <- "global"
Else If |K_low| / |K| > theta then
mode <- "local"
Else
mode <- "hybrid"
End
// Step 3: Retrieval execution
ctx <- empty set
If mode in {"global", "hybrid"} then
communities <- VectorSearch(G_idx.community_emb, SBERT(q), top_k=3)
ctx <- ctx union communities
End
If mode in {"local", "hybrid"} then
entities <- VectorSearch(L_idx.entity_emb, SBERT(q), top_k=5)
For each e in entities do
subgraph <- BFS(e, depth=2, undirected=true)
chunks <- RetrieveSourceChunks(subgraph)
ctx <- ctx union subgraph union chunks
End
End
// Step 4: Context window budget management
ctx <- RerankAndTruncate(ctx, max_tokens=128k)
return ctx
在继续之前,让我们回顾一下这里的权衡。轻量级方案牺牲了部分语义精确性(无向图、简化摘要)和覆盖范围(更浅的 BFS 深度),但换来了可接受的索引成本 和毫秒级查询延迟。根据 E2GraphRAG 的实验报告,这种简化后的系统在检索准确率上只比原版 GraphRAG 低 2-3%,但索引速度提升 10 倍,查询延迟降低 100 倍。
这在实际使用中意味着什么? 如果你的团队没有无限的 LLM API 预算,或者你的知识库需要小时级更新,那么轻量级 GraphRAG 是更务实的起点。你可以先用无向图+双级检索搭建 MVP,等业务验证成功后再逐步引入有向关系、社区摘要和 PPR 重排序。
闭环总结与延伸阅读
好了,我们已经一起走完了一遍 GraphRAG 的完整技术栈:
- 宏观:GraphRAG 用图结构替代了传统 RAG 的平面向量空间,解决了跨文档关系推理和全局主题概括的问题。
- 中观:多粒度索引(实体-关系-社区-摘要)把理解成本前置;双级检索(全局/局部)把查询成本后置并优化;PPR 提供了数学上严谨的图节点相关性度量。
- 微观:GPU 上的 CSR-SpMV 内核、Warp 级协作、三阶段路径剪枝、自适应查询路由------这些工程细节决定了系统能否从论文走向生产。
如果你要动手实现,建议从以下开源项目入手:
- Microsoft GraphRAG(官方实现,适合研究完整索引流程)
- LightRAG(适合快速部署和低成本场景)
- E2GraphRAG(适合需要频繁更新的动态知识库)
延伸阅读(按出现顺序):
- Edge et al. (2024). From Local to Global: A Graph RAG Approach to Query-Focused Summarization. Microsoft Research.
- Traag et al. (2019). From Louvain to Leiden: Guaranteeing Well-Connected Communities. Scientific Reports.
- Andersen et al. (2006). Local Graph Partitioning using PageRank Vectors. FOCS.
- Haveliwala (2002). Topic-Sensitive PageRank. WWW.
- PoG (2025). Paths-over-Graph: Knowledge Graph Empowered LLM Reasoning. OpenReview.
- Guo et al. (2024). LightRAG: Simple and Fast Retrieval-Augmented Generation. arXiv.
- E2GraphRAG (2025). Streamlining Graph-based RAG for High Efficiency. arXiv.
全文完。希望这篇由宏观到微观、由直觉到工程的拆解,能帮你把 GraphRAG 从"听说过"变成"能动手"。