跳转至

25 - Graph RAG 与知识图谱增强检索

⚠️ 时效性说明:本章涉及的知识图谱工具(Neo4j、LlamaIndex、Microsoft GraphRAG)版本可能随时间更新,请以官方文档为准。

学习目标:理解 Graph RAG 的核心原理,掌握知识图谱构建与检索方法,学会使用 Graph RAG 提升复杂问答质量。 前置知识05-RAG系统构建18-高级RAG技术


目录

  1. Graph RAG 概述
  2. 知识图谱基础
  3. Graph RAG 架构
  4. 实体抽取与关系构建
  5. 图检索策略
  6. Graph RAG 实战
  7. Graph RAG vs 传统 RAG

1. Graph RAG 概述

1.1 为什么需要 Graph RAG

传统 RAG 基于向量相似度检索,存在以下局限:

问题 说明
语义鸿沟 向量相似度无法捕捉实体间的精确关系
多跳推理 需要跨越多个文档的综合推理能力不足
全局理解 对整个文档集的全局概览能力弱
实体消歧 "苹果"是水果还是公司?向量检索难以区分

Graph RAG 通过引入知识图谱来解决这些问题:将文档中的实体和关系抽取为结构化的三元组(如 DeepSeek → 开发了 → DeepSeek-V3),构建图结构索引。检索时,先在图谱中定位相关实体,再沿边扩展到关联实体和关系,从而天然支持多跳推理(如"DeepSeek → 总部位于 → 杭州"需要两跳)。同时,知识图谱的实体消歧能力(通过上下文关系判断"苹果"是公司还是水果)也弥补了向量检索的不足。

1.2 Graph RAG 的核心思想

graph TD
    A[原始文档] --> B[实体抽取]
    B --> C[关系抽取]
    C --> D[知识图谱]

    E[用户查询] --> F[实体链接]
    F --> D
    D --> G[子图检索]
    G --> H[图结构上下文]
    H --> I[LLM 生成回答]

    J[向量索引] --> K[向量检索]
    K --> I

    style D fill:#e1f5fe
    style I fill:#c8e6c9

2. 知识图谱基础

2.1 知识图谱的核心概念

Python
# 知识图谱由三元组 (Subject, Predicate, Object) 组成
triples = [
    ("DeepSeek", "开发了", "DeepSeek-V3"),
    ("DeepSeek", "总部位于", "杭州"),
    ("DeepSeek-V3", "参数量", "671B"),
    ("DeepSeek-V3", "架构", "MoE"),
    ("MoE", "全称", "Mixture of Experts"),
    ("DeepSeek-V3", "激活参数", "37B"),
    ("DeepSeek-V3", "训练数据", "14.8T tokens"),
]

# 知识图谱可以表示为有向图
# 节点 = 实体 (Entities)
# 边 = 关系 (Relations)

2.2 使用 NetworkX 构建知识图谱

Python
import networkx as nx

def build_knowledge_graph(triples):
    """从三元组构建知识图谱"""
    G = nx.DiGraph()

    for subj, pred, obj in triples:
        G.add_node(subj, type='entity')
        G.add_node(obj, type='entity')
        G.add_edge(subj, obj, relation=pred)

    return G

# 构建图谱
kg = build_knowledge_graph(triples)

# 查询:DeepSeek 的所有关系
print("DeepSeek 的所有关系:")
for neighbor in kg.successors("DeepSeek"):
    relation = kg.edges["DeepSeek", neighbor]["relation"]
    print(f"  DeepSeek --[{relation}]--> {neighbor}")

# 最短路径查询
path = nx.shortest_path(kg, "DeepSeek", "MoE")
print(f"\nDeepSeek 到 MoE 的路径: {' → '.join(path)}")

2.3 使用 Neo4j 图数据库

Python
# Neo4j:生产级图数据库,适合百万级以上节点的大规模知识图谱
# 与 NetworkX(内存图库,适合小规模实验)不同,Neo4j 使用 Cypher 查询语言,
# 原生支持索引、持久化、事务和分布式,适合生产环境部署
# 安装:pip install neo4j

from neo4j import GraphDatabase

class KnowledgeGraphDB:
    def __init__(self, uri="bolt://localhost:7687", user="neo4j", password="password"):
        self.driver = GraphDatabase.driver(uri, auth=(user, password))

    def close(self):
        self.driver.close()

    def add_triple(self, subject, predicate, object_):
        """添加三元组"""
        with self.driver.session() as session:
            session.run(
                """
                MERGE (s:Entity {name: $subject})
                MERGE (o:Entity {name: $object})
                MERGE (s)-[r:RELATION {type: $predicate}]->(o)
                """,
                subject=subject, predicate=predicate, object=object_
            )

    def query_neighbors(self, entity_name, max_depth=2):
        """查询实体的邻居"""
        with self.driver.session() as session:
            result = session.run(
                """
                MATCH (e:Entity {name: $name})-[r*1..%d]-(neighbor)
                RETURN neighbor.name AS name, 
                       type(r[-1]) AS relation,
                       length(r) AS depth
                """ % max_depth,
                name=entity_name
            )
            return [record.data() for record in result]

    def multi_hop_query(self, start_entity, end_entity, max_hops=3):
        """多跳查询"""
        with self.driver.session() as session:
            result = session.run(
                """
                MATCH path = (s:Entity {name: $start})-[r*1..%d]-(e:Entity {name: $end})
                RETURN [node in nodes(path) | node.name] AS entities,
                       [rel in relationships(path) | rel.type] AS relations
                """ % max_hops,
                start=start_entity, end=end_entity
            )
            return [record.data() for record in result]

# 使用
db = KnowledgeGraphDB()
for s, p, o in triples:
    db.add_triple(s, p, o)

# 查询 DeepSeek 的邻居
neighbors = db.query_neighbors("DeepSeek")

3. Graph RAG 架构

3.1 Microsoft Graph RAG 架构

微软在 2024 年提出的 Graph RAG 是最知名的实现。其核心创新在于:先用 LLM 从文档中抽取实体和关系构建知识图谱,再通过社区检测(Leiden 算法)将图谱划分为多个语义社区,并为每个社区生成摘要。这种"图+社区摘要"的架构使得全局性查询(如"所有文档的核心主题是什么")不再需要遍历所有文本块,而是直接检索相关社区摘要,在效率和全局理解之间取得平衡。

graph TD
    A[文档集合] --> B[文本分块]
    B --> C[LLM 实体/关系抽取]
    C --> D[知识图谱构建]
    D --> E[社区检测<br/>Leiden 算法]
    E --> F[社区摘要生成]

    G[用户查询] --> H{查询类型}
    H -->|局部查询| I[实体链接 + 子图检索]
    H -->|全局查询| J[社区摘要检索]

    I --> K[LLM 生成]
    J --> K

3.2 Graph RAG 两种查询模式

Python
class GraphRAG:
    def __init__(self, llm, graph_db, vector_store):
        self.llm = llm
        self.graph_db = graph_db
        self.vector_store = vector_store

    def local_search(self, query: str, top_k: int = 5) -> str:
        """局部搜索:精确实体匹配 + 子图扩展

        适合:具体事实型问题
        例如:"DeepSeek-V3 的架构是什么?"
        """
        # 1. 从查询中抽取实体
        entities = self._extract_entities(query)

        # 2. 在图谱中查找匹配实体
        subgraph_context = []
        for entity in entities:
            neighbors = self.graph_db.query_neighbors(entity, max_depth=2)
            subgraph_context.extend(neighbors)

        # 3. 向量检索补充
        vector_results = self.vector_store.search(query, top_k=top_k)

        # 4. 组合上下文生成回答
        context = self._format_context(subgraph_context, vector_results)
        return self.llm.generate(query, context)

    def global_search(self, query: str) -> str:
        """全局搜索:社区摘要 + Map-Reduce

        适合:全局概览型问题
        例如:"所有大模型的技术趋势是什么?"
        """
        # 1. 检索相关社区摘要
        community_summaries = self._search_communities(query)

        # 2. Map: 每个社区独立回答
        intermediate_answers = []
        for summary in community_summaries:
            answer = self.llm.generate(
                query, 
                f"基于以下社区信息回答:\n{summary}"
            )
            intermediate_answers.append(answer)

        # 3. Reduce: 合并所有中间回答
        final_context = "\n\n".join(intermediate_answers)
        return self.llm.generate(
            query,
            f"基于以下多个来源的信息,综合回答问题:\n{final_context}"
        )

    def _extract_entities(self, text: str) -> list:
        """使用 LLM 从文本中抽取实体"""
        prompt = f"""从以下文本中提取关键实体名称,每行一个:

文本:{text}

实体列表:"""
        response = self.llm.generate(prompt)
        return [e.strip() for e in response.strip().split('\n') if e.strip()]

4. 实体抽取与关系构建

4.1 LLM 驱动的知识抽取

Python
import json

def extract_triples_from_text(llm, text: str) -> list:
    """使用 LLM 从文本中抽取三元组"""

    prompt = f"""请从以下文本中抽取知识三元组 (subject, predicate, object)。

要求:
1. 实体名称要规范统一
2. 关系要简洁明确
3. 以 JSON 数组格式返回

文本:
{text}

输出格式示例:
[
    {{"subject": "实体1", "predicate": "关系", "object": "实体2"}},
    ...
]

三元组:"""

    response = llm.generate(prompt)

    try:
        triples = json.loads(response)
        return [(t["subject"], t["predicate"], t["object"]) for t in triples]
    except json.JSONDecodeError:
        return []

# 示例
sample_text = """
DeepSeek-V3 是深度求索公司于2024年12月发布的大语言模型。
该模型采用 MoE(混合专家)架构,总参数量671B,激活参数37B。
训练使用了14.8T tokens的数据,在多个基准测试上接近GPT-4水平。
"""

# triples = extract_triples_from_text(llm, sample_text)
# 预期输出:
# [
#   ("DeepSeek-V3", "发布者", "深度求索"),
#   ("DeepSeek-V3", "发布时间", "2024年12月"),
#   ("DeepSeek-V3", "架构", "MoE"),
#   ("DeepSeek-V3", "总参数量", "671B"),
#   ("DeepSeek-V3", "激活参数", "37B"),
#   ("DeepSeek-V3", "训练数据量", "14.8T tokens"),
#   ("DeepSeek-V3", "性能水平", "接近GPT-4"),
# ]

4.2 实体消歧与对齐

Python
class EntityResolver:
    """实体消歧与对齐"""

    def __init__(self, llm):
        self.llm = llm
        self.entity_aliases = {}  # 实体别名映射

    def resolve(self, entity_name: str) -> str:
        """将实体名称规范化"""
        # 检查已知别名
        for canonical, aliases in self.entity_aliases.items():
            if entity_name.lower() in [a.lower() for a in aliases]:
                return canonical

        # 使用 LLM 进行消歧
        prompt = f"""以下实体名称是否指向同一个实体?

已知实体:{list(self.entity_aliases.keys())}
待消歧实体:{entity_name}

如果是已知实体的别名,返回规范名称。否则返回原始名称。"""

        resolved = self.llm.generate(prompt).strip()
        return resolved

    def add_alias(self, canonical: str, alias: str):
        """添加实体别名"""
        if canonical not in self.entity_aliases:
            self.entity_aliases[canonical] = [canonical]
        self.entity_aliases[canonical].append(alias)

# 使用
resolver = EntityResolver(llm)
resolver.add_alias("DeepSeek", "深度求索")
resolver.add_alias("DeepSeek", "深度求索公司")
resolver.add_alias("DeepSeek-V3", "DeepSeek V3")

print(resolver.resolve("深度求索"))  # → "DeepSeek"

5. 图检索策略

5.1 子图检索

Python
def retrieve_subgraph(graph_db, query_entities: list, max_depth: int = 2) -> str:
    """基于查询实体的子图检索"""
    all_context = []

    for entity in query_entities:
        # 获取实体周围 N 跳的子图
        neighbors = graph_db.query_neighbors(entity, max_depth=max_depth)

        for record in neighbors:
            context_line = f"- {entity} --[{record['relation']}]--> {record['name']}"
            all_context.append(context_line)

    return "\n".join(all_context)

# 示例
context = retrieve_subgraph(db, ["DeepSeek-V3"])
print(context)
# - DeepSeek-V3 --[发布者]--> 深度求索
# - DeepSeek-V3 --[架构]--> MoE
# - DeepSeek-V3 --[参数量]--> 671B
# - MoE --[全称]--> Mixture of Experts

5.2 混合检索(向量 + 图谱)

Python
class HybridGraphRAG:
    """混合检索:向量检索 + 图谱检索"""

    def __init__(self, vector_store, graph_db, llm):
        self.vector_store = vector_store
        self.graph_db = graph_db
        self.llm = llm

    def search(self, query: str, top_k: int = 5) -> dict:
        """混合检索"""

        # 1. 向量检索(语义相似度)
        vector_results = self.vector_store.search(query, top_k=top_k)

        # 2. 图谱检索(结构化关系)
        entities = self._extract_entities(query)
        graph_context = []
        for entity in entities:
            neighbors = self.graph_db.query_neighbors(entity, max_depth=2)
            graph_context.extend(neighbors)

        # 3. 合并上下文
        combined_context = self._merge_contexts(vector_results, graph_context)

        # 4. 生成回答
        answer = self.llm.generate(
            query=query,
            context=combined_context
        )

        return {
            "answer": answer,
            "vector_results": len(vector_results),
            "graph_triples": len(graph_context),
            "entities_found": entities,
        }

    def _merge_contexts(self, vector_results, graph_context):
        """合并向量检索和图谱检索的上下文"""
        context_parts = ["=== 相关文档片段 ==="]
        for i, result in enumerate(vector_results):
            context_parts.append(f"[文档{i+1}] {result['text']}")

        context_parts.append("\n=== 知识图谱关系 ===")
        for record in graph_context:
            context_parts.append(f"- {record}")

        return "\n".join(context_parts)

6. Graph RAG 实战

6.1 使用 LlamaIndex 构建 Graph RAG

Python
from llama_index.core import KnowledgeGraphIndex, SimpleDirectoryReader
from llama_index.core.graph_stores import SimpleGraphStore
from llama_index.llms.openai import OpenAI

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

# 2. 构建知识图谱索引
graph_store = SimpleGraphStore()
llm = OpenAI(model="gpt-5-mini")

kg_index = KnowledgeGraphIndex.from_documents(
    documents,
    graph_store=graph_store,
    llm=llm,
    max_triplets_per_chunk=10,  # 每个文本块最多抽取10个三元组
    show_progress=True,
)

# 3. 查询
query_engine = kg_index.as_query_engine(
    include_text=True,      # 包含原文
    response_mode="tree_summarize",
    embedding_mode="hybrid",  # 混合模式:向量 + 图谱
    sim_score_threshold=0.5,
)

response = query_engine.query("DeepSeek-V3 的技术特点是什么?")
print(response)

6.2 使用 Microsoft GraphRAG

Bash
# 安装
pip install graphrag

# 初始化项目
graphrag-init --root ./my_graphrag_project

# 配置 settings.yaml
# 设置 LLM API key 和模型

# 索引文档
graphrag index --root ./my_graphrag_project

# 查询
graphrag query --root ./my_graphrag_project \
    --method local \
    --query "所有大模型的技术趋势"

7. Graph RAG vs 传统 RAG

7.1 性能对比

维度 传统 RAG Graph RAG
事实型问答 ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
多跳推理 ⭐⭐ ⭐⭐⭐⭐⭐
全局概览 ⭐⭐ ⭐⭐⭐⭐
构建成本 高(需构建图谱)
查询延迟 中(图遍历)
存储需求 中(图数据库)
可解释性 ⭐⭐ ⭐⭐⭐⭐⭐

7.2 适用场景

Text Only
选择决策:
├── 简单问答(单文档检索)→ 传统 RAG
├── 多跳推理(跨文档关联)→ Graph RAG
├── 全局概览(数据集总结)→ Graph RAG (Global Search)
├── 实体密集型(人物/组织/地点)→ Graph RAG
├── 实时性要求高 → 传统 RAG
└── 需要可解释性 → Graph RAG

7.3 最佳实践:混合架构

Python
class AdaptiveRAG:
    """自适应 RAG:根据查询类型选择策略"""

    def __init__(self, vector_rag, graph_rag, llm):
        self.vector_rag = vector_rag
        self.graph_rag = graph_rag
        self.llm = llm

    def query(self, question: str) -> str:
        # 1. 分类查询类型
        query_type = self._classify_query(question)

        if query_type == "simple_fact":
            return self.vector_rag.query(question)
        elif query_type == "multi_hop":
            return self.graph_rag.local_search(question)
        elif query_type == "global_overview":
            return self.graph_rag.global_search(question)
        else:
            # 混合模式
            vector_answer = self.vector_rag.query(question)
            graph_answer = self.graph_rag.local_search(question)
            return self._merge_answers(question, vector_answer, graph_answer)

    def _classify_query(self, question: str) -> str:
        prompt = f"""将以下问题分类为:
- simple_fact: 简单事实查询
- multi_hop: 需要多步推理
- global_overview: 全局概览

问题:{question}
分类:"""
        return self.llm.generate(prompt).strip().lower()

💡 进阶补充:Graph RAG 前沿框架对比

除了 Microsoft GraphRAG,近年来涌现了多个优秀的 Graph RAG 框架,各有特色:

框架 核心思想 优势 局限
Microsoft GraphRAG 社区检测 + Map-Reduce 全局摘要 全局概览能力强,适合总结性问题 索引成本高,社区摘要消耗大量 Token
LightRAG 轻量级图构建,增量更新友好 构建速度快,支持流式增量 全局推理能力弱于 GraphRAG
FRAG (Fast RAG) 跳过社区检测,直接用实体链接 极简架构,延迟低 缺乏全局视角
GraphIRAG 结合信息检索与图推理 检索精度高,支持多跳推理 实现复杂度高

选型决策树

Text Only
Graph RAG 选型
├── 需要全局概览 / 总结性问答 → Microsoft GraphRAG
├── 需要快速原型 / 增量更新 → LightRAG
├── 追求低延迟 / 简单部署 → FRAG
└── 需要多跳推理 + 高精度 → GraphIRAG

生产环境建议:先用 LightRAG 做快速验证,确认 Graph RAG 路线可行后,再根据场景迁移到 Microsoft GraphRAG 或 GraphIRAG。

🔥 实战经验:智能查询路由与降级策略

在生产级 Graph RAG 系统中,不同复杂度的查询应走不同的检索路径。参考 all-in-rag 的智能路由设计:

Python
class IntelligentQueryRouter:
    """基于查询复杂度的智能路由"""

    def route(self, query: str) -> str:
        complexity = self._analyze_complexity(query)

        if complexity == "simple":
            # 简单查询:直接向量检索
            return "vector_search"
        elif complexity == "medium":
            # 中等查询:向量检索 + 图谱扩展
            return "hybrid_search"
        else:
            # 复杂查询:全局图谱搜索 + 社区摘要
            return "global_graph_search"

    def _analyze_complexity(self, query: str) -> str:
        """分析查询复杂度"""
        # 启发式规则 + LLM 判断
        if any(kw in query for kw in ["所有", "总结", "趋势", "对比"]):
            return "complex"  # 全局性问题
        elif any(kw in query for kw in ["关系", "之间", "连接"]):
            return "medium"   # 关系型问题
        else:
            return "simple"   # 事实型问题

降级策略:当图谱检索失败或超时时,自动降级到纯向量检索,确保系统可用性:

Python
def safe_query(query: str, timeout: float = 5.0) -> str:
    try:
        return graph_rag.query(query, timeout=timeout)
    except (TimeoutError, GraphDBError):
        logging.warning("Graph RAG 失败,降级到向量检索")
        return vector_rag.query(query)

📝 本章小结

概念 核心要点
知识图谱 三元组 (S, P, O) 构建实体关系网络
Graph RAG 图结构增强检索,支持多跳推理
Local Search 实体链接 + 子图检索,适合事实型查询
Global Search 社区摘要 + Map-Reduce,适合全局概览
混合架构 向量检索 + 图谱检索互补

✏️ 练习与面试

练习 1 :知识图谱构建(⭐)

使用 NetworkX 从一段文本中构建知识图谱,实现实体和关系的可视化展示。

💡 参考答案
Python
# 练习1:知识图谱构建参考实现
import networkx as nx
import json
from openai import OpenAI

client = OpenAI()

def extract_triples(text: str) -> list[dict]:
    """使用 LLM 从文本中抽取三元组"""
    resp = client.chat.completions.create(
        model="gpt-5-mini",
        messages=[
            {"role": "system", "content": """从文本中抽取知识图谱三元组。
输出JSON格式:{"triples": [{"subject": "...", "predicate": "...", "object": "..."}]}
只输出JSON。"""},
            {"role": "user", "content": text},
        ],
        max_tokens=500,
    )
    return json.loads(resp.choices[0].message.content)["triples"]

def build_knowledge_graph(triples: list[dict]) -> nx.DiGraph:
    """构建有向知识图谱"""
    G = nx.DiGraph()
    for t in triples:
        G.add_edge(t["subject"], t["object"], relation=t["predicate"])
    return G

def query_graph(G: nx.DiGraph, entity: str) -> list[str]:
    """查询实体的所有关系"""
    if entity not in G:
        return []
    results = []
    for neighbor in G.successors(entity):
        relation = G.edges[entity, neighbor]["relation"]
        results.append(f"{entity} --[{relation}]--> {neighbor}")
    for neighbor in G.predecessors(entity):
        relation = G.edges[neighbor, entity]["relation"]
        results.append(f"{neighbor} --[{relation}]--> {entity}")
    return results

# 使用示例
text = """
DeepSeek 是一家中国 AI 公司,总部位于杭州。
DeepSeek 发布了 DeepSeek-V3 模型,该模型采用 MoE 架构。
DeepSeek-V3 的参数量为 671B,其中激活参数为 37B。
DeepSeek 还发布了 DeepSeek-R1,这是一个推理模型。
"""

triples = extract_triples(text)
G = build_knowledge_graph(triples)

print(f"图谱统计: {G.number_of_nodes()} 个节点, {G.number_of_edges()} 条边")
print(f"\nDeepSeek 的所有关系:")
for r in query_graph(G, "DeepSeek"):
    print(f"  {r}")

# 可视化(需要 matplotlib)
# import matplotlib.pyplot as plt
# pos = nx.spring_layout(G)
# nx.draw(G, pos, with_labels=True, node_color='lightblue', arrows=True)
# plt.savefig("knowledge_graph.png")
**关键要点**: - LLM 驱动的实体关系抽取是 Graph RAG 的基础步骤 - NetworkX 适合原型验证和中小规模图谱,生产环境建议使用 Neo4j - 可视化有助于理解图谱结构,发现抽取错误

练习 2 : Graph RAG 检索实现(⭐⭐)

实现一个简单的 Graph RAG 检索系统:构建知识图谱 → 实体链接 → 子图检索 → 生成回答。对比 Graph RAG 和普通向量 RAG 在多跳推理问题上的表现。

💡 参考答案
Python
# 练习2:Graph RAG 检索实现参考
import networkx as nx
from openai import OpenAI
import json

client = OpenAI()

class SimpleGraphRAG:
    def __init__(self):
        self.graph = nx.DiGraph()
        self.documents = {}

    def add_documents(self, docs: list[str]):
        """添加文档并构建知识图谱"""
        for i, doc in enumerate(docs):
            self.documents[i] = doc
            triples = self._extract_triples(doc)
            for t in triples:
                self.graph.add_edge(
                    t["subject"], t["object"],
                    relation=t["predicate"],
                    doc_id=i
                )

    def _extract_triples(self, text: str) -> list[dict]:
        resp = client.chat.completions.create(
            model="gpt-5-mini",
            messages=[
                {"role": "system", "content": "抽取三元组,输出JSON: {\"triples\": [{\"subject\":\"\", \"predicate\":\"\", \"object\":\"\"}]}"},
                {"role": "user", "content": text},
            ],
            max_tokens=300,
        )
        try:
            return json.loads(resp.choices[0].message.content)["triples"]
        except:
            return []

    def _entity_linking(self, query: str) -> list[str]:
        """从查询中识别实体"""
        resp = client.chat.completions.create(
            model="gpt-5-mini",
            messages=[
                {"role": "system", "content": "从问题中提取关键实体名词,输出JSON: {\"entities\": [\"实体1\", \"实体2\"]}"},
                {"role": "user", "content": query},
            ],
            max_tokens=100,
        )
        try:
            return json.loads(resp.choices[0].message.content)["entities"]
        except:
            return []

    def _get_subgraph(self, entities: list[str], hops: int = 2) -> list[str]:
        """获取实体周围的子图"""
        doc_ids = set()
        for entity in entities:
            if entity in self.graph:
                # BFS 获取 hops 跳内的邻居
                visited = {entity}
                current_level = {entity}
                for _ in range(hops):
                    next_level = set()
                    for node in current_level:
                        for neighbor in self.graph.successors(node):
                            if neighbor not in visited:
                                next_level.add(neighbor)
                                visited.add(neighbor)
                                edge_data = self.graph.edges[node, neighbor]
                                doc_ids.add(edge_data.get("doc_id", -1))
                        for neighbor in self.graph.predecessors(node):
                            if neighbor not in visited:
                                next_level.add(neighbor)
                                visited.add(neighbor)
                                edge_data = self.graph.edges[neighbor, node]
                                doc_ids.add(edge_data.get("doc_id", -1))
                    current_level = next_level

        return [self.documents[doc_id] for doc_id in doc_ids if doc_id in self.documents]

    def query(self, question: str) -> str:
        """Graph RAG 查询"""
        # Step 1: 实体链接
        entities = self._entity_linking(question)

        # Step 2: 子图检索
        context_docs = self._get_subgraph(entities, hops=2)

        if not context_docs:
            return "无法找到相关信息"

        # Step 3: 生成回答
        context = "\n".join(context_docs)
        resp = client.chat.completions.create(
            model="gpt-5-mini",
            messages=[
                {"role": "system", "content": f"基于以下信息回答问题:\n{context}"},
                {"role": "user", "content": question},
            ],
            max_tokens=500,
        )
        return resp.choices[0].message.content

# 使用示例
docs = [
    "张三毕业于清华大学计算机系,现在在字节跳动担任AI工程师。",
    "字节跳动开发了豆包大模型,该模型支持多轮对话。",
    "清华大学位于北京海淀区,是中国顶尖大学之一。",
    "豆包大模型的参数量未公开,但据估计超过千亿级别。",
]

graph_rag = SimpleGraphRAG()
graph_rag.add_documents(docs)

# 多跳推理问题
answer = graph_rag.query("张三工作的地方开发了什么大模型?这个模型有什么特点?")
print(f"回答: {answer}")
**关键要点**: - Graph RAG 的核心流程:实体链接 → 子图检索 → 上下文组装 → LLM 生成 - 多跳推理是 Graph RAG 相对向量 RAG 的核心优势 - 实体链接的准确性直接影响检索质量,可使用模糊匹配或 LLM 辅助

练习 3 :混合架构设计(⭐⭐⭐)

设计并实现一个"向量 + 图谱"混合 RAG 系统:简单事实型问题走向量检索,复杂关联型问题走图谱检索,系统自动判断使用哪种方式。

💡 参考答案
Python
# 练习3:混合架构参考实现
from openai import OpenAI
import json

client = OpenAI()

class HybridRAG:
    """向量 + 图谱混合 RAG"""

    def __init__(self):
        self.vector_docs = []  # 向量检索文档
        self.graph_triples = []  # 图谱三元组
        self.route_prompt = """判断以下问题适合用哪种检索方式:
- "vector": 简单事实型问题(单实体、定义、描述)
- "graph": 复杂关联型问题(多实体关系、多跳推理、比较分析)
- "both": 需要两种检索结合

输出JSON: {"route": "vector"或"graph"或"both", "reason": "..."}"""

    def add_documents(self, docs: list[str]):
        """同时建立向量索引和图谱"""
        self.vector_docs.extend(docs)
        for doc in docs:
            resp = client.chat.completions.create(
                model="gpt-5-mini",
                messages=[
                    {"role": "system", "content": "抽取三元组: {\"triples\": [{\"subject\":\"\", \"predicate\":\"\", \"object\":\"\"}]}"},
                    {"role": "user", "content": doc},
                ],
                max_tokens=300,
            )
            try:
                triples = json.loads(resp.choices[0].message.content)["triples"]
                self.graph_triples.extend(triples)
            except:
                pass

    def _route_query(self, question: str) -> str:
        """查询路由"""
        resp = client.chat.completions.create(
            model="gpt-5-mini",
            messages=[
                {"role": "system", "content": self.route_prompt},
                {"role": "user", "content": question},
            ],
            max_tokens=100,
        )
        try:
            return json.loads(resp.choices[0].message.content)["route"]
        except:
            return "vector"  # 默认走向量

    def _vector_search(self, query: str, top_k: int = 3) -> list[str]:
        """向量检索(简化版:关键词匹配模拟)"""
        scored = []
        query_words = set(query.split())
        for doc in self.vector_docs:
            doc_words = set(doc.split())
            overlap = len(query_words & doc_words)
            scored.append((overlap, doc))
        scored.sort(reverse=True)
        return [doc for _, doc in scored[:top_k]]

    def _graph_search(self, query: str) -> list[str]:
        """图谱检索"""
        # 提取查询实体
        resp = client.chat.completions.create(
            model="gpt-5-mini",
            messages=[
                {"role": "system", "content": "提取实体: {\"entities\": [\"...\"]}"},
                {"role": "user", "content": query},
            ],
            max_tokens=100,
        )
        try:
            entities = json.loads(resp.choices[0].message.content)["entities"]
        except:
            entities = []

        # 查找相关三元组
        relevant = []
        for t in self.graph_triples:
            for e in entities:
                if e in t["subject"] or e in t["object"]:
                    relevant.append(f"{t['subject']} {t['predicate']} {t['object']}")
        return relevant

    def query(self, question: str) -> str:
        route = self._route_query(question)

        contexts = []
        if route in ("vector", "both"):
            contexts.extend(self._vector_search(question))
        if route in ("graph", "both"):
            contexts.extend(self._graph_search(question))

        if not contexts:
            contexts = self._vector_search(question)  # 降级到向量

        context = "\n".join(contexts[:5])
        resp = client.chat.completions.create(
            model="gpt-5-mini",
            messages=[
                {"role": "system", "content": f"基于以下信息回答:\n{context}"},
                {"role": "user", "content": question},
            ],
            max_tokens=500,
        )
        return f"[路由: {route}] {resp.choices[0].message.content}"

# 测试
hybrid = HybridRAG()
hybrid.add_documents([
    "DeepSeek-V3 采用 MoE 架构,671B 参数。",
    "DeepSeek 是中国 AI 公司,总部在杭州。",
    "DeepSeek-R1 是推理模型,使用 GRPO 训练。",
    "GRPO 是一种无需外部奖励模型的强化学习方法。",
])

print(hybrid.query("DeepSeek-V3 的参数量是多少?"))  # → vector
print(hybrid.query("DeepSeek 的模型之间有什么关系?"))  # → graph
print(hybrid.query("DeepSeek-R1 使用了什么训练方法?这个方法有什么特点?"))  # → both
**关键要点**: - 混合架构的核心是查询路由:根据问题类型自动选择检索策略 - 简单事实型问题 → 向量检索(快速、高覆盖率) - 复杂关联型问题 → 图谱检索(多跳推理、关系发现) - 生产环境建议使用 LlamaIndex 或 LangChain 的 Graph RAG 模块

面试题

Q1: Graph RAG 和传统向量 RAG 各自的优劣势是什么?

向量 RAG 优势:实现简单、适合事实型查询、生态成熟。劣势:无法处理多跳推理、缺乏实体关系理解。Graph RAG 优势:支持多跳推理、关系发现、全局概览(社区摘要)。劣势:构建成本高(实体抽取+图谱构建)、查询延迟较高、需要领域知识设计 Schema。

Q2: Microsoft GraphRAG 的 Local Search 和 Global Search 有什么区别?

Local Search:从查询实体出发,获取相关子图和关联文档片段,适合具体事实型问题(如"DeepSeek-V3 的参数量")。Global Search:利用社区检测(Leiden 算法)生成的社区摘要,通过 Map-Reduce 聚合多个社区的答案,适合全局概览型问题(如"总结所有 AI 公司的技术路线")。

Q3: 如何评估 Graph RAG 系统的效果?

三个维度:①检索质量(实体链接准确率、子图覆盖率);②生成质量(忠实度、相关性,可用 RAGAS);③推理能力(多跳问答准确率,如 HotpotQA 数据集)。建议构建包含事实型、关联型、全局型三类问题的评估集。


🔗 参考资料


导航:上一章 24-多模态 RAG 与向量数据库进阶 | 返回目录

最后更新日期: 2026-04-21 适用版本: LLM 应用指南 v2026

⚠️ 核验说明(2026-04-21):本页已纳入深度优化批次。若文中涉及外部工具、API、版本号或第三方产品名称,请以官方文档和实际运行环境为准。