type
status
date
slug
summary
tags
category
icon
password
comment
Status
 
使用langchain创建检索增强生成(RAG)应用_哔哩哔哩_bilibili
我们已经介绍了构建基本问答应用程序的步骤:- 使用文档加载器加载数据- 使用文本分割器对索引数据进行分块,以便模型更容易使用- 对数据进行嵌入,并将数据存储在向量数据库中- 根据传入的问题检索先前存储的分块- 使用检索到的分块作为上下文生成答案相关文档https://capricious-sesame-e56.notion.site/RAG-LangChain-08a53958496f4d2091, 视频播放量 1990、弹幕量 0、点赞数 64、投硬币枚数 39、收藏人数 163、转发人数 15, 视频作者 小灰的AI笔记, 作者简介 探索AI的无限可能,相关视频:构建一个基于SQL数据的问答链和Agent智能体,GraphRAG RAG LLM 小白电影教程——二部曲】三上悠亚大模型学习路线+langchain+Agent+Qwen,跟电脑F盘的女神一起操练起来——10,基于Langchain开发并部署一个简单大模型应用,创建一个拥有上下文记忆的RAG 链和agent应用,【2025大模型电影教程】桥本有菜(新有菜) 大模型学习路线 LLM+GPTo3+RAG+LangChain+Agent+Qwen,跟电脑F盘的女神一起操练—9,【KAG】知识增强式生成 - 比RAG更强大的检索与推理框架,【Dify第二大脑】(下) 解说篇 提升 RAG 精度!深入解析混合检索与Rerank | RAG精度优化 | Dify设置 | Rerank模型 | Cros,居然有比LangChain还好用的多Agent应用开发框架?,第一个智能体|Coze AI|2025扣子手把手教程,基于LLamaindex轻松实现RAG多知识库智能问答
使用langchain创建检索增强生成(RAG)应用_哔哩哔哩_bilibili
 
LLM最主要的应用之一是复杂问答(Q&A)聊天机器人。这些应用可以回答关于特定信息来源的问题。这些应用使用一种称为检索增强生成(RAG)的技术。
这个教程将展示如何在文本数据源(本地知识库)上构建一个简单的问答应用程序。在这个过程中,我们将会使用 LangChain 构建本地知识库,并让大模型利用知识库检索的内容,帮助回答用户问题。我们还将使用 LangSmith 帮助我们追踪和理解我们的应用程序。

什么是检索增强生成(RAG)?

RAG是一种用外挂数据增强LLM知识的技术。
LLMs具备推理能力,但它们的知识仅限于接受训练时的公共数据,他缺少最新的实时数据和一些未在互联网公开的数据。如果希望构建的应用程序能够推理个人数据或模型训练数据截止日期之后的数据,您需要用外挂模型缺少的信息,来增强模型的知识。将外来的信息引入并插入到模型提示中的过程称为检索增强生成(RAG)。
LangChain有多个帮助构建问答应用程序和RAG应用程序的组件,让我们可以轻松搭建自己想要的应用程序。
注意:这里我们专注于非结构化数据的问答。如果对在结构化数据上进行检索增强生成感兴趣,请在评论区下留言,我会尽快录制关于在SQL数据上进行问答生成的视频。

概念

一个典型的 RAG 应用有两个重要组成部分:
Indexing(创建索引): 从数据源获取数据并对其生成可检索向量的流程,这通常是离线进行的。
Retrieval and generation(检索到生成): 实际的RAG链(chain),是在运行时接收用户查询并从索引中检索相关数据,然后将其传递给模型。
 
从原始数据到答案输出的最常见的流程如下:

创建索引

  1. Load(加载): 首先需要加载我们的数据。这可以通过文档加载器(DocumentLoaders)来实现。
  1. Split(分割): 使用文本分割器(Text splitters )将大型“文档”拆分成较小的块。这是为了解决两个问题:提高检索效率,解决模型单次传入token数量限制,因为块越大越难以检索,并且无法适应模型的有限上下文窗口。
  1. Store(存储): 我们需要一个地方来存储我们的分割数据,以便以后可以进行搜索。我们可以使用向量数据库(VectorStore)和向量化模型(Embeddings models)来实现这一点。
notion image

检索和生成

  1. Retrieve(检索): 根据用户输入,使用检索器(Retriever)从存储中检索相关的块。
  1. Generate(生成): 通过使用包含用户问题和检索到的数据来组成提示词,ChatModel / LLM 根据提示词生成回答。
notion image

Setup

安装依赖

LangSmith

使用LangChain构建的许多应用程序将都包含多个步骤和多次调用LLM调用。随着这些应用程序变得越来越复杂,能够检查chain或agent内部发生的细节变得至关重要。这样做的最佳方式是使用LangSmith
请注意,LangSmith并非必需,它只是在我们开发调试应用的时候非常有用。如果想使用可以在官网注册后申请秘钥,每个月都会有一定的免费使用额度,足够我们学习和测试,将key设置在的环境变量中就可以轻松使用LangSmith。
notebook中设置

Preview

在本指南中,我们将基于网页上构建一个问答应用程序。
我们可以创建一个简单的本地知识库索引和 RAG 链来实现这个功能,这大约只需要 20 行代码。
  • OpenAI

详细流程

让我们逐步解释上面的代码,以便真正理解代码到底进行了什么操作。

1. 创建索引: 加载数据

加载数据我们可以使用文档加载器(DocumentLoaders)来完成,它们是从源加载数据并返回Documents列表的对象。Document 是一个带有 page_content (str) 和 metadata (dict) 的对象。
对于加载网页内容,我们将使用WebBaseLoader(属于文档加载器的一种),它使用 urllib 从 web URL 加载 HTML,然后使用 BeautifulSoup 将其解析为文本。我们可以通过向 bs_kwargs 传递参数来自定义 HTML -> 文本解析(参见BeautifulSoup文档)。在这种情况下,只有具有“post-content”、“post-title”或“post-header”类的 HTML 标签是相关的,所以我们将删除所有其他标签。
API 调用:WebBaseLoader

DocumentLoader

DocumentLoader: Object that loads data from a source as list of Documents.
  • Docs: 怎样使用文档加载器 DocumentLoaders.

2. 创建索引: 分割

我们的加载文档超过42,000个字符。这太长了,很多模型的上下文窗口无法完全容纳。即使是那些可以完全容纳整篇文章的模型,也很难在非常长的输入中找到信息。
为了处理这个问题,我们将把“文档”分成多个块进行嵌入和向量存储。这样可以帮助我们在运行时只检索出最相关的部分博客文章。
在这种情况下,我们将把我们的文档分成每1000个字符一组,每组之间有200个字符的重叠。重叠有助于减少分离语句与与之相关的重要上下文的可能性。我们使用了RecursiveCharacterTextSplitter,它将递归地使用常见的分隔符(例如换行符)拆分文档,直到每个块的大小合适。这是通用文本用例的推荐文本拆分器。
我们设置 add_start_index=True,这样每个拆分的文档在初始文档中开始的字符索引位置将被保留为元数据 start_index属性。

TextSplitter

TextSplitterDocumentTransformer的子类): 将Document列表分割为更小的块的对象。
DocumentTransformer: 执行对不同类型文档进行切割转换的对象。
  • Docs: 如何使用 DocumentTransformers

3. 创建索引: 存储

现在我们需要为我们的 66 个文本块建立索引,以便我们可以在运行时对它们进行搜索。最常见的做法是嵌入每个文档分割的内容,并将这些嵌入插入到向量数据库(或向量存储)中。当我们想要在我们的分割上搜索时,我们获取一个文本搜索查询,并对其进行嵌入,然后执行某种“相似性”搜索,以识别与我们的查询嵌入最相似的存储分割。最简单的相似度度量是余弦相似度 — 我们测量每对嵌入之间的角的余弦(这些嵌入是高维向量)。
我们可以使用Chroma向量存储和OpenAIEmbeddings模型,在一个方法中完成嵌入和存储所有分割文档。
API 调用:OpenAIEmbeddings

Go deeper

Embeddings: 文本向量嵌入模型的包装器,用于将文本转换为向量。
  • Docs: 文本嵌入详情
  • Integrations: langchain 的向量模型集成,实现了Embeddings 接口的对象
VectorStore: 向量数据库的封装,用于存储和查询向量。
  • Docs: 如何使用向量数据库
  • Integrations: 向量数据库继承,实现了VectorStore 接口的对象
这完成了链的索引部分。此时,我们拥有一个可查询的向量存储,其中包含博客文章内容的切分部分。针对用户提出的问题,理想情况下我们应该能够返回回答问题的博客文章内容。

4. 检索和生成: 检索器

现在让我们编写实际的应用逻辑。我们希望创建一个简单的应用,该应用接收用户提出的问题,搜索与该问题相关的文档,将检索到的文档和初始问题传递给一个模型,并返回一个答案。
首先我们需要定义我们搜索文档的逻辑。LangChain定义了一个Retriever接口,它包装了一个索引,可以根据字符串查询返回相关的文档Documents
向量存储转换为可执行的检索器,最常见的 Retriever 类型是VectorStoreRetriever,它利用向量存储的相似度搜索功能来实现检索。任何 VectorStore 可以轻松地转换为 Retriever 使用VectorStore.as_retriever()

Go deeper

对于检索器(retrieval)向量存储(Vector stores)是最常用的,但是也有其他检索器可以使用。
Retriever: 一个传入查询的内容返回相关文本Document 列表的类
  • Docs: 更多关于创建不同检索器的文档:
    • MultiQueryRetriever 生成输入问题的变形以提高检索命中率。
    • MultiVectorRetriever 生成嵌入向量的变形,也是为了提高检索命中率。
    • Max marginal relevance 选择在检索的文档中相关性和多样性,以避免在重复的上下文中传递。
    • 在矢量存储检索期间,可以使用元数据过滤器对文档进行过滤,例如使用Self Query retriver

5. 检索和生成: 生成器

让我们把所有内容整合成一个链条,以便接收问题,检索相关文档,构建提示,传递给模型,并解析输出。
我们将使用gpt-3.5-turbo OpenAI聊天模型,但可以替换成任何LangChain LLM或ChatModel。
我们使用LangChain封装好的RAG提示词模板。
我们使用LangChain的 LCEL 协议定义一个链
这里是实现:
剖析理解什么是 LCEL
首先:这些组件(retrieverpromptllm等)都是Runnable的实例。这意味着它们实现了相同的方法,比如同步和异步的.invoke.stream,或者.batch,这使它们更容易连接在一起。它们可以通过|操作符连接到RunnabaleSequence(另一个Runnable)。
在遇到|操作符时,LangChain会自动将某些对象转换为Runnable。在这里,format_docs被转换为RunnableLambda,包含contextquestion的字典被转换为RunnableParallel。只需要记住,那就是每个对象都是Runnable
让我们跟踪上面整个链,怎样输入问题到每一步的执行。
正如我们在上面看到的,提示的输入应该是一个带有 "context""question"键的字典。因此,这个链的第一个元素将从输入的问题中拿到这两个值。
  • retriever | format_docs 通过检索器传递问题,生成文档对象,然后传递给 format_docs 生成字符串;
  • RunnablePassthrough() 将输入的问题原封不动地传入进来
That is, if you constructed
那么,chain.invoke(question) 将构建一个格式化的提示词,准备进行推理。(注意:在使用 LCEL 开发时,一步一步拼装子链打印结果,非常有用)
链的最后步骤是 llm,它进行推理,而 StrOutputParser() 则只是从 LLM 的输出消息中提取字符串内容。
可以通过其 LangSmith 追踪来分析此链的各个步骤。

内置链

如果需要,LangChain 包含实现上述 LCEL 的便捷功能。我们组合了两个函数:
  • create_stuff_documents_chain 指定了如何检索上下文填充到提示词模板和 LLM中。在这种情况下,将把 context 内容全部“填充”到提示词中。这就实现了输入键为 context (上下文)和 input (输入问题)从提示词生成到大模型调用的过程。
  • create_retrieval_chain 这个链中,相当于在上面定义的链前面加了个检索本地知识库的节点,将检索到的上下文交给上面定义好的链。它的输入键是 input,并在其输出中包含了 inputcontextanswer
这种方法比上面 LCEL 链的优点是能够同时在输出中拿到contextanswer ,而在 LCEL 链中我们只能拿到相关的输出,需要单独记录相应的上下文。

返回来源

在问答应用中,经常需要向用户展示用于生成答案的信息来源。LangChain 内置的 create_retrieval_chain 将检索到的源文档通过 context 键传递到输出中:

Go deeper

模型选择

ChatModel: 一个 LLM 支持的聊天模型。接收一系列消息并返回一条消息。
LLM: 一个 text-in-text-out LLM。接受一个字符串并返回一个字符串。

自定义模板

如上所示,我们可以从直接加载预先设定的提示词(例如,这个 RAG 提示词)。自定义提示词也很容易:
API 调用:PromptTemplate

总结

我们已经介绍了构建基本问答应用程序的步骤:
  • 使用文本拆分器对索引数据进行分块,以便模型更容易使用
  • 根据传入的问题检索先前存储的分块
  • 使用检索到的分块作为上下文生成答案
 
官网链接:
上一篇
核心概念 | 🦜️🔗 LangChain
下一篇
创建一个拥有上下文记忆的RAG 链和agent应用 | 🦜️🔗 LangChain