LangChain入门2 RAG详解

ops/2024/12/22 21:45:10/

RAG概述

一个典型的RAG应用程序,它有两个主要组件:

  • 索引:从源中获取数据并对其进行索引的管道。这通常在脱机情况下发生。
  • 检索和生成:在运行时接受用户查询,并从索引中检索相关数据,然后将其传递给模型。

从原始数据到答案的完整序列如下所示:

索引

  • 加载:首先我们需要加载我们的数据。我们将为此使用DocumentLoaders。
  • 拆分:文本拆分器将大型文档拆分成更小的块。这对于索引数据和将数据传递给模型都很有用,因为大块更难搜索,也不适合模型的有限上下文窗口。
  • 信息存储:我们需要一个地方来存储和索引我们的拆分,以便以后可以搜索它们。这通常使用VectorStore和Embeddings模型来完成。

检索和生成

  • 检索:给定用户输入,使用Retriever从存储中检索相关拆分。
  • 生成:ChatModel/LLM使用包含问题和检索到的数据的提示生成答案。

代码实例

依赖加载

from langchain_community.llms import Ollama
import bs4
from langchain import hub
from langchain_community.document_loaders import WebBaseLoader
from langchain_chroma import Chroma
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_community.embeddings import OllamaEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
#实例化大模型
llm = Ollama(model="llama2")
#添加向量化
embeddings = OllamaEmbeddings()
# 加载数据
loader = WebBaseLoader(web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",),bs_kwargs=dict(parse_only=bs4.SoupStrainer(class_=("post-content", "post-title", "post-header"))),
)
docs = loader.load()
#我们这里查看具体的下载内容
print(docs)

查看具体的下载内容
在这里插入图片描述
加载数据的拆分和灌库

#添加数据的拆分 每1000个为一组并重叠200个字符
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
#拆分下载的数据
splits = text_splitter.split_documents(docs)
#灌入向量数据库
vectorstore = Chroma.from_documents(documents=splits, embedding=embeddings)

构建检索生成

retriever = vectorstore.as_retriever()
#下载预制的提示词
prompt = hub.pull("rlm/rag-prompt")#调整文本
def format_docs(docs):return "\n\n".join(doc.page_content for doc in docs)

加载数据链

#RunnablePassthrough是Langchain库中的一个类,它允许您传递未更改的输入或带有附加键的输入。
#它可以与RunnableParallel一起使用,将数据传递到映射中的新键。它还可以用于通过assign()方法向链状态添加值
rag_chain = ({"context": retriever | format_docs, "question": RunnablePassthrough()} #检索加强| prompt #提示词| llm #大模型| StrOutputParser() #输出结果样式定义
)
#进行对话
rag_chain.invoke("What is Task Decomposition?")

返回内容
在这里插入图片描述

详细说明

首先加载博客文章的内容。我们可以为此使用DocumentLoaders,它们是从源加载数据并返回文档列表的对象。Document是一个具有一些page_content(str)和元数据(dict)的对象。

在这种情况下,我们将使用WebBaseLoader,它使用urllib从web URL加载HTML,并使用BeautifulSoup将其解析为文本。我们可以通过bs_kwargs将参数传递给BeautifulSoup解析器来自定义HTML->文本解析(请参阅Beautiful Soup文档)。在这种情况下,只有类为“post-content”、“posttitle”或“post-header”的HTML标记是相关的,所以我们将删除所有其他标记。

数据加载

import bs4
from langchain_community.document_loaders import WebBaseLoader# Only keep post title, headers, and content from the full HTML.
bs4_strainer = bs4.SoupStrainer(class_=("post-title", "post-header", "post-content"))
loader = WebBaseLoader(web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",),bs_kwargs={"parse_only": bs4_strainer},
)
docs = loader.load()
print(docs)

在这里插入图片描述

print(len(docs[0].page_content))
#43131
print(docs[0].page_content[:500])
"""LLM Powered Autonomous AgentsDate: June 23, 2023  |  Estimated Reading Time: 31 min  |  Author: Lilian WengBuilding agents with LLM (large language model) as its core controller is a cool concept. Several proof-of-concepts demos, such as AutoGPT, GPT-Engineer and BabyAGI, serve as inspiring examples. The potentiality of LLM extends beyond generating well-written copies, stories, essays and programs; it can be framed as a powerful general problem solver.
Agent System Overview#
In
"""

文本拆分

from langchain_text_splitters import RecursiveCharacterTextSplittertext_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200, add_start_index=True
)
all_splits = text_splitter.split_documents(docs)

RecursiveCharacterTextSplitter 通过递归地查看字符来拆分文本。 递归地尝试按不同的字符进行拆分,以找到一个有效的字符。 创建一个新的TextSplitter。

print(len(all_splits))
#66
print(len(all_splits[0].page_content))
#969
print(all_splits[10].metadata)
#{'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/',
#'start_index': 7056}

索引:存储

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

from langchain_chroma import Chroma
from langchain_community.embeddings import OllamaEmbeddings
#添加向量化
embeddings = OllamaEmbeddings()
#灌库
vectorstore = Chroma.from_documents(documents=all_splits, embedding=embeddings)

检索与生成:检索

现在让我们来编写实际的应用程序逻辑。我们想要创建一个简单的应用程序,该应用程序接受用户问题,搜索与该问题相关的文档,将检索到的文档和初始问题传递给模型,并返回答案。

首先,我们需要定义搜索文档的逻辑。LangChain定义了一个Retriever接口,该接口封装了一个索引,该索引可以在给定字符串查询的情况下返回相关文档。

最常见的Retriever类型是VectorStoreRetriever,它使用向量存储的相似性搜索功能来促进检索。使用VectorStore.as_Retriever(),任何VectorStore都可以很容易地转换为Retriever:

"""
矢量存储是用于有效存储和查询矢量嵌入的数据结构,矢量嵌入是数据点的高维数值表示。向量存储通常用于机器学习应用程序中的任务,如相似性搜索、异常检测和聚类。
在Langchain的上下文中,VectorStore类是用于处理向量存储的接口。它提供了添加和查询矢量的方法,以及执行各种操作的方法,例如计算矢量之间的距离和根据某些标准过滤矢量。
"""
retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 6})
retrieved_docs = retriever.invoke("What are the approaches to Task Decomposition?")
print(len(retrieved_docs))
#6
print(retrieved_docs[0].page_content)

在这里插入图片描述

检索与生成

from langchain import hub
#加载的提示词
prompt = hub.pull("rlm/rag-prompt")example_messages = prompt.invoke({"context": "filler context", "question": "filler question"}
).to_messages()
print(example_messages)

返回

[HumanMessage(content=“You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don’t know the answer, just say that you don’t know. Use three sentences maximum and keep the answer concise.\nQuestion: filler question \nContext: filler context \nAnswer:”)]

print(example_messages[0].content)

You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don’t know the answer, just say that you don’t know. Use three sentences maximum and keep the answer concise.
Question: filler question
Context: filler context

LCEL

我们将使用LCEL Runnable协议来定义链,使我们能够以透明的方式将组件和函数管道连接在一起,在LangSmith中自动跟踪我们的链,从而获得流式、异步和批量调用

from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthroughdef format_docs(docs):return "\n\n".join(doc.page_content for doc in docs)rag_chain = ({"context": retriever | format_docs, "question": RunnablePassthrough()}| prompt| llm| StrOutputParser()
)
for chunk in rag_chain.stream("What is Task Decomposition?"):print(chunk, end="", flush=True)

在这里插入图片描述

模型选择

#构建提示词模版
from langchain_core.prompts import PromptTemplate
#提示词
template = """Use the following pieces of context to answer the question at the end.
If you don't know the answer, just say that you don't know, don't try to make up an answer.
Use three sentences maximum and keep the answer as concise as possible.
Always say "thanks for asking!" at the end of the answer.{context}Question: {question}Helpful Answer:"""
#模版加载
custom_rag_prompt = PromptTemplate.from_template(template)rag_chain = ({"context": retriever | format_docs, "question": RunnablePassthrough()}| custom_rag_prompt| llm| StrOutputParser()
)
#结果输出
rag_chain.invoke("What is Task Decomposition?")

在这里插入图片描述
以上是整体使用LangChain 构建RAG 的总体流程。
感谢阅读。


http://www.ppmy.cn/ops/26179.html

相关文章

python Django 的内置权限系统或自定义模型来存储更复杂的角色和权限关系

在 Django 中,管理用户权限和角色通常涉及到使用 Django 的内置权限系统或自定义模型来存储更复杂的角色和权限关系。下面是一个基本的指南,说明如何在 Django 中为后台管理系统分配权限并将其保存在数据库中,同时结合 Vue.js 和 Element UI 作为前端框架。 后端(Django)…

c++11 标准模板(STL)本地化库 - 平面类别(time_get) - 从输入字符序列中解析时间/日期值到 std::tm 中(一)

本地化库 本地环境设施包含字符分类和字符串校对、数值、货币及日期/时间格式化和分析,以及消息取得的国际化支持。本地环境设置控制流 I/O 、正则表达式库和 C 标准库的其他组件的行为。 平面类别 从输入字符序列中解析时间/日期值到 std::tm 中 std::time_get …

深入剖析Tomcat(五) 剖析Servlet容器并实现一个简易Context与Wrapper容器

上一章介绍了Tomcat的默认连接器,后续程序都会使用默认连接器。前面有讲过Catalina容器的两大块内容就是连接器与Servlet容器。不同于第二章的自定义丐版Servlet容器,这一章就来探讨下Catalina中的真正的Servlet容器究竟长啥样。 四种容器 在Catalina中…

短视频生成背景文字工具(前端工具)

过年这两天有些无聊就刷刷抖音,刷着刷着自己也蠢蠢欲动,想发上几个,可是却找不到合适自己的模板。由于个人喜欢一些古诗文之类的,所以自己简单的编写了一个小工具,如下图: 当设置好了之后,将浏…

web server apache tomcat11-30-The Tomcat JDBC Connection Pool

前言 整理这个官方翻译的系列,原因是网上大部分的 tomcat 版本比较旧,此版本为 v11 最新的版本。 开源项目 从零手写实现 tomcat minicat 别称【嗅虎】心有猛虎,轻嗅蔷薇。 系列文章 web server apache tomcat11-01-官方文档入门介绍 web…

香港BGP服务器和香港双线服务器的区别

香港BGP服务器和香港双线服务器在网络架构和连接方式上有一些区别,主要体现在以下几个方面: 网络连接方式: 香港BGP服务器采用BGP(边界网关协议)技术,通常连接到多个不同的网络服务提供商(ISP),并通过BGP协议动态选择最…

图计算浅谈:主流图存储引擎/图搜索算法

在数据关联与复杂网络越来越突显其价值的今日,图数据库(Graph Database)逐渐成为在大数据领域不可或缺的一部分。图数据库强调数据项之间的关系,它不仅能存储大量的顶点(Vertex)和边(Edge&#…

QT - 创建Qt Widgets Application项目

在Qt中结合OpenGL使用,可以创建一个Qt Widgets应用程序项目。在创建项目时,您可以选择使用OpenGL模板来生成一个已经集成了OpenGL的项目。这个模板会自动帮助您集成OpenGL和Qt,并生成一个基本的OpenGL窗口。您可以在这个窗口中进行OpenGL的开…