知识图谱与大模型融合:Neo4j在RAG中的应用探索
·
一、从“扁平检索”到“关系推理”
传统RAG系统通过向量检索从文档库中召回相关片段,在处理事实性问答时表现尚可,但面对需要多跳推理和关系理解的问题时往往力不从心。例如,当用户问“糖尿病患者应该避免哪些高糖食物?”时,传统RAG需要分别检索“糖尿病”和“高糖食物”的相关文档,再让LLM进行跨文档推理——这个过程既低效又容易出错。
GraphRAG(图检索增强生成) 的解决方案是用知识图谱显式编码实体间的语义关系,让检索从“找相似的文本块”升级为“沿着关系路径推理”。Neo4j作为最成熟的图数据库,在这一架构中扮演着知识存储与关系查询的核心角色。
两者的关系可以这样理解:Neo4j负责“存关系、查路径”,LLM负责“理解问题、生成答案” 。当用户提出复杂问题时,系统先在Neo4j中沿着关系遍历找到相关实体和路径,再将结构化的检索结果作为上下文交给LLM生成回答。
二、系统架构:从向量到图谱的双路检索
2.1 GraphRAG的典型工作流程
一个完整的Neo4j + RAG系统包含以下核心环节:
2.2 环境配置
以医疗知识图谱问答系统为例,需要配置Neo4j和LLM服务:
# 使用Docker启动Neo4j
docker run -d --name neo4j \
-p 7474:7474 -p 7687:7687 \
-e NEO4J_AUTH=neo4j/your_password \
neo4j:latest
# 安装Python依赖
pip install neo4j langchain openai chromadb
环境变量配置(.env文件):
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=your_password
OPENAI_API_KEY=sk-xxx
三、核心实现:Neo4j知识图谱构建与检索
3.1 知识图谱构建
从结构化数据或文档中抽取实体和关系,构建知识图谱。以下是一个医疗领域知识图谱的构建示例:
from neo4j import GraphDatabase
import json
class KnowledgeGraphBuilder:
def __init__(self, uri, user, password):
self.driver = GraphDatabase.driver(uri, auth=(user, password))
def create_entity(self, tx, entity_type, name, properties=None):
"""创建实体节点"""
query = f"""
CREATE (e:{entity_type} {{
name: $name,
{', '.join([f'{k}: ${k}' for k in (properties or {}).keys()])}
}})
RETURN e
"""
params = {"name": name, **(properties or {})}
result = tx.run(query, **params)
return result.single()[0]
def create_relationship(self, tx, from_name, to_name, rel_type, from_type=None, to_type=None):
"""创建实体间关系"""
query = f"""
MATCH (a {{name: $from_name}})
MATCH (b {{name: $to_name}})
CREATE (a)-[:{rel_type}]->(b)
"""
tx.run(query, from_name=from_name, to_name=to_name)
def build_from_json(self, json_path):
"""从JSON数据批量构建图谱"""
with open(json_path, 'r', encoding='utf-8') as f:
data = json.load(f)
with self.driver.session() as session:
for item in data:
# 创建疾病节点
disease = session.execute_write(
self.create_entity,
"Disease",
item["disease"],
{"symptom": item.get("symptom", "")}
)
# 创建关联实体
for drug in item.get("drugs", []):
drug_node = session.execute_write(
self.create_entity, "Drug", drug
)
session.execute_write(
self.create_relationship,
disease["name"], drug, "recommand_drug"
)
# 使用示例
builder = KnowledgeGraphBuilder(
uri="bolt://localhost:7687",
user="neo4j",
password="your_password"
)
builder.build_from_json("./medical_data.json")
3.2 混合检索实现
结合向量检索的“语义相似”和图检索的“关系推理”,实现更精准的召回:
from langchain.embeddings import OpenAIEmbeddings
from chromadb import Client as ChromaClient
from neo4j import GraphDatabase
class HybridRetriever:
def __init__(self, neo4j_uri, neo4j_user, neo4j_password):
self.neo4j_driver = GraphDatabase.driver(
neo4j_uri, auth=(neo4j_user, neo4j_password)
)
self.chroma_client = ChromaClient()
self.embeddings = OpenAIEmbeddings()
self.vector_collection = self.chroma_client.get_or_create_collection(
name="knowledge_chunks"
)
def vector_search(self, query: str, top_k: int = 5):
"""语义向量检索"""
query_embedding = self.embeddings.embed_query(query)
results = self.vector_collection.query(
query_embeddings=[query_embedding],
n_results=top_k
)
return results['documents'][0] if results['documents'] else []
def graph_search(self, query: str, top_k: int = 5):
"""图谱关系检索"""
# 用LLM将自然语言转为Cypher查询
cypher_query = self._nl_to_cypher(query)
with self.neo4j_driver.session() as session:
result = session.run(cypher_query)
return [record.data() for record in result[:top_k]]
def hybrid_search(self, query: str):
"""混合检索:向量 + 图谱双路召回后融合排序"""
vector_results = self.vector_search(query)
graph_results = self.graph_search(query)
# 融合去重(简化版:按来源加权合并)
combined = vector_results + graph_results
# 实际生产环境可使用Reranker精排
return self._build_context(combined)
3.3 智能路由:何时走图谱,何时走向量
根据查询复杂度自动路由到不同检索策略,是GraphRAG系统的核心能力:
from langchain.chat_models import ChatOpenAI
class QueryRouter:
def __init__(self):
self.llm = ChatOpenAI(temperature=0)
def analyze_query(self, query: str) -> dict:
"""用LLM分析查询类型和复杂度"""
prompt = f"""
分析以下查询的特征,输出JSON格式:
- complexity: 0-1,1表示需要多跳推理
- entities: 提到的实体数量
- needs_relation: 是否需要关系推理
查询:{query}
"""
response = self.llm.predict(prompt)
# 解析响应(简化)
return {"complexity": 0.7, "needs_relation": True}
def route(self, query: str) -> str:
analysis = self.analyze_query(query)
if analysis["needs_relation"] and analysis["complexity"] > 0.5:
return "graph" # 走图谱检索
return "vector" # 走向量检索
# 集成到RAG Pipeline
router = QueryRouter()
retriever = HybridRetriever(...)
def graph_rag_answer(query: str):
route = router.route(query)
if route == "graph":
context = retriever.graph_search(query)
else:
context = retriever.vector_search(query)
# 调用LLM生成最终答案
return generate_answer(query, context)
四、实战案例:医疗知识图谱问答系统
医疗领域是对回答准确性要求最高的场景之一。通过Neo4j构建的疾病知识图谱,可以支撑精准的医学问答。
4.1 知识图谱规模
| 实体类型 | 数量 | 示例 |
|---|---|---|
| 疾病 | 8,807 | 糖尿病、高血压 |
| 症状 | 5,998 | 多饮、多尿、头晕 |
| 药品 | 3,828 | 胰岛素、二甲双胍 |
| 食物 | 4,870 | 西瓜、苦瓜 |
| 检查项目 | 3,353 | 血糖检测 |
4.2 典型问答流程
def medical_qa(query: str):
# Step 1: 实体识别
entities = extract_medical_entities(query) # 如["糖尿病", "高糖食物"]
# Step 2: 图谱检索
with neo4j_driver.session() as session:
# 查询糖尿病患者应避免的食物
cypher = """
MATCH (d:Disease {name: $disease})
MATCH (d)-[:no_eat]->(f:Food)
RETURN f.name AS food, f.reason AS reason
"""
result = session.run(cypher, disease=entities[0])
food_list = [record["food"] for record in result]
# Step 3: LLM生成回答
context = f"根据医学知识图谱,{entities[0]}患者应避免以下高糖食物:{', '.join(food_list)}"
return llm.generate(f"{context}\n用户问题:{query}")
五、总结与最佳实践
5.1 核心优势
| 传统RAG | Neo4j增强的GraphRAG |
|---|---|
| 扁平检索,难以处理多跳推理 | 图遍历支持多跳关系推理 |
| 实体关系隐式存在于文本中 | 关系显式建模,可追溯推理路径 |
| 无法回答“A和B有什么关系” | 通过图谱直接查询关系 |
5.2 避坑指南
| 常见问题 | 解决方案 |
|---|---|
| 知识图谱构建成本高 | 先用LLM从文档自动抽取实体关系,再人工校验 |
| Cypher查询生成不稳定 | 使用Few-shot提示,提供多个示例约束LLM输出格式 |
| 向量与图谱结果难以融合 | 使用Reranker模型(如Cohere Rerank)统一排序 |
| Neo4j性能瓶颈 | 创建索引(包括向量索引),对复杂查询使用APOC优化 |
5.3 适用场景
Neo4j+RAG的GraphRAG方案特别适合以下场景:
- 医疗/法律/金融:对回答准确性要求高,且实体关系密集
- 企业内部知识库:文档间存在大量交叉引用和层级关系
- 教育/科研领域:概念之间的依赖和递进关系需要显式建模
GraphRAG不是要替代传统RAG,而是为“需要关系推理”的问题提供更好的解决方案。在实践中,混合检索 + 智能路由往往是最优策略——让简单查询走向量路,让复杂推理走图谱路,各取所长。
更多推荐




所有评论(0)