轻量级 GraphRAG 实践:LightRAG、nano-GraphRAG 与 RAPTOR

当 Microsoft GraphRAG 太重时,如何用更低成本获得图增强检索的核心收益


目录

  1. 为什么需要轻量级 GraphRAG
  2. LightRAG:双层检索的工程化方案
  3. nano-GraphRAG:1100 行代码的极简实现
  4. RAPTOR:树形递归摘要的类图替代方案
  5. 实现对比:同一语料库的三种接入方式
  6. 选型指南:决策矩阵与流程图
  7. 混合架构:轻量图构建 + 现有向量数据库

为什么需要轻量级 GraphRAG

经典 RAG 的局限

传统 Vector RAG 的核心操作是"切块 -> 向量化 -> 相似度检索 -> 拼接上下文 -> LLM 生成"。这条链路在处理事实性问答时效果出色,但在以下场景中会显著退化:

  • 跨文档关系推理:用户问"A 公司的供应商中,哪些同时也是 B 公司的客户",单纯的语义相似度无法捕捉实体之间的关系链。
  • 全局性问题:用户问"这批文档的核心主题有哪些",需要对整个语料库进行宏观理解,而不是检索几个最相似的 chunk。
  • 因果链推理:用户问"政策 X 的出台导致了哪些连锁反应",答案分散在多个文档中,且需要沿着因果关系逐步推导。

Microsoft GraphRAG 正是为了解决这些问题而诞生的:先从文档中抽取实体和关系构建知识图谱,再通过 Leiden 社区检测算法将图划分为语义社区,最后在社区层面生成摘要,支持 local search(实体邻域)和 global search(社区摘要遍历)两种查询模式。

GraphRAG 的工程代价

然而,在实际落地中,很多团队在评估 Microsoft GraphRAG 后选择了放弃,原因集中在三个方面:

1) 图构建阶段的 LLM 调用成本极高

GraphRAG 的图构建管道需要对每个文本块进行实体抽取和关系抽取,这些步骤都依赖 LLM(通常是 GPT-4 级别)。对于一个中等规模的企业文档库(约 10 万个文本块),图构建阶段可能消耗数百万 token,仅 API 费用就可达数百美元。更关键的是,每次文档更新都需要重新构建受影响的社区结构,增量更新的成本同样不低。

2) 查询阶段的 token 消耗惊人

GraphRAG 的 global search 模式需要遍历所有社区摘要,每次查询消耗约 610,000 个 token,需要发起数百次 API 调用。即便使用 GPT-4o-mini,单次全局查询的成本也在 0.05-0.10 美元左右。对于高频查询场景,这个成本完全不可接受。

3) 基础设施复杂度

完整的 GraphRAG 部署通常需要:Neo4j 或同类图数据库用于存储知识图谱,向量数据库用于 embedding 存储,外加一个编排层来协调图遍历与向量检索。运维团队需要同时掌握图数据库和向量数据库的调优,运维复杂度几乎翻倍。

轻量级方案的核心思路

轻量级 GraphRAG 方案的共同策略是:保留图增强检索的核心收益(关系推理、全局理解),同时大幅削减工程复杂度和运行成本。具体手段包括:

优化方向 Microsoft GraphRAG 轻量级方案
社区检测 Leiden 算法 + 多级社区 去掉或简化(LightRAG 用双层检索替代)
图存储 Neo4j / 图数据库 NetworkX 内存图 / 文件持久化
查询模式 global search 遍历所有社区 向量检索 + 图邻域扩展
增量更新 需重建社区结构 直接合并新节点/边到现有图
每次查询 token ~610,000 ~100(LightRAG)

下面依次介绍三个代表性的轻量级方案。


LightRAG:双层检索的工程化方案

项目概况

LightRAG 由香港大学数据科学实验室(HKU Data Science Lab / HKUDS)开发,论文被 EMNLP 2025 收录。截至 2026 年 2 月,GitHub 仓库获得超过 20,000 star,是目前最活跃的轻量级 GraphRAG 实现。

  • 仓库地址github.com/HKUDS/LightRAG
  • PyPI 包名lightrag-hku
  • Python 版本要求:>= 3.10

核心架构

LightRAG 的架构可以分为两个阶段:Graph-Enhanced Text Indexing(图增强文本索引)和 Dual-Level Retrieval(双层检索)。

索引阶段

  1. 文本分块:将文档切分为语义连贯的文本块(chunk)。
  2. 实体与关系抽取:使用 LLM 从每个 chunk 中抽取实体(entity)和关系(relation),构建三元组 (entity_A, relation, entity_B)
  3. 图构建:将所有三元组合并为一张知识图谱,使用 NetworkX 作为默认图引擎(也支持 Neo4j 等后端)。
  4. 双层向量化
    • Low-level(实体层):对每个实体和关系的描述文本生成 embedding,存入向量数据库。
    • High-level(主题层):对实体的高层语义类别和主题生成 embedding,同样存入向量数据库。

检索阶段

LightRAG 提供四种查询模式:

  • naive:纯向量检索(等价于传统 RAG)。
  • local:从实体层向量中检索相关实体,再沿图边扩展到邻域,获取关系上下文。
  • global:从主题层向量中检索相关高层概念,获取宏观语义。
  • hybrid:同时执行 local 和 global 检索,合并去重后送入 LLM。

这种双层设计是 LightRAG 的核心创新:它用向量检索替代了 GraphRAG 昂贵的社区遍历,同时通过 high-level 主题层保留了全局理解能力。

代码实现

安装

# 基础安装
pip install lightrag-hku

# 含 API Server 的完整安装
pip install "lightrag-hku[api]"

# 必要的额外依赖
pip install aioboto3 tiktoken nano_vectordb

基础用法

import os
import asyncio
from lightrag import LightRAG, QueryParam
from lightrag.llm.openai import gpt_4o_mini_complete, openai_embed
from lightrag.kg.shared_storage import initialize_pipeline_status

WORKING_DIR = "./lightrag_workspace"
os.makedirs(WORKING_DIR, exist_ok=True)

async def main():
    # 1. 初始化 LightRAG 实例
    rag = LightRAG(
        working_dir=WORKING_DIR,
        embedding_func=openai_embed,
        llm_model_func=gpt_4o_mini_complete,
    )

    # 2. 初始化存储和管道状态
    await rag.initialize_storages()
    await initialize_pipeline_status()

    # 3. 插入文档
    with open("./corpus/financial_reports.txt", "r") as f:
        content = f.read()
    await rag.ainsert(content)

    # 4. 四种模式查询
    # naive 模式 -- 纯向量检索
    result_naive = await rag.aquery(
        "公司的主要收入来源是什么?",
        param=QueryParam(mode="naive")
    )

    # local 模式 -- 实体邻域检索
    result_local = await rag.aquery(
        "A 公司和 B 公司之间的供应链关系是什么?",
        param=QueryParam(mode="local")
    )

    # global 模式 -- 主题层检索
    result_global = await rag.aquery(
        "这批报告反映了哪些行业趋势?",
        param=QueryParam(mode="global")
    )

    # hybrid 模式 -- 推荐的默认模式
    result_hybrid = await rag.aquery(
        "政策变化对公司合规成本的影响链条是什么?",
        param=QueryParam(mode="hybrid")
    )

    print(result_hybrid)

if __name__ == "__main__":
    asyncio.run(main())

使用本地模型(Ollama)

from lightrag.llm.ollama import ollama_model_complete, ollama_embed

rag = LightRAG(
    working_dir=WORKING_DIR,
    llm_model_func=ollama_model_complete,
    llm_model_name="qwen2.5:14b",
    embedding_func=ollama_embed,
    embedding_model_name="nomic-embed-text:latest",
    embedding_dim=768,
)

存储后端体系

LightRAG 的一大工程优势是其灵活的存储后端体系。系统使用四类存储,每类都支持多种后端实现:

存储类型 用途 默认后端 可选后端
KV Storage LLM 缓存、chunk 文本、文档元数据 JsonKVStorage MongoDB, Redis, PostgreSQL, TiDB, Oracle
Vector Storage 实体/关系/chunk 向量 NanoVectorDBStorage Milvus, Chroma, Qdrant, Faiss, PostgreSQL (pgvector), MongoDB
Graph Storage 知识图谱 NetworkXStorage Neo4j, PostgreSQL (AGE), MongoDB, TiDB, Gremlin, Oracle
Document Storage 原始文档管理 JsonDocStorage MongoDB, PostgreSQL

需要注意:一旦插入文档后,不能更换存储后端实现,目前不支持跨后端的数据迁移。因此在项目初始化阶段就应确定存储后端的选型。

与 Microsoft GraphRAG 的量化对比

基于公开 benchmark 数据和论文实验结果,以处理 10 万个 chunk 的中等规模语料库为基准:

指标 Microsoft GraphRAG LightRAG 差异倍数
图构建 token 消耗 ~500 万 ~500 万 1x(两者相近)
单次查询 token(global) ~610,000 ~100 6,000x
单次查询 API 调用数 数百次 1 次 数百倍
查询延迟 ~5-10s ~80ms 60-120x
增量更新开销 需重建社区(高) 图合并(低,~50%节省) ~2x
法律文档查询胜率 基线 84.8% LightRAG 胜出
基础设施复杂度 Neo4j + VectorDB + 编排层 单进程可运行 显著简化

核心结论:图构建阶段两者成本相当(都依赖 LLM 做实体抽取),但查询阶段 LightRAG 的成本优势是压倒性的。对于查询密集型场景(如企业内部知识问答),这个差异直接决定了方案的经济可行性。


nano-GraphRAG:1100 行代码的极简实现

项目定位

nano-GraphRAG 是一个极简的 GraphRAG 实现,整个核心代码(不含测试和类型提示)仅 1100 行 Python。它的目标不是替代 Microsoft GraphRAG 或 LightRAG,而是提供一个可 hack 的参考实现,让开发者快速理解 GraphRAG 的核心机制,并在此基础上定制自己的方案。

  • 仓库地址github.com/gusye1234/nano-graphrag
  • PyPI 包名nano-graphrag
  • 核心特性:异步(async)、全类型标注(fully typed)、支持批量和增量插入

架构简析

nano-GraphRAG 保留了 Microsoft GraphRAG 的核心管道,但做了大幅简化:

保留的核心能力

  1. 实体与关系抽取:使用 LLM 从文本块中抽取三元组,构建知识图谱。
  2. 社区检测:支持基于 Leiden 算法的社区划分(但可选跳过)。
  3. 社区摘要:对每个社区生成摘要文本。
  4. Local/Global Search:支持本地搜索(实体邻域)和全局搜索(社区摘要)。

简化的部分

  • 图引擎:默认使用 NetworkX(纯 Python 内存图),无需外部图数据库。
  • 向量引擎:默认使用 nano-vectordb(一个极简的向量检索库),无需部署 Milvus/Qdrant。
  • 缓存机制:使用本地文件系统缓存 LLM 响应,避免重复调用。
  • 配置复杂度:所有配置通过构造函数参数传入,没有复杂的 YAML/JSON 配置文件。

双 LLM 设计

nano-GraphRAG 明确区分两类 LLM 的用途:

  • Best Model(默认 gpt-4o):用于实体抽取、关系推理、最终回答生成等需要高质量推理的任务。
  • Cheap Model(默认 gpt-4o-mini):用于社区摘要、文本清洗等不需要深度推理的任务。

这种设计在成本控制上非常实用:高质量推理占总 token 的比例通常不到 20%,其余 80% 的 token 可以使用更便宜的模型。

代码实现

安装

pip install nano-graphrag

# 或从源码安装(获取最新特性)
git clone https://github.com/gusye1234/nano-graphrag.git
cd nano-graphrag
pip install -e .

基础用法

import os
from nano_graphrag import GraphRAG, QueryParam

os.environ["OPENAI_API_KEY"] = "sk-..."

# 1. 初始化
graph = GraphRAG(working_dir="./nano_workspace")

# 2. 插入文档
with open("./corpus/financial_reports.txt", "r") as f:
    graph.insert(f.read())

# 3. 查询(local 模式 -- 实体邻域搜索)
answer_local = graph.query(
    "A 公司和 B 公司之间的关系是什么?",
    param=QueryParam(mode="local")
)

# 4. 查询(global 模式 -- 社区摘要搜索)
answer_global = graph.query(
    "这批文档的核心主题有哪些?",
    param=QueryParam(mode="global")
)

# 5. 查询(naive 模式 -- 纯向量检索)
answer_naive = graph.query(
    "公司去年的营收是多少?",
    param=QueryParam(mode="naive")
)

print(answer_local)

异步用法

import asyncio
from nano_graphrag import GraphRAG, QueryParam

async def main():
    graph = GraphRAG(working_dir="./nano_workspace")

    with open("./corpus/financial_reports.txt", "r") as f:
        await graph.ainsert(f.read())

    result = await graph.aquery(
        "政策变化的影响链条是什么?",
        param=QueryParam(mode="local")
    )
    print(result)

asyncio.run(main())

自定义 LLM 和 Embedding

import numpy as np
from nano_graphrag import GraphRAG
from nano_graphrag._utils import wrap_embedding_func_with_attrs

# 自定义 LLM 函数(必须是 async)
async def my_llm_func(
    prompt: str,
    system_prompt: str | None = None,
    history_messages: list[dict] | None = None,
    **kwargs
) -> str:
    # 对接任意 LLM API(OpenAI 兼容 / Ollama / vLLM 等)
    response = await my_api_call(prompt, system_prompt)
    return response

# 自定义 Embedding 函数
@wrap_embedding_func_with_attrs(embedding_dim=1024, max_token_size=8192)
async def my_embed_func(texts: list[str]) -> np.ndarray:
    # 对接任意 Embedding 服务
    embeddings = await my_embedding_api(texts)
    return np.array(embeddings)

graph = GraphRAG(
    working_dir="./custom_workspace",
    best_model_func=my_llm_func,       # 高质量推理
    cheap_model_func=my_llm_func,       # 摘要/清洗(可用同一模型或更便宜的模型)
    embedding_func=my_embed_func,
)

适用场景

nano-GraphRAG 最适合以下场景:

  1. 学习与原型验证:想快速理解 GraphRAG 的工作原理,不想和复杂的框架配置搏斗。
  2. 深度定制:需要修改 GraphRAG 管道的某个环节(如实体抽取 prompt、社区检测算法),1100 行代码比 Microsoft GraphRAG 的数万行代码容易得多。
  3. 小规模应用:文档量在 1,000 篇以下,内存图和文件系统存储完全够用。
  4. 研究实验:需要对比不同的 LLM / prompt / 检索策略对 GraphRAG 效果的影响。

RAPTOR:树形递归摘要的类图替代方案

方案概述

RAPTOR(Recursive Abstractive Processing for Tree-Organized Retrieval)由斯坦福大学团队提出,论文发表于 ICLR 2024。与前两个方案不同,RAPTOR 并不构建实体-关系知识图谱,而是通过递归聚类 + 逐层摘要构建一棵层次化的文本摘要树。

  • 论文arxiv.org/abs/2401.18059
  • 仓库github.com/parthsarthi03/raptor
  • 许可证:MIT

核心机制

RAPTOR 的工作流程可以用一个自下而上的金字塔来理解:

Level 3:    [全局摘要]
              /    \
Level 2:  [主题摘要A] [主题摘要B]
            / \        / \
Level 1: [聚类摘要] [聚类摘要] [聚类摘要] [聚类摘要]
          / | \      / | \      / | \      / | \
Level 0: [chunk] [chunk] [chunk] [chunk] [chunk] [chunk] ...

构建过程

  1. Level 0(叶节点):将文档切分为标准 chunk(如 100 token),对每个 chunk 生成 embedding。
  2. 聚类:使用 UMAP 降维 + GMM(Gaussian Mixture Model)软聚类,将语义相似的 chunk 分组。GMM 支持一个 chunk 属于多个聚类(软分配),这比 K-Means 的硬分配更符合文档的语义结构。
  3. 摘要:对每个聚类内的 chunk 使用 LLM 生成一段摘要文本。
  4. 递归:将 Level 1 的摘要文本视为新的"文档",重复步骤 2-3,生成更高层的摘要。
  5. 终止:当所有文本归入单一聚类时停止,顶层节点即为全局摘要。

检索过程

查询时有两种策略:

  • Tree Traversal:从根节点开始,在每一层选择与 query 最相关的节点,逐层向下直到叶节点。适合已知宏观方向的查询。
  • Collapsed Tree:将所有层的节点平铺(去层次化),直接做 top-k 向量检索。适合不确定答案在哪个抽象层级的查询。

实验数据显示,Collapsed Tree 模式在大多数 benchmark 上表现更好,因为它允许跨层检索,同一个 query 可以同时获取细节(低层 chunk)和宏观(高层摘要)信息。

与 GraphRAG 的本质区别

维度 GraphRAG / LightRAG RAPTOR
核心结构 实体-关系图(Knowledge Graph) 层次摘要树(Summary Tree)
构建依赖 实体抽取 + 关系抽取(NER/RE) 聚类 + 摘要(Clustering + Summarization)
关系推理 原生支持(图遍历) 不支持(需要关系信息被摘要覆盖)
全局理解 社区摘要 / 主题层检索 高层摘要节点
增量更新 可增量加入新节点/边 需重建受影响的子树
对 LLM 的要求 NER/RE 需要较强模型 摘要任务对模型要求较低

关键洞察:如果你的查询主要是"跨文档关系推理"(如供应链分析、人物关系网络),GraphRAG/LightRAG 是更好的选择;如果查询主要是"多层级抽象问答"(如从细节到宏观的渐进式理解),RAPTOR 更合适。

代码实现

安装

git clone https://github.com/parthsarthi03/raptor.git
cd raptor
pip install -r requirements.txt

# 或直接 pip 安装(如可用)
pip install raptor-rag

基础用法

import os
from raptor import RetrievalAugmentation, RetrievalAugmentationConfig
from raptor import BaseSummarizationModel, BaseQAModel, BaseEmbeddingModel

os.environ["OPENAI_API_KEY"] = "sk-..."

# 1. 使用默认配置(OpenAI GPT + text-embedding-ada-002)
ra = RetrievalAugmentation()

# 2. 添加文档(触发递归构建摘要树)
with open("./corpus/financial_reports.txt", "r") as f:
    ra.add_documents(f.read())

# 3. 查询
answer = ra.answer_question(
    "政策变化对行业的长期影响是什么?"
)
print(answer)

自定义模型配置

from raptor import (
    RetrievalAugmentation,
    RetrievalAugmentationConfig,
    BaseSummarizationModel,
    BaseQAModel,
    BaseEmbeddingModel,
)

# 自定义摘要模型
class CustomSummarizer(BaseSummarizationModel):
    def summarize(self, context: str, max_tokens: int = 150) -> str:
        # 对接自定义 LLM(如 Ollama / vLLM)
        return my_llm_summarize(context, max_tokens)

# 自定义 QA 模型
class CustomQA(BaseQAModel):
    def answer_question(self, context: str, question: str) -> str:
        prompt = f"Context:\n{context}\n\nQuestion: {question}\nAnswer:"
        return my_llm_generate(prompt)

# 自定义 Embedding 模型
class CustomEmbedding(BaseEmbeddingModel):
    def create_embedding(self, text: str) -> list[float]:
        return my_embedding_api(text)

# 组装配置
config = RetrievalAugmentationConfig(
    summarization_model=CustomSummarizer(),
    qa_model=CustomQA(),
    embedding_model=CustomEmbedding(),
    tb_num_layers=3,           # 摘要树层数
    tb_max_tokens=100,         # 每个 chunk 的最大 token
    tb_summarization_length=100,  # 摘要目标长度
)

ra = RetrievalAugmentation(config=config)

手动实现 RAPTOR 核心逻辑(不依赖官方库):

对于想要完全控制每个步骤的开发者,RAPTOR 的核心逻辑可以在约 200 行代码内实现:

import numpy as np
from sklearn.mixture import GaussianMixture
from umap import UMAP

def build_raptor_tree(chunks: list[str], embed_fn, summarize_fn, max_levels: int = 4):
    """
    构建 RAPTOR 摘要树

    Args:
        chunks: 底层文本块列表
        embed_fn: embedding 函数 (str -> np.ndarray)
        summarize_fn: 摘要函数 (list[str] -> str)
        max_levels: 最大树层数

    Returns:
        tree: dict[int, list[dict]]  # level -> [{text, embedding, children}]
    """
    tree = {0: [{"text": c, "embedding": embed_fn(c)} for c in chunks]}

    for level in range(1, max_levels + 1):
        current_nodes = tree[level - 1]
        if len(current_nodes) <= 1:
            break

        # 1. 获取所有节点的 embedding
        embeddings = np.array([n["embedding"] for n in current_nodes])

        # 2. UMAP 降维(高维聚类效果差)
        n_components = min(10, len(embeddings) - 1)
        if n_components < 2:
            break
        reduced = UMAP(
            n_components=n_components,
            metric="cosine"
        ).fit_transform(embeddings)

        # 3. GMM 软聚类
        n_clusters = max(2, len(current_nodes) // 5)  # 每个聚类约 5 个节点
        gmm = GaussianMixture(n_components=n_clusters, covariance_type="full")
        gmm.fit(reduced)
        probs = gmm.predict_proba(reduced)

        # 4. 分配节点到聚类(软分配:概率 > 阈值的都算)
        threshold = 0.1
        clusters = {i: [] for i in range(n_clusters)}
        for idx, prob_row in enumerate(probs):
            for cluster_id, prob in enumerate(prob_row):
                if prob > threshold:
                    clusters[cluster_id].append(idx)

        # 5. 对每个聚类生成摘要
        new_nodes = []
        for cluster_id, member_indices in clusters.items():
            if not member_indices:
                continue
            member_texts = [current_nodes[i]["text"] for i in member_indices]
            summary = summarize_fn(member_texts)
            new_nodes.append({
                "text": summary,
                "embedding": embed_fn(summary),
                "children": member_indices,
            })

        tree[level] = new_nodes

    return tree

def collapsed_tree_search(tree: dict, query_embedding: np.ndarray, top_k: int = 5):
    """
    Collapsed Tree 检索:将所有层节点平铺,做 top-k 向量检索
    """
    all_nodes = []
    for level, nodes in tree.items():
        for node in nodes:
            all_nodes.append({
                "text": node["text"],
                "embedding": node["embedding"],
                "level": level,
            })

    # 计算余弦相似度
    embeddings = np.array([n["embedding"] for n in all_nodes])
    similarities = embeddings @ query_embedding / (
        np.linalg.norm(embeddings, axis=1) * np.linalg.norm(query_embedding)
    )

    # 返回 top-k
    top_indices = np.argsort(similarities)[-top_k:][::-1]
    return [all_nodes[i] for i in top_indices]

实现对比:同一语料库的三种接入方式

为了直观对比三个框架,我们使用同一个语料库(一组企业财务报告文本文件),展示从初始化到查询的完整流程。

统一的准备工作

import os

# 假设语料库是一个目录,包含多个 .txt 文件
CORPUS_DIR = "./corpus/financial_reports/"

def load_corpus(directory: str) -> str:
    """加载目录下所有 .txt 文件,合并为一个字符串"""
    texts = []
    for filename in sorted(os.listdir(directory)):
        if filename.endswith(".txt"):
            with open(os.path.join(directory, filename), "r") as f:
                texts.append(f.read())
    return "\n\n".join(texts)

corpus = load_corpus(CORPUS_DIR)
query = "这些报告中,哪些公司之间存在供应链上下游关系?这些关系如何影响了各自的财务表现?"

方案 A:LightRAG

import asyncio
from lightrag import LightRAG, QueryParam
from lightrag.llm.openai import gpt_4o_mini_complete, openai_embed
from lightrag.kg.shared_storage import initialize_pipeline_status

async def run_lightrag(corpus: str, query: str):
    rag = LightRAG(
        working_dir="./workspace/lightrag",
        embedding_func=openai_embed,
        llm_model_func=gpt_4o_mini_complete,
    )
    await rag.initialize_storages()
    await initialize_pipeline_status()

    # 索引(首次运行耗时较长,后续增量更新)
    await rag.ainsert(corpus)

    # 推荐使用 hybrid 模式获得最佳效果
    result = await rag.aquery(query, param=QueryParam(mode="hybrid"))
    return result

answer = asyncio.run(run_lightrag(corpus, query))
print(answer)

方案 B:nano-GraphRAG

from nano_graphrag import GraphRAG, QueryParam as NanoQueryParam

def run_nano_graphrag(corpus: str, query: str):
    graph = GraphRAG(working_dir="./workspace/nano_graphrag")

    # 索引
    graph.insert(corpus)

    # local 模式适合关系类查询
    result = graph.query(query, param=NanoQueryParam(mode="local"))
    return result

answer = run_nano_graphrag(corpus, query)
print(answer)

方案 C:RAPTOR

from raptor import RetrievalAugmentation

def run_raptor(corpus: str, query: str):
    ra = RetrievalAugmentation()

    # 构建摘要树
    ra.add_documents(corpus)

    # 查询(默认使用 collapsed tree 模式)
    result = ra.answer_question(query)
    return result

answer = run_raptor(corpus, query)
print(answer)

三者对比总结

维度 LightRAG nano-GraphRAG RAPTOR
代码行数(核心) ~5,000+ ~1,100 ~2,000
初始化复杂度 中(需 async 初始化) 低(一行构造) 低(一行构造)
图/树构建 实体-关系图 实体-关系图 + 社区 聚类摘要树
关系推理能力 强(图遍历) 强(图遍历 + 社区) 弱(依赖摘要覆盖)
全局理解能力 强(high-level 主题层) 强(global search) 强(高层摘要节点)
查询 token 消耗 极低(~100) 中等(社区遍历) 低(top-k 检索)
存储后端选择 丰富(9 种向量后端) 有限(默认 nano-vectordb) 基础(内存/文件)
增量更新 支持(图合并) 支持(增量插入) 较难(需重建子树)
生产就绪度 高(API Server、多后端) 中(原型/研究级) 中(研究级)
社区活跃度 极高(20K+ star) 中(3K+ star) 中(2K+ star)

选型指南:决策矩阵与流程图

决策矩阵

根据团队的具体需求,可以从以下五个维度进行评估:

决策维度 LightRAG nano-GraphRAG RAPTOR Microsoft GraphRAG
文档规模 < 1K 篇 OK OK OK 过度
文档规模 1K-10K 篇 推荐 可行 可行 可行
文档规模 > 10K 篇 推荐 不建议 不建议 推荐
查询频率:高频(>100 QPS) 推荐 不适合 不适合 不适合
查询频率:中频(1-100 QPS) 推荐 可行 可行 可行
查询频率:低频(<1 QPS) OK OK OK OK
关系推理需求 最强
多层级摘要需求
API 预算:充裕 OK OK OK 推荐
API 预算:有限 推荐 OK 推荐 不建议
API 预算:极有限 推荐 + 本地模型 OK + 本地模型 推荐 + 本地模型 不可行
团队技术栈偏好:生产部署 推荐 不建议 不建议 可行
团队技术栈偏好:快速原型 OK 推荐 OK 不建议
需要定制管道逻辑 中等难度 最容易 容易 最难
已有向量数据库 直接集成 需适配 需适配 需适配

决策流程图

flowchart TD
    A[开始选型] --> B{查询是否需要<br/>实体关系推理?}

    B -->|是| C{文档规模?}
    B -->|否| D{是否需要<br/>多层级抽象理解?}

    D -->|是| E[RAPTOR]
    D -->|否| F[传统 Vector RAG<br/>无需图增强]

    C -->|< 1K 篇| G{是否需要<br/>深度定制管道?}
    C -->|1K-10K 篇| H{API 预算?}
    C -->|> 10K 篇| I{API 预算?}

    G -->|是| J[nano-GraphRAG<br/>1100 行代码可完全掌控]
    G -->|否| K[LightRAG<br/>开箱即用]

    H -->|充裕| L{是否需要最强<br/>关系推理精度?}
    H -->|有限| M[LightRAG<br/>查询成本低 6000x]

    L -->|是| N[Microsoft GraphRAG]
    L -->|否| M

    I -->|充裕| O[Microsoft GraphRAG<br/>或 LightRAG + Neo4j]
    I -->|有限| P[LightRAG + 本地模型<br/>Ollama/vLLM]

    style E fill:#e1f5fe
    style F fill:#f5f5f5
    style J fill:#fff3e0
    style K fill:#e8f5e9
    style M fill:#e8f5e9
    style N fill:#fce4ec
    style O fill:#fce4ec
    style P fill:#e8f5e9

快速决策规则

如果只想要一个快速答案,参考以下三条规则:

  1. 默认选 LightRAG:对大多数团队来说,LightRAG 在功能完整性、查询成本、生产就绪度三个维度上都是最优平衡点。
  2. 选 nano-GraphRAG 的唯一理由:你需要深入理解或大幅修改 GraphRAG 管道的内部逻辑(研究 / 教学 / 高度定制场景)。
  3. 选 RAPTOR 的唯一理由:你的查询主要是"不同粒度的摘要性问答"而非"实体关系推理",且你不想引入知识图谱的构建复杂度。

混合架构:轻量图构建 + 现有向量数据库

在实际企业环境中,团队通常已经部署了向量数据库(如 Qdrant、Chroma、Milvus)用于传统 RAG。引入 GraphRAG 能力时,最务实的做法不是推倒重来,而是在现有向量基础设施上叠加轻量级图构建。

架构模式

                    ┌──────────────────────────────────┐
                    │           Query Router            │
                    │  (根据查询类型路由到不同检索路径)    │
                    └──────────┬───────────┬────────────┘
                               │           │
                    ┌──────────▼──┐   ┌────▼───────────┐
                    │ Vector Path │   │  Graph Path     │
                    │ (事实问答)   │   │ (关系推理)      │
                    └──────────┬──┘   └────┬───────────┘
                               │           │
                    ┌──────────▼──┐   ┌────▼───────────┐
                    │ Existing    │   │ LightRAG       │
                    │ Vector DB   │   │ Knowledge Graph │
                    │ (Qdrant/    │   │ (NetworkX /     │
                    │  Milvus/    │   │  Neo4j)         │
                    │  Chroma)    │   │                 │
                    └──────────┬──┘   └────┬───────────┘
                               │           │
                    ┌──────────▼───────────▼────────────┐
                    │        Context Merger              │
                    │   (合并两路检索结果,去重,排序)      │
                    └──────────────────┬────────────────┘
                                      │
                    ┌─────────────────▼─────────────────┐
                    │              LLM                   │
                    │     (基于合并上下文生成回答)          │
                    └───────────────────────────────────┘

LightRAG + Qdrant 集成

LightRAG 原生支持 Qdrant 作为向量后端。配置方式如下:

import asyncio
from lightrag import LightRAG, QueryParam
from lightrag.llm.openai import gpt_4o_mini_complete, openai_embed
from lightrag.kg.shared_storage import initialize_pipeline_status

async def setup_lightrag_with_qdrant():
    rag = LightRAG(
        working_dir="./workspace/lightrag_qdrant",
        embedding_func=openai_embed,
        llm_model_func=gpt_4o_mini_complete,

        # 指定 Qdrant 作为向量后端
        vector_storage="QdrantVectorDBStorage",

        # Qdrant 连接配置
        vector_db_storage_cls_kwargs={
            "url": "http://localhost:6333",      # Qdrant 服务地址
            "api_key": None,                      # 如使用 Qdrant Cloud 则填写
            "collection_name": "lightrag_vectors", # 集合名称
        },

        # 可选:使用 Neo4j 作为图后端(替代 NetworkX)
        # graph_storage="Neo4JStorage",
        # graph_db_storage_cls_kwargs={
        #     "uri": "bolt://localhost:7687",
        #     "user": "neo4j",
        #     "password": "your_password",
        # },
    )

    await rag.initialize_storages()
    await initialize_pipeline_status()
    return rag

Qdrant 后端在 LightRAG 中使用 payload-based partitioning(基于 payload 的分区)实现数据隔离,这是 Qdrant 官方推荐的多租户方案,支持在单一集合中隔离多个工作空间的数据。

LightRAG + Milvus 集成

async def setup_lightrag_with_milvus():
    rag = LightRAG(
        working_dir="./workspace/lightrag_milvus",
        embedding_func=openai_embed,
        llm_model_func=gpt_4o_mini_complete,

        # 指定 Milvus 作为向量后端
        vector_storage="MilvusVectorDBStorge",  # 注意原文拼写

        vector_db_storage_cls_kwargs={
            "uri": "http://localhost:19530",
            "collection_name": "lightrag_vectors",
        },
    )

    await rag.initialize_storages()
    await initialize_pipeline_status()
    return rag

Milvus 后端通过在 collection 名称中添加 workspace 前缀来实现数据隔离。

LightRAG + Chroma 集成

async def setup_lightrag_with_chroma():
    rag = LightRAG(
        working_dir="./workspace/lightrag_chroma",
        embedding_func=openai_embed,
        llm_model_func=gpt_4o_mini_complete,

        # 指定 Chroma 作为向量后端
        vector_storage="ChromaVectorDBStorage",

        vector_db_storage_cls_kwargs={
            "persist_directory": "./chroma_data",
            "collection_name": "lightrag_vectors",
        },
    )

    await rag.initialize_storages()
    await initialize_pipeline_status()
    return rag

自建混合检索管道

如果团队需要更精细的控制(例如对两路检索结果的权重分配),可以自建混合管道:

import asyncio
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct
from lightrag import LightRAG, QueryParam
from lightrag.llm.openai import gpt_4o_mini_complete, openai_embed
from lightrag.kg.shared_storage import initialize_pipeline_status

class HybridGraphVectorRAG:
    """
    混合检索管道:
    - Vector Path: 直接查询现有 Qdrant 集合(传统 RAG 数据)
    - Graph Path: 通过 LightRAG 进行图增强检索
    """

    def __init__(self, qdrant_url: str, existing_collection: str):
        self.qdrant = QdrantClient(url=qdrant_url)
        self.existing_collection = existing_collection
        self.graph_rag = None

    async def initialize(self):
        self.graph_rag = LightRAG(
            working_dir="./workspace/hybrid",
            embedding_func=openai_embed,
            llm_model_func=gpt_4o_mini_complete,
        )
        await self.graph_rag.initialize_storages()
        await initialize_pipeline_status()

    async def query(
        self,
        question: str,
        vector_weight: float = 0.4,
        graph_weight: float = 0.6,
        top_k: int = 10,
    ) -> dict:
        # 1. Vector Path -- 查询现有集合
        query_embedding = await openai_embed([question])
        vector_results = self.qdrant.search(
            collection_name=self.existing_collection,
            query_vector=query_embedding[0],
            limit=top_k,
        )
        vector_contexts = [hit.payload.get("text", "") for hit in vector_results]

        # 2. Graph Path -- LightRAG hybrid 检索
        graph_answer = await self.graph_rag.aquery(
            question,
            param=QueryParam(mode="hybrid")
        )

        # 3. 合并上下文
        merged_context = self._merge_contexts(
            vector_contexts, graph_answer, vector_weight, graph_weight
        )

        # 4. 最终生成
        final_answer = await self._generate(question, merged_context)

        return {
            "answer": final_answer,
            "vector_sources": len(vector_contexts),
            "graph_enhanced": True,
        }

    def _merge_contexts(
        self,
        vector_contexts: list[str],
        graph_context: str,
        v_weight: float,
        g_weight: float,
    ) -> str:
        # 简单策略:按权重分配 token 预算
        # 生产环境可以用更复杂的去重 + 重排序
        vector_budget = int(3000 * v_weight)
        graph_budget = int(3000 * g_weight)

        v_text = "\n---\n".join(vector_contexts)[:vector_budget]
        g_text = graph_context[:graph_budget]

        return f"[Vector Search Results]\n{v_text}\n\n[Graph-Enhanced Analysis]\n{g_text}"

    async def _generate(self, question: str, context: str) -> str:
        prompt = f"""Based on the following context, answer the question.

Context:
{context}

Question: {question}

Answer:"""
        return await gpt_4o_mini_complete(prompt)

# 使用示例
async def main():
    hybrid = HybridGraphVectorRAG(
        qdrant_url="http://localhost:6333",
        existing_collection="my_existing_rag_collection",
    )
    await hybrid.initialize()

    # 新增文档到图索引
    with open("./new_documents.txt") as f:
        await hybrid.graph_rag.ainsert(f.read())

    # 混合查询
    result = await hybrid.query("公司间的供应链关系如何影响各自的财务表现?")
    print(result["answer"])

asyncio.run(main())

混合架构的设计要点

1) Query Router 的分类策略

不是所有查询都需要图增强。一个简单但有效的分类规则:

查询特征 路由目标 示例
包含实体名称 + 关系动词 Graph Path "A 公司的供应商有哪些?"
全局性/趋势性 Graph Path (global) "行业整体趋势是什么?"
事实性/数值性 Vector Path "2024 年 Q3 营收是多少?"
混合型 两路并行 "政策变化如何影响各公司?"

可以用一个轻量 LLM(如 GPT-4o-mini)做 query classification,或者基于关键词规则硬编码。

2) 避免图索引与向量索引的数据重复

如果现有的向量数据库已经包含了所有文档的 chunk embedding,不需要让 LightRAG 再重复存储一份。可以:

  • 让 LightRAG 只负责图结构(实体/关系/图拓扑),向量检索走现有数据库。
  • 或者让 LightRAG 使用现有向量数据库作为后端(通过 QdrantVectorDBStorage 等),在同一个数据库实例中使用不同的 collection 名称隔离。

3) 增量更新的同步策略

新文档到达时,需要同时更新两条路径:

async def ingest_new_document(doc_text: str, doc_metadata: dict):
    # 1. 更新现有向量数据库(传统 RAG 路径)
    chunks = chunk_text(doc_text)
    embeddings = await embed_chunks(chunks)
    qdrant_client.upsert(
        collection_name="my_collection",
        points=[
            PointStruct(id=i, vector=emb, payload={"text": chunk, **doc_metadata})
            for i, (chunk, emb) in enumerate(zip(chunks, embeddings))
        ]
    )

    # 2. 更新 LightRAG 图索引(图增强路径)
    await graph_rag.ainsert(doc_text)

LightRAG 的增量插入会自动将新文档的实体和关系合并到现有图中,无需重建整个图结构。


附录

参考资料

项目 仓库 / 论文 许可证
LightRAG github.com/HKUDS/LightRAG MIT
nano-GraphRAG github.com/gusye1234/nano-graphrag MIT
RAPTOR github.com/parthsarthi03/raptor, arxiv.org/abs/2401.18059 MIT
Microsoft GraphRAG github.com/microsoft/graphrag MIT

版本与时效声明

本文基于 2026 年 2 月的项目状态撰写。各框架仍在快速迭代中,API 和配置方式可能发生变化,请以各项目的官方文档为准。

成本估算速查表

以 GPT-4o-mini 为例(输入 $0.15/1M token,输出 $0.60/1M token),处理 10,000 个 chunk(约 500 万 token 原始文本):

阶段 LightRAG nano-GraphRAG RAPTOR Microsoft GraphRAG
图/树构建(一次性) ~$5-15 ~$5-15 ~$3-8 ~$5-15
单次 local 查询 ~$0.0001 ~$0.001 N/A ~$0.01
单次 global 查询 ~$0.0001 ~$0.05 ~$0.001 ~$0.10
1000 次 hybrid 查询 ~$0.20 ~$50 ~$1.00 ~$100+
增量更新(100 篇新文档) ~$0.50 ~$0.50 ~$0.30(可能需重建) ~$2-5

以上为粗略估算,实际成本取决于文档复杂度、chunk 大小、实体密度等因素。RAPTOR 的构建成本较低是因为摘要任务(相比 NER/RE)对 prompt 复杂度的要求较低。


Maurice | maurice_wen@proton.me