向量检索与图检索融合架构

GraphRAG vs VectorRAG 与混合检索实战 | 2026-02

一、两种检索范式的本质差异

大模型时代的知识检索有两条路径:基于向量相似度的语义检索(VectorRAG)和基于图结构的关系检索(GraphRAG)。二者解决的是不同维度的问题:

VectorRAG 擅长回答:
  "跟 X 语义最相似的内容是什么?"
  -> 基于 embedding 余弦相似度,模糊匹配

GraphRAG 擅长回答:
  "X 和 Y 之间有什么关系?经过了哪些中间节点?"
  -> 基于图遍历,精确的结构化推理

对比分析:

维度 VectorRAG GraphRAG 混合架构
检索方式 语义相似度 (ANN) 图遍历 + 子图匹配 两者结合
擅长问题 模糊语义查询 多跳关系推理 全类型问题
数据结构 文档块 -> 向量 实体-关系三元组 文档+图谱
构建成本 低(切片+嵌入) 高(NER+关系抽取+图构建) 最高
维护成本 中-高 中-高
幻觉控制 中(可能检索到不相关块) 强(结构化事实约束) 最强
全局视角 弱(局部文档块) 强(全图社区+摘要) 最强

二、VectorRAG 架构解析

2.1 标准流程

文档 -> 分块(Chunking) -> 向量化(Embedding) -> 存入向量数据库
                                                     |
用户查询 -> 向量化 -> ANN 检索 -> Top-K 文档块 -> LLM 生成回答

2.2 关键组件选型

组件 选项 推荐
向量数据库 Milvus / Qdrant / Weaviate / Pinecone / pgvector Qdrant(轻量)/ Milvus(规模)
Embedding text-embedding-3-large / bge-m3 / jina-v3 bge-m3(多语言性价比最高)
分块策略 固定长度 / 递归分割 / 语义分块 语义分块(LlamaIndex SemanticChunker)
重排序 bge-reranker-v2 / Cohere Rerank bge-reranker-v2(本地部署)

2.3 LangChain 实现

from langchain_community.vectorstores import Qdrant
from langchain_openai import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA

# 1. 文档分块
splitter = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=64)
chunks = splitter.split_documents(documents)

# 2. 向量化并存入 Qdrant
embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
vectorstore = Qdrant.from_documents(
    chunks,
    embeddings,
    location=":memory:",
    collection_name="knowledge_base",
)

# 3. 检索增强生成
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
llm = ChatOpenAI(model="gpt-4o", temperature=0)
qa_chain = RetrievalQA.from_chain_type(llm=llm, retriever=retriever)

answer = qa_chain.invoke("灵阙科技的主要产品是什么?")

2.4 VectorRAG 的局限

  • 多跳问题失效:"张三的老板的客户是谁?"需要跨文档块关联,单纯向量召回困难
  • 全局摘要缺失:"这个领域的整体趋势是什么?"向量检索只返回局部片段
  • 结构化关系丢失:文档被切片后,实体间的精确关系信息被稀释

三、GraphRAG 架构解析

3.1 微软 GraphRAG 流程

文档 -> LLM 实体抽取 -> LLM 关系抽取 -> 构建知识图谱
                                            |
                                   社区检测 (Leiden)
                                            |
                                   社区摘要 (LLM)
                                            |
                        ┌───────────────────┴───────────────────┐
                        |                                       |
                  Local Search                            Global Search
                  (实体 + 关系 +                         (社区摘要 +
                   局部上下文)                             Map-Reduce)

3.2 Local Search vs Global Search

模式 适用问题 检索策略 示例
Local 具体实体查询 实体匹配 -> 子图展开 -> 上下文组装 "Neo4j 的创始人是谁"
Global 全局概述/趋势 社区摘要 -> Map(各社区回答) -> Reduce(汇总) "图数据库行业趋势"

3.3 LlamaIndex PropertyGraph 实现

from llama_index.core import PropertyGraphIndex, SimpleDirectoryReader
from llama_index.llms.openai import OpenAI
from llama_index.graph_stores.neo4j import Neo4jPropertyGraphStore

# 1. 加载文档
documents = SimpleDirectoryReader("./data").load_data()

# 2. 配置图存储
graph_store = Neo4jPropertyGraphStore(
    username="neo4j",
    password="password",
    url="bolt://localhost:7687",
)

# 3. 构建 PropertyGraph 索引(自动抽取实体和关系)
llm = OpenAI(model="gpt-4o", temperature=0)
index = PropertyGraphIndex.from_documents(
    documents,
    llm=llm,
    graph_store=graph_store,
    show_progress=True,
)

# 4. 图检索问答
query_engine = index.as_query_engine(
    include_text=True,       # 包含原始文本上下文
    response_mode="tree_summarize",
)
response = query_engine.query("张三和灵阙科技之间的关系是什么?")

3.4 GraphRAG 的局限

  • 构建成本高:LLM 抽取实体和关系的 API 调用费用显著
  • 抽取质量不稳定:LLM 可能遗漏实体或抽取错误关系
  • 语义模糊查询弱:图检索依赖精确的实体匹配,模糊语义查询不如向量检索

四、混合架构设计

4.1 架构总览

                    ┌─────────────────┐
                    │   用户查询       │
                    └────────┬────────┘
                             |
                    ┌────────┴────────┐
                    │  查询路由器      │
                    │  (意图分类)      │
                    └───┬────┬────┬───┘
                        |    |    |
              ┌─────────┘    |    └──────────┐
              v              v               v
     ┌────────────┐  ┌──────────┐  ┌─────────────┐
     │ 向量检索    │  │ 图检索   │  │ 混合检索     │
     │ (语义匹配)  │  │ (结构化) │  │ (两者结合)   │
     └─────┬──────┘  └────┬─────┘  └──────┬──────┘
           |               |               |
           └───────────────┼───────────────┘
                           |
                    ┌──────┴──────┐
                    │  结果融合     │
                    │  + 重排序     │
                    └──────┬──────┘
                           |
                    ┌──────┴──────┐
                    │  LLM 生成    │
                    │  (带引用)     │
                    └─────────────┘

4.2 查询路由策略

from enum import Enum

class QueryType(Enum):
    SEMANTIC = "semantic"       # "什么是知识图谱?"
    STRUCTURAL = "structural"   # "张三的同事有哪些?"
    HYBRID = "hybrid"          # "知识图谱在风控中有哪些应用案例?"
    GLOBAL = "global"          # "图数据库行业整体趋势"

def classify_query(query: str, llm) -> QueryType:
    """用 LLM 对查询意图分类"""
    prompt = f"""
    将以下查询分类为四种类型之一:
    - semantic: 概念解释、定义、描述类问题
    - structural: 涉及具体实体间关系、路径、多跳推理
    - hybrid: 既需要语义理解又需要结构化关系
    - global: 全局概述、趋势、总结类问题

    查询: {query}
    类型:
    """
    result = llm.invoke(prompt).content.strip().lower()
    return QueryType(result)

def route_query(query: str, query_type: QueryType):
    """根据类型路由到不同检索器"""
    if query_type == QueryType.SEMANTIC:
        return vector_retrieve(query, k=5)
    elif query_type == QueryType.STRUCTURAL:
        return graph_retrieve(query)
    elif query_type == QueryType.GLOBAL:
        return global_search(query)
    else:  # HYBRID
        vec_results = vector_retrieve(query, k=3)
        graph_results = graph_retrieve(query)
        return merge_and_rerank(vec_results, graph_results)

4.3 结果融合策略

策略 方法 适用场景
分数归一化 将向量和图分数归一到 [0,1] 后加权求和 两路结果质量稳定
RRF (Reciprocal Rank Fusion) 1/(k+rank) 加权合并 通用场景
LLM 重排序 用 LLM 对合并结果做相关性打分 质量优先
交叉验证 两路都命中的结果优先 高精度场景
def reciprocal_rank_fusion(results_lists: list[list], k: int = 60) -> list:
    """RRF 融合多路检索结果"""
    scores = {}
    for results in results_lists:
        for rank, item in enumerate(results):
            item_id = item["id"]
            if item_id not in scores:
                scores[item_id] = {"item": item, "score": 0}
            scores[item_id]["score"] += 1.0 / (k + rank + 1)

    fused = sorted(scores.values(), key=lambda x: x["score"], reverse=True)
    return [entry["item"] for entry in fused]

五、何时用哪种方案

决策树:

你的数据是非结构化文档为主?
  |
  是 -> 实体关系对业务重要吗?
  |       |
  |       是 -> 混合架构(Vector + Graph)
  |       否 -> VectorRAG 足够
  |
  否 -> 数据已经是结构化的?
          |
          是 -> 已有知识图谱?
          |       |
          |       是 -> GraphRAG
          |       否 -> 先建图谱,再 GraphRAG
          |
          否 -> 半结构化 -> 混合架构

六、性能基准参考

以 10 万文档(约 50 万文档块、200 万三元组)规模测试:

指标 VectorRAG GraphRAG (Local) GraphRAG (Global) 混合架构
构建时间 2 小时 18 小时 同左 + 社区摘要 6h 24 小时
构建成本 $5 (embedding) $150 (LLM 抽取) $200 (含社区摘要) $205
检索延迟 50-100ms 100-300ms 2-5s (Map-Reduce) 200-500ms
事实型问题准确率 72% 85% 68% 88%
多跳推理准确率 35% 78% 55% 82%
全局概述质量 4.2/10 6.5/10 8.8/10 8.5/10
存储空间 2 GB 8 GB 10 GB 12 GB

关键发现:

  • 事实型单跳问题:VectorRAG 够用,GraphRAG 成本回报比不高
  • 多跳推理问题:GraphRAG 大幅领先,VectorRAG 几乎不可用
  • 全局概述问题:GraphRAG Global Search 独有优势
  • 混合架构在多数场景取得最佳综合表现,但构建成本最高

七、生产级实现建议

7.1 增量更新策略

class HybridIndexUpdater:
    """混合索引增量更新"""

    def update(self, new_documents: list):
        # 1. 向量索引更新(低成本)
        chunks = self.splitter.split_documents(new_documents)
        self.vectorstore.add_documents(chunks)

        # 2. 图谱增量更新(高成本,可异步)
        for doc in new_documents:
            entities, relations = self.extract_knowledge(doc)
            self.graph_store.upsert_entities(entities)
            self.graph_store.upsert_relations(relations)

        # 3. 社区重计算(周期性,非每次更新)
        if self.should_recompute_communities():
            self.recompute_communities()

7.2 成本优化

  • 图谱构建用小模型(GPT-4o-mini / Gemini Flash)抽取,质量损失约 5-10%
  • 社区摘要按周更新而非实时更新
  • 向量检索用开源 embedding(bge-m3),节省 API 费用
  • 查询路由将 60-70% 的简单查询路由到纯向量检索,节省图查询开销

7.3 监控指标

指标 目标 告警阈值
检索延迟 P95 <500ms >1000ms
回答准确率(人工抽检) >85% <75%
图谱更新延迟 <24h >48h
向量索引碎片率 <20% >40%
LLM Token 消耗 按预算 超预算 120%

Maurice | maurice_wen@proton.me