Neo4j + LangChain:如何构建基于知识图谱的最强RAG系统?

news2024/12/28 9:33:58

自从 Neo4j 宣布与 LangChain 的集成以来,我们已经看到许多围绕使用 Neo4j 和大语言模型(LLM)构建检索增强生成(RAG)系统的用例。这导致了近年来知识图谱在 RAG 中使用的快速增加。基于知识图谱的 RAG 系统在处理幻觉方面的表现似乎优于传统的 RAG 系统。我们还注意到,使用基于代理的系统可以进一步增强 RAG 应用程序。为此,LangGraph 框架已被添加到 LangChain 生态系统中,以为 LLM 应用程序添加循环和持久性。

我将向你演示如何使用 LangChain 和 LangGraph 为 Neo4j 创建一个 GraphRAG 工作流程。我们将开发一个相当复杂的工作流程,在多个阶段使用 LLM,并采用动态提示词查询分解技术。我们还将使用一种路由技术,将查询在向量语义搜索和图 QA 链之间进行分流。使用 LangGraph 的 GraphState,我们将通过从早期步骤中提取的上下文来丰富我们的提示模板。

我们的工作流程的高级示例大致如下图所示。

图片

在深入细节之前,首先回顾一下基于 LangChain 的 GraphRAG 工作流程:

图片

来源:LangChain

一个典型的 GraphRAG 应用涉及使用 LLM 生成 Cypher 查询语言。然后,LangChain 的 GraphCypherQAChain 将生成的 Cypher 查询提交到图数据库(例如 Neo4j)以检索查询输出。最后,LLM 将根据初始查询和图查询的响应返回一个答案。此时,响应仅基于传统的图查询。自从 Neo4j 引入向量索引功能以来,我们也可以执行语义查询。在处理属性图时,有时将语义查询和图查询结合使用或在两者之间进行分流是有益的。

图查询示例

假设我们有一个学术期刊的图数据库,其中包含文章、作者、期刊、机构等节点。

图片

一个典型的图查询“查找引用次数最多的前 10 篇文章”将如下所示:
在这里插入图片描述

语义检索示例

“查找关于气候变化的文章”将如下所示:
在这里插入图片描述

混合查询

混合查询可能涉及先执行语义相似性搜索,然后使用语义搜索的结果进行图查询。这在我们希望使用属性图(例如学术图)时特别有用。一个典型的问题是“查找关于气候变化的文章,并返回其作者和机构。”

在这种情况下,我们需要将问题解析为多个子查询,以执行必要的任务。向量搜索在这里作为图查询的上下文使用。因此,我们需要设计一个能够容纳此类上下文的复杂提示模板。

LangGraph 工作流程

我们当前的工作流程将有两个分支(见下图)——一个是使用图模式进行简单图查询检索 QA,另一个是使用向量相似性搜索。要跟随这个工作流程,我创建了一个 GitHub 仓库,其中包含所有用于此实验的代码:我的LangGraph示例(https://github.com/sgautam666/my_graph_blogs/tree/main/neo4j_rag_with_langGraph)。该实验的数据集来自 OpenAlex,该平台提供学术元数据。此外,你还需要一个 Neo4j AuraDB 实例。

一般的工作流程设计如下:

def route_question(state: GraphState):
    print("---ROUTE QUESTION---")    
    question = state["question"]    
    source = question_router.invoke({"question": question})    
    if source.datasource == "vector search":        
        print("---ROUTE QUESTION TO VECTOR SEARCH---")        
        return "decomposer"    
    elif source.datasource == "graph query":     
        print("---ROUTE QUESTION TO GRAPH QA---")        
        return "prompt_template"

workflow = StateGraph(GraphState)

workflow.add_node(PROMPT_TEMPLATE, prompt_template)
workflow.add_node(GRAPH_QA, graph_qa)

workflow.add_node(DECOMPOSER, decomposer)
workflow.add_node(VECTOR_SEARCH, vector_search)
workflow.add_node(PROMPT_TEMPLATE_WITH_CONTEXT, prompt_template_with_context)
workflow.add_node(GRAPH_QA_WITH_CONTEXT, graph_qa_with_context)

workflow.set_conditional_entry_point(
    route_question,    
    {    
        'decomposer': DECOMPOSER,         
        'prompt_template': PROMPT_TEMPLATE     
    },
)

workflow.add_edge(DECOMPOSER, VECTOR_SEARCH)
workflow.add_edge(VECTOR_SEARCH, PROMPT_TEMPLATE_WITH_CONTEXT)
workflow.add_edge(PROMPT_TEMPLATE_WITH_CONTEXT, GRAPH_QA_WITH_CONTEXT)
workflow.add_edge(GRAPH_QA_WITH_CONTEXT, END)

workflow.add_edge(PROMPT_TEMPLATE, GRAPH_QA)
workflow.add_edge(GRAPH_QA, END)

app = workflow.compile()

app.get_graph().draw_mermaid_png(output_file_path="graph.png")

这段代码将生成如下所示的工作流程:

图片

在这个 GraphRAG 流程中,我们的工作流程从一个条件入口点开始,该入口点允许我们决定查询流的路线。在这个例子中,START 节点从用户查询开始。根据查询的不同,信息会流向两侧。如果查询需要查找向量嵌入,它将流向右侧。如果查询是简单的基于图的查询,则工作流程遵循左侧部分。工作流程的左侧基本上是之前讨论的典型图查询,唯一的区别是我们在这里使用了 LangGraph。

让我们看看上面工作流程的右侧。我们从一个 DECOMPOSER 节点开始。该节点基本上将用户问题分解为子查询。假设我们有一个用户问题,要求“查找关于氧化应激的文章。返回最相关文章的标题。”

子查询:

  • • 查找与氧化应激相关的文章——用于向量相似性搜索
  • • 返回最相关文章的标题——用于图 QA 链

你可以理解为什么我们需要分解问题。当将整个用户问题作为输入查询时,图 QA 链会遇到困难。分解是通过使用 GPT-3.5 Turbo 模型和一个基本的提示模板的 query_analyzer 链完成的:

class SubQuery(BaseModel):
    """将给定问题/查询分解为子查询"""    
    
    sub_query: str = Field(   
        ...,        
        description="对原始问题的唯一释义。",    
    )

system = """你是一名专家,能够将用户问题转换为 Neo4j Cypher 查询。
执行查询分解。给定用户问题,将其分解为两个独立的子查询,
你需要回答这些子查询以回答原始问题。

对于给定的输入问题,创建一个用于相似性搜索的查询,并创建一个用于执行 neo4j 图查询的查询。
以下是示例:
问题:查找关于光合作用的文章并返回其标题。
答案:
sub_query1:查找与光合作用相关的文章。
sub_query2:返回文章的标题
"""
prompt = ChatPromptTemplate.from_messages(
    [     
        ("system", system),        
        ("human", "{question}"),    
    ]
)

llm_with_tools = llm.bind_tools([SubQuery])
parser = PydanticToolsParser(tools=[SubQuery])
query_analyzer = prompt | llm_with_tools | parser

向量搜索

右侧分支的另一个重要节点是带有上下文的提示模板。当我们针对属性图进行查询时,如果我们的 Cypher 生成使用图模式,我们将得到期望的结果。通过向量搜索创建上下文,使我们能够将 Cypher 模板聚焦于向量搜索提供的特定节点,从而获得更准确的结果:
在这里插入图片描述

带有上下文的提示模板

我们使用存储的向量嵌入的相似性搜索创建上下文。我们可以生成语义上下文或将节点本身作为上下文。例如,这里我们正在检索表示与用户查询最相似的文章的节点 ID。这些节点 ID 作为上下文传递给我们的提示模板。

一旦捕获上下文,我们还希望确保我们的提示模板获得正确的 Cypher 示例。随着 Cypher 示例的增加,我们可以预期静态提示示例开始变得无关紧要,导致 LLM 处理困难。我们引入了一种动态提示机制,根据相似性选择最相关的 Cypher 示例。我们可以在运行时使用 Chroma 向量存储根据用户查询选择 k 样本。因此,我们的最终提示模板如下所示:
在这里插入图片描述
注意,动态选择的 Cypher 示例通过 suffix 参数传递。最后,我们将模板传递给调用图 QA 链的节点。我们在工作流程的左侧也使用了类似的动态提示模板,但没有上下文。

与典型的 RAG 工作流程不同,在将上下文引入提示模板时,我们通过创建输入变量并在调用模型链(例如 GraphCypherQAChain())时传递这些变量来实现:
在这里插入图片描述
有时通过 LangChain 链传递多个变量会变得更加棘手:
在这里插入图片描述

上述工作流程将不起作用,因为 GraphCypherQAChain() 需要提示模板,而不是提示文本(当你调用链时,提示模板的输出将是文本)。这促使我尝试使用 LangGraph,它似乎可以传递尽可能多的上下文并执行工作流程。

图 QA 链

带有上下文的提示模板之后的最后一步是图查询。从这里开始,典型的图 QA 链用于将提示传递给图数据库以执行查询,并且 LLM 生成响应。请注意,工作流程左侧的类似路径是在提示生成之后。此外,我们还使用类似的动态提示方法在任一侧生成提示模板。

在执行工作流程之前,以下是关于路由链和 GraphState 的一些思考。

路由链

如前所述,我们的工作流程从一个条件入口点开始,该入口点允许我们决定查询流的路线。通过路由链实现这一点,我们使用了一个简单的提示模板和 LLM。Pydantic 模型在这种情况下非常有用:

class RouteQuery(BaseModel):
    """将用户查询路由到最相关的数据源。"""    
    
    datasource: Literal["vector search", "graph query"] = Field(    
        ...,        
        description="给定用户问题选择将其路由到向量存储或图数据库。",    
    )    

llm = ChatOpenAI(temperature=0)
structured_llm_router = llm.with_structured_output(RouteQuery)

system = """你是一名专家,能够将用户问题路由以执行向量搜索或图查询。
向量存储包含与文章标题、摘要和主题相关的文档。以下是三个路由情况:
如果用户问题涉及相似性搜索,请执行向量搜索。用户查询可能包含类似“相似”、“相关”、“相关性”、“相同”、“最近”等术语,表明向量搜索。对于其他情况,请使用图查询。
向量搜索案例的问题示例:
    查找关于光合作用的文章    
    查找与氧化应激相关的类似文章

图数据库查询的问题示例:
    MATCH (n:Article) RETURN COUNT(n)    
    MATCH (n:Article) RETURN n.title

图 QA 链的问题示例:
    查找特定年份发表的文章并返回其标题、作者    
    查找来自位于特定国家(例如日本)的机构的作者
"""

route_prompt = ChatPromptTemplate.from_messages(
    [    
        ("system", system),        
        ("human", "{question}")    
    ]
)

question_router = route_prompt | structured_llm_router

def route_question(state: GraphState):
    print("---ROUTE QUESTION---")    
    question = state["question"]    
    source = question_router.invoke({"question": question})    
    if source.datasource == "vector search":        
        print("---ROUTE QUESTION TO VECTOR SEARCH---")        
        return "decomposer"    
    elif source.datasource == "graph query":    
        print("---ROUTE QUESTION TO GRAPH QA---")        
        return "prompt_template"

GraphState

LangGraph 的一个美妙之处在于信息通过 GraphState 的流动。你需要在 GraphState 中定义所有潜在数据,以便某个节点在任何阶段都可以访问:
在这里插入图片描述

要访问这些数据,你只需在定义节点或任何函数时继承 state。例如:

def prompt_template_with_context(state: GraphState):
    question = state["question"]      
    queries = state["subqueries"]    
     
    prompt_with_context = create_few_shot_prompt_with_context(state)       
     
    return {"prompt_with_context": prompt_with_context, "question":question, "subqueries": queries}

讨论完这些主要话

题后,让我们执行 Neo4j GraphRAG 应用程序。

图 QA:

app.invoke({"question": "查找引用次数最多的前 5 篇文章并返回其标题"})

---ROUTE QUESTION---
---ROUTE QUESTION TO GRAPH QA---

> 正在进入新的 GraphCypherQAChain 链...
> 生成的 Cypher:
> MATCH (a:Article) WITH a ORDER BY a.citation_count DESC RETURN a.title LIMIT 5

> 链完成。

graph_qa_result['documents']

{'query': '查找引用次数最多的前 5 篇文章并返回其标题',
 'result': [
  {'a.title': '从蚯蚓堆肥中分离出的腐殖酸增强了玉米根的根伸长、侧根出现和质膜 H+-ATPase 活性'},  
  {'a.title': '快速估算相对含水量'},  
  {'a.title': 'ARAMEMNON,一个用于阿拉伯芥整合膜蛋白的新数据库'},  
  {'a.title': '植物生理学中的多胺。'},  
  {'a.title': '对阿拉伯芥根和芽中硝酸盐反应的微阵列分析揭示了 1000 多个快速反应基因以及与葡萄糖、海藻糖-6-磷酸、铁和硫酸盐代谢的新联系'}]}

带有向量搜索的图 QA:

app.invoke({"question": "查找关于氧化应激的文章。返回最相关文章的标题"})
---ROUTE QUESTION---
---ROUTE QUESTION TO VECTOR SEARCH---

> 正在进入新的 RetrievalQA 链...

> 链完成。

graph_qa_result['documents']

{'query': '返回最相关文章的标题。',
 'result': [{'a.title': '苔藓和谷类之间对脱落酸和应激的分子反应的保守性'}]}

graph_qa_result.keys()
dict_keys(['question', 'documents', 'article_ids', 'prompt_with_context', 'subqueries'])

graph_qa_result['subqueries']

[SubQuery(sub_query='查找与氧化应激相关的文章。'),
 SubQuery(sub_query='返回最相关文章的标题。')]

如你所见,根据用户问题,我们能够成功地将问题路由到正确的分支并检索到所需的输出。随着复杂性的增加,我们必须修改路由链本身的提示。虽然分解对于像这样的应用程序至关重要,但查询扩展是 LangChain 中的另一个功能,尤其是当有多种方式编写 Cypher 查询以返回类似答案时,这可能也是一个有用的工具。

我们已经涵盖了工作流程中最重要的部分。请跟随我的LangGraph示例(https://github.com/sgautam666/my_graph_blogs/tree/main/neo4j_rag_with_langGraph) 代码库以进行更深入的探索。

总结

这个工作流程结合了许多步骤,而我在这里没有讨论所有步骤。然而,我承认,仅使用 LangChain 构建高级 GraphRAG 应用程序遇到了一些困难。通过使用 LangGraph 解决了这些困难。最让我沮丧的是无法在提示模板中引入所需的多个输入变量,并将该模板传递给 LangChain Expression Language 中的 Graph QA 链。

起初,LangGraph 看起来需要大量的学习,但一旦你克服了这个障碍,它就会变得顺畅起来。未来,我会尝试将代理引入到工作流程中。如果你有任何建议,请与我联系。我正在尽可能多地学习。

如何学习大模型

现在社会上大模型越来越普及了,已经有很多人都想往这里面扎,但是却找不到适合的方法去学习。

作为一名资深码农,初入大模型时也吃了很多亏,踩了无数坑。现在我想把我的经验和知识分享给你们,帮助你们学习AI大模型,能够解决你们学习中的困难。

我已将重要的AI大模型资料包括市面上AI大模型各大白皮书、AGI大模型系统学习路线、AI大模型视频教程、实战学习,等录播视频免费分享出来,需要的小伙伴可以扫取。

一、AGI大模型系统学习路线

很多人学习大模型的时候没有方向,东学一点西学一点,像只无头苍蝇乱撞,我下面分享的这个学习路线希望能够帮助到你们学习AI大模型。

在这里插入图片描述

二、AI大模型视频教程

在这里插入图片描述

三、AI大模型各大学习书籍

在这里插入图片描述

四、AI大模型各大场景实战案例

在这里插入图片描述

五、结束语

学习AI大模型是当前科技发展的趋势,它不仅能够为我们提供更多的机会和挑战,还能够让我们更好地理解和应用人工智能技术。通过学习AI大模型,我们可以深入了解深度学习、神经网络等核心概念,并将其应用于自然语言处理、计算机视觉、语音识别等领域。同时,掌握AI大模型还能够为我们的职业发展增添竞争力,成为未来技术领域的领导者。

再者,学习AI大模型也能为我们自己创造更多的价值,提供更多的岗位以及副业创收,让自己的生活更上一层楼。

因此,学习AI大模型是一项有前景且值得投入的时间和精力的重要选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2078531.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

找搭子的社交软件有哪些?国内靠谱找搭子APP排行榜前10名推荐!

咕哇小程序:这是一个实名制的找搭子交友平台,没错是实名制的,所以骗子会少很多,比较纯粹,是我用得最久且一直在用的找搭子平台。在这个平台上,可以轻松找到兴趣相投的各类搭子,无论是旅行搭子、…

html+css+js网页设计 程序员个人编程学习网站15个页面

htmlcssjs网页设计 程序员个人编程学习网站15个页面 网页作品代码简单,可使用任意HTML编辑软件(如:Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作)。 获取源…

【原创教程】电气制图01:启航EPLAN电气设计

从这篇文章开始,我们将阐述在实际电气制图的过程中,遇到的问题,以及我们应有的思路,从而能够胜任电气设计工作。 我们在接到电气图纸设计的项目时,前期的有效沟通是很关键的,他直接影响到你图纸设计的准确度。 首先,我们需要了解项目的规模、客户的工艺需求、‌预算和…

Meta祭出三篇最详尽Llama微调指南!千字长文,0基础小白必备

如何使用特定领域数据微调LLM,如何确定微调适配自己的用例,以及如何管理良好训练数据集的经验法则。 接下来,直接进入正题。 针对所有自学遇到困难的同学们,我帮大家系统梳理大模型学习脉络,将这份 LLM大模型资料 分享…

蓝牙信令测试(二)

本文BLE测试主要依据RF-PHY.TS.5.0.0协议,且仅包含了NOC(正常工作条件)的不需添加干扰的测试项目。EOC(极限工作条件)需要根据实际情况(温度范围、湿度范围以及电源类型等)而定,干扰信号需要信号发生器,在这里不再展开。 BLE支持三个物理层,如下图: 其中S=2代表2个…

pyro 教程和实例 支持贝叶斯神经网络实现 (pyro 1.8以上的)bnn Bayesian Neural Network pyro ,人工智能

Example: Bayesian Neural Network — NumPyro documentation https://uvadlc-notebooks.readthedocs.io/en/latest/tutorial_notebooks/DL2/Bayesian_Neural_Networks/dl2_bnn_tut1_students_with_answers.html 注意,这些文档,也是有对应的版本的&…

记一次 .NET某实验室自动进样系统 崩溃分析

一:背景 1. 讲故事 前些天有位朋友在微信上联系到我,说他们的程序在客户那边崩掉了,让我帮忙看下怎么回事,dump也拿到了,那就上手分析吧。 二:WinDbg 分析 1. 哪里的崩溃 既然是程序的崩溃&#xff0c…

《黑神话:悟空》中的黑科技及未来高端游戏展望

一、《黑神话:悟空》中的黑科技 1. 虚幻引擎5与次世代渲染技术 《黑神话:悟空》是首批采用虚幻引擎5(Unreal Engine 5)的游戏之一,虚幻引擎5代表了当前游戏引擎技术的顶尖水平。其核心技术“Nanite”和“Lumen”分别…

【学术英语】Unit3:Academic Writing(学术写作)

文章目录 一、摘要1.1 摘要简介1.2 摘要的两种类型1.3 写摘要的技巧1.4 摘要的关键词1.5 例子1.5.1 例子11.5.2 例子2 二、注释和致谢2.1 注释的格式2.1.1 Book2.1.2 论文2.1.3 例子 2.2 致谢的格式 三、书目选编 一、摘要 1.1 摘要简介 摘要对学术写作极其重要和有用&#x…

健康管理小程序怎么做 健康管理小程序系统开发制作方法

很多老板想要做一个自己公司的健康管理小程序,但是不知道该怎么做,本次瀚林就为大家详细介绍一下各种中心小程序系统的开发制作方法为大家做参考。 目前市面上的健康管理有很多类型例如常见的健康管理中心、健康服务平台、健康生活馆、健康科技管家、健康…

mysql定位慢查询和分析

1. 使用工具? 运维工具:skywalking,可以监测出哪个接口,然后找到对应的sql。2. 在调试阶段使用mysql配置? 确保慢查询日志功能已经开启。设置慢查询阈值(可选)。分析慢查询日志。 在调试阶段中…

不使用 JS 纯 CSS 获取屏幕宽高

在现代前端开发中,获取屏幕的宽度和高度通常依赖于 JavaScript。然而现代 CSS 也可以获取到屏幕的宽高,通过自定义属性(CSS Variables)和一些数学函数来实现这一目标。本文将详细解析如何使用 CSS 的 property 规则和一些数学运算…

ToB福音?专业场景数据生成的“大杀器”接连发布

Meta官方发布的LLaMA3.1-405B的各项得分 ©作者|格林 来源|神州问学 最近,AI领域掀起了一股数据合成的热潮,各大厂商最近推出的模型都或多或少有数据合成的影子。英伟达的Nemotron-4-340B-Instruct、微软的Orca-3,以及Meta的Meta-Llama…

三维尺寸公差分析软件哪个最好用?推荐上海棣拓自研软件DTAS

软件简介 国产自研-3D公差分析软件 DTAS 3D (Dimensional Tolerance Analysis System 3D)基于蒙特卡洛原理,按照产品的公差及装配关系进行建模,然后进行解析、仿真计算,最终预测产品设计是否能够满足其关键尺寸要求,同时预测产品…

【Linux操作系统】进程间通信(2)——共享内存

目录 一、共享内存 一、共享内存 共享内存的原理: 进程A把它的数据在地址空间中通过页表映射到物理内存中,进程B通过页表获取物理内存的物理地址,得到数据。 共享内存在物理内存可能有多个,那么两个进程如何确定找到的是同一个共…

深入了解搜索引擎蜘蛛:从定义到最新技术应用

撰写一篇关于搜索引擎蜘蛛的详细文章,需涵盖从基础概念到未来趋势的多个方面。以下是根据您提供的大纲撰写的长篇文章,适合用于了解搜索引擎蜘蛛的重要性及其在现代互联网中的作用。 1. 引言 在互联网的浩瀚世界中,搜索引擎就像是庞大的图书…

Ubuntu 22.04中MySQL 8 设置忽略大小写

Ubuntu 22.04中MySQL 8 设置忽略大小写 一、解决完整流程 //根据官网内容说的大概意思就是不能安装完了修改忽略大小写了,只能在初始化的时候做修改。我用的版本是8.0.39//更新软件包 1、sudo apt update //安装MySQL 如果安装了可以忽略这个步骤 2、sudo apt ins…

【网络】子网掩码

1.IP地址的表示形式 事实上在计算机内部IP地址是32位比特位的数字,为了方便表示,就会采用点分十进制的形式 上面这个是万变不离其中的。 本节重点介绍 IPv4 地址,该地址以四个十进制数字(以句点分隔)的形式表示&#…

spring security 自定义图形验证码(web/前后端分离)

一、准备工作 1.1 导入pom 所需依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.3</version><!-- <version>2.7.18</version>-->&l…

代码随想录算法训练营第13天 |二叉树的学习

目录 二叉树 理论基础 二叉树的分类 1. 满二叉树 (Full Binary Tree) 2. 完全二叉树 (Complete Binary Tree) 3. 平衡二叉树 (Balanced Binary Tree) 5. 二叉搜索树 (Binary Search Tree, BST) 二叉树的存储 1. 链式存储 (Linked Representation) 2. 顺序存储 (Sequent…