最新LangChain+GLM4开发AI应用程序系列(三):RAG检索增强生成篇

news2024/9/24 15:23:22

最新LangChain+GLM4开发AI应用程序系列(三):RAG检索增强生成篇

  • 一、前言
  • 二、RAG介绍
    • 1、文档加载器
    • 2、文本分割器
    • 3、嵌入模型
    • 4、向量数据库
  • 三、RAG开发案例
    • 1、创建智谱GLM4大模型对象
    • 2、加载文档
    • 3、文本分割
    • 4、向量化存储
    • 5、向量库检索
    • 6、生成问答结果
  • 四、总结

一、前言

上两篇文章分别介绍了LangChain的快速入门和Agent智能体开发。在LLM的实际应用场景中,经常会需要用到特定领域用户的数据,但这些数据不属于模型训练集的一部分,要实现这一需求,最好的方法是通过检索增强生成(RAG)。在用户提问时,先检索特定的外部数据,把检索结果作为上下文传递给LLM,以便大模型返回更精准的结果。今天我们就带大家了解下在LangChain里RAG的使用,结合智谱AI GLM4大模型开发一个基于特定知识库的智能问答应用。

二、RAG介绍

LangChain中提供了RAG应用程序的所有构建模块。结构图如下: image

1、文档加载器

文档加载器用来加载各种不同类型的文档。LangChain提供了100 多种不同的文档加载器,可以加载各种类型文档,包括:CSV、HTML、JSON、Markdown、PDF、DOC、XLS、图片、视频、音频等等。具体的文档加载器参考:https://python.langchain.com/docs/integrations/document_loaders/

2、文本分割器

加载文档后,对于长文档,需要分割成更小的块,以适合LLM的上下文窗口。LangChain 有许多内置的文档转换器,可以轻松地拆分、组合、过滤和以其他方式操作文档。
LangChain 提供了多种不同类型的文本分割器。下表列出了所有这些以及一些特征:
类型:文本分割器的类型
分割依据:此文本拆分器如何拆分文本
添加元数据:此文本拆分器是否添加有关每个块来自何处的元数据。
描述:分离器的描述,包括有关何时使用它的建议。

类型分割依据添加元数据描述
Recursive用户定义的字符列表 递归地分割文本。递归地分割文本的目的是尝试使相关的文本片段彼此相邻。这是开始分割文本的推荐方法。
HTMLHTML 特定字符根据 HTML 特定字符分割文本。值得注意的是,这添加了有关该块来自何处的相关信息(基于 HTML)
MarkdownMarkdown 特定字符根据 Markdown 特定字符分割文本。值得注意的是,这添加了有关该块来自何处的相关信息(基于 Markdown)
Code代码(Python、JS)特定字符 根据特定于编码语言的字符分割文本。有 15 种不同的语言可供选择。
TokenTokens 基于tokens分割文本。有几种不同的方法来衡量tokens.
Character用户定义的字符 根据用户定义的字符分割文本。比较简单的方法之一。
[Experimental] Semantic Chunker句子 首先对句子进行分割。然后,如果它们在语义上足够相似,则将它们相邻地组合起来。

3、嵌入模型

嵌入(Embedding)是一种将单词、短语或整个文档转换为密集向量的技术。每个单词或短语被转换成一组数字,这组数字捕捉了该文本的某些语义特征。
嵌入模型通过将文本转换为计算机可以处理的数值形式(即向量),使得计算机能够理解和处理自然语言。通过嵌入捕获文本的语义,可以快速有效地找到文本的其他相似部分。
LangChain提供了超过25种不同嵌入提供商和方法的集成,可以参考:https://python.langchain.com/docs/integrations/text_embedding
抱脸上有个中文、英文嵌入模型排行榜,可以作为参考,具体还要选适合自己的,比如开源的,对中文支持比较好的等等:https://huggingface.co/spaces/mteb/leaderboard

4、向量数据库

有了嵌入模型对数据的向量化,就需要数据库来支持这些嵌入数据的高效存储和搜索。LangChain提供了50多种不同向量数据库的集成,可以参考:https://python.langchain.com/docs/integrations/vectorstores
目前Milvus评分最高。

三、RAG开发案例

接下来我们实战开发一个基于特定知识库的智能问答应用。还是在Jupyter Notebook环境中执行。

1、创建智谱GLM4大模型对象

首先定义LangChain里智谱大模型的包装类,参考第一篇文章里有,或者从github上下载:https://github.com/taoxibj/docs/blob/main/zhipuai.py
创建大模型对象

# 填写您自己的APIKey
ZHIPUAI_API_KEY = "..."
llm = ChatZhipuAI(
    temperature=0.1,
    api_key=ZHIPUAI_API_KEY,
    model_name="glm-4",
)

2、加载文档

这里特定领域用户的数据来源于一个示例的ordersample.csv文件,这个文件可以从我的github上获取:https://github.com/taoxibj/docs/blob/main/ordersample.csv
文件具体内容如下:
image
把orersample.csv下载到jupyter notebook当前ipynb文件目录,使用CSV文档加载器,加载文档内容:

from langchain_community.document_loaders.csv_loader import CSVLoader

loader = CSVLoader(file_path='ordersample.csv')
data = loader.load()

打印加载后的文档内容:

from pprint import pprint
pprint(data)

按一行一个文档,分成了6个文档。

[Document(page_content='\ufeff订单ID: 123456\n订单金额: 100.18\n收货人姓名: 李四同\n收货人电话: 1234567890\n收货地址: 北京市海淀区\n图片: https://qnssl.niaogebiji.com/152701334564123796dd0f43.47234870.png', metadata={'source': 'ordersample.csv', 'row': 0}),
 Document(page_content='\ufeff订单ID: 111222\n订单金额: 367.58\n收货人姓名: 王五一\n收货人电话: 1314567890\n收货地址: 北京市朝阳区\n图片: ', metadata={'source': 'ordersample.csv', 'row': 1}),
 Document(page_content='\ufeff订单ID: 333444\n订单金额: 1234.5\n收货人姓名: 张三丰\n收货人电话: 1331112390\n收货地址: 北京市西城区\n图片: ', metadata={'source': 'ordersample.csv', 'row': 2}),
 Document(page_content='\ufeff订单ID: 123458\n订单金额: 120.18\n收货人姓名: 李四同\n收货人电话: 1234567890\n收货地址: 北京市海淀区\n图片: https://qnssl.niaogebiji.com/152701334564123796dd0f43.47234870.png', metadata={'source': 'ordersample.csv', 'row': 3}),
 Document(page_content='\ufeff订单ID: 119822\n订单金额: 267.58\n收货人姓名: 王五一\n收货人电话: 1314567890\n收货地址: 北京市朝阳区\n图片: ', metadata={'source': 'ordersample.csv', 'row': 4}),
 Document(page_content='\ufeff订单ID: 329444\n订单金额: 234.5\n收货人姓名: 张三丰\n收货人电话: 1331112390\n收货地址: 北京市西城区\n图片: ', metadata={'source': 'ordersample.csv', 'row': 5})]

3、文本分割

通常可以使用递归字符文本分割器对文档对象进行分割,可以设置分割块字符的大小,和重合字符大小。因为我的文档对象比较小,所以每个文档基本上没有达到要切分的大小阈值,可以尝试把文档内容变多,试下效果。

from langchain.text_splitter import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000, chunk_overlap=200, add_start_index=True
)
all_splits = text_splitter.split_documents(data)

可以打印一下分割后块的数量。

len(all_splits)

6

4、向量化存储

接下来我们首先使用嵌入模型对分割后的文档进行向量化,然后存储到向量数据库中。
嵌入模型使用的是bge-large-zh-v1.5,这是BAAI北京智源人工智能研究院开源的,支持中、英文的嵌入模型,在开源嵌入模型中算是效果比较好的一个。
向量数据库使用的是FAISS,是FAIR(Meta人工智能实验室)开源的向量数据库。可以在本地使用,相对轻量些,没有Milvus那么强, 但在本地环境使用也够用了。

from langchain_community.embeddings import HuggingFaceBgeEmbeddings

model_name = "BAAI/bge-large-zh-v1.5"
model_kwargs = {"device": "cpu"}
encode_kwargs = {"normalize_embeddings": True}
bgeEmbeddings = HuggingFaceBgeEmbeddings(
    model_name=model_name, model_kwargs=model_kwargs, encode_kwargs=encode_kwargs
)
from langchain_community.vectorstores import FAISS
vector = FAISS.from_documents(all_splits, bgeEmbeddings)

5、向量库检索

接下来尝试下使用向量库进行检索。

retriever = vector.as_retriever(search_type="similarity", search_kwargs={"k": 3})
retriever.invoke("收货人姓名是张三丰的,有几个订单?金额分别是多少,总共是多少?")

检索结果如下。

[Document(page_content='\ufeff订单ID: 329444\n订单金额: 234.5\n收货人姓名: 张三丰\n收货人电话: 1331112390\n收货地址: 北京市西城区\n图片:', metadata={'source': 'ordersample.csv', 'row': 5, 'start_index': 0}),
 Document(page_content='\ufeff订单ID: 333444\n订单金额: 1234.5\n收货人姓名: 张三丰\n收货人电话: 1331112390\n收货地址: 北京市西城区\n图片:', metadata={'source': 'ordersample.csv', 'row': 2, 'start_index': 0}),
 Document(page_content='\ufeff订单ID: 123456\n订单金额: 100.18\n收货人姓名: 李四同\n收货人电话: 1234567890\n收货地址: 北京市海淀区\n图片: https://qnssl.niaogebiji.com/152701334564123796dd0f43.47234870.png', metadata={'source': 'ordersample.csv', 'row': 0, 'start_index': 0})]

6、生成问答结果

使用检索链,串联向量库检索和大模型,根据用户的提问,生成问答结果。

from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_template("""仅根据所提供的上下文回答以下问题:

<context>
{context}
</context>

问题: {question}""")

from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
retriever_chain = (
    {"context": retriever , "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

可以开始进行提问。
提问一:

retriever_chain.invoke("订单ID是123456的收货人是谁,电话是多少?")

回答:

'根据所提供的上下文,订单ID为123456的收货人是李四同,电话是1234567890。'

提问二:

retriever_chain.invoke("收货人张三丰有几个订单?金额分别是多少,总共是多少?")

回答:

'收货人张三丰有两个订单。第一个订单的订单ID是329444,金额是234.5元;第二个订单的订单ID是333444,金额是1234.5元。这两个订单的总金额是1469元。'

提问三:

retriever_chain.invoke("收货地址是朝阳区的有哪些订单?")

回答:

'收货地址是朝阳区的订单有:\n\n1. 订单ID: 119822,订单金额: 267.58,收货人姓名: 王五一,收货人电话: 1314567890,收货地址: 北京市朝阳区。\n2. 订单ID: 111222,订单金额: 367.58,收货人姓名: 王五一,收货人电话: 1314567890,收货地址: 北京市朝阳区。\n\n以上两个订单的收货地址均为北京市朝阳区。'

整体的回答结果还算准确。但是也有一些情况,回答的不对的,基本上是向量库检索出的答案有出入造成的问题,所以语料知识的向量化和结构化,还是很重要的。

四、总结

这篇文章首先介绍了在LangChain中使用RAG的重要组成部分,包括文档加载器、文本分割器、嵌入模型、向量数据库,然后通过一个示例演示了LLM如何通过RAG检索增强生成的方式,借助特定领域用户数据,更准确的回答用户的提问。
对于RAG来说,语料的结构化是很重要的,后面也会深入分析一些文档材料如DOC、XLS、PDF的结构化以及如何更好的进行切分来获取更精准的答案。敬请期待。

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

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

相关文章

初始网络 --- 网络基础

目录 0、 前言 1、 计算机网络发展背景 1.1. 局域网(LAN) && 广域网(WAN) 2、 认识并理解协议 3、 初始网络协议 3.1. 协议分层 4、 TCP/IP 五层(或四层)模型 4.1. 简单了解TCP/IP层状体系 4.2. TCP/IP协议层状结构和计算机层状结构的关系 5、 OSI七层模型 …

程序员如何选择职业赛道:探索未知,寻找激情

作为程序员&#xff0c;我们时常面临职业选择的难题。在这个充满变革的行业中&#xff0c;如何选择适合自己的职业赛道成为了我们关注的焦点。本文将探讨程序员如何选择职业赛道&#xff0c;帮助你找到适合自己的发展方向。 一、认识自己的兴趣和激情 首先&#xff0c;我们需要…

为什么要用云手机进行国外社媒监控?

随着全球化的不断发展&#xff0c;社交媒体已成为企业推动全球品牌知名度和业务流量的关键渠道。在这个数字时代&#xff0c;云手机作为一种强大的工具&#xff0c;为国外社交媒体监控提供了全新的可能性。在这篇文章中&#xff0c;我们将探讨使用云手机进行国外社媒监控的重要…

RHCSA练习2

一、实验题目 1、文件查找 &#xff08;1&#xff09;在当前目录及子目录中&#xff0c;查找小写字母开头的txt文件 [rootroot ~]# cd /etc [rootroot etc]# find . -type f -name [a-z]*.txt &#xff08;2&#xff09;在/etc及其子目录中&#xff0c;查找host开头的文件 …

Java解决杨辉三角

Java解决杨辉三角 01 题目 给定一个非负整数 *numRows&#xff0c;*生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]示例 2: 输入: numRo…

人工智能-飞桨

文章目录 概要安装零基础教程基础知识小结 概要 集核心框架、基础模型库、端到端开发套件、丰富的工具组件于一体的深度学习平台 官方入口 安装 python安装 python官方下载 PaddlePaddle安装 python -m pip install paddlepaddle2.6.0 -i https://mirror.baidu.com/pypi/s…

【C++】类和对象终篇

个人主页 &#xff1a; zxctscl 文章封面来自&#xff1a;艺术家–贤海林 如有转载请先通知 文章目录 1. 前言2. 友元2.1 友元函数2.2 友元类 3. 内部类4. 匿名对象5. 拷贝对象时的一些编译器优化6. 再次理解类和对象 1. 前言 在上一篇博客中提到了类和对象中的构造函数与stat…

数据库系统概论(超详解!!!) 第二节 数据模型

1.数据模型分为两类&#xff08;两个不同的层次&#xff09; &#xff08;1&#xff09; 概念模型 &#xff0c;也称信息模型&#xff0c;它是按用户的观点来对数据和信息建模&#xff0c;用于数据库设计。 &#xff08;2&#xff09; 逻辑模型 &#xff0c;逻辑模型主要包括…

Mybatis实现分页查询数据(代码实操讲解)

在MyBatis中实现分页查询的常见方式有两种&#xff1a;使用MyBatis内置的分页插件如PageHelper&#xff0c;或者手动编写分页的SQL语句。下面我将为你提供两种方式的示例代码。 使用PageHelper分页插件 首先&#xff0c;确保你的项目中已经添加了PageHelper的依赖。在Maven项…

2023 2024年全国职业院校技能大赛中职组网络建设与运维赛项服务器Linux部分教程解析

欢迎合作 需要资料请私 Rocky 9 包含各种常考服务(包括新题型KVM等)

RabbitMQ是如何保证高可用的?

RabbitMQ可以通过多种方式来实现高可用性&#xff0c;以确保在硬件故障或其他不可预测的情况下&#xff0c;消息队列系统仍然能够正常 运行。RabbitMQ有三种模式&#xff1a;单机模式&#xff0c;普通集群模式&#xff0c;镜像集群模式。 其中单机模式一般用于demo搭建&#x…

unocss 究竟比 tailwindcss 快多少?

unocss 究竟比 tailwindcss 快多少&#xff1f; 前言 我们知道 unocss 很快&#xff0c;也许是目前最快的原子化 CSS 引擎 (没有之一)。 unocss 解释它为什么这么快的原因&#xff0c;是因为它不用去解析 CSS 抽象语法树&#xff0c;直接在 content 里面通过正则表达式从内容…

基本设计模式

单例模式 ES5 function Duck1(name:string){this.namenamethis.instancenull }Duck1.prototype.getNamefunction(){console.log(this.name) }Duck1.getInstancefunction(name:string){if(!this.instance){this.instance new Duck1(name)} } const aDuck1.getInstance(a) const…

【王道操作系统】ch2进程与线程-01进程与线程(长文预警)

文章目录 【王道操作系统】ch2进程与线程-01进程与线程01 进程的概念、组成和特征&#xff08;1&#xff09;进程的概念&#xff08;2&#xff09;进程&#xff08;进程实体&#xff09;的组成①PCB&#xff08;给操作系统用的&#xff09;②程序段&#xff08;给进程自己用的&…

【YOLO v5 v7 v8 v9小目标改进】DWRSeg:优化的多尺度处理,传统的深度学习模型可能在不同尺度的特征提取上存在冗余

DWRSeg&#xff1a;优化的多尺度处理&#xff0c;传统的深度学习模型可能在不同尺度的特征提取上存在冗余 提出背景问题&#xff1a;实时语义分割需要快速且准确地处理图像数据&#xff0c;提取出有意义的特征来识别不同的对象。 小目标涨点YOLO v5 魔改YOLO v7 魔改YOLO v8 魔…

开发知识点-Python-爬虫

爬虫 scrapybeautifulsoupfind_all find祖先/父节点兄弟节点nextpreviousCSS选择器属性值 attrsselect 后 class 正则使用字符串来描述、匹配一系列符合某个规则的字符串组成元字符使用grep匹配正则组与捕获断言与标记条件匹配正则表达式的标志 特定中文 匹配 scrapy scrapy内…

ArcGIS学习(十三)多源数据下的城市街道功能评估

ArcGIS学习(十三)多源数据下的城市街道功能评估 本任务带来的内容是多元数据下的城市街道功能评估。本任务包括两个关卡: 城市街道空间中观解读 城市街道功能详细评价 首先,我们来看看本任务的分析思路。 1.城市街道空间中观解读 下面我们正式进入第一关的内容一- 城市…

[Flutter get_cli] 配置 sub_folder:false报错

flutter get_cli 配置 get_cli:sub_folder:false报错如下 Because getx_cli_learn01 depends on get_cli from unknown source "sub_folder", version solving failed. 原因是在 pubspec.yaml文件中, get_cli:sub_folder:false要和 dependencies: xxx dev_depe…

每日学习总结20240301

20240301 1. strchr VS strrchr strchr和strrchr是C语言标准库中的字符串处理函数&#xff0c;用于在字符串中查找特定字符的位置。 1.1 strchr函数 strchr函数用于在字符串中查找第一次出现指定字符的位置&#xff0c;并返回该位置的指针。函数原型如下&#xff1a; char…

Cannot install Microsoft Office 64-bit after removing Office 32-bit 卸载微软之前版本

问题描述 win10系统安装了一个Viso2019&#xff0c;无法安装Excel 64位版本。弹窗报错 解决办法1 SaRACmd https://winitpro.ru/index.php/2017/12/11/office-all-versions-removal-scripts/用于完全删除任何版本的 MS Office 的脚本 使用 Microsoft SaRa 实用程序自动卸载…