12|检索增强生成:通过RAG助力鲜花运营

news2025/1/21 22:05:18

什么是 RAG?其全称为 Retrieval-Augmented Generation,即检索增强生成,它结合了检 索和生成的能力,为文本序列生成任务引入外部知识。RAG 将传统的语言生成模型与大规模 的外部知识库相结合,使模型在生成响应或文本时可以动态地从这些知识库中检索相关信息。
RAG 的工作原理可以概括为几个步骤 :

  1. 检索:对于给定的输入(问题),模型首先使用检索系统从大型文档集合中查找相关的文档 或段落。这个检索系统通常基于密集向量搜索,例如 ChromaDB、Faiss 这样的向量数据库。
  2. 上下文编码:找到相关的文档或段落后,模型将它们与原始输入(问题)一起编码。
  3. 生成:使用编码的上下文信息,模型生成输出(答案)。这通常当然是通过大模型完成的。

RAG 的一个关键特点是,它不仅仅依赖于训练数据中的信息,还可以从大型外部知识库中检 索信息。
image.png

文档加载

RAG 的第一步是文档加载。LangChain 提供了多种类型的文档加载器,以加载各种类型的文 档(HTML、PDF、代码),并与该领域的其他主要提供商如 Airbyte 和 Unstructured.IO 进 行了集成。
image.png

文本转换

加载文档后,下一个步骤是对文本进行转换,而最常见的文本转换就是把长文档分割成更小的 块(或者是片,或者是节点),以适合模型的上下文窗口。LangChain 有许多内置的文档转 换器,可以轻松地拆分、组合、过滤和以其他方式操作文档。

文本分割器

理想情况下,我们希望将语义相关的文本片段保留在一起
LangChain 中,文本分割器的工作原理如下:

  1. 将文本分成小的、具有语义意义的块(通常是句子)。
  2. 开始将这些小块组合成一个更大的块,直到达到一定的大小。
  3. 一旦达到该大小,一个块就形成了,可以开始创建新文本块。这个新文本块和刚刚生成的块
    要有一些重叠,以保持块之间的上下文。

但是存在几个问题

  • 文本如何分割
  • 块的大小
  • 块之间重叠文本的长度

image.png

  • 需要细致查看文本的任务,最好使用较小的分块。例如,拼写检查、语法检查和文本分析可 能需要识别文本中的单个单词或字符。垃圾邮件识别、查找剽窃和情感分析类任务,以及搜 索引擎优化、主题建模中常用的关键字提取任务也属于这类细致任务。
  • 需要全面了解文本的任务,则使用较大的分块。例如,机器翻译、文本摘要和问答任务需要 理解文本的整体含义。而自然语言推理、问答和机器翻译需要识别文本中不同部分之间的关 系。还有创意写作,都属于这种粗放型的任务。

最后,你也要考虑所分割的文本的性质。例如,如果文本结构很强,如代码或 HTML,你可能 想使用较大的块,如果文本结构较弱,如小说或新闻文章,你可能想使用较小的块。

其他形式的文本转换

除拆分文本之外,LangChain 中还集成了各种工具对文档执行的其他类型的转换。

  1. 过滤冗余的文档:使用 EmbeddingsRedundantFilter 工具可以识别相似的文档并过滤掉冗余信息。
  2. 翻译文档:通过与工具 doctran 进行集成,可以将文档从一种语言翻译成另一种语言。
  3. 提取元数据:通过与工具 doctran 进行集成,可以从文档内容中提取关键信息(如日期、 作者、关键字等),并将其存储为元数据。
  4. 转换对话格式:通过与工具 doctran 进行集成,可以将对话式的文档内容转化为问答 (Q/A)格式,从而更容易地提取和查询特定的信息或回答。

文本嵌入

文本块形成之后,我们就通过 LLM 来做嵌入(Embeddings),将文本转换为数值表示,使 得计算机可以更容易地处理和比较文本。
Embeddings 会创建一段文本的向量表示,让我们可以在向量空间中思考文本,并执行语义 搜索之类的操作,在向量空间中查找最相似的文本片段。
image.png

# 初始化Embedding类
from langchain.embeddings import OpenAIEmbeddings
embeddings_model = OpenAIEmbeddings()

它提供两种方法:

  1. embed_documents 方法,为文档创建嵌入。这个方法接收多个文本作为输入,
    意味着你可以一次性将多个文档转换为它们的向量表示。
  2. embed_query 方法,为查询创建嵌入。这个方法只接收一个文本作为输入,通
    常是用户的搜索查询。

embed_documents 方法的示例代码如下:

embeddings = embeddings_model.embed_documents(
[
"您好,有什么需要帮忙的吗?",
"哦,你好!昨天我订的花几天送达",
"请您提供一些订单号?",
"12345678",
]
)
len(embeddings), len(embeddings[0])
# 输出
(4, 1536)

embedded_query = embeddings_model.embed_query("刚才对话中的订单号是多少?")
embedded_query[:3]
# 输出
[-0.0029746221837547455, -0.007710168602107487, 0.00923260021751183]

存储嵌入

计算嵌入可能是一个时间消耗大的过程。为了加速这一过程,我们可以将计算出的嵌入存储或 临时缓存,这样在下次需要它们时,就可以直接读取,无需重新计算。

缓存存储

CacheBackedEmbeddings 是一个支持缓存的嵌入式包装器,它可以将嵌入缓存在键值存储中。具体操作是:对文本进行哈希处理,并将此哈希值用作缓存的键。
**主要的方式是使用 from_bytes_store **

  • underlying_embedder:实际计算嵌入的嵌入器。
  • document_embedding_cache:用于存储文档嵌入的缓存。
  • namespace(可选):用于文档缓存的命名空间,避免与其他缓存发生冲突。

不同的缓存策略如下:

  1. InMemoryStore:在内存中缓存嵌入。主要用于单元测试或原型设计。如果需要长期存储嵌入,请勿使用此缓存。
  2. LocalFileStore:在本地文件系统中存储嵌入。适用于那些不想依赖外部数据库或存储解决 方案的情况。
  3. RedisStore:在 Redis 数据库中缓存嵌入。当需要一个高速且可扩展的缓存解决方案时, 这是一个很好的选择。

在内存中缓存嵌入的示例代码如下

# 导入内存存储库,该库允许我们在RAM中临时存储数据
from langchain.storage import InMemoryStore

# 创建一个InMemoryStore的实例
store = InMemoryStore()

# 导入与嵌入相关的库。OpenAIEmbeddings是用于生成嵌入的工具,而CacheBackedEmbeddings允许我们缓存这些嵌入
from langchain.embeddings import OpenAIEmbeddings, CacheBackedEmbeddings

# 创建一个OpenAIEmbeddings的实例,这将用于实际计算文档的嵌入
underlying_embeddings = OpenAIEmbeddings()

# 创建一个CacheBackedEmbeddings的实例。
# 这将为underlying_embeddings提供缓存功能,嵌入会被存储在上面创建的InMemoryStore中。
# 我们还为缓存指定了一个命名空间,以确保不同的嵌入模型之间不会出现冲突。
embedder = CacheBackedEmbeddings.from_bytes_store(
    underlying_embeddings,  # 实际生成嵌入的工具
    store,  # 嵌入的缓存位置
    namespace=underlying_embeddings.model  # 嵌入缓存的命名空间
)

# 使用embedder为两段文本生成嵌入。
# 结果,即嵌入向量,将被存储在上面定义的内存存储中。
embeddings = embedder.embed_documents(["你好", "智能鲜花客服"])

首先我们在内存中设置了一个存储空间,然后初始化了一个嵌入工具,该工 具将实际生成嵌入。之后,这个嵌入工具被包装在一个缓存工具中,用于为两段文本生成嵌 入

向量数据库(向量存储)

image.png

数据检索

向量存储检索器

下面实现一个端到端的数据检索功能,我们通过 VectorstoreIndexCreator 来创建索引,并在索引的 query 方法中,通过 vectorstore 类的 as_retriever 方法,把向量数据库(VectorStore)直接作为检索器,来完成检索任务。

# 设置OpenAI的API密钥
import os
os.environ["OPENAI_API_KEY"] = 'Your OpenAI Key'

# 导入文档加载器模块,并使用TextLoader来加载文本文件
from langchain.document_loaders import TextLoader
loader = TextLoader('LangChainSamples/OneFlower/易速鲜花花语大全.txt', encoding='utf8')

# 使用VectorstoreIndexCreator来从加载器创建索引
from langchain.indexes import VectorstoreIndexCreator
index = VectorstoreIndexCreator().from_loaders([loader])

# 定义查询字符串, 使用创建的索引执行查询
query = "玫瑰花的花语是什么?"
result = index.query(query)
print(result) # 打印查询结果

这个实现看起来简单,是因为langchain把 vectorstore、embedding 以及 text_splitter,甚至 document loader 都封装了。
index.query(query),又是如何完成具体的检索及文本生成任务的呢?
在 VectorStoreIndexWrapper 类的 query 方法中,可以看到,在 调用方法的同时,RetrievalQA 链被启动,以完成检索功能。

各种类型的检索器

image.png

索引

LangChain 提供的索引 API 为开 发者带来了一个高效且直观的解决方案。具体来说,它的优势包括:

  • 避免重复内容:确保你的向量存储中不会有冗余数据。
  • 只更新更改的内容:能检测哪些内容已更新,避免不必要的重写。
  • 省时省钱:不对未更改的内容重新计算嵌入,从而减少了计算资源的消耗。
  • 优化搜索结果:减少重复和不相关的数据,从而提高搜索的准确性。

在进行索引时,API 会对每个文档进行哈希处理,确保每个文档都有一个唯一的标识。这个哈 希值不仅仅基于文档的内容,还考虑了文档的元数据
一旦哈希完成,以下信息会被保存在记录管理器中:

  • 文档哈希:基于文档内容和元数据计算出的唯一标识。
  • 写入时间:记录文档何时被添加到向量存储中。
  • 源 ID:这是一个元数据字段,表示文档的原始来源。

总结

image.png

【第二种是 embed_query 方法,为查询创建嵌入】一直有个疑问,如果query是需要“复杂理 解”的,那么是怎么通过“相似度”去match到文档内容的呢。比如文档是一片小说,而query 是:请解读文中描写主人翁心理活动的部分?这里面 LangChain 是否做了特殊处理?
问题特别好。这边LangChain并没有做特别的处理,而目前的LLM还无法处理超长的 文本。那么,首先:你的问题要非常细。解读心理活动,解读那部分?哪年哪月?哪个环境?你泛 泛,回答肯定不准。你问题细,就有可能检索出来相关的嵌入块。另外,工程上需要设计Metadata给 每个块,需要做Summary,需要考虑递归式的检索策略,分层的检索策略。这是另一个大课题了。

如果对文件切片时候,文件中的的问题和回答被切成2个不同的chunck那么经常会无法检 索到答案,这种场景有什么优化方法吗?
1. 一般来说,都可以在切片时设置上下文的重叠区域,也就是重叠窗口。可以设大一点。 2. 有一些包支持把上一个片,和下一个片,都作为Metadata来保存在索引中,这样,检索出一个片, 也就同时检索了上下文。 3. 如果你的文档都是一问,一答的形式。你可以自己修改切片逻辑,刻意的把问答捆绑在一起。

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

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

相关文章

HarmonyOS NEXT应用开发之元素超出List区域

介绍 本示例介绍在List组件内实现子组件超出容器边缘的布局样式的实现方法。 List组件clip属性默认为true,超出容器边缘的子组件会按照List的布局范围被裁剪。为此,可以在List组件内部添加一个占位的ListItem,以达到预期的布局效果。List占…

什么是组态软件?Web组态软件又是什么?

从事相关工作的对“组态软件”应该都不陌生,那Web组态软件又是什么呢?本文将对Web组态可视化软件(下称“Web组态软件”)做简单介绍,可视化编辑器是Web组态软件中的一个重要功能模块。除了编辑器,还有哪些功能模块?又…

腾讯云服务器多少钱一个月?5元1个月,这价格没谁了

2024腾讯云服务器多少钱一个月?5元1个月起,腾讯云轻量服务器4核16G12M带宽32元1个月、96元3个月,8核32G22M配置115元一个月、345元3个月,腾讯云轻量应用服务器61元一年折合5元一个月、4核8G12M配置646元15个月、2核4G5M服务器165元…

亚信安慧AntDB:数字化创新背后的数据力量

亚信安慧AntDB的“融合实时”的特性,不仅使得数据库具备了更强大的适应性,更让企业在不同业务场景下能够更好地实现业务目标,释放出更大的商业价值。融合实时的特性让AntDB具有了高度灵活性和实时性,使其能够满足企业在不同业务需…

为 java 开发者设计的性能测试框架,用于压测+测试报告生成

拓展阅读 junit5 系列教程 基于 junit5 实现 junitperf 源码分析 Auto generate mock data for java test.(便于 Java 测试自动生成对象信息) Junit performance rely on junit5 and jdk8.(java 性能测试框架。压测测试报告生成。) junitperf junitperf 是一款为 java 开…

MQ之Spring AMQP学习

Spring AMQP学习 Spring AMQP AMQP是Advanced Message Queuing Protocol的缩写。AMQP是用于在应用程序之间传递消息的开放标准,该协议与语言和平台无关,更符合微服务中独立性的要求。 Spring AMQP是基于AMQP协议定义的一套API规范,提供了模…

JavaScript高级(十八)---进程和线程,宏任务和微任务

进程和线程 进程(process):计算机已经运行的程序,是操作系统管理程序的一种方式,我们可以认为,启动一个应用程序,就会默认启动一个进程(也可能是多个进程)。 线程&…

JDK21虚拟线程

目录 虚拟线程 话题 什么是平台线程? 什么是虚拟线程? 为什么要使用虚拟线程? 创建和运行虚拟线程 使用线程类和线程创建虚拟线程。生成器界面 使用Executor.newVirtualThreadPerTaskExecutor()方法创建和运行…

k8s详细教程

Kubernetes详细教程 1. Kubernetes介绍 1.1 应用部署方式演变 在部署应用程序的方式上,主要经历了三个时代: 传统部署:互联网早期,会直接将应用程序部署在物理机上 优点:简单,不需要其它技术的参与 缺点…

Spring Cloud Alibaba微服务从入门到进阶(三)(Spring Cloud Alibaba)

Spring Cloud Alibaba是spring Cloud的子项目 Spring Cloud Alibaba的主要组件(红框内是开源的) Spring Cloud是快速构建分布式系统的工具集, Spring Cloud提供了很多分布式功能 Spring Cloud常用子项目 项目整合 Spring Cloud Alibaba …

VUE-组件间通信(一)props

props 1、单向绑定 props是父组件给子组件传输数据 当父组件的属性变化时&#xff0c;将传导给子组件&#xff0c;但是反过来不会 2、使用示例 子组件&#xff08;类似于方法&#xff09; <template> <div><h2>姓名:{{ name }}</h2><h2>性别:{{…

微信小程序接口请求出错:request:fail url not in domain list:xxxxx

一、微信小程序后台和开发者工具配的不一样导致了这个错误 先说结论&#xff1a; 开发者工具配置了https://www.xxx.cn/prod-api/ 微信后台配置了 https://www.xxx.cn 一、最开始 开发者工具配置了https://www.xxx.cn:7500 微信后台配置了 https://www.xxx.cn 报错:reques…

面试六分钟,难题显真章

职场&#xff0c;这个充满机遇与挑战的舞台&#xff0c;总会在不经意间上演着意想不到的转折。我从一家小公司转投到另一家&#xff0c;原本期待着新的工作环境和更多的发展机会&#xff0c;然而现实却给了我一个不小的打击。 新公司的加班文化&#xff0c;如同一个巨大的漩涡…

yolov7 gui 轻松通过GUI来实现YOLOv7对象检测

YOLOv7 GUI 是一款用户友好型图形界面应用程序&#xff0c;专为简化基于YOLOv7&#xff08;You Only Look Once version 7&#xff09;的目标检测流程而设计。该工具允许用户无需深入掌握命令行操作和复杂编程细节&#xff0c;即可方便快捷地运行YOLOv7模型来检测图像或视频中的…

3.19总结

A计划 题解&#xff1a;这题其实就是一个很简单的三维搜索&#xff0c;有了一个传送门&#xff0c;并且要确定是否传过去的对应位置是墙&#xff0c;防止被装死&#xff0c;同事呢又要在对应的t时间内完成&#xff08;不一定要卡着t时间恰好完成&#xff09; #include<ios…

【项目实践day06】JWT令牌相关

什么是JWT 简洁的、自包含的格式&#xff0c;用于在通信双方以json数据格式安全的传输信息。 由于数字签名的存在&#xff0c;这些信息是可靠的。 jwt就是将原始的json数据格式进行了安全的封装&#xff0c;这样就可以直接基于jwt在通信双方安全的进行信息传输了。简洁&#…

全栈的自我修养 ———— 让uniapp开发更加舒服!!(与别的博主思路不一样,小编这里只讲实用的,直提重点!)

小编是web的&#xff0c;然后现在开始接手微信小程序&#xff0c;有很多不习惯的的地方&#xff0c;经过一段时间的使用&#xff0c;部分得到了妥善的解决方法 一、用vscode开发小程序二、组件库的选择三、注意 一、用vscode开发小程序 发现用Hbuilder开发小程序有很多不习惯的…

odoo17开发教程(8):设置界面UI的字段属性

目录 添加字段 给字段设置只读和不可拷贝 给字段添加默认值 保留字段 本节目标&#xff1a;在本文末尾&#xff0c;售价(selling price)应为只读值&#xff0c;卧室数量(bedrooms)和可用日期(availability date)应为默认值。此外&#xff0c;在复制记录时&#xff0c;售价和…

day09-Mybatis

一、Mybatis 基础操作 1 需求 功能列表&#xff1a; 查询 根据主键ID查询 条件查询新增更新删除 根据主键ID删除 根据主键ID批量删除 2 准备 实施前的准备工作&#xff1a; 准备数据库表创建一个新的 springboot 工程&#xff0c;选择引入对应的起步依赖&#xff08;mybatis、…

【DevOps趣味篇】你为什么要数程序员的代码行数?

【DevOps趣味篇】你为什么要数程序员的代码行数&#xff1f; 目录 【DevOps趣味篇】你为什么要数程序员的代码行数&#xff1f;代码行数统计方法手动计数代码行数统计命令使用语句统计代码行数IL指令 需要计算代码行数吗&#xff1f; 推荐超级课程&#xff1a; Docker快速入门…