从 0 开始搞定 RAG 应用系列(第一篇):构建简单 RAG
从 0 开始搞定 RAG 应用系列(第一篇):构建简单 RAG
前言
LLM 已经从最初的研究性转变为实际应用性,尤其在今年各大 LLM 厂商都在研究 LLM 的商业化落地方案(包括我司)。而在各种商业化场景中个人觉得最具有使用价值和最能体现 LLM 的商业化场景就是 RAG 的使用。
什么是 RAG
大家都知道 LLM 的能做的事情很多,但是有一些工作它是无法做到的,那就是领域知识和最新的知识。
领域知识一般指企业里面私有化的数据,因为这部分的知识 LLM 是无法获取和知道的。
LLM 训练的数据是基于比较老的数据 ,然而最新的信息它也无法知道,这部分的数据一般的情况是基于 RAG 来实现(目前可以联网的 LLM 也是基于 RAG 实现的)。
RAG,即检索增强生成(Retrieval-Augmented Generation),是一种结合了信息检索技术与语言生成模型的人工智能技术。这种技术主要用于增强大型语言模型(Large Language Models, LLMs)处理知识密集型任务的能力,如问答、文本摘要、内容生成等。
RAG的核心思想是让语言模型在生成回答或文本时能够动态地从外部知识库中检索相关信息。这种方法能够提高模型生成内容的准确性、可靠性和透明度,丰富大模型的知识,同时减少“幻觉”(即模型生成看似合理但实际上错误的信息)。
也许有的人会问 RAG 难么?
我一般的回答是:说简单就简单,说难也挺难。如下图:
从上图我们可以看到,构建一个如此的 RAG 涉及的流程和功能是挺多的。而且这也是一个工程化的项目。 本系列文章逐步揭开 RAG 领域的神秘面纱:
构建简单 RAG
本系列文章需要你简单懂 langchain、Python、向量数据库等相关的知识
构建一个简单的流程一般有下面的几个步骤:
- 文档加载
- 文档拆分为 chunk
- 文档向量化(需要向量化模型)
- 向量化存储
- prompt 编写
- LLM 配置、初始化
- 提问(query -> 检索 -> 构建 prompt -> LLM -> answer)
简单 RAG 如图所示:
安装环境以及安装包
pip install langchain_community tiktoken langchain-openai langchainhub chromadb langchain
配置
如果你是用 openAI
作为 LLM,那么你需要在环境变量中设置 OPENAI_API_KEY
os.environ['OPENAI_API_KEY'] = <your-api-key>
本文中是用 AZURE
服务,在项目目录下面新建一个 .env
文件,配置内容如下:
我们只需要设置 model = os.environ.get("AZURE_EMBEDDING_TEXT_MODEL")
就可以了。langchain
会自动读取这些环境变量。
AZURE_OPENAI_API_KEY=""AZURE_OPENAI_ENDPOINT=""AZURE_DEPLOYMENT_NAME_GPT35="gpt-35-turbo"AZURE_DEPLOYMENT_NAME_GPT4="gpt-4"AZURE_EMBEDDING_TEXT_MODEL="text-embedding-ada-002"OPENAI_API_VERSION="2024-03-01-preview"
然后加载配置信息
import os, dotenv# 加载.env 文件dotenv.load_dotenv(".env")
代码实现过程
我们根据官方文档中 [RAG 快速开始]的代码,结合我们自己实际的情况来实现一个简单的 RAG
应用。
1、加载依赖包
import bs4, osfrom langchain import hubfrom langchain.text_splitter import RecursiveCharacterTextSplitterfrom langchain_community.document_loaders import WebBaseLoaderfrom langchain_community.vectorstores import FAISSfrom langchain_core.output_parsers import StrOutputParserfrom langchain_core.runnables import RunnablePassthroughfrom langchain_openai import AzureChatOpenAI, AzureOpenAIEmbeddingsfrom langchain.prompts import PromptTemplate
2、加载文档
这里我们抓取我先前发布的一篇文章:
使用bs4
来抓取文章,并配置抓取的内容为文章块(也就是class 为 “article”, “article-title” 中的内容)。关于 bs4
的是用可以查看官方文档。
# 加载文档loader = WebBaseLoader(web_paths=("https://juejin.cn/post/7366149991159955466",),bs_kwargs=dict(parse_only=bs4.SoupStrainer(class_=("article", "article-title"))),)
3、文档拆分为小块
是用 langchain
的文档加载器,并进行文本的拆分。
chunk_size
:文档块大小
chunk_overlap
:文档块重叠大小
这两个属性挺重要的,会直接影响到最终的答案的准确性。
# 文档加载器加载文档docs = loader.load()# 进行文档拆分text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)splits = text_splitter.split_documents(docs)
4、实例化向量模型
实例化 embedding
模型 AzureOpenAIEmbeddings
,关于向量化模型选择什么的问题,需要注意的是与选择的向量数据库需要兼容,由于不同的向量模型向量化文本后的维度与向量数据库不一致的情况,会导致查询或者向量存储的时候出现问题。
文档向量化过程:
embeddings = AzureOpenAIEmbeddings(model=os.environ.get("AZURE_EMBEDDING_TEXT_MODEL"))
5、向量数据库保存、检索
文档检索过程:
- 向量数据库,这里我们是用本地向量数据库 FAISS。
- 配置向量数据库的需要向量化的文档(splits)
- 配置向量数据库的向量模型(embeddings)
vectorstore = FAISS.from_documents(documents=splits, embedding = embeddings)# 设置检索器(用来向量查找文档相似度的)retriever = vectorstore.as_retriever()
# 打印检索的 documentsdef format_docs(docs):return "\n\n".join(doc.page_content for doc in docs)
检索已经的功能已经完成,我们可以尝试向量检索文本:
result = retriever.invoke("提高LLM的方法有哪些?")format(result)
检索到的文档内容(这里还是文档的原始内容)
'[Document(metadata={'source': 'https://juejin.cn/post/7366149991159955466'}, page_content='回答不完整\n有时候 LLM 的回答并不完全错误,但会遗漏了一些细节。这些细节虽然在上下文中有所体现,但并未被充分呈现出来。例如,如果有人询问“文档A、B和C主要讨论了哪些方面?”对于每个文档分别提问可能会更加适合.....
6、构建 prompt
在 LLM
的是用场景中,prompt
是一个重要的缓解,相信大家已经对 prompt
有了一定的了解。
简单说 prompt
是我们告诉 LLM
的要做什么、怎么做的指令(可以简单也可以很复杂)。下面的 prompt
是构建 RAG
的简单的 prompt
:
# 首先我们需要一个 prompt 模板# prompt = hub.pull("rlm/rag-prompt")prompt_template = """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: {question} Context: {context} Answer:"""prompt = PromptTemplate.from_template(prompt_template) # 这一步是为了把 prompt 模板格式化
初始化 LLM
, LLM
的配置会自动获取环境变量中的值。读者可以根据自己的实际情况配置,并不一定要是用 OpenAI
服务,字节的豆包大模型也是可以的。
# 初始化 LLM, LLM 的配置会自动获取环境变量中的值。llm = AzureChatOpenAI(azure_deployment=os.getenv("AZURE_DEPLOYMENT_NAME_GPT35"),temperature=0)
7、构建 chain
最后一步就是想 LLM
提问,是用 langchain
构建 chain
。 关于 langchain
的 chain
能力,不熟悉的可以去官网了解。
# 构建 chainrag_chain = ({"context": retriever | format_docs, "question": RunnablePassthrough()}| prompt| llm| StrOutputParser())
所有的步骤已经完成,我们可以是用 LLM 来回答问题
# 开始提问rag_chain.invoke("提高LLM的方法有哪些?")# '提高LLM的方法包括使用查询转换来确保全面理解问题并作出回应,以及添加一层查询理解层,进行一系列的Query Rewriting。另外,可以采用路由、查询重写、子问题和ReAct代理选择器等转换方法来优化RAG系统的效能。最后,通过对RAG系统挑战的深入分析和优化,可以提升LLM的准确性和可靠性,大幅提高用户对技术的信任度和满意度。'
rag_chain.invoke("文章中提到了哪些优化方案?")# '抱歉,我不知道。'
总结
本文是 RAG 系列文章的第一篇,初步介绍了 RAG 是什么,有什么作用,构建一个 RAG 应用的几个步骤
- 文档加载:将文档(网页、常用文档)解析为
langchain documents
对象的过程 - 文档拆分: 将大的文档进行拆分为 chunk,并配置 chunk_size,overloop 属性,两个属性会影响 rag 的最后的效果。
- 文档向量化:将高维度的文本转换为向量表示的过程
- 向量化存储:将向量数组、原文当一起存储到向量数据库
- prompt 编写:构建 LLM 的指令
- LLM 配置、初始化
- 提问(query -> 检索 -> 构建 prompt -> LLM -> answer)
如何学习AI大模型?
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
第一阶段: 从大模型系统设计入手,讲解大模型的主要方法;
第二阶段: 在通过大模型提示词工程从Prompts角度入手更好发挥模型的作用;
第三阶段: 大模型平台应用开发借助阿里云PAI平台构建电商领域虚拟试衣系统;
第四阶段: 大模型知识库应用开发以LangChain框架为例,构建物流行业咨询智能问答系统;
第五阶段: 大模型微调开发借助以大健康、新零售、新媒体领域构建适合当前领域大模型;
第六阶段: 以SD多模态大模型为主,搭建了文生图小程序案例;
第七阶段: 以大模型平台应用与开发为主,通过星火大模型,文心大模型等成熟大模型构建大模型行业应用。
👉学会后的收获:👈
• 基于大模型全栈工程实现(前端、后端、产品经理、设计、数据分析等),通过这门课可获得不同能力;
• 能够利用大模型解决相关实际项目需求: 大数据时代,越来越多的企业和机构需要处理海量数据,利用大模型技术可以更好地处理这些数据,提高数据分析和决策的准确性。因此,掌握大模型应用开发技能,可以让程序员更好地应对实际项目需求;
• 基于大模型和企业数据AI应用开发,实现大模型理论、掌握GPU算力、硬件、LangChain开发框架和项目实战技能, 学会Fine-tuning垂直训练大模型(数据准备、数据蒸馏、大模型部署)一站式掌握;
• 能够完成时下热门大模型垂直领域模型训练能力,提高程序员的编码能力: 大模型应用开发需要掌握机器学习算法、深度学习框架等技术,这些技术的掌握可以提高程序员的编码能力和分析能力,让程序员更加熟练地编写高质量的代码。
1.AI大模型学习路线图
2.100套AI大模型商业化落地方案
3.100集大模型视频教程
4.200本大模型PDF书籍
5.LLM面试题合集
6.AI产品经理资源合集
👉获取方式:
😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费】🆓
相关文章:

从 0 开始搞定 RAG 应用系列(第一篇):构建简单 RAG
从 0 开始搞定 RAG 应用系列(第一篇):构建简单 RAG 前言 LLM 已经从最初的研究性转变为实际应用性,尤其在今年各大 LLM 厂商都在研究 LLM 的商业化落地方案(包括我司)。而在各种商业化场景中个人觉得最具…...

接口(Interface)和端点(Endpoint)的区别
在软件开发和相关的文档中,我们经常会看到两个专有名词:接口(Interface)和端点(Endpoint)。而它们的使用场景有很大的重合部分,让人有些分不清到底用哪个。那么,这两者到底有什么区别…...

小米汽车再陷“抄袭”争议,上汽高管直言“真不要脸”
小米SU7在上市初期就曾面临来自各方的争议与质疑,不少人将其戏称为“米时捷”或“保时米”。 转载科技新知 原创 作者丨杰瑞 编辑丨影蕨 近日,在成都车展上,上汽乘用车常务副总经理俞经民对小米汽车提出了尖锐批评,指责其“抄袭”…...

VS C++ 加入dump实现崩溃日志 可以再崩溃的时候使用VS调试
调用 // 加入崩溃dump文件功能SetUnhandledExceptionFilter(ExceptionFilter); 实现 #include "DbgHelp.h"//生成dump int GenerateMiniDump(PEXCEPTION_POINTERS pExceptionPointers) {// 定义函数指针typedef BOOL(WINAPI * MiniDumpWriteDumpT)(HANDLE,DWORD,H…...

Ubuntu22.04版本左右,开机自动启动脚本
Ubuntu22.04版本左右,开机自动启动脚本 1. 新增/lib/systemd/system/rc-local.service中[Install]内容 vim /lib/systemd/system/rc-local.service 按 i 进入插入模式后,新增内容如下: [Install] WantedBymulti-user.target Aliasrc-local.…...

中秋之美——html5+css+js制作中秋网页
中秋之美——html5cssjs制作中秋网页 一、前言二、功能展示三、系统实现四、其它五、源码下载 一、前言 八月十五,秋已过半,是为中秋。 “但愿人长久,千里共婵娟”,中秋时节,气温已凉未寒,天高气爽&#x…...

java设计模式day03--(结构型模式:代理模式、适配器模式、装饰者模式、桥接模式、外观模式、组合模式、享元模式)
5,结构型模式 结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。 由于组合关系或聚合关系比继承关系耦合度低,满足“…...

Golang path/filepath包详解:高效路径操作与实战案例
Golang path/filepath包详解:高效路径操作与实战案例 引言基础用法Abs 函数Base 函数Clean 函数Dir 函数Ext 函数FromSlash 和 ToSlash 函数 基础用法Abs 函数Base 函数Clean 函数Dir 函数Ext 函数FromSlash 和 ToSlash 函数 路径操作Join 函数Split 函数Rel 函数Ma…...

【Shiro】Shiro 的学习教程(四)之 SpringBoot 集成 Shiro 原理
目录 1、第一阶段:启动服务,构建类的功能2、第二阶段:正式请求 1、第一阶段:启动服务,构建类的功能 查看 Shiro 配置类 ShiroConfiguration: Configuration public class ShiroConfiguration {// 创建 sh…...

多线程篇(阻塞队列- PriorityBlockingQueue)(持续更新迭代)
目录 一、简介 二、类图 三、源码解析 1. 字段讲解 2. 构造方法 3. 入队方法 put 浮调整比较器方法的实现 入队图解 4. 出队方法 take dequeue 下沉调整比较器方法的实现 出队图解 四、总结 一、简介 PriorityBlockingQueue队列是 JDK1.5 的时候出来的一个阻塞…...

strstr函数的使用和模拟实现
目录 1.头文件 2.strstr函数的使用 3.strstr函数模拟实现 小心!VS2022不可直接接触,否则!没这个必要,方源面色淡然一把抓住!顷刻炼化! 1.头文件 strstr函数的使用需要头文件 #include<string.h>…...

使用Selenium与WebDriver实现跨浏览器自动化数据抓取
背景/引言 在数据驱动的时代,网络爬虫成为了收集和分析海量数据的关键工具。为了应对不同浏览器环境下的兼容性问题,Selenium与WebDriver成为了开发者实现跨浏览器自动化数据抓取的首选工具。本文将深入探讨如何利用Selenium和WebDriver实现跨浏览器的数…...

信创实践(3):基于x2openEuler将CentOS升级成openEuler,享受其带来的创新和安全特性
引言: 在当前的 IT 行业中,创新和安全性是两大关键趋势。随着 CentOS 停止维护,许多用户正在寻找替代方案,以保持其系统的更新和安全。openEuler 作为一个强大的开源操作系统,成为了理想的迁移目标。本教程将指导您如…...

LEAN 类型理论之注解(Annotations of LEAN Type Theory)-- 相等类型(Equality Type)
《何谓相等 (Equality),在类型理论(Type Theory)语境下》 与 《转化(conversion and reduction)后的相等(Equality)》,两文中,已对相等(Equality)的概念进行了描述&#…...

Idea 创建 Maven项目的时候卡死
文章目录 一、Archetype 和 Catalog1.1 Archetype(原型)1.2 Catalog(目录) 二、可能遇到的问题2.1 问题描述2.2 原因分析2.3 解决方案 参考资料 一、Archetype 和 Catalog 1.1 Archetype(原型) Archetype…...

C++入门(02)简单了解C++应用程序的开发部署
文章目录 1. 开发C应用程序2. 简单示例计算器程序3. 需求分析4. 设计5. 编码6. 编译7. 调试8. 测试9. 部署10. 部署示例10.1 使用Visual Studio Installer Projects创建安装程序10.2 安装VisualStudio Installer Projects扩展10.3 在calculator解决方案中创建安装项目10.3.1 添…...

有了室内外一体化人行导航,你还怕迷路吗?
在快节奏的现代生活中,无论是穿梭于繁华的都市丛林,还是漫步于错综复杂的购物中心,迷路似乎成了不少人的“小确丧”。然而,随着科技的飞速发展,一项革命性的创新——室内外一体化人行导航系统,正悄然改变着…...

Python虚拟环境包迁移
1. 激活源虚拟环境 首先,激活你想要导出包的源虚拟环境。在命令行中输入: Windows: path\to\your\source_env\Scripts\activatemacOS/Linux: source path/to/your/source_env/bin/activate 2. 导出已安装包的列表 使用以下命令生成一个requirements…...

利用分布式锁在ASP.NET Core中实现防抖
前言 在 Web 应用开发过程中,防抖(Debounce) 是确保同一操作在短时间内不会被重复触发的一种有效手段。常见的场景包括防止用户在短时间内重复提交表单,或者避免多次点击按钮导致后台服务执行多次相同的操作。无论在单机环境中&a…...

Django+Vue3前后端分离学习(二)(重写User类)
一、重写User类: 1、首先导入User类: from django.contrib.auth.models import User 2、然后点在User上,按住ctrl 点进去,发现 User类继承AbstractUser Ctrl点进去AbstractUser,然后将此方法全部复制到自己APP的mo…...

兔英语语法体系——观后笔记
目录 一、视频链接 二、视频前言 三、简单句(Simple Sentences) 1. 可独立完成的动作 2. 有1个动作的承受者 3. 有两个动作承受者 4. 只有一个动作承受者(但需补充) 5. 非 “动作” 6. 总结 四、五大基本句型 五、句子成分 6. 定语 7. 状语 8. 同位语 9. 总结 …...

哈希表如何避免冲突
系列文章: 1. 先导片--Map&Set之二叉搜索树 2. Map&Set之相关概念 3. 哈希表如何避免冲突 目录 1.概念 2. 冲突-概念 3. 冲突-避免 3.1 冲突-避免-哈希函数设计 3.2 冲突-避免-负载因子调节 4. 冲突-解决 4.1 冲突-解决-闭散列 4.1.1 线性探…...

内核模块驱动开发
内核模块开始学习前,一定是最先接触到内核模块三要素(面试),驱动入口、驱动出口和协议的遵循。 1.内核模块三要素(面试)//修饰模块化驱动的入口函数module_init(demo_init);//修饰模块化驱动的出口函数module_eixt(demo_exit);//遵循GPL开源协议MODULE_…...

Linux 下 alsa 库录音并保存为 WAV 格式
麦克风列表: [jnjn build]$ arecord -l **** List of CAPTURE Hardware Devices **** card 0: AudioPCI [Ensoniq AudioPCI], device 0: ES1371/1 [ES1371 DAC2/ADC]Subdevices: 1/1Subdevice #0: subdevice #0 card 1: Camera [2K USB Camera], device 0: USB Aud…...

使用stripe进行在线支付、退款、订阅、取消订阅功能(uniapp+h5)
stripe官网:Stripe 登录 | 登录 Stripe 管理平台 然后在首页当中打开测试模式,使用测试的公钥跟私钥进行开发 测试卡号 4242 4242 4242 4242 1234 567 在线支付 stripe的在线支付有两种,第一种就是无代码,第二中就是使用api进行自定义,一般来说推荐第二种进行开发 无…...

深度学习中常见的损失函数
关注B站可以观看更多实战教学视频:hallo128的个人空间 深度学习中常见的损失函数 损失函数的作用 损失函数是衡量神经网络输出与真实标签之间差距的指标。在训练过程中,神经网络的目标是最小化损失函数的值。常见的损失函数包括均方误差(MS…...

认识Linux及Linux的环境搭建
目录 1、什么是Linux2、Linux环境搭建2.1 下载安装 Xshell2.2 下载安装 VMware Workstation Pro2.3 选择适合自己系统 1、什么是Linux Linux,一般指GNU/Linux(单独的Linux内核并不可直接使用,一般搭配GNU套件,故得此称呼ÿ…...

Java之线程篇三
目录 线程状态 观察线程的所有状态 线程状态及其描述 线程状态转换 代码示例1 代码示例2 线程安全 概念 线程不安全的代码示例 线程不安全的原因 线程安全的代码示例-加锁 synchronized关键字 synchronized的特性 小结 形成死锁的四个必要条件 …...

Bootstrap动态设置表格title项
页面searchType <form id"formId"><div class"select-list"><ul><li><select name"searchType" id"searchType"><option value"1">按各节点统计</option><option value"…...

Arrays.sort()方法在Java中的使用:理论与实践
目录 一.概述 二.实现方式 三.具体介绍 1.基本数据类型数组 2.对象数组 1)使对象实现Comparable接口 2)为对象再专门实现一个比较器类 四.进阶技巧 1.基础类型数组实现自定义比较 2.如何进行逆序排序 3.lambda表达式实现比较器类 4.List的排序方法Collection.sort()…...