在本教程中,我们将介绍如何在图数据库上创建问答链(Q&A Chain)。这种系统可以让我们针对图数据库中的数据提出问题,并以自然语言的形式返回答案。
架构
在高层次上,大多数图链的步骤如下:
- 将问题转换为图数据库查询:模型将用户的输入转换为图数据库查询(例如 Cypher 查询)。
- 执行图数据库查询:执行生成的图数据库查询。
- 回答问题:模型使用查询结果回答用户的问题。
环境设置
首先,我们需要安装所需的包并设置环境变量。以下示例中,我们使用的是 Neo4j 图数据库。
pip install --upgrade --quiet langchain langchain-community langchain-openai neo4j
我们将在本教程中默认使用 OpenAI 模型。
import getpass
import os
os.environ["OPENAI_API_KEY"] = getpass.getpass()
此外,您可以选择使用 LangSmith 来跟踪链的执行情况:
# 可选使用 LangSmith 进行链的追踪(非必须)
# os.environ["LANGCHAIN_API_KEY"] = getpass.getpass()
# os.environ["LANGCHAIN_TRACING_V2"] = "true"
接下来,我们需要定义 Neo4j 的连接凭证。请按照此链接中的安装步骤设置一个 Neo4j 数据库。
Neo4j使用docker安装命令:
docker run -d --name neo4j \
-p7474:7474 -p7687:7687 \
-e NEO4J_AUTH=neo4j/neo4j1234 \
-e NEO4JLABS_PLUGINS='["apoc"]' \
-e NEO4J_dbms_security_procedures_unrestricted="apoc.*" \
-e NEO4J_dbms_security_procedures_allowlist="apoc.*" \
neo4j
设置数据库:
os.environ["NEO4J_URI"] = "bolt://localhost:7687"
os.environ["NEO4J_USERNAME"] = "neo4j"
os.environ["NEO4J_PASSWORD"] = "neo4j1234"
下面的示例代码将连接到 Neo4j 数据库,并导入有关电影及其演员的示例数据。
from langchain_community.graphs import Neo4jGraph
graph = Neo4jGraph()
# 导入电影信息
movies_query = """
LOAD CSV WITH HEADERS FROM
'https://raw.githubusercontent.com/tomasonjo/blog-datasets/main/movies/movies_small.csv'
AS row
MERGE (m:Movie {id:row.movieId})
SET m.released = date(row.released),
m.title = row.title,
m.imdbRating = toFloat(row.imdbRating)
FOREACH (director in split(row.director, '|') |
MERGE (p:Person {name:trim(director)})
MERGE (p)-[:DIRECTED]->(m))
FOREACH (actor in split(row.actors, '|') |
MERGE (p:Person {name:trim(actor)})
MERGE (p)-[:ACTED_IN]->(m))
FOREACH (genre in split(row.genres, '|') |
MERGE (g:Genre {name:trim(genre)})
MERGE (m)-[:IN_GENRE]->(g))
"""
graph.query(movies_query)
图数据库模式
为了让大型语言模型(LLM)能够生成 Cypher 查询语句,它需要了解图数据库的模式信息。当您实例化 Neo4jGraph
对象时,系统会自动检索图的模式信息。如果您之后对图进行了更改,可以调用 refresh_schema
方法刷新模式信息。
graph.refresh_schema()
print(graph.schema)
假设数据库中定义的节点和关系如下:
-
节点属性:
Movie
:{imdbRating: 浮点数, id: 字符串, released: 日期, title: 字符串}Person
:{name: 字符串}Genre
:{name: 字符串}
-
关系:
(:Movie)-[:IN_GENRE]->(:Genre)
(:Person)-[:DIRECTED]->(:Movie)
(:Person)-[:ACTED_IN]->(:Movie)
构建问答链
接下来,我们将使用一个简单的链来处理问题。该链将问题转换为 Cypher 查询,执行查询,并使用查询结果来回答问题。
from langchain.chains import GraphCypherQAChain
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
chain = GraphCypherQAChain.from_llm(graph=graph, llm=llm, verbose=True, allow_dangerous_requests=True # 显式允许危险请求)
response = chain.invoke({"query": "《Casino》中的演员有哪些?"})
print(response)
在执行这个链时,生成的 Cypher 查询如下:
MATCH (:Movie {title: "Casino"})<-[:ACTED_IN]-(actor:Person)
RETURN actor.name
通过执行该查询,系统返回了《Casino》的演员名单:
{'query': '《Casino》中的演员有哪些?',
'result': 'James Woods, Joe Pesci, Robert De Niro, Sharon Stone.'}
验证关系方向
LLM 生成的 Cypher 查询中,关系方向可能会有错误。由于图模式是预定义的,我们可以使用 validate_cypher
参数来验证并自动修正生成的查询中的关系方向。
chain = GraphCypherQAChain.from_llm(
graph=graph, llm=llm, verbose=True, validate_cypher=True, allow_dangerous_requests=True # 显式允许危险请求
)
response = chain.invoke({"query": "《Casino》中的演员有哪些?"})
print(response)
完整代码实例
# 导入必要的库
import getpass
import os
from langchain_community.graphs import Neo4jGraph
from langchain.chains import GraphCypherQAChain
from langchain_openai import ChatOpenAI
# 设置OpenAI API密钥
os.environ["OPENAI_API_KEY"] = getpass.getpass(prompt="请输入您的OpenAI API密钥: ")
# 设置Neo4j连接凭证
os.environ["NEO4J_URI"] = "bolt://localhost:7687"
os.environ["NEO4J_USERNAME"] = "neo4j"
os.environ["NEO4J_PASSWORD"] = getpass.getpass(prompt="请输入您的Neo4j密码: ")
# 初始化Neo4j连接
graph = Neo4jGraph()
# 定义查询语句以导入电影及演员数据
movies_query = """
LOAD CSV WITH HEADERS FROM
'https://raw.githubusercontent.com/tomasonjo/blog-datasets/main/movies/movies_small.csv'
AS row
MERGE (m:Movie {id:row.movieId})
SET m.released = date(row.released),
m.title = row.title,
m.imdbRating = toFloat(row.imdbRating)
FOREACH (director in split(row.director, '|') |
MERGE (p:Person {name:trim(director)})
MERGE (p)-[:DIRECTED]->(m))
FOREACH (actor in split(row.actors, '|') |
MERGE (p:Person {name:trim(actor)})
MERGE (p)-[:ACTED_IN]->(m))
FOREACH (genre in split(row.genres, '|') |
MERGE (g:Genre {name:trim(genre)})
MERGE (m)-[:IN_GENRE]->(g))
"""
# 执行查询以导入数据
graph.query(movies_query)
# 刷新并打印数据库模式
graph.refresh_schema()
print("图数据库模式: ", graph.schema)
# 设置LLM(使用OpenAI的GPT-3.5 Turbo)
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 创建GraphCypherQAChain对象,允许危险查询
chain = GraphCypherQAChain.from_llm(
graph=graph,
llm=llm,
verbose=True,
validate_cypher=True,
allow_dangerous_requests=True # 显式允许危险请求
)
# 生成并执行Cypher查询,获取电影《Casino》的演员
response = chain.invoke({"query": "《Casino》中的演员有哪些?"})
print("问答结果: ", response)
总结
通过本教程,我们了解了如何基于 Neo4j 图数据库和 LangChain 构建问答应用。从问题转换为 Cypher 查询到执行查询并生成自然语言答案,我们简要介绍了实现流程。您可以根据实际需求进一步定制和扩展此系统。
系列文章索引
LangChain教程 - 系列文章