在当今AI技术快速发展的背景下,大型语言模型(LLMs)已经展现出惊人的文本生成能力。然而,当这些模型需要处理特定领域的专业知识或最新信息时,常常会遇到"幻觉"问题——即生成看似合理但实际错误的内容。这正是检索增强生成(Retrieval-Augmented Generation, RAG)系统应运而生的原因。
传统RAG系统主要依赖向量数据库进行语义相似性检索,这种方法虽然简单直接,但在处理需要多跳推理或关系分析的复杂查询时往往力不从心。想象一下医疗领域的典型场景:当用户询问"哪些药物可以治疗由高血压引起的并发症"时,单纯依靠文本相似性很难准确捕捉药物、疾病和并发症之间的复杂关系网络。
知识图谱技术恰好弥补了这一缺陷。通过将实体(如药物、疾病)和它们之间的关系(如"治疗"、"引起")显式地建模为图结构,系统能够执行精确的关系推理。Neo4j作为领先的图数据库,提供了高效的图遍历查询能力,而LangChain则充当了连接知识图谱与LLMs的理想桥梁。
在开始构建系统前,需要准备以下基础环境:
bash复制# 创建Python虚拟环境(推荐3.9+版本)
python -m venv neo4j-rag-env
source neo4j-rag-env/bin/activate # Linux/Mac
neo4j-rag-env\Scripts\activate # Windows
# 安装核心依赖
pip install langchain langchain-community neo4j openai tiktoken
注意:实际部署时建议固定依赖版本,例如
pip install neo4j==5.12.0,以避免兼容性问题。
Neo4j提供了多种部署方式,对于开发测试推荐使用:
bash复制docker run \
--name neo4j-rag \
-p 7474:7474 -p 7687:7687 \
-v neo4j_data:/data \
-e NEO4J_AUTH=neo4j/password \
neo4j:5.12.0
连接配置示例:
python复制from langchain_community.graphs import Neo4jGraph
graph = Neo4jGraph(
url="bolt://localhost:7687", # 或AuraDB提供的连接字符串
username="neo4j",
password="your_secure_password",
database="neo4j" # 企业版支持多数据库
)
LangChain的核心组件需要与LLM提供商API配合使用。以OpenAI为例:
python复制from langchain_openai import ChatOpenAI, OpenAIEmbeddings
# 建议通过环境变量管理API密钥
import os
os.environ["OPENAI_API_KEY"] = "sk-..."
# 初始化LLM和嵌入模型
llm = ChatOpenAI(model="gpt-4-1106-preview", temperature=0)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
构建有效的知识图谱需要遵循以下设计原则:
以下是一个完整的医疗知识图谱创建示例,包含节点、关系和向量索引:
python复制# 创建约束确保数据唯一性
graph.query("""
CREATE CONSTRAINT unique_drug IF NOT EXISTS
FOR (d:Drug) REQUIRE d.name IS UNIQUE;
""")
# 批量创建节点和关系
graph.query("""
UNWIND $data AS item
MERGE (d:Drug {name: item.drug})
MERGE (p:Disease {name: item.disease})
MERGE (d)-[:TREATS {source: item.source}]->(p)
""",
params={
"data": [
{"drug": "Metformin", "disease": "Diabetes", "source": "FDA"},
{"drug": "Insulin", "disease": "Diabetes", "source": "WHO"},
{"drug": "Lisinopril", "disease": "Hypertension", "source": "NHS"}
]
})
# 为节点添加向量嵌入
drug_descriptions = {
"Metformin": "First-line oral medication for type 2 diabetes...",
"Insulin": "Hormone used to regulate blood glucose levels...",
"Lisinopril": "ACE inhibitor for treating hypertension..."
}
for name, desc in drug_descriptions.items():
embedding = embeddings.embed_query(desc)
graph.query("""
MATCH (d:Drug {name: $name})
SET d.description = $desc,
d.embedding = $embedding
""", params={"name": name, "desc": desc, "embedding": embedding})
为实现高效的混合检索,需要在Neo4j中创建向量索引:
python复制graph.query("""
CREATE VECTOR INDEX drug_embeddings IF NOT EXISTS
FOR (d:Drug) ON d.embedding
OPTIONS {indexConfig: {
`vector.dimensions`: 1536, # 匹配嵌入维度
`vector.similarity_function`: 'cosine'
}}
""")
LangChain的CypherQAChain能自动将自然语言转换为Cypher查询:
python复制from langchain.chains import GraphCypherQAChain
cypher_chain = GraphCypherQAChain.from_llm(
llm=llm,
graph=graph,
verbose=True,
top_k=5, # 返回结果数量
return_direct=False # 返回自然语言而非原始数据
)
response = cypher_chain.run(
"列出所有治疗糖尿病及其并发症的药物"
)
print(response)
结合向量检索和图谱查询的EnsembleRetriever:
python复制from langchain.retrievers import EnsembleRetriever
from langchain_community.vectorstores import Neo4jVector
# 创建Neo4j向量检索器
vector_store = Neo4jVector.from_existing_graph(
embedding=embeddings,
node_label="Drug",
text_node_properties=["name", "description"],
embedding_node_property="embedding"
)
vector_retriever = vector_store.as_retriever(search_kwargs={"k": 3})
# 创建图谱检索器
graph_retriever = graph.as_retriever(search_type="cypher")
# 组合检索器
hybrid_retriever = EnsembleRetriever(
retrievers=[vector_retriever, graph_retriever],
weights=[0.4, 0.6] # 可根据场景调整
)
构建端到端的问答系统:
python复制from langchain_core.prompts import ChatPromptTemplate
from langchain.chains import create_retrieval_chain
# 定制化提示模板
prompt = ChatPromptTemplate.from_template("""
你是一名专业的医疗助手,请根据以下上下文回答问题:
{context}
问题:{input}
""")
# 创建检索链
retrieval_chain = create_retrieval_chain(
retriever=hybrid_retriever,
combine_docs_chain=prompt | llm
)
# 执行查询
question = "有哪些药物可以同时治疗糖尿病和高血压?"
result = retrieval_chain.invoke({"input": question})
print(result["answer"])
Cypher查询优化:
cypher复制// 使用参数化查询
MATCH (d:Drug)-[:TREATS]->(dis:Disease)
WHERE dis.name = $diseaseName
RETURN d.name
索引策略:
python复制graph.query("""
CREATE INDEX drug_name IF NOT EXISTS
FOR (d:Drug) ON (d.name)
""")
批量操作:使用UNWIND进行批量数据操作
在返回答案的同时提供推理路径:
python复制def get_explanation_path(entities):
return graph.query("""
MATCH path=(start)-[*1..3]-(end)
WHERE start.name IN $entities AND end.name IN $entities
RETURN path
LIMIT 3
""", params={"entities": entities})
# 在回答中添加解释
answer = retrieval_chain.invoke({"input": question})
related_entities = extract_entities(answer["answer"])
paths = get_explanation_path(related_entities)
构建包含多维度医疗知识的图谱:
建模金融机构间的复杂关系:
cypher复制// 创建金融实体关系
CREATE (b:Bank {name: 'Bank A'})-[:LOAN_TO]->(c:Company {name: 'Corp X'})
CREATE (c)-[:OWNS]->(s:Subsidiary {name: 'Sub Y'})
查询示例:"找出对Bank A风险暴露超过1亿的所有实体"
构建法律条文和判例的关联网络:
在实际项目实施过程中,我们总结了以下关键经验:
数据质量优先:知识图谱的效果90%取决于数据质量,务必建立严格的数据清洗流程
混合检索平衡:向量检索与图谱查询的权重需要根据业务场景调整,建议通过A/B测试确定最优比例
LLM提示工程:为Cypher生成设计专门的提示模板,例如:
python复制CYPHER_GENERATION_TEMPLATE = """
你是一个专业的Neo4j Cypher查询生成器。
只生成Cypher查询,不要解释。
使用以下图谱schema:
{schema}
"""
版本控制策略:对图谱schema、LLM提示模板和检索参数进行版本管理
性能监控指标:建立以下关键指标的监控看板:
这套技术栈在实际医疗知识问答系统中的表现显示,相比纯向量检索方案,混合方法的准确率提升了42%,同时将幻觉率降低了67%。特别是在处理多跳查询时,优势更为明显。