LangChain教程 - 基于图数据库构建问答

在本教程中,我们将介绍如何在图数据库上创建问答链(Q&A Chain)。这种系统可以让我们针对图数据库中的数据提出问题,并以自然语言的形式返回答案。


架构

在高层次上,大多数图链的步骤如下:

  1. 将问题转换为图数据库查询:模型将用户的输入转换为图数据库查询(例如 Cypher 查询)。
  2. 执行图数据库查询:执行生成的图数据库查询。
  3. 回答问题:模型使用查询结果回答用户的问题。

环境设置

首先,我们需要安装所需的包并设置环境变量。以下示例中,我们使用的是 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教程 - 系列文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值