构建LangChain应用程序的示例代码:37、基于LangGraph的文档检索与答案生成系统教程

这示例它实现了一个基于LangGraph的系统,用于处理文档检索和生成答案的过程。
好的,我会按照Markdown格式完整翻译并保留文件结构和格式:


! pip install langchain_community tiktoken langchain-openai langchainhub chromadb langchain langgraph tavily-python

CRAG

Corrective-RAG 是一篇最新的论文,介绍了一种有趣的主动 RAG 方法。

该框架根据问题对检索到的文档进行评分:

  1. 正确的文档 -

    • 如果至少有一个文档超过了相关性的阈值,则继续生成。
    • 在生成之前,它会进行知识细化。
    • 这会将文档分成“知识条带”。
    • 它对每个条带进行评分,并过滤掉无关的条带。
  2. 含糊或错误的文档 -

    • 如果所有文档都低于相关性阈值或评分器不确定,则框架会寻找额外的数据源。
    • 它会使用网络搜索来补充检索。
    • 论文中的图表还表明,这里使用了查询重写。

在这里插入图片描述

论文链接:https://arxiv.org/pdf/2401.15884.pdf


让我们使用 LangGraph 从头开始实现这一点。

我们可以做一些简化:

  • 作为初步尝试,让我们跳过知识细化阶段。如果需要,可以将其添加回节点中。
  • 如果任何文档不相关,我们选择使用网络搜索来补充检索。
  • 我们将使用 Tavily Search 进行网络搜索。
  • 我们将使用查询重写来优化网络搜索查询。

设置 TAVILY_API_KEY

检索器

让我们索引3篇博客文章。

from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddingsurls = ["https://lilianweng.github.io/posts/2023-06-23-agent/","https://lilianweng.github.io/posts/2023-03-15-prompt-engineering/","https://lilianweng.github.io/posts/2023-10-25-adv-attack-llm/",
]docs = [WebBaseLoader(url).load() for url in urls]
docs_list = [item for sublist in docs for item in sublist]text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(chunk_size=250, chunk_overlap=0
)
doc_splits = text_splitter.split_documents(docs_list)# Add to vectorDB
vectorstore = Chroma.from_documents(documents=doc_splits,collection_name="rag-chroma",embedding=OpenAIEmbeddings(),
)
retriever = vectorstore.as_retriever()

状态

我们将定义一个图。
我们的状态将是 dict 。
我们可以从任何图形节点 state[‘keys’] 访问它。

from typing import Dict, TypedDictfrom langchain_core.messages import BaseMessageclass GraphState(TypedDict):"""Represents the state of an agent in the conversation.Attributes:keys: A dictionary where each key is a string and the value is expected to be a list or another structurethat supports addition with `operator.add`. This could be used, for instance, to accumulate messagesor other pieces of data throughout the graph."""keys: Dict[str, any]

节点和边

每个 node 将简单地修改 state 。
每个 edge 将选择接下来调用哪个 node 。
它将遵循上面显示的图表。
在这里插入图片描述

import json
import operator
from typing import Annotated, Sequence, TypedDict# 导入langchain相关模块
from langchain import hub
from langchain.output_parsers import PydanticOutputParser
from langchain.output_parsers.openai_tools import PydanticToolsParser
from langchain.prompts import PromptTemplate
from langchain.schema import Document
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_community.vectorstores import Chroma
from langchain_core.messages import BaseMessage, FunctionMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_core.runnables import RunnablePassthrough
from langchain_core.utils.function_calling import convert_to_openai_tool
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langgraph.prebuilt import ToolInvocation### 节点函数 ###def retrieve(state):"""检索文档参数:state (dict): 代理当前状态,包括所有键。返回:dict: 在状态中添加新的键'documents',包含检索到的文档。"""print("---RETRIEVE---")state_dict = state["keys"]question = state_dict["question"]documents = retriever.invoke(question)return {"keys": {"documents": documents, "question": question}}def generate(state):"""生成回答参数:state (dict): 代理当前状态,包括所有键。返回:dict: 在状态中添加新的键'generation',包含生成的回答。"""print("---GENERATE---")state_dict = state["keys"]question = state_dict["question"]documents = state_dict["documents"]# 提示模板prompt = hub.pull("rlm/rag-prompt")# 大语言模型llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0, streaming=True)# 后处理函数def format_docs(docs):return "\n\n".join(doc.page_content for doc in docs)# 链rag_chain = prompt | llm | StrOutputParser()# 运行generation = rag_chain.invoke({"context": documents, "question": question})return {"keys": {"documents": documents, "question": question, "generation": generation}}def grade_documents(state):"""判断检索到的文档是否与问题相关。参数:state (dict): 代理当前状态,包括所有键。返回:dict: 在状态中添加新的键'filtered_documents',包含相关的文档。"""print("---CHECK RELEVANCE---")state_dict = state["keys"]question = state_dict["question"]documents = state_dict["documents"]# 数据模型class grade(BaseModel):"""相关性检查的二进制评分。"""binary_score: str = Field(description="相关性评分 'yes' 或 'no'")# 大语言模型model = ChatOpenAI(temperature=0, model="gpt-4-0125-preview", streaming=True)# 工具grade_tool_oai = convert_to_openai_tool(grade)# 绑定工具和强制调用的语言模型llm_with_tool = model.bind(tools=[convert_to_openai_tool(grade_tool_oai)],tool_choice={"type": "function", "function": {"name": "grade"}},)# 解析器parser_tool = PydanticToolsParser(tools=[grade])# 提示模板prompt = PromptTemplate(template="""你是一个评分员,评估检索到的文档与用户问题的相关性。\n 这是检索到的文档:\n\n {context} \n\n这是用户的问题:{question} \n如果文档包含与用户问题相关的关键词或语义,请评为相关。\n给出一个 'yes' 或 'no' 的二进制评分,表示文档是否与问题相关。""",input_variables=["context", "question"],)# 链chain = prompt | llm_with_tool | parser_tool# 评分filtered_docs = []search = "No"  # 默认不进行网络搜索来补充检索for d in documents:score = chain.invoke({"question": question, "context": d.page_content})grade = score[0].binary_scoreif grade == "yes":print("---GRADE: DOCUMENT RELEVANT---")filtered_docs.append(d)else:print("---GRADE: DOCUMENT NOT RELEVANT---")search = "Yes"  # 进行网络搜索continuereturn {"keys": {"documents": filtered_docs,"question": question,"run_web_search": search,}}def transform_query(state):"""转换查询以生成更好的问题。参数:state (dict): 代理当前状态,包括所有键。返回:dict: 保存新的问题。"""print("---TRANSFORM QUERY---")state_dict = state["keys"]question = state_dict["question"]documents = state_dict["documents"]# 创建一个提示模板,包含格式指令和查询prompt = PromptTemplate(template="""你正在生成一个优化检索的问题。\n 查看输入并试图推理其潜在的语义意图。\n 这是初始问题:\n ------- \n{question} \n ------- \n生成一个改进的问题:""",input_variables=["question"],)# 评分员model = ChatOpenAI(temperature=0, model="gpt-4-0125-preview", streaming=True)# 链chain = prompt | model | StrOutputParser()better_question = chain.invoke({"question": question})return {"keys": {"documents": documents, "question": better_question}}def web_search(state):"""使用Tavily进行网络搜索。参数:state (dict): 代理当前状态,包括所有键。返回:state (dict): 将网络搜索结果附加到文档中。"""print("---WEB SEARCH---")state_dict = state["keys"]question = state_dict["question"]documents = state_dict["documents"]tool = TavilySearchResults()docs = tool.invoke({"query": question})web_results = "\n".join([d["content"] for d in docs])web_results = Document(page_content=web_results)documents.append(web_results)return {"keys": {"documents": documents, "question": question}}### 边函数 ###def decide_to_generate(state):"""决定是生成回答还是重新生成问题。参数:state (dict): 代理当前状态,包括所有键。返回:dict: 在状态中添加新的键'filtered_documents',包含相关的文档。"""print("---DECIDE TO GENERATE---")state_dict = state["keys"]question = state_dict["question"]filtered_documents = state_dict["documents"]search = state_dict["run_web_search"]if search == "Yes":# 所有文档已被过滤# 我们将重新生成一个新的查询print("---DECISION: TRANSFORM QUERY and RUN WEB SEARCH---")return "transform_query"else:# 我们有相关文档,所以生成回答print("---DECISION: GENERATE---")return "generate"import pprint
from langgraph.graph import END, StateGraphworkflow = StateGraph(GraphState)# 定义节点
workflow.add_node("retrieve", retrieve)  # 检索
workflow.add_node("grade_documents", grade_documents)  # 评分文档
workflow.add_node("generate", generate)  # 生成
workflow.add_node("transform_query", transform_query)  # 转换查询
workflow.add_node("web_search", web_search)  # 网络搜索# 构建图
workflow.set_entry_point("retrieve")
workflow.add_edge("retrieve", "grade_documents")
workflow.add_conditional_edges("grade_documents",decide_to_generate,{"transform_query": "transform_query","generate": "generate",},
)
workflow.add_edge("transform_query", "web_search")
workflow.add_edge("web_search", "generate")
workflow.add_edge("generate", END)# 编译
app = workflow.compile()# 运行
inputs = {"keys": {"question": "Explain how the different types of agent memory work?"}}
for output in app.stream(inputs):for key, value in output.items():pprint.pprint(f"Output from node '{key}':")pprint.pprint("---")pprint.pprint(value["keys"], indent=2, width=80, depth=None)pprint.pprint("\n---\n")# 对不在上下文中的问题进行修正
inputs = {"keys": {"question": "What is the approach taken in the AlphaCodium paper?"}}
for output in app.stream(inputs):for key, value in output.items():pprint.pprint(f"Output from node '{key}':")pprint.pprint("---")pprint.pprint(value["keys"], indent=2, width=80, depth=None)pprint.pprint("\n---\n")

扩展知识点:

  1. LangChain:是一个用于构建语言模型应用的Python库,提供了文本分割、文档加载、向量存储、嵌入和检索等功能。
  2. Tavily Search:是一个网络搜索引擎,可以用于补充检索过程中的数据源。
  3. RecursiveCharacterTextSplitter:用于将长文本分割成更小的块,以便更好地处理和索引。
  4. Chroma:是一个向量数据库,可以存储和检索文档的嵌入表示。
  5. OpenAIEmbeddings:使用OpenAI的模型来生成文档的嵌入表示。
  6. StateGraph:是一个用于构建和执行状态图的类,状态图是一种用于控制流程的有向图。

总结:

本文介绍了一个使用LangGraph实现的系统,该系统通过文档检索、文档评估、问题转换和网络搜索等步骤,来生成针对特定问题的答案。系统的核心是一个状态图,它定义了各个节点和边,通过这些节点和边来控制整个检索和生成流程。代码中使用了多个库,包括langchainlangchain_communitylangchain_openai等,这些库为系统提供了文本分割、文档加载、向量存储、嵌入和检索等功能。

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

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

相关文章

怎么做到源代码防泄密?9种方法教会你

源代码加密是一种安全措施,其目的是为了保护软件的源代码不被未授权的个人或实体访问或泄露。源代码是软件应用程序的基础,它包含了程序的逻辑结构、核心算法以及设计理念。由于源代码承载了软件的核心知识和创新,因此它具有极高的商业价值和…

Multisim仿真之万用表、安捷伦万用表、信号发生器操作方法

1、XMM是安捷伦示波器 如下图所示,实物安捷伦的外围3个插孔对应于 XMM图标示波器的右侧3个引脚,上下一一对应 2、函数信号发生器XFG 如下图所示,COM就是GND,正负的意思就是相对于GND而言,有正负电压; 3、…

由于bug造成truncate table卡住问题

客户反应truncate table卡主,检查awr发现多个truncate在awr报告期内一直没执行完,如下: 检查ash,truncate table表的等待事件都是“enq: RO - fast object reuse”和“local write wait” 查找“enq: RO - fast object reuse”&am…

实验1_配置标准IPv4 ACL

1、实验目的 通过本实验可以掌握: IPv4 ACL工作方式和工作过程定义编号和命名的标准IPv4 ACL的方法接口和VTY下应用标准IPv4 ACL的方法 2、实验拓扑 配置IPv4 ACL的实验拓扑如图9-2所示 配置 ACL 实验拓扑如下图所示。本实验中,通过配置标准 ACL 实现…

三星SAINT-D技术引领HBM内存堆叠革命

三星电子即将在今年推出一项革命性技术,允许在CPU或GPU上堆叠高带宽内存(HBM),根据韩国经济日报的报道,这一消息在圣何塞举办的三星晶圆代工论坛2024上公布,并得到业内消息人士证实。这项3D封装技术为2025年…

四十七、openlayers官网示例Image Filters——给地图添加锐化、浮雕、边缘等滤镜效果

官网demo示例: Image Filters 这篇讲的是如何给地图添加滤镜。 一看代码,,好家伙,信息量满满,全都看不懂。。。 咱只能一段一段扒。。。 首先添加一个底图到地图上,这个好理解。 const imagery new Til…

利用C#和Snap7工具模拟S7通信(包含DB地址讲解)

之前写过一篇用KepServerEx做模拟S7的通信数据,参考链接: 通过C#和KepServer完成模拟S7协议通信_c# 与kepserver-CSDN博客 但KepServerEx是收费的,而且模拟的DB块超过64就不行了,当然Snap7在本文中也是只能模拟DB1、DB2和DB3的数…

举个栗子!Tableau 技巧(277):创建径向 WIFI 信号图

之前为大家分享了 🌰:学做径向柱状图(Radial Column Chart)。在此基础上,我们又做了进一步的延展:径向 WIFI 信号图。 它的用法和径向柱状图一致,区别在于它将柱体分切成多个弧线(内…

自监督几何引导:开启单目视觉里程计的鲁棒新时代

论文标题: Self-Supervised Geometry-Guided Initialization for Robust Monocular Visual Odometry 论文作者: Takayuki Kanai, Igor Vasiljevic, Vitor Guizilini, Kazuhiro Shintani 导读: 本文提出了一种用于单目视觉里程计的自监督几…

新火种AI|Sora发布半年之后,AI视频生成领域风云再起

作者:一号 编辑:美美 AI视频最近有些疯狂,Sora可能要着急了。 自OpenAI的Sora发布以来,AI视频生成技术便成为了科技界的热门话题。尽管Sora以其卓越的性能赢得了广泛关注,但其迟迟未能面向公众开放,让人…

[渗透测试学习] BoardLight-HackTheBox

BoardLight-HackTheBox 信息搜集 nmap扫描一下 nmap -sV -v 10.10.11.11扫描结果如下 PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0) 80/tcp open http Apache httpd 2.4.41 ((Ubuntu))80端口有h…

【vue baidu-map】解决更新数据,bm-marker显示不完全问题

实现效果&#xff1a; 问题&#xff1a;切换上面基地tab键&#xff0c;导致地图图标展示不完全&#xff1b;刷新页面就可以正常展示。判断是<bm-marker>标记元素没有动态刷新dom元素引起的问题。 方案&#xff1a;this.$nextTick({}) this.$nextTick(()>{this.equipm…

用户需求分析揭秘:最佳实践与策略

大多数产品团队都有自己处理客户需求的一套流程。但是那些潜在的客户和他们的需求呢&#xff1f;如果在产品管理上已经有一定的资历&#xff0c;很可能对此见惯不怪了。 通常&#xff0c;这些需求是销售人员跑来告诉你的&#xff0c;大概就是说&#xff1a;“超棒的潜在客户一…

Airbind - hackmyvm

简介 靶机名称&#xff1a;Airbind 难度&#xff1a;中等 靶场地址&#xff1a;https://hackmyvm.eu/machines/machine.php?vmAirbind 本地环境 虚拟机&#xff1a;vitual box 靶场IP&#xff08;Airbind&#xff09;&#xff1a;192.168.56.121 跳板机IP(windows 11)&…

Java开发的构建神器:Maven以及如何安装部署Maven

目录 一、Maven引言1.1 Maven的核心概念✍. POM (Project Object Model)✌. 依赖管理✍. 生命周期与构建阶段✌. 插件系统 1.2 Maven的工作流程✍. 读取POM文件&#xff1a;✌. 依赖解析&#xff1a;✍. 构建生命周期&#xff1a;✌. 插件执行&#xff1a;✍. 构建输出&#xf…

C++使用教程

目录 一、软件使用 二、C基础规则补充 关键字 整型取值范围 浮点型取值范围 字符型使用规则 字符串型使用规则 布尔类型 常用的转义移字符 三、数组、函数、指针、结构体补充 1.数组 2.函数 声明&#xff1a; 分文件编写&#xff1a; 值传递&#xff1a; 3.指…

Coursera耶鲁大学金融课程:Financial Markets 笔记Week 02

Financial Markets 本文是学习 https://www.coursera.org/learn/financial-markets-global这门课的学习笔记 这门课的老师是耶鲁大学的Robert Shiller https://en.wikipedia.org/wiki/Robert_J._Shiller Robert James Shiller (born March 29, 1946)[4] is an American econom…

Vue3鼠标悬浮个人头像时出现修改头像,点击出现弹框,上传头像使用cropperjs可裁剪预览

实现效果&#xff1a; 鼠标悬浮到头像上&#xff0c;下方出现修改头像 点击修改头像出现弹框&#xff0c;弹框中可上传头像&#xff0c;并支持头像的裁剪及预览 实现方式&#xff1a; 1.tempalte中 <div class"img-box"><img v-if"avatarImgUrl&qu…

常见的网络设备

引入 园区网络安全部署场景 1、路由器&#xff1a; 跨网段通信设备 。 2、交换机&#xff1a; 同网段或跨网段通信设备。 3、AntiDDoS &#xff1a; DDoS 防御系统&#xff0c;通常旁挂部署于网络出口处&#xff0c; 位于防火墙上游&#xff0c;用于减轻防火墙报文处理负担。 …

CENTOS7.9下服务器双网卡bond模式6配置示例

​1.bond口的特点 bond口通过将多个网口进行聚合&#xff0c;多个网口聚合后一方面实现了大带宽传输&#xff0c;另外多网口聚合后也同时具有冗余特性&#xff0c;当其中一个网口down掉后&#xff0c;其他网口会继续转发流量&#xff0c;不会导致流量中断。 2.使用条件 当环境…