GraphRAG:知识图谱 + 向量检索的检索增强生成实践

GraphRAG 落地指南:传统向量 RAG 的三大局限、实体链接与图检索增强、Neo4j Vector Index 构建与相似度查询、LangChain 集成、微软 GraphRAG/LightRAG 变体对比,以及可评测的工程落地路径。

导语:RAG 的第三次进化

第一代 RAG 用向量相似度检索文本块,第二代加了重排(rerank)与混合检索,但都困在一个问题上:文本块之间没有关系,模型无法理解"谁和谁在什么时候有关联"。GraphRAG 把知识图谱引入 RAG——检索的不再是孤立的文本块,而是带关系的子图。这让多跳推理(multi-hop reasoning)、全局问题(global question)的回答质量显著提升。本专题的 知识图谱构建与应用 已介绍 KG 概念,本文聚焦 GraphRAG 的工程实现。

一句话总结:GraphRAG = 知识图谱(结构)+ 向量检索(语义)+ LLM(生成)——用图谱约束检索范围,用向量补足语义模糊,让答案可溯源、可多跳。


1. 从 RAG 到 GraphRAG

1.1 传统向量 RAG 的三大局限

局限表现后果
缺乏关系只检索文本块,无法回答"A 和 B 有什么关系"多跳问题答非所问
全局性差检索受限于 top-k 块,宏观问题无答案“总结全部研究主题"类问题失效
可溯源弱证据是文本片段,无法精确到实体-关系审计与纠错困难

1.2 GraphRAG 的两种范式

文本块 RAG(传统):
  问题 → 向量化 → 向量库 top-k 文本块 → LLM 生成

图增强 RAG(Text2Cypher):
  问题 → LLM 解析 → 实体/意图 → Cypher 查子图 → LLM 生成

图语义 RAG(GraphRAG 原版):
  文档 → 抽取实体关系 → 构建图谱 + 社区检测
  问题 → 检索相关社区/子图 → LLM 生成

1.3 为什么"图谱 + 向量"优于单一路径

  • 向量检索擅长:语义模糊、说法不同但意思相近
  • 图谱检索擅长:精确实体、多跳路径、可解释的关系
  • 混合策略:向量召回候选实体 → 图谱展开一跳/多跳 → 组合成上下文

一句话总结:GraphRAG 不是抛弃向量检索,而是用图谱给向量检索"加持”——向量负责语义召回,图谱负责结构化扩展。


2. 实体链接与图检索增强

2.1 问题到实体的实体链接

第一步是把用户问题映射到图中的实体:

from neo4j import GraphDatabase

driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "password"))

def link_entities(question_entities):
    """把 NER/LLM 抽取的实体名链接到图内实体(别名优先)"""
    with driver.session() as session:
        result = session.run("""
            MATCH (a:EntityAlias {alias: $alias})
            RETURN a.entityId AS eid, a.entityName AS name, a.type AS type
            UNION
            MATCH (e:Entity {name: $alias})
            RETURN e.id AS eid, e.name AS name, e.type AS type
        """, alias=question_entities[0])  # 循环处理每个实体
        return [r.data() for r in result]

2.2 子图检索(Cypher 召回)

// 以命中实体为中心,展开 1-2 跳子图
MATCH path = (e:Entity)-[r*1..2]-(neighbor)
WHERE e.id IN $entity_ids
RETURN path
LIMIT $max_paths
// 带权重的关系子图(打分排序)
MATCH (e:Entity)-[r]-(n)
WHERE e.id IN $entity_ids
WITH e, n, r, coalesce(r.confidence, 0.5) AS score
ORDER BY score DESC
LIMIT 50
RETURN e.name, type(r) AS rel, n.name, score

2.3 混合检索:向量 + 图

def hybrid_retrieve(question, top_k_text=5, top_k_graph=20):
    # 1. 向量召回:找语义相关的实体描述块
    vector_hits = vector_search(question, top_k_text)

    # 2. 从文本块中提取实体,链接到图谱
    entities = extract_entities(vector_hits + question)
    entity_ids = link_to_graph(entities)

    # 3. 图谱展开:沿关系扩展 1-2 跳
    subgraph = expand_subgraph(entity_ids, top_k_graph)

    # 4. 组合上下文(文本块 + 三元组 + 子图描述)
    return build_prompt(vector_hits, subgraph)

一句话总结:实体链接是 GraphRAG 的"入口",子图检索是"放大器"——先语义定位实体,再图结构扩展关系。


3. Neo4j Vector Index

3.1 启用向量能力

Neo4j 5.11+ 内置 Vector Index(基于 HNSW 近似最近邻)。容器需安装 GDS 或使用 5.15+ 的内置向量支持:

# Docker 启动时启用
docker run \
  --name neo4j-graphrag \
  -p 7474:7474 -p 7687:7687 \
  -e NEO4J_AUTH=neo4j/password \
  -e NEO4J_PLUGINS='["apoc"]' \
  neo4j:5.15-community

3.2 创建向量索引

// 创建向量索引(L2 或 COSINE 或 DOT 相似度)
CREATE VECTOR INDEX entity_embedding IF NOT EXISTS
FOR (e:Entity) ON (e.embedding)
OPTIONS {
  indexConfig: {
    `vector.dimensions`: 1536,        // 与嵌入模型维度一致
    `vector.similarity_function`: 'cosine'
  }
}

3.3 相似度查询

// 最近邻查询
WITH $question_embedding AS qvec
CALL db.index.vector.queryNodes("entity_embedding", 10, qvec)
YIELD node, score
RETURN node.name, node.type, score
ORDER BY score DESC

与图谱展开组合:

// 向量召回实体 → 图谱扩展(两跳内)→ 输出子图
CALL db.index.vector.queryNodes("entity_embedding", 5, $qvec)
YIELD node, score AS sem_score
WITH node, sem_score
MATCH (node)-[r*1..2]-(n)
RETURN node.name AS root, type(r) AS rel, n.name AS neighbor, sem_score
LIMIT 100

3.4 嵌入生成与存储

# 生成实体描述嵌入并写入
import openai
from neo4j import GraphDatabase

openai_client = openai.OpenAI()
driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "password"))

def embed(text):
    resp = openai_client.embeddings.create(
        model="text-embedding-3-small", input=text
    )
    return resp.data[0].embedding

def index_entity(eid, description):
    vec = embed(description)
    with driver.session() as session:
        session.run(
            "MATCH (e:Entity {id: $eid}) SET e.embedding = $vec",
            eid=eid, vec=vec
        )

一句话总结:Neo4j Vector Index 用 HNSW 提供近似最近邻,维度与相似度函数在索引创建时固定,是 GraphRAG 的语义检索地基。


4. 端到端 GraphRAG 落地

4.1 LangChain 集成

LangChain 提供 Neo4jGraph 与 GraphCypherQAChain,几行代码即可搭建:

from langchain_community.graphs import Neo4jGraph
from langchain_community.chains.graph_qa.cypher import GraphCypherQAChain
from langchain_openai import ChatOpenAI

graph = Neo4jGraph(
    url="bolt://localhost:7687",
    username="neo4j", password="password",
    database="neo4j"
)

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

chain = GraphCypherQAChain.from_llm(
    llm=llm, graph=graph, verbose=True,
    allow_dangerous_requests=True,   # 允许 LLM 生成 Cypher
    validate_cypher=True             # 用 EXPLAIN 验证生成语句
)

answer = chain.invoke("华为和阿里云在 AI 领域有什么关系?")
print(answer)

4.2 自定义流水线:向量 + 图 + LLM

class GraphRAG:
    def __init__(self, uri, user, pw, llm_client):
        self.driver = GraphDatabase.driver(uri, auth=(user, pw))
        self.llm = llm_client

    def answer(self, question):
        # Step 1: 实体抽取(LLM 解析)
        entities = self.extract_entities(question)

        # Step 2: 向量召回 + 图扩展 混合检索
        qvec = self.embed(question)
        graph_context = self.hybrid_search(qvec, entities)

        # Step 3: 组织上下文提示
        prompt = self.build_prompt(question, graph_context)

        # Step 4: 生成(要求给出事实来源)
        return self.llm.chat.completions.create(
            model="gpt-4o",
            messages=[{"role": "user", "content": prompt}]
        ).choices[0].message.content

    def hybrid_search(self, qvec, entities):
        facts = []
        # 向量召回
        with self.driver.session() as s:
            r = s.run(
                "CALL db.index.vector.queryNodes('entity_embedding', 5, $qvec)"
                " YIELD node, score RETURN node.name, score",
                qvec=qvec
            )
            facts += [x.data() for x in r]
            # 图扩展
            r = s.run(
                "MATCH (e:Entity)-[rel]-(n) WHERE e.name IN $names"
                " RETURN e.name, type(rel) AS rel, n.name LIMIT 30",
                names=entities
            )
            facts += [x.data() for x in r]
        return facts

    def build_prompt(self, question, facts):
        return f"""基于以下知识图谱事实回答。每个事实都是 {head}-{rel}-{tail} 结构。
事实:
{chr(10).join(str(f) for f in facts)}

问题:{question}
要求:如果事实不足,明确回答"知识不足";引用事实编号说明依据。"""

4.3 评测:GraphRAG 需要多跳评测集

# 构建评测集:2-hop 问答(A 与 C 通过 B 关联)
eval_set = [
    {
        "question": "任正非创立的公司在深圳发布了什么芯片?",
        "expected_path": [("任正非","FOUNDED_BY","华为"),("华为","DEVELOPED","昇腾AI芯片")],
        "answer_keywords": ["昇腾AI芯片"]
    },
    # ... 覆盖 1-hop、2-hop、全局三类
]

def evaluate(chain, eval_set):
    correct = 0
    for item in eval_set:
        answer = chain(item["question"])
        hit = any(kw in answer for kw in item["answer_keywords"])
        correct += hit
        print(f"{'✓' if hit else '✗'} {item['question']}")
    print(f"Acc: {correct}/{len(eval_set)}")

一句话总结:落地三件套——LangChain 快速起步、自定义流水线控细节、多跳评测集守质量;validate_cypher 是防 LLM 生成坏 Cypher 的安全网。


5. GraphRAG 变体与选型

方案原理优点局限
Text2CypherLLM 直接生成 Cypher实现简单、查询精确复杂图谱易生成错误语句
微软 GraphRAG社区检测 + 社区摘要 + MapReduce全局问题强构建成本高(多次 LLM 调用)
LightRAG双级检索(低阶/高阶)效率高、增量更新依赖实现成熟度
Vector-Graph 混合向量召回 + 图扩展稳定、可控需要实体链接质量
自研 Cypher 模板预定义模板参数化最稳、可审计覆盖面受模板限制

选型建议:

  • 查询模式稳定 → 模板化 Cypher + 向量兜底
  • 问题开放多变 → Text2Cypher + validate_cypher
  • 需要全局综述 → 微软 GraphRAG 的社区摘要思路

一句话总结:GraphRAG 没有银弹——模板、Text2Cypher、社区摘要按"查询确定性"从高到低排列,越开放的问题成本越高。


6. 最佳实践与总结

工程清单:

  1. 实体链接是质量门:问题实体链接不准,后续全部失真
  2. 向量维度对齐:vector.dimensions 必须与嵌入模型输出一致
  3. Cypher 安全:LLM 生成 Cypher 必须 validate_cypher + 只读账号
  4. 混合检索有兜底:图检索为空时回退向量检索,再回退原文档
  5. 证据可溯源:提示词要求 LLM 引用事实编号,答案可核对
  6. 评测常态化:1-hop/2-hop/全局三类题集,每次改动跑一遍

核心认知:

  1. GraphRAG 的价值是"可解释的多跳"——关系即证据
  2. 向量与图谱是互补不是替代——语义召回 + 结构扩展
  3. 工程化顺序:先建图谱与向量索引,再套 LLM 层,最后做评测

延伸阅读:

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「graphdb」更多文章

  1. 图驱动推荐系统:从协同过滤到图嵌入的实战路径
  2. 图数据建模模式与反模式:从关系思维到图谱思维
  3. 图嵌入与图神经网络:从 node2vec 到 GCN 的完整图谱