开源 AI 食谱文档
使用知识图谱增强 RAG 推理能力
并获得增强的文档体验
开始使用
使用知识图谱增强 RAG 推理能力
知识图谱提供了一种以人类和机器都能理解的格式来建模和存储相互关联信息的方法。这些图由 *节点* 和 *边* 组成,分别代表实体及其关系。与传统数据库不同,图固有的表达能力可以实现更丰富的语义理解,同时提供了适应新实体类型和关系的灵活性,而不受固定模式的限制。
通过将知识图谱与嵌入 (向量搜索) 相结合,我们可以利用 *多跳连接* 和 *信息的上下文理解* 来增强大语言模型 (LLM) 的推理和可解释性。
本笔记本探讨了这种方法的实际实现,演示了如何:
- 使用合成数据集在 Neo4j 中构建一个与研究出版物相关的知识图谱,
- 使用 嵌入模型 将我们的数据字段子集投影到高维向量空间中,
- 在这些嵌入上构建向量索引以实现相似性搜索,以及
- 通过使用 LangChain 将用户查询轻松转换为 Cypher 语句,从而用自然语言从我们的图谱中提取见解。
初始化
%pip install neo4j langchain langchain_openai langchain-community python-dotenv --quiet
设置 Neo4j 实例
我们将使用 Neo4j 创建我们的知识图谱,它是一个专门从事图数据库技术的开源数据库管理系统。
为了快速简便地进行设置,您可以在 Neo4j Aura 上启动一个免费实例。
然后,您可以使用一个 .env
文件将 NEO4J_URI
、NEO4J_USERNAME
和 NEO4J_PASSWORD
设置为环境变量。
import dotenv
dotenv.load_dotenv(".env", override=True)
Langchain 提供了 Neo4jGraph
类来与 Neo4j 交互。
import os
from langchain_community.graphs import Neo4jGraph
graph = Neo4jGraph(
url=os.environ["NEO4J_URI"],
username=os.environ["NEO4J_USERNAME"],
password=os.environ["NEO4J_PASSWORD"],
)
将数据集加载到图谱中
下面的示例创建了与我们的 Neo4j
数据库的连接,并用包含研究文章及其作者的 合成数据 填充它。
实体有:
- 研究员 (Researcher)
- 文章
- 主题
关系有:
- 研究员 —[发表了]—> 文章
- 文章 —[属于主题]—> 主题
from langchain_community.graphs import Neo4jGraph
graph = Neo4jGraph()
q_load_articles = """
LOAD CSV WITH HEADERS
FROM 'https://raw.githubusercontent.com/dcarpintero/generative-ai-101/main/dataset/synthetic_articles.csv'
AS row
FIELDTERMINATOR ';'
MERGE (a:Article {title:row.Title})
SET a.abstract = row.Abstract,
a.publication_date = date(row.Publication_Date)
FOREACH (researcher in split(row.Authors, ',') |
MERGE (p:Researcher {name:trim(researcher)})
MERGE (p)-[:PUBLISHED]->(a))
FOREACH (topic in [row.Topic] |
MERGE (t:Topic {name:trim(topic)})
MERGE (a)-[:IN_TOPIC]->(t))
"""
graph.query(q_load_articles)
让我们检查一下节点和关系是否已正确初始化。
>>> graph.refresh_schema()
>>> print(graph.get_schema)
Node properties: Article {title: STRING, abstract: STRING, publication_date: DATE, embedding: LIST} Researcher {name: STRING} Topic {name: STRING} Relationship properties: The relationships: (:Article)-[:IN_TOPIC]->(:Topic) (:Researcher)-[:PUBLISHED]->(:Article)
我们的知识图谱可以在 Neo4j 工作区中进行检查。
构建向量索引
现在我们构建一个向量索引,以便根据 *主题、标题和摘要* 高效地搜索相关的 *文章* 。该过程涉及使用这些字段计算每篇文章的嵌入。在查询时,系统通过采用相似性度量 (例如余弦距离) 来找到与用户输入最相似的文章。
from langchain_community.vectorstores import Neo4jVector
from langchain_openai import OpenAIEmbeddings
vector_index = Neo4jVector.from_existing_graph(
OpenAIEmbeddings(),
url=os.environ["NEO4J_URI"],
username=os.environ["NEO4J_USERNAME"],
password=os.environ["NEO4J_PASSWORD"],
index_name="articles",
node_label="Article",
text_node_properties=["topic", "title", "abstract"],
embedding_node_property="embedding",
)
注意:要访问 OpenAI 嵌入模型,您需要创建一个 OpenAI 帐户,获取一个 API 密钥,并将 OPENAI_API_KEY
设置为环境变量。您可能还会发现尝试使用其他 嵌入模型 集成也很有用。
基于相似性的问答
Langchain RetrievalQA
使用上述向量索引作为检索器创建一个问答 (QA) 链。
from langchain.chains import RetrievalQA
from langchain_openai import ChatOpenAI
vector_qa = RetrievalQA.from_chain_type(llm=ChatOpenAI(), chain_type="stuff", retriever=vector_index.as_retriever())
让我们问一下 “*哪些文章讨论了人工智能可能如何影响我们的日常生活?*”
>>> r = vector_qa.invoke(
... {
... "query": "which articles discuss how AI might affect our daily life? include the article titles and abstracts."
... }
... )
>>> print(r["result"])
The articles that discuss how AI might affect our daily life are: 1. **The Impact of AI on Employment: A Comprehensive Study** *Abstract:* This study analyzes the potential effects of AI on various job sectors and suggests policy recommendations to mitigate negative impacts. 2. **The Societal Implications of Advanced AI: A Multidisciplinary Analysis** *Abstract:* Our study brings together experts from various fields to analyze the potential long-term impacts of advanced AI on society, economy, and culture. These two articles would provide insights into how AI could potentially impact our daily lives from different perspectives.
遍历知识图谱进行推理
知识图谱非常适合建立实体之间的联系,从而能够提取模式和发现新的见解。
本节演示了如何实现这一过程,并使用自然语言查询将结果集成到 LLM 管道中。
使用 LangChain 的 Graph-Cypher-Chain
为了构建富有表现力和高效的查询,Neo4j
用户使用 Cypher
,这是一种受 SQL 启发的声明式查询语言。LangChain
提供了 GraphCypherQAChain
包装器,这是一个抽象层,允许使用自然语言查询图数据库,从而更容易将基于图的数据检索集成到 LLM 管道中。
实际上,GraphCypherQAChain
会:
- 应用上下文学习 (提示工程),从用户输入 (自然语言) 生成 Cypher 语句 (用于 Neo4j 等图数据库的查询),
- 针对图数据库执行所述语句,并且
- 将结果作为上下文提供,以使 LLM 的响应基于准确、最新的信息。
注意: 此实现涉及执行模型生成的图查询,这带来了固有的风险,例如对数据库中敏感数据的意外访问或修改。为了减轻这些风险,请确保您的数据库连接权限受到尽可能严格的限制,以满足您的链/代理的特定需求。虽然这种方法降低了风险,但并不能完全消除它。
from langchain.chains import GraphCypherQAChain
from langchain_openai import ChatOpenAI
graph.refresh_schema()
cypher_chain = GraphCypherQAChain.from_llm(
cypher_llm=ChatOpenAI(temperature=0, model_name="gpt-4o"),
qa_llm=ChatOpenAI(temperature=0, model_name="gpt-4o"),
graph=graph,
verbose=True,
)
使用自然语言的查询示例
在下面的示例中请注意,Cypher 查询执行的结果是如何作为上下文提供给 LLM 的。
“ Emily Chen 发表了多少篇文章?”
在这个例子中,我们的问题“*Emily Chen 发表了多少篇文章?*” 将被翻译成 Cypher 查询:
MATCH (r:Researcher {name: "Emily Chen"})-[:PUBLISHED]->(a:Article)
RETURN COUNT(a) AS numberOfArticles
该查询匹配标签为 Author
且姓名为 ‘Emily Chen’ 的节点,并遍历 PUBLISHED
关系到 Article
节点。然后,它计算与 ‘Emily Chen’ 连接的 Article
节点的数量。
>>> # the answer should be '7'
>>> cypher_chain.invoke({"query": "How many articles has published Emily Chen?"})
[1m> Entering new GraphCypherQAChain chain...[0m Generated Cypher: [32;1m[1;3mcypher MATCH (r:Researcher {name: "Emily Chen"})-[:PUBLISHED]->(a:Article) RETURN COUNT(a) AS numberOfArticles [0m Full Context: [32;1m[1;3m[{'numberOfArticles': 7}][0m [1m> Finished chain.[0m
“ 有没有哪对研究人员一起发表了超过三篇文章?”
在这个例子中,查询“*有没有哪对研究人员一起发表了超过三篇文章?*”会生成以下 Cypher 查询:
MATCH (r1:Researcher)-[:PUBLISHED]->(a:Article)<-[:PUBLISHED]-(r2:Researcher)
WHERE r1 <> r2
WITH r1, r2, COUNT(a) AS sharedArticles
WHERE sharedArticles > 3
RETURN r1.name, r2.name, sharedArticles
该查询会从 Researcher
节点遍历到 PUBLISHED
关系以找到连接的 Article
节点,然后再遍历回来以找到 Researcher
对。
>>> # the answer should be David Johnson & Emily Chen, Robert Taylor & Emily Chen
>>> cypher_chain.invoke(
... {"query": "are there any pair of researchers who have published more than three articles together?"}
... )
[1m> Entering new GraphCypherQAChain chain...[0m Generated Cypher: [32;1m[1;3mcypher MATCH (r1:Researcher)-[:PUBLISHED]->(a:Article)<-[:PUBLISHED]-(r2:Researcher) WHERE r1 <> r2 WITH r1, r2, COUNT(a) AS sharedArticles WHERE sharedArticles > 3 RETURN r1.name, r2.name, sharedArticles [0m Full Context: [32;1m[1;3m[{'r1.name': 'David Johnson', 'r2.name': 'Emily Chen', 'sharedArticles': 4}, {'r1.name': 'Robert Taylor', 'r2.name': 'Emily Chen', 'sharedArticles': 4}, {'r1.name': 'Emily Chen', 'r2.name': 'David Johnson', 'sharedArticles': 4}, {'r1.name': 'Emily Chen', 'r2.name': 'Robert Taylor', 'sharedArticles': 4}][0m [1m> Finished chain.[0m
“ 哪位研究员与最多的同行合作过?”
让我们找出与最多同行合作的研究员是谁。我们的查询“*哪位研究员与最多的同行合作过?*”现在会生成以下 Cypher:
MATCH (r:Researcher)-[:PUBLISHED]->(:Article)<-[:PUBLISHED]-(peer:Researcher)
WITH r, COUNT(DISTINCT peer) AS peerCount
RETURN r.name AS researcher, peerCount
ORDER BY peerCount DESC
LIMIT 1
在这里,我们需要从所有 Researcher
节点开始,遍历它们的 PUBLISHED
关系以找到连接的 Article
节点。对于每个 Article
节点,Neo4j 随后会遍历回来,找到也发表了同一篇文章的其他 Researcher
节点 (同行)。
>>> # the answer should be 'David Johnson'
>>> cypher_chain.invoke({"query": "Which researcher has collaborated with the most peers?"})
[1m> Entering new GraphCypherQAChain chain...[0m Generated Cypher: [32;1m[1;3mcypher MATCH (r1:Researcher)-[:PUBLISHED]->(:Article)<-[:PUBLISHED]-(r2:Researcher) WHERE r1 <> r2 WITH r1, COUNT(DISTINCT r2) AS collaborators RETURN r1.name AS researcher, collaborators ORDER BY collaborators DESC LIMIT 1 [0m Full Context: [32;1m[1;3m[{'researcher': 'David Johnson', 'collaborators': 6}][0m [1m> Finished chain.[0m
< > 在 GitHub 上更新