开源 AI 食谱文档
使用知识图谱增强 RAG 推理
并获得增强的文档体验
开始使用
使用知识图谱增强 RAG 推理
作者: Diego Carpintero
知识图谱提供了一种对互联信息进行建模和存储的方法,其格式既可供人类理解,也可供机器理解。这些图谱由节点和边组成,分别表示实体及其关系。与传统数据库不同,图谱固有的表达性允许更丰富的语义理解,同时提供灵活性,以适应新的实体类型和关系,而不会受到固定模式的约束。
通过将知识图谱与嵌入(向量搜索)相结合,我们可以利用多跳连接和信息的上下文理解来增强 LLM 中的推理和可解释性。
本笔记本探讨了这种方法的实际实现,演示了如何
- 使用合成数据集在 Neo4j 中构建与研究出版物相关的知识图谱,
- 使用 嵌入模型 将我们数据字段的子集投影到高维向量空间中,
- 在这些嵌入上构建向量索引以实现相似性搜索,以及
- 通过使用 LangChain 轻松将用户查询转换为 cypher 语句,从而从我们的图谱中提取见解
初始化
%pip install neo4j langchain langchain_openai langchain-community python-dotenv --quiet
设置 Neo4j 实例
我们将使用 Neo4j 创建知识图谱,Neo4j 是一个专门从事图数据库技术的开源数据库管理系统。
为了快速简便地设置,您可以在 Neo4j Aura 上启动一个免费实例。
然后,您可以将 NEO4J_URI
、NEO4J_USERNAME
和 NEO4J_PASSWORD
设置为环境变量,使用 .env
文件
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
数据库的连接,并使用包含研究文章及其作者的 合成数据 填充该数据库。
实体是
- 研究员
- 文章
- 主题
关系是
- 研究员 —[PUBLISHED]—> 文章
- 文章 —[IN_TOPIC]—> 主题
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())
让我们问“哪些文章讨论了 AI 如何影响我们的日常生活?”
>>> 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 管道中。
Graph-Cypher-Chain 与 LangChain
为了构建富有表现力且高效的查询,Neo4j
用户使用 Cypher
,这是一种受 SQL 启发的声明性查询语言。LangChain
提供了包装器 GraphCypherQAChain
,这是一个抽象层,允许使用自然语言查询图数据库,从而更容易将基于图的数据检索集成到 LLM 管道中。
在实践中,GraphCypherQAChain
- 从用户输入(自然语言)生成 Cypher 语句(用于 Neo4j 等图数据库的查询),应用上下文学习(prompt 工程),
- 针对图数据库执行所述语句,以及
- 提供结果作为上下文,以根据准确、最新的信息来确定 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 发表了多少篇文章?” 将被翻译成 Cyper 查询
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
节点,然后再遍历回来以查找 Researchers
对。
>>> # 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
“ 哪位研究人员与同行合作最多? ”
让我们找出与同行合作最多的研究人员。我们的查询“哪位研究人员与同行合作最多?” 现在生成 Cyper
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 上