[langchain教程]langchain03——用langchain构建RAG应用

RAG

RAG过程

离线过程:

  1. 加载文档
  2. 将文档按一定条件切割成片段
  3. 将切割的文本片段转为向量,存入检索引擎(向量库)

在线过程:

  1. 用户输入Query,将Query转为向量
  2. 从向量库检索,获得相似度TopN信息
  3. 将检索结果和用户输入,共同组成Prompt
  4. 将Prompt 输入大模型,获取LLM的回复

用langchian构建RAG

1. 加载文档

LangChain中的文档对象Document有两个属性:

  • page_content: str类型,记录此文档的内容。
  • metadata: dict类型,保存与此文档相关的元数据,包括文档ID、文件名等。

在langchain中,加载文档使用 文档加载器DocumentLoader 来实现,它从源文档加载数据并返回文档列表。每个DocumentLoader都有其特定的参数,但它们都可以通过.load()方法以相同的方式调用。

加载pdf

需要先安装pypdf库

pip install pypdf

PyPDFLoader加载pdf,输入是文件路径,输出提取出的内容列表:

from langchain_community.document_loaders import PyPDFLoaderloader = PyPDFLoader(file_path)
pages = []
for page in loader.load():pages.append(page)

返回的文档列表如下所示:

[
Document(metadata={'source': 'D:\\桌面\\RAG分析.pdf', 'page': 0}, page_content='11111111111111111111111111111111111111111111111111111111'), 
Document(metadata={'source': 'D:\\桌面\\RAG分析.pdf', 'page': 1}, page_content='2222222222222222222222222222222222222222222222222222222')
]

加载网页

简单快速的文本提取

对于“简单快速”解析,需要 langchain-community 和 beautifulsoup4 库:

pip install  langchain-community beautifulsoup4

使用WebBaseLoader,输入是url的列表,返回一个 Document 对象的列表,列表里的内容是一系列包含页面文本的字符串。在底层,它使用的是 beautifulsoup4 库。

import bs4
from langchain_community.document_loaders import WebBaseLoaderpage_url = "https://python.langchain.com/docs/how_to/chatbots_memory/"loader = WebBaseLoader(web_paths=[page_url])
docs = loader.load()assert len(docs) == 1
print(docs)

这样提取出的基本上是页面 HTML 中文本的转储,可能包含多余的信息,提取的信息如下所示(截取了首部)

[Document(metadata={'source': 'https://python.langchain.com/', 'title': 'How to add memory to chatbots | 🦜️🔗 LangChain',},page_content='\n\n\n\n\nHow to add memory to chatbots | 🦜️🔗 LangChain\n\n\n\n\n\n\nSkip to main contentJoin us at  ')
]

如果了解底层 HTML 中主体文本的表示,可以通过 BeautifulSoup 指定所需的 <div> 类和其他参数。

下面仅解析文章的主体文本:

loader = WebBaseLoader(web_paths=[page_url],bs_kwargs={"parse_only": bs4.SoupStrainer(class_="theme-doc-markdown markdown"),},bs_get_text_kwargs={"separator": " | ", "strip": True},
)docs = []
async for doc in loader.alazy_load():docs.append(doc)assert len(docs) == 1
doc = docs[0]

提取出的内容如下所示:

[Document(metadata={'source': 'https://python.langchain.com/docs/how_to/chatbots_memory/'}, 	page_content='How to add memory to chatbots | A key feature of chatbots is their ability to use the content of previous conversational turns as context. ')
]

可以使用各种设置对 WebBaseLoader 进行参数化,允许指定请求头、速率限制、解析器和其他 BeautifulSoup 的关键字参数。详细信息参见 API 参考。

高级解析

如果想对页面内容进行更细粒度的控制或处理,可以用langchain-unstructured进行高级解析。

pip install langchain-unstructuredpip install unstructured

注意: 如果不安装unstructured会报错!

下面的代码不是为每个页面生成一个 Document 并通过 BeautifulSoup 控制其内容,而是生成多个 Document 对象,表示页面上的不同结构。这些结构包括章节标题及其对应的主体文本、列表或枚举、表格等。

from langchain_unstructured import UnstructuredLoaderpage_url = "https://python.langchain.com/docs/how_to/chatbots_memory/"
loader = UnstructuredLoader(web_url=page_url)docs = []
for doc in loader.load():docs.append(doc)print(docs[:5])

输出如下所示(太长了,截取了部分):

[Document(metadata={'image_url': 'https://colab.research.google.com/assets/colab-badge.svg', 'link_texts': ['Open In Colab'], 'link_urls': ['https://colab.research.google.com/github/langchain-ai/langchain/blob/master/docs/docs/how_to/chatbots_memory.ipynb'], 'languages': ['eng'], 'filetype': 'text/html', 'url': 'https://python.langchain.com/docs/how_to/chatbots_memory/', 'category': 'Image', 'element_id': '76f10732f139a03f24ecf55613a5116a'}, page_content='Open In Colab'), Document(metadata={'category_depth': 0, 'languages': ['eng'], 'filetype': 'text/html', 'url': 'https://python.langchain.com/docs/how_to/chatbots_memory/','category': 'Title', 'element_id': 'b6bfe64119578f39e0dd7d0287b2964a'}, page_content='How to add memory to chatbots'), Document(metadata={'languages': ['eng'], 'filetype': 'text/html', 'parent_id': 'b6bfe64119578f39e0dd7d0287b2964a', 'url': 'https://python.langchain.com/docs/how_to/chatbots_memory/', 'category': 'NarrativeText', 'element_id': 'ac3524f3e30afbdf096b186a665188ef'},page_content='A key feature of chatbots is their ability to use the content of previous conversational turns as context. This state management can take several forms, including:'), Document(metadata={'category_depth': 1, 'languages': ['eng'], 'filetype': 'text/html', 'parent_id': 'b6bfe64119578f39e0dd7d0287b2964a', 'url': 'https://python.langchain.com/docs/how_to/chatbots_memory/', 'category': 'ListItem', 'element_id': '0c9193e450bacf9a4d716208b2e7b1ee'}, page_content='Simply stuffing previous messages into a chat model prompt.'), 
]

可以用doc.page_content来输出正文文本:

for doc in docs[:5]:print(doc.page_content)

输出如下所示:

Open In Colab
Open on GitHub
How to add memory to chatbots
A key feature of chatbots is their ability to use the content of previous conversational turns as context. This state management can take several forms, including:
Simply stuffing previous messages into a chat model prompt.    

2. 切割文档

当文档过长时,模型存在两方面问题:一是可能无法完整加载至上下文窗口,二是即便能加载,从中提取有用信息也较为困难。

为破解此困境,可将文档拆分成多个块来进行嵌入与存储,如此一来,分块后还能快速精准地定位到与查询最相关的部分。

在具体操作中,可以把文档按照每块 1000 个字符的规格进行拆分,同时在块之间设置 200 个字符的重叠。之所以设置重叠,是为了降低将某块有效信息与其关联的重要上下文被人为割裂开的风险,保证内容的连贯性与完整性。

以下代码采用的文本分割器是 RecursiveCharacterTextSplitter,它会利用常见分隔符对文档进行递归拆分,直至各块大小满足既定要求。此外,代码还启用了 add_start_index=True 的设置,使每个分割后的文档块在初始文档中的字符起始位置得以保留,这一位置信息将以元数据属性 “start_index” 的形式呈现。

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)

3. 将块转为向量存储到数据库

为了在运行时能够对文本块进行高效搜索,我们通常需要对文本块进行索引,而嵌入内容是实现这一目标的常见方法。

我们将每个文档分割后的文本块进行嵌入处理,并将这些嵌入插入到向量数据库中。当要搜索这些分割后的文本块时,可以将文本搜索查询进行嵌入,然后执行“相似性”度量,以识别出与查询嵌入最相似的存储文本块。其中,余弦相似性是一种简单且常用的相似性度量方法,它通过测量每对嵌入(高维向量)之间的角度的余弦值,来评估它们的相似程度。

我们可以通过Chroma 向量存储和 Ollama嵌入模型,完成对所有文档分割后的文本块的嵌入和存储工作。

from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddingsembedding = OllamaEmbeddings(model_url="http://localhost:11434")
vectorstore = Chroma.from_documents(documents=all_splits, embedding=embedding)

4. 检索

创建一个简单的应用程序,接受用户问题,搜索相关文档,并将检索到的文档和初始问题传递给模型以返回答案,具体步骤如下:

  • 定义搜索文档的逻辑:LangChain 定义了一个检索器接口,它封装了一个可以根据字符串返回相关文档的索引查询。检索器的唯一要求是能够接受查询并返回文档,具体的底层逻辑由检索器指定。

  • 使用向量存储检索器:最常见的检索器类型是向量存储检索器,它利用向量存储的相似性搜索能力来促进检索。任何 VectorStore 都可以轻松转换为一个 Retriever,使用 VectorStore.as_retriever() 方法。

retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 6})retrieved_docs = retriever.invoke("What are the approaches to Task Decomposition?")print(retrieved_docs[0].page_content)

输出如下所示:

Tree of Thoughts (Yao et al. 2023) extends CoT by exploring multiple reasoning possibilities at each step. It first decomposes the problem into multiple thought steps and generates multiple thoughts per step, creating a tree structure. The search process can be BFS (breadth-first search) or DFS (depth-first search) with each state evaluated by a classifier (via a prompt) or majority vote.
Task decomposition can be done (1) by LLM with simple prompting like "Steps for XYZ.\n1.", "What are the subgoals for achieving XYZ?", (2) by using task-specific instructions; e.g. "Write a story outline." for writing a novel, or (3) with human inputs.

5. 生成

生成时,将所有内容整合到一个链中,该链接受一个问题,检索相关文档,构建提示,将其传递给模型,并解析输出。

from langchain_community.document_loaders import UnstructuredURLLoader
from langchain_community.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import OllamaEmbeddings
from langchain_community.retrievers import VectorStoreRetriever
from langchain_community.prompts import PromptTemplate
from langchain_community.chains import RetrievalQA# 加载文档
page_url = "https://python.langchain.com/docs/how_to/chatbots_memory/"
loader = UnstructuredURLLoader(urls=[page_url])# 拆分文档
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200, add_start_index=True
)
all_splits = text_splitter.split_documents(loader.load())# 创建向量存储和嵌入
embedding = OllamaEmbeddings(model_url="http://localhost:11434")
vectorstore = Chroma.from_documents(documents=all_splits, embedding=embedding)
retriever = VectorStoreRetriever(vectorstore=vectorstore)# 构建提示和生成链
prompt_template = """Use the following context to answer the question at the end. If the answer isn't found in the context, just say that you don't know.{context}Question: {question}"""
PROMPT = PromptTemplate.from_template(prompt_template)# 创建 RAG 链
rag_chain = RetrievalQA.from_chain_type(retriever=retriever,chain_type="stuff",prompt=PROMPT
)# 执行查询
result = rag_chain.invoke("What are the approaches to Task Decomposition?")
print(result['result'])

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

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

相关文章

C语言复习笔记--字符函数和字符串函数(下)

在上篇我们了解了部分字符函数及字符串函数,下面我们来看剩下的字符串函数. strstr 的使用和模拟实现 老规矩,我们先了解一下strstr这个函数,下面看下这个函数的函数原型. char * strstr ( const char * str1, const char * str2); 如果没找到就返回NULL指针. 下面我们看下它的…

FreeRTOS中的优先级翻转问题及其解决方案:互斥信号量详解

FreeRTOS中的优先级翻转问题及其解决方案&#xff1a;互斥信号量详解 在实时操作系统中&#xff0c;任务调度是基于优先级的&#xff0c;高优先级任务应该优先于低优先级任务执行。但在实际应用中&#xff0c;有时会出现"优先级翻转"的现象&#xff0c;严重影响系统…

深度学习-全连接神经网络

四、参数初始化 神经网络的参数初始化是训练深度学习模型的关键步骤之一。初始化参数&#xff08;通常是权重和偏置&#xff09;会对模型的训练速度、收敛性以及最终的性能产生重要影响。下面是关于神经网络参数初始化的一些常见方法及其相关知识点。 官方文档参考&#xff1…

GIS开发笔记(9)结合osg及osgEarth实现三维球经纬网格绘制及显隐

一、实现效果 二、实现原理 按照5的间隔分别创建经纬线的节点,挂在到组合节点,组合节点挂接到根节点。可以根据需要设置间隔度数和线宽、线的颜色。 三、参考代码 //创建经纬线的节点 osg::Node *GlobeWidget::createGraticuleGeometry(float interval, const osg::Vec4 …

《Relay IR的基石:expr.h 中的表达式类型系统剖析》

TVM Relay源码深度解读 文章目录 TVM Relay源码深度解读一 、从Constant看Relay表达式的设计哲学1. 类定义概述2. ConstantNode 详解1. 核心成员2. 关键方法3. 类型系统注册 3. Constant 详解1. 核心功能 二. 核心内容概述(1) Relay表达式基类1. RelayExprNode 和 RelayExpr 的…

自动驾驶地图数据传输协议ADASIS v2

ADASIS&#xff08;Advanced Driver Assistance Systems Interface Specification&#xff09;直译过来就是 ADAS 接口规格&#xff0c;它要负责的东西其实很简单&#xff0c;就是为自动驾驶车辆提供前方道路交通相关的数据&#xff0c;这些数据被抽象成一个标准化的概念&#…

Flutter 状态管理 Riverpod

Android Studio版本 Flutter SDK 版本 将依赖项添加到您的应用 flutter pub add flutter_riverpod flutter pub add riverpod_annotation flutter pub add dev:riverpod_generator flutter pub add dev:build_runner flutter pub add dev:custom_lint flutter pub add dev:riv…

【EasyPan】MySQL主键与索引核心作用解析

【EasyPan】项目常见问题解答&#xff08;自用&持续更新中…&#xff09;汇总版 MySQL主键与索引核心作用解析 一、主键&#xff08;PRIMARY KEY&#xff09;核心作用 1. 数据唯一标识 -- 创建表时定义主键 CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY,use…

IcePlayer音乐播放器项目分析及学习指南

IcePlayer音乐播放器项目分析及学习指南 项目概述 IcePlayer是一个基于Qt5框架开发的音乐播放器应用程序&#xff0c;使用Visual Studio 2013作为开发环境。该项目实现了音乐播放、歌词显示、专辑图片获取等功能&#xff0c;展现了桌面应用程序开发的核心技术和设计思想。 技…

vscode 打开新页签

目录 vscode 打开新页签 完整settings.json内容&#xff1a; vscode 打开新页签 .vscode目录中 新建settings.json 在 settings.json 文件中&#xff0c;添加或修改以下行&#xff1a; json "workbench.editor.enablePreview": false 这将禁用预览模式&#xff0…

C语言高频面试题——常量指针与指针常量区别

1. 常量指针&#xff08;Pointer to Constant&#xff09; 定义&#xff1a; 常量指针是指向一个常量数据的指针&#xff0c;即指针指向的内容不能通过该指针被修改。 语法&#xff1a; const int* ptr;或者&#xff1a; int const* ptr;解释&#xff1a; const修饰的是指…

c++基础·列表初始化

目录 一、列表初始化的核心优势 二、基础数据类型与数组初始化 1. 基础类型初始化 2. 数组初始化 三、类与结构体初始化 1. 构造函数匹配规则 2. 注意事项 四、标准容器初始化 五、聚合类型&#xff08;Aggregate Types&#xff09;初始化 1. 聚合类型定义 2. 初始化…

数据分析与产品、运营、市场之间如何有效对齐

数据分析的重要性在于它能够将海量的原始信息转化为可操作的洞察。以产品开发为例,通过用户行为数据的分析,产品经理可以清晰了解哪些功能被频繁使用,哪些设计导致用户流失,从而优化迭代方向。运营团队则依靠数据分析来监控供应链效率、预测需求波动,甚至通过实时数据调整…

[C]基础11.深入理解指针(3)

博客主页&#xff1a;向不悔本篇专栏&#xff1a;[C]您的支持&#xff0c;是我的创作动力。 文章目录 0、总结1、字符指针变量2、数组指针变量2.1 数组指针变量是什么&#xff1f;2.2 数组指针变量怎么初始化&#xff1f; 3、二维数组传参的本质4、函数指针变量4.1 函数指针变量…

【漏洞复现】CVE-2024-38856(ApacheOfbiz RCE)

【漏洞复现】CVE-2024-38856&#xff08;ApacheOfbiz RCE&#xff09; 1. 漏洞描述 Apache OFBiz 是一个开源的企业资源规划&#xff08;ERP&#xff09;系统。它提供了一套企业应用程序&#xff0c;用于集成和自动化企业的许多业务流程。 这个漏洞是由于对 CVE-2023-51467 的…

C++入门小馆: 深入string类(二)

嘿&#xff0c;各位技术潮人&#xff01;好久不见甚是想念。生活就像一场奇妙冒险&#xff0c;而编程就是那把超酷的万能钥匙。此刻&#xff0c;阳光洒在键盘上&#xff0c;灵感在指尖跳跃&#xff0c;让我们抛开一切束缚&#xff0c;给平淡日子加点料&#xff0c;注入满满的pa…

【nginx】服务的信号控制

目录 1. 说明2. 常用信号及作用3. 信号控制的具体操作3.1 获取 Nginx 主进程 PID3.2 发送信号 4. 应用场景4.1 重新加载配置文件4.2 日志切割 5. 平滑升级 Nginx6. 注意事项 1. 说明 1.Nginx 的信号控制是其管理服务的重要机制&#xff0c;通过向主进程发送特定信号&#xff0…

Ubuntu下展锐刷机工具spd_dump使用说明

spd_dump使用说明 源码地址&#xff1a;https://github.com/ilyakurdyukov/spreadtrum_flash 编译环境准备&#xff1a; sudo apt update sudo apt install git sudo apt install build-essential sudo apt install libusb-1.0-0-devIf you create /etc/udev/rules.d/80-spd…

鸿蒙NEXT开发LRUCache缓存工具类(单例模式)(ArkTs)

import { util } from kit.ArkTS;/*** LRUCache缓存工具类&#xff08;单例模式&#xff09;* author 鸿蒙布道师* since 2025/04/21*/ export class LRUCacheUtil {private static instance: LRUCacheUtil;private lruCache: util.LRUCache<string, any>;/*** 私有构造函…

笔记:react中 父组件怎么获取子组件中的属性或方法

在子组件中我们可以使用下面两个方法去暴露你所要放行的属性或方法&#x1f447; 1.useImperativeHandle 2.orwardRef 搭配使用例子 import React, { useState, forwardRef, useImperativeHandle } from "react"function Son(props, ref) {const [data] useStat…