GraphRAG实战:用Neo4j构建知识图谱,让AI问答更懂“关系”

最近和几个做AI应用落地的朋友聊天,大家普遍有个痛点:传统的RAG(检索增强生成)系统,在处理复杂、多跳的提问时,总感觉差点意思。比如,用户问“我们公司去年在华东区销售额最高的产品,它的主要供应商最近有什么新动态?”。这类问题需要系统先找到“华东区”、“去年销售额最高”、“产品”,再顺着找到“供应商”,最后获取“新动态”。纯靠向量相似度检索,就像在关键词的海洋里捞针,很容易漏掉关键信息,或者把不相关的片段硬凑在一起,导致答案要么片面,要么跑偏。

这正是GraphRAG开始受到关注的原因。它不是在取代RAG,而是在RAG的“检索”环节,引入了一个强大的导航仪——知识图谱。想象一下,你的数据不再是一堆孤立的文档碎片,而是变成了一个相互连接的语义网络。当问题进来时,系统能沿着这个网络的“关系”路径,精准定位到相关的实体群,并理解它们之间的上下文,从而组装出更完整、更准确的答案。今天,我们就抛开理论,直接从实战出发,手把手带你用Neo4j图数据库,搭建一个GraphRAG技术栈,看看它如何实实在在地提升GenAI问答的准确率。

1. 核心概念:为什么图是理解复杂问题的关键

在深入代码之前,我们需要先建立几个关键的认知。这能帮助我们在设计系统时,做出更明智的决策。

首先,明确“图”在这里指什么。 它不是Excel里的柱状图或折线图,而是一种数据结构,由“节点”和“关系”构成。节点代表实体(如“产品A”、“上海分公司”、“张经理”),关系则定义实体间的联系(如“隶属于”、“供应”、“汇报给”)。这种结构天然适合表达现实世界中错综复杂的关联。

其次,理解向量检索与图谱检索的根本差异。 传统的向量RAG,其核心能力是“语义相似度匹配”。它将文本转化为高维空间中的点(向量),通过计算点与点之间的距离来寻找相似内容。这种方法对于“找类似段落”非常高效。但其局限性在于,它缺乏对实体间显式关系全局结构的理解。它知道“苹果”和“水果”在语义上接近,但无法直接推理出“苹果由供应商A提供,而供应商A位于陕西”。

提示:你可以把向量检索想象成一个拥有绝佳记忆力的助手,能快速找到所有提到“苹果”的文档。而图谱检索则像一位深谙公司组织架构和业务流程的专家,当被问到“苹果部门的负责人最近批准了哪些预算?”时,他能立刻理清“苹果→部门→负责人→审批流程→预算”这条逻辑链。

为了更直观地对比,我们看一个简单的表格:

特性维度传统向量RAG基于知识图谱的GraphRAG
检索核心文本块间的语义相似度实体与关系的结构化查询
优势场景事实型问答、段落复现多跳推理、关系查询、全局摘要
可解释性较低,依赖向量近邻,答案可追溯至具体节点与路径
数据维护以文档/块为单位,关联性隐含以实体-关系为单位,关联性显式声明
开发调试较黑盒,调优依赖向量模型与分块策略较直观,可通过可视化图谱直接探查数据关联

这种差异决定了它们的互补性。一个健壮的问答系统,往往需要先用图谱检索锁定核心实体和关系网络,再用向量检索在相关的文本块中捕捉最细腻的语义细节。两者结合,才能既把握全局脉络,又不失局部精准。

2. 环境搭建与Neo4j知识图谱构建

理论清晰后,我们进入实战环节。本节将完成从原始文本到Neo4j知识图谱的完整构建流程,并提供可运行的代码示例。

2.1 技术栈选择与环境准备

我们选择Python作为主要开发语言,因为它拥有最丰富的AI和数据库生态。核心组件如下:

  • 图数据库:Neo4j。它是图数据库领域的标杆,提供原生的图存储与查询语言Cypher,并且从5.x版本开始原生支持向量索引,让我们能在一个数据库内同时处理图查询和向量搜索,简化了架构。
  • 大语言模型:选用OpenAI的GPT-4或ChatGPT的API。主要用于从文本中提取实体关系(信息抽取)和最终的答案生成。你也可以替换为任何兼容OpenAI API的模型或本地模型。
  • 开发框架:LlamaIndex。它提供了对GraphRAG的高层抽象,特别是其PropertyGraphIndex,能极大地简化图谱构建与查询的流程。当然,LangChain也是优秀的选择,本文以LlamaIndex为例。
  • 文本处理:基本的NLP库,如spaCynltk用于句子分割、分词等预处理。

首先,安装必要的Python包:

pip install llama-index llama-index-llms-openai llama-index-graph-stores-neo4j neo4j openai spacy
python -m spacy download en_core_web_sm  # 下载英文模型,中文可选zh_core_web_sm

接着,启动并配置Neo4j。你可以使用Neo4j Desktop(本地开发推荐)或Docker快速启动一个实例:

docker run \
    --name neo4j-graphrag \
    -p 7474:7474 -p 7687:7687 \
    -e NEO4J_AUTH=neo4j/your_password \
    -e NEO4J_PLUGINS=\[\"apoc\",\"graph-data-science\",\"n10s\"] \
    neo4j:5-enterprise

这里我们加载了apoc(高级图算法)、graph-data-science(图数据科学库)和n10s(语义图)插件,它们在未来扩展功能时会非常有用。访问http://localhost:7474,使用neo4j/your_password登录Neo4j Browser。

2.2 从非结构化文本到知识图谱

假设我们有一批公司内部的项目文档和市场报告,目标是构建一个关于“产品”、“客户”、“技术”的知识图谱。核心步骤是信息抽取图谱构建

步骤一:文档加载与预处理 我们使用LlamaIndex的简单阅读器加载文档,并进行基础分块。

from llama_index.core import SimpleDirectoryReader
from llama_index.core.node_parser import SentenceSplitter

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

# 使用句子分割器进行分块,保持一定上下文
node_parser = SentenceSplitter(chunk_size=512, chunk_overlap=50)
nodes = node_parser.get_nodes_from_documents(documents)

步骤二:定义图谱Schema并提取实体关系 这是最关键的一步。我们需要告诉LLM,我们希望从文本中提取哪些类型的实体和关系。这构成了我们知识图谱的“领域图”部分。

from llama_index.core import KnowledgeGraphIndex
from llama_index.core.graph_stores import SimpleGraphStore
from llama_index.llms.openai import OpenAI
from llama_index.graph_stores.neo4j import Neo4jGraphStore

# 初始化LLM和Neo4j连接
llm = OpenAI(model="gpt-4-turbo")
graph_store = Neo4jGraphStore(
    url="bolt://localhost:7687",
    username="neo4j",
    password="your_password",
    database="neo4j"
)

# 定义我们关心的实体和关系类型
kg_index = KnowledgeGraphIndex.from_documents(
    documents,
    kg_triplet_extract_fn=lambda x: extract_triplets(x), # 自定义提取函数
    max_triplets_per_chunk=10,
    graph_store=graph_store,
    llm=llm,
    include_embeddings=True  # 关键:同时为文本块生成向量嵌入
)

上面的extract_triplets函数需要你根据领域自定义。一个简单的方法是构造一个提示词,让LLM以结构化格式(如JSON)输出三元组(头实体,关系,尾实体)。例如:

def extract_triplets(text):
    prompt = f"""
    请从以下文本中提取实体和关系。以JSON列表格式输出,每个元素是一个三元组:`["头实体", "关系", "尾实体"]`。
    只提取真实存在于文本中的明确关系。
    实体类型包括:产品、技术、客户、公司、人物、地点、事件。
    关系类型包括:使用、开发、购买、位于、属于、参与、影响。

    文本:{text}

    输出:
    """
    response = llm.complete(prompt)
    # 解析response.text中的JSON
    # ... 解析逻辑 ...
    return parsed_triplets

运行这段代码后,LlamaIndex会驱动LLM扫描每个文本块,提取三元组,并自动通过graph_store将节点和关系插入到Neo4j数据库中。同时,因为设置了include_embeddings=True,每个文本块(节点)也会生成一个向量嵌入,并存储在Neo4j中(需要Neo4j 5.x以上并创建了向量索引)。

步骤三:在Neo4j中验证与可视化 构建完成后,立即到Neo4j Browser中查看成果。执行一个简单的查询:

MATCH (n) RETURN n LIMIT 50

你就能看到初步的知识图谱可视化。你可能会发现一些数据质量问题,比如同一实体有不同名称(“GPT-4”和“GPT4”)。这时,就需要引入实体链接消歧的步骤,这可以通过定义实体归一化规则或使用专门的NLP工具来完善。构建知识图谱是一个迭代过程,从“最小可行图”开始,逐步清洗、丰富和扩展。

3. 实现GraphRAG查询引擎:融合图检索与向量检索

图谱建好了,接下来是如何利用它进行智能检索。GraphRAG的查询通常遵循一个“图优先,向量精修”的混合模式。

3.1 构建混合检索器

在LlamaIndex中,我们可以轻松组合不同的检索器。

from llama_index.core import VectorStoreIndex
from llama_index.core.retrievers import KGTableRetriever, VectorIndexRetriever
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core import get_response_synthesizer

# 1. 创建基于向量的检索器(检索原始文本块)
vector_index = VectorStoreIndex.from_documents(documents)
vector_retriever = VectorIndexRetriever(index=vector_index, similarity_top_k=5)

# 2. 创建基于知识图谱的检索器
# KnowledgeGraphIndex本身可以作为检索器,它支持基于实体和关系的查询
kg_retriever = kg_index.as_retriever()

# 3. 创建混合检索器
from llama_index.core.retrievers import QueryFusionRetriever
from llama_index.core.postprocessor import LLMRerank

hybrid_retriever = QueryFusionRetriever(
    [vector_retriever, kg_retriever],
    llm=llm,
    similarity_top_k=10,
    num_queries=4,  # 对原始查询进行多角度重写,以触发不同检索器
    mode="reciprocal_rerank", # 使用互惠排序融合算法
)

# 4. (可选)添加重排序器,用LLM对初步检索结果进行精排
reranker = LLMRerank(top_n=5, llm=llm)

这个QueryFusionRetriever是强大之处。它不会简单地将两个检索器的结果合并去重,而是:

  1. 利用LLM将用户原始问题生成多个不同侧重点的查询。
  2. 分别用这些查询去调用向量检索器和图谱检索器。
  3. 使用融合算法(如Reciprocal Rank Fusion)对所有结果进行综合排序,确保最相关的内容排在前面。

3.2 执行图增强的查询

现在,我们可以用这个混合检索器来组装最终的问答引擎。

# 创建响应合成器
response_synthesizer = get_response_synthesizer(llm=llm, response_mode="compact")

# 组装查询引擎
query_engine = RetrieverQueryEngine(
    retriever=hybrid_retriever,
    response_synthesizer=response_synthesizer,
    node_postprocessors=[reranker]  # 应用重排序
)

# 进行查询
query = "我们旗舰产品‘星辰AI平台’的主要竞争对手有哪些?它们各自采用了什么关键技术?"
response = query_engine.query(query)
print(response)

在这个过程中,图谱检索器(kg_retriever)内部是如何工作的呢?它可能会将查询“星辰AI平台”的竞争对手”转化为如下的Cypher查询逻辑:

  1. 找到名为“星辰AI平台”的产品节点。
  2. 查找与该产品节点具有竞争关系的其他产品节点。
  3. 获取这些竞争产品节点,并进一步查找与它们相连的采用使用关系,指向技术节点。
  4. 返回这些产品节点和技术节点,以及它们相关的原始文本块(通过“提及”关系连接)。

这些从图谱中检索出的高度结构化的信息,与向量检索器找到的相关文本片段一起,被送入LLM生成最终答案。LLM因此获得了明确的实体关系网络作为上下文,而不仅仅是零散的文本,从而能生成更具逻辑性和准确性的回答。

4. 性能对比、调优与部署考量

任何技术方案的价值都需要通过对比和实测来验证。下面我们设计一个简单的评测,并分享几个关键的调优点。

4.1 设计对比实验

为了直观感受GraphRAG的优势,我们可以针对同一批数据,搭建三个系统进行对比:

  • 系统A(纯向量RAG):仅使用向量数据库进行检索。
  • 系统B(纯图谱RAG):仅使用知识图谱进行检索(依赖实体识别和关系查询)。
  • 系统C(GraphRAG混合模式):使用我们上面实现的混合检索器。

准备一组测试问题,特别是包含多跳推理和关系查询的问题。例如:

  1. 单跳事实:“公司的总部在哪里?”(基准问题)
  2. 多跳推理:“为项目‘北极星’提供云服务的供应商,其CEO是谁?”
  3. 聚合/摘要:“总结一下我们过去一年在金融行业客户中遇到的三大常见技术挑战。”

答案准确性(人工或LLM评估是否答对)、答案完整性(是否涵盖了所有关键方面)、证据可追溯性(能否清晰指出答案来源节点/路径)三个维度进行评分。

在我的一个内部项目测试中,针对一批技术文档,结果趋势如下:

问题类型纯向量RAG纯图谱RAGGraphRAG混合模式
单跳事实准确率高,响应快准确率高,依赖实体识别准确率最高,响应快
多跳推理准确率低,易遗漏中间环节准确率高,路径清晰准确率高且稳定,信息更丰富
聚合摘要信息碎片化,整合能力弱能抓住核心实体,但细节缺失信息全面,结构清晰

GraphRAG混合模式在复杂问题上优势明显,因为它结合了图谱的结构化推理能力和向量的语义捕捉能力。

4.2 关键调优技巧

在实际部署中,以下几个点的调优能极大影响系统效果:

  1. 图谱Schema设计:这是地基。实体和关系的定义是否贴合业务?过于粗放会丢失信息,过于精细则增加构建和维护成本。建议从核心业务概念开始,逐步扩展。
  2. 信息抽取的准确性:这是瓶颈。LLM抽取的三元组可能存在错误或遗漏。除了优化提示词,可以考虑:
    • 多阶段抽取:先用小模型快速筛选可能包含关系的句子,再用大模型精细抽取。
    • 后处理与验证:设定规则对抽取结果进行过滤(如关系必须为动词短语),或利用图谱的一致性进行验证(如两个同类实体不应出现“属于”关系)。
  3. 查询重写与Cypher生成:如何将自然语言问题精准转化为图谱查询(Cypher语句)?LlamaIndex和LangChain都提供了相关组件,但其生成质量依赖LLM和对图谱Schema的描述。可以为常见查询模式编写模板,或对LLM进行微调。
  4. 向量与图谱的权重平衡:在混合检索中,如何设置两种检索结果的权重?这需要根据你的问题分布进行调整。对于事实型问题,可以偏向向量;对于关系型问题,则偏向图谱。QueryFusionRetriever的融合算法参数需要根据评测结果进行调整。
  5. Neo4j性能优化
    • 索引:务必为实体名称、属性创建索引,并为向量字段创建向量索引。
    CREATE INDEX product_name_index IF NOT EXISTS FOR (p:Product) ON (p.name);
    CREATE VECTOR INDEX chunk_embeddings IF NOT EXISTS FOR (c:Chunk) ON (c.embedding) OPTIONS {indexConfig: {`vector.dimensions`: 1536, `vector.similarity_function`: 'cosine'}};
    
    • 查询优化:避免在Cypher查询中使用全图扫描,尽量从已知标签和索引属性开始遍历。

4.3 部署与运维建议

将GraphRAG系统投入生产环境,还需要考虑:

  • 增量更新:新文档进来后,如何增量更新知识图谱?需要设计流水线,定期运行信息抽取和图谱合并任务,并处理实体对齐。
  • 监控与评估:建立线上问答的反馈循环。记录用户问题、系统检索到的节点/文本、以及最终答案。通过人工抽样或模型自动评估,持续发现检索或生成的薄弱环节。
  • 成本控制:GraphRAG增加了图谱构建和查询的步骤,可能会增加LLM API调用(用于信息抽取)和数据库计算的开销。需要对文档分批处理,缓存常见的图谱查询结果,并监控Token使用量。

从我个人的经验来看,GraphRAG带来的最大好处不仅仅是准确率的提升,更是开发调试体验的飞跃。当答案出错时,我可以直接打开Neo4j Browser,沿着可视化的图谱路径查看检索到了哪些节点和关系,立刻判断是信息抽取错了,还是查询路径设计有问题。这种可解释性和可操控性,在构建复杂的企业级AI应用时,是无价的。它让AI系统从一个“黑盒”变成了一个你可以理解、调整和信任的伙伴。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐