随着 Neo4j 宣布与 LangChain 的集成,我们见证了大量基于知识图谱和大语言模型(LLM)的检索增强生成(RAG)应用场景的涌现。这一发展在最近几个月显著推动了知识图谱在 RAG 中的应用。相比传统的 RAG 系统,基于知识图谱的 RAG 系统在管理幻觉(hallucinations)方面表现更佳。此外,基于代理的系统的使用也在快速增长,以进一步增强 RAG 应用。为了更进一步,LangChain 生态系统中新增了 LangGraph 框架,为 LLM 应用添加了循环和持久化功能。
本文将详细介绍如何使用 LangChain 和 Neo4j 构建一个 GraphRAG 工作流,并通过实际代码示例进行分步讲解,最后提供完整的代码实现。
目录
- 项目简介
- 环境配置
- 安装必要的库
- 配置环境变量
- 代码结构与功能概述
- 分步讲解代码
- 导入库与加载环境变量
- 配置 OpenAI LLM
- 读取和分割文本
- 转换为图文档
- 连接 Neo4j 数据库
- 写入图数据库
- 查询与验证
- 完整代码
- 总结与展望
项目简介
在本项目中,我们将使用 LangChain 构建一个工作流,该工作流能够读取文本数据,使用大语言模型进行处理,并将结果存储在 Neo4j 图数据库中。这种方式不仅能够有效管理和查询知识,还能通过图数据库的强大关系分析能力,增强 RAG 应用的表现。
环境配置
安装必要的库
首先,确保您的开发环境中安装了以下必要的 Python 库:
pip install langchain_openai dotenv langchain_community python-dotenv
配置环境变量
为了确保敏感信息(如 API 密钥、数据库链接和密码)的安全,我们将使用环境变量来存储这些信息。您可以通过创建一个 .env
文件来管理这些变量。
- 创建
.env
文件
在项目的根目录下创建一个名为 .env
的文件,并添加以下内容:
OPENAI_API_KEY=your_openai_api_key
OPENAI_API_URL=http://your_api_url:port/v1
NEO4J_URI=bolt://your_neo4j_host:port
NEO4J_USERNAME=your_neo4j_username
NEO4J_PASSWORD=your_neo4j_password
注意: 请将 your_openai_api_key
、your_api_url
、your_neo4j_host
、your_neo4j_username
和 your_neo4j_password
替换为您的实际凭证信息。
- 加载环境变量
在代码中使用 python-dotenv
库加载 .env
文件中的环境变量:
from dotenv import load_dotenv
# 加载 .env 文件中的环境变量
load_dotenv()
代码结构与功能概述
整个工作流分为以下几个主要步骤:
- 导入库与加载环境变量:引入必要的 Python 库并加载环境变量。
- 配置 OpenAI LLM:设置大语言模型的相关参数。
- 读取和分割文本:从文件中读取文本内容,并使用
RecursiveCharacterTextSplitter
进行分块处理。 - 转换为图文档:将分割后的文档转换为适合存储在图数据库中的格式。
- 连接 Neo4j 数据库:配置并连接到 Neo4j 图数据库。
- 写入图数据库:将转换后的图文档写入 Neo4j 数据库中。
- 查询与验证:执行数据库查询以验证数据的正确写入。
分步讲解代码
下面我们将逐步解析每一部分代码,理解其功能与实现。
导入库与加载环境变量
from langchain_openai import OpenAI
from langchain_openai import ChatOpenAI
from langchain.schema import HumanMessage
from langchain_core.documents import Document
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_experimental.graph_transformers import LLMGraphTransformer
# 对接国产模型需要修改use_function_call
# from llm_cn import LLMGraphTransformer
import os
from dotenv import load_dotenv
from langchain_community.graphs import Neo4jGraph
# 加载 .env 文件中的环境变量
load_dotenv()
说明:
- 导入了 LangChain 相关的库,用于与 OpenAI 的 API 交互以及处理文档。
- 使用
dotenv
库加载环境变量,确保敏感信息不直接暴露在代码中。 - 导入
Neo4jGraph
用于与 Neo4j 图数据库进行交互。
配置 OpenAI LLM
api_key = os.getenv("OPENAI_API_KEY")
api_url = os.getenv("OPENAI_API_URL")
model = "glm-4-plus"
llm = ChatOpenAI(
model_name=model,
openai_api_key=api_key,
openai_api_base=api_url,
temperature=0.9
)
说明:
- 从环境变量中获取 OpenAI API 密钥和 API URL。
- 配置大语言模型(LLM)的相关参数,包括模型名称和温度(控制生成文本的随机性)。
读取和分割文本
# 从文件读取文本
file_path = 'data/****.txt' # 请替换为您实际的文件路径
with open(file_path, 'r', encoding='utf-8') as file:
text = file.read()
# 使用 RecursiveCharacterTextSplitter 对文本进行分片
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=2000, # 每个分片的最大字符数
chunk_overlap=20 # 分片之间的重叠字符数
)
# 分片文档
chunks = text_splitter.split_text(text)
documents = [Document(page_content=chunk) for chunk in chunks]
说明:
- 从指定路径读取文本文件内容。
- 使用
RecursiveCharacterTextSplitter
将文本按指定的字符数进行分块,并设置块之间的重叠部分,以确保上下文的连贯性。 - 将分块后的文本封装成
Document
对象列表,便于后续处理。
转换为图文档
llm_transformer = LLMGraphTransformer(llm=llm)
# 调用国产模型,需要增加关闭use_function_call的参数
# llm_transformer = LLMGraphTransformer(llm=llm, use_function_call=False)
graph_documents = llm_transformer.convert_to_graph_documents(documents)
print(len(graph_documents))
print(f"Nodes: {graph_documents[0].nodes}")
print(f"Relationships: {graph_documents[0].relationships}")
说明:
- 创建一个
LLMGraphTransformer
对象,传入配置好的 LLM。 - 使用 LLM 将文档转换为适合存储在图数据库中的图文档(包含节点和关系)。
- 打印转换后的图文档的节点和关系数量,以验证转换效果。
连接 Neo4j 数据库
# 配置 Neo4j 连接信息
neo4j_uri = os.getenv("NEO4J_URI")
neo4j_username = os.getenv("NEO4J_USERNAME")
neo4j_password = os.getenv("NEO4J_PASSWORD")
os.environ["NEO4J_URI"] = neo4j_uri
os.environ["NEO4J_USERNAME"] = neo4j_username
os.environ["NEO4J_PASSWORD"] = neo4j_password
graph = Neo4jGraph()
说明:
- 从环境变量中获取 Neo4j 的连接 URI、用户名和密码。
- 设置环境变量,确保
Neo4jGraph
能够正确读取这些信息进行连接。 - 创建一个
Neo4jGraph
对象,作为后续数据库操作的接口。
写入图数据库
# 写入到图数据库中,不包括原始文档
graph.add_graph_documents(
graph_documents,
baseEntityLabel=True,
include_source=False
)
说明:
- 使用
add_graph_documents
方法将转换后的图文档写入 Neo4j 数据库。 - 参数说明:
baseEntityLabel=True
:为每个实体添加基本标签。include_source=False
:不包括原始文档内容,仅存储转换后的节点和关系。
显示节点名称
# 看不到节点名称,可以用下面的查询复制一下 name
# 基于查询语句复制 name 属性
QUERY = """
MATCH (n)
WHERE n.id IS NOT NULL
SET n.name = n.id
RETURN n
"""
graph.query(QUERY)
print("success")
说明:
- 执行一段 Cypher 查询语句,将每个节点的
name
属性设置为其id
属性的值,便于在图数据库中查看和识别节点。 - 打印
"success"
以确认操作成功完成。
完整代码
以下是完整的代码实现,整合了上述所有步骤:
from langchain_openai import OpenAI
from langchain_openai import ChatOpenAI
from langchain.schema import HumanMessage
from langchain_core.documents import Document
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_experimental.graph_transformers import LLMGraphTransformer
# 对接国产模型需要修改use_function_call
# from llm_cn import LLMGraphTransformer
import os
from dotenv import load_dotenv
from langchain_community.graphs import Neo4jGraph
# 加载 .env 文件中的环境变量
load_dotenv()
api_key = os.getenv("OPENAI_API_KEY")
api_url = os.getenv("OPENAI_API_URL")
model = "glm-4-plus"
llm = ChatOpenAI(
model_name=model,
openai_api_key=api_key,
openai_api_base=api_url,
temperature=0.9
)
# 从文件读取文本
file_path = 'data/******.txt' # 请替换为您实际的文件路径
with open(file_path, 'r', encoding='utf-8') as file:
text = file.read()
# 使用 RecursiveCharacterTextSplitter 对文本进行分片
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=2000, # 每个分片的最大字符数
chunk_overlap=20 # 分片之间的重叠字符数
)
# 分片文档
chunks = text_splitter.split_text(text)
documents = [Document(page_content=chunk) for chunk in chunks]
llm_transformer = LLMGraphTransformer(llm=llm)
# 调用国产模型,需要增加关闭use_function_call的参数
# llm_transformer = LLMGraphTransformer(llm=llm, use_function_call=False)
graph_documents = llm_transformer.convert_to_graph_documents(documents)
print(len(graph_documents))
print(f"Nodes: {graph_documents[0].nodes}")
print(f"Relationships: {graph_documents[0].relationships}")
# 配置 Neo4j 连接信息
neo4j_uri = os.getenv("NEO4J_URI")
neo4j_username = os.getenv("NEO4J_USERNAME")
neo4j_password = os.getenv("NEO4J_PASSWORD")
os.environ["NEO4J_URI"] = neo4j_uri
os.environ["NEO4J_USERNAME"] = neo4j_username
os.environ["NEO4J_PASSWORD"] = neo4j_password
graph = Neo4jGraph()
# 写入到图数据库中,不包括原始文档
graph.add_graph_documents(
graph_documents,
baseEntityLabel=True,
include_source=False
)
# 看不到节点名称,可以用下面的查询复制一下 name
# 基于查询语句复制 name 属性
QUERY = """
MATCH (n)
WHERE n.id IS NOT NULL
SET n.name = n.id
RETURN n
"""
graph.query(QUERY)
print("success")
总结与展望
通过本文的介绍,我们展示了如何结合使用 LangChain 和 Neo4j 构建一个强大的 GraphRAG 工作流。这种工作流不仅能够高效地处理和存储大量文本数据,还能利用知识图谱的优势,提升检索与生成的能力。随着 LangChain 生态系统的不断发展,诸如 LangGraph 等新框架的加入,将进一步增强 LLM 应用的循环与持久化功能,为开发者提供更多可能性。
未来,基于知识图谱的 RAG 系统有望在各类应用场景中发挥更大的作用,如智能问答、推荐系统、知识管理等。同时,随着技术的进步和社区的贡献,相关工具和框架将变得更加成熟和易用,开发者可以更轻松地构建出符合自身需求的智能应用。