RAG 的主要动机 大模型训练的时候虽然使用了庞大的世界数据,但是并没有涵盖用户关心的所有数据,
其预训练令牌(token)数量虽大但相对这些数据仍有限。另外大模型输入的上下文窗口越来越大,从几千个token到几万个token,这相当于几十几百页内容,但是遇到你有几个G的文献资料你还是不能完全用上下文输入大模型来找到你想要的内容。大模型就像是新型操作系统的核心,将核心与各种各样的大量的外部链接起来,是这个新兴的操作系统发展中的一个非常核心的能力,如下图:
在这里插入图片描述

RAG(Retrieval Augmented Generation)是实现这一目标的通用范式,RAG管道组成的三个部分通常包括:索引、检索、生成三个部分。这三个阶段,索引是对外部文档进行处理以便根据查询轻松检索;检索是根据输入查询获取相关文档;生成是将检索到的文档喂给 LLM 以产生基于这些文档的答案,如下图:

在这里插入图片描述

以下是一个RAG过程的简单流程
import bs4
from langchain import hub
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import Chroma
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_openai  import ChatOpenAI
import os# 先验证环境变量是否加载成功
ali_api_key = os.getenv("DASHSCOPE_API_KEY")
print(ali_api_key)
llm = ChatOpenAI(model="qwen-max-latest",base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",openai_api_key = ali_api_key,temperature = 0,
)#respons = llm.invoke("你是谁,能帮我解决什么问题")
#print(respons.content)
# 确保正确初始化 embedding 模型
embedding_model = DashScopeEmbeddings(model="text-embedding-v4",dashscope_api_key=os.getenv("DASHSCOPE_API_KEY")  # 显式传递 API 密钥
)#### INDEXING ##### Load Documents
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"))),requests_kwargs={"headers": {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"}}
)
docs = loader.load()
# Split
from langchain_chroma import Chroma
# 初始化 RecursiveCharacterTextSplitter 实例
# chunk_size=1000 表示每个文本块的最大字符数为 1000
# chunk_overlap=200 表示相邻文本块之间重叠的字符数为 200,这有助于保持上下文的连贯性
text_splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=50)# 调用 text_splitter 的 split_documents 方法,将文档列表 docs 分割成多个较小的文本块
# 分割后的文本块存储在 splits 列表中
splits = text_splitter.split_documents(docs)# 创建空集合
# 初始化 Chroma 向量数据库实例,使用 embedding_model 作为嵌入函数
# 嵌入函数用于将文本转换为向量表示,以便在向量数据库中存储和检索
vectorstore = Chroma(embedding_function=embedding_model)# 手动分批次添加文档(每次最多10个)
# 使用 for 循环和 range 函数,以 10 为步长遍历 splits 列表
for i in range(0, len(splits), 10):# 从 splits 列表中截取当前批次的文档,每次最多 10 个batch = splits[i:i+10]# 调用 vectorstore 的 add_documents 方法,将当前批次的文档添加到向量数据库中vectorstore.add_documents(documents=batch)# 调用 vectorstore 的 as_retriever 方法,将向量数据库转换为检索器
# 检索器可以根据输入的查询向量,从向量数据库中检索出相关的文档
retriever = vectorstore.as_retriever()
# Prompt
prompt = hub.pull("rlm/rag-prompt")
print(prompt)# Post-processing
# 定义一个名为 format_docs 的函数,用于对文档列表进行后处理
# 参数 docs 是一个包含文档对象的列表,每个文档对象应有 page_content 属性
def format_docs(docs):# 使用生成器表达式遍历 docs 列表中的每个文档对象,获取其 page_content 属性# 然后使用 \n\n 作为分隔符将所有文档的内容连接成一个字符串并返回return "\n\n".join(doc.page_content for doc in docs)# Chain
# 构建一个可运行的链式结构 rag_chain,用于执行问答任务
rag_chain = (# 构建一个字典,包含两个键值对# "context" 键对应的值是一个链式操作,先通过 retriever 检索相关文档,# 再将检索到的文档列表传递给 format_docs 函数进行格式化# "question" 键对应的值是 RunnablePassthrough(),表示直接传递输入的问题{"context": retriever | format_docs, "question": RunnablePassthrough()}# 将上述字典作为输入传递给 prompt,生成提示信息| prompt# 将生成的提示信息传递给大语言模型 llm,获取模型的回答| llm# 使用 StrOutputParser() 对大语言模型的输出进行解析,提取纯文本内容| StrOutputParser()
)# Question
# 调用 rag_chain 的 invoke 方法,传入问题 "What is Task Decomposition?"
# 执行整个问答流程,最终返回关于 "任务分解是什么" 的答案
rag_chain.invoke("What is Task Decomposition?")

实际上围绕着索引、检索和生成这三个组件,衍生出很多有趣的方法和技巧,如下图:

在这里插入图片描述

首先我们先从检索器开始,索引是 RAG(检索增强生成)系统堆栈管道的核心环节之一,指将外部文档(如网页、论文、本地文件等)进行处理(如分割、转换为数值向量等),使其转化为可被高效检索的形式,并存储起来的过程。其核心是将非结构化的文本信息转化为结构化、可计算的格式(如向量),为后续检索做准备。
索引的作用
1.适配检索需求:外部文档通常是原始文本,直接用于检索效率极低。索引通过分割文档(因嵌入模型上下文窗口有限)、将文本转换为向量(捕捉语义信息)等操作,使文档能被快速匹配和检索。
2.支撑语义匹配:索引过程中,文档会被嵌入为固定长度的向量(如视频中提到的 1536 维向量),这些向量编码了文本的语义含义,便于通过余弦相似性等数值方法与问题向量进行比较,从而找到相关文档。
3.连接外部知识与检索器:索引将外部文档 “加载” 到向量存储中,并与原始文档关联,为检索器提供可查询的 “知识库”,使检索器能基于输入问题精准定位相关信息。

索引与检索器的关系
索引是检索器的 “前置依赖”,二者是 “准备” 与 “使用” 的关系:

  • 索引为检索器提供数据基础:索引处理后的文档(以向量形式存储)是检索器的核心数据源。没有索引,检索器无法高效获取和匹配外部文档。
  • 检索器依赖索引实现功能:检索器的核心任务是根据输入问题,从索引后的向量存储中找到最相关的文档片段。它通过将问题也转换为向量,与索引中的文档向量进行数值比较(如余弦相似性),完成 “检索相关文档” 的过程
    首先要对文档进行数值表示,建立文档与问题的关系通常使用文档的数值表示,因为对于计算机来说向量(数字)容易比较,相对随意文本更便于处理。
    文档压缩为数值表示的方法有哪些:多年来有多种方法将文本文档压缩为可轻松搜索的数值表示,
    1,谷歌等公司开发的统计方法,通过查看单词频率构建稀疏向量,向量位置对应大词汇表,值代表单词出现次数,因词汇表庞大而稀疏;
    2,还有较新的机器学习嵌入方法,将文档构建为压缩的固定长度表示,有强大的对应搜索方法。

由于嵌入模型有受限的上下文窗口输入,所以通常将大文档分割成小部分来输入限制,每个分割部分通过嵌入模型转化为向量作为文档的数值表示,然后通将向量和原始文档的片段链接一起存储在我们的向量存储中,一般是向量数据库比如chroma,Faiss等数据库,其中向量作为做为索引存储的。
如下图:

在这里插入图片描述

index过程代码

# Documents
question = "What kinds of pets do I like?"
document = "My favorite pet is a cat."
import tiktoken
#返回文本字符串中的 token 数量。
def num_tokens_from_string(string: str, encoding_name: str) -> int:"""Returns the number of tokens in a text string."""encoding = tiktoken.get_encoding(encoding_name)num_tokens = len(encoding.encode(string))return num_tokens
# 调用 num_tokens_from_string 函数,计算变量 question 中的文本使用 "cl100k_base" 编码后的 token 数量
num_tokens_from_string(question, "cl100k_base")from langchain_community.embeddings import DashScopeEmbeddings
# 确保正确初始化 embedding 模型
embd = DashScopeEmbeddings(model="text-embedding-v4",dashscope_api_key=os.getenv("DASHSCOPE_API_KEY")  # 显式传递 API 密钥
)
query_result = embd.embed_query(question)
document_result = embd.embed_query(document)#文本转向量
len(query_result)import numpy as npdef cosine_similarity(vec1, vec2):"""计算两个向量之间的余弦相似度。余弦相似度是通过计算两个向量的夹角余弦值来评估它们的方向相似性。取值范围在 -1 到 1 之间,值越接近 1 表示两个向量越相似,值越接近 -1 表示两个向量越不相似,值为 0 表示两个向量正交。参数:vec1 (array-like): 第一个输入向量。vec2 (array-like): 第二个输入向量。返回:float: 两个向量的余弦相似度。"""# 计算两个向量的点积dot_product = np.dot(vec1, vec2)# 计算第一个向量的 L2 范数(欧几里得范数)norm_vec1 = np.linalg.norm(vec1)# 计算第二个向量的 L2 范数(欧几里得范数)norm_vec2 = np.linalg.norm(vec2)# 计算并返回余弦相似度return dot_product / (norm_vec1 * norm_vec2)
# 调用 cosine_similarity 函数计算 query_result 和 document_result 两个向量的余弦相似度
similarity = cosine_similarity(query_result, document_result)
# 打印计算得到的余弦相似度
print("Cosine Similarity:", similarity)#### INDEXING ##### Load blog
import bs4
from langchain_community.document_loaders import WebBaseLoader
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"))),
)
blog_docs = loader.load()# Split
# 从 langchain 库中导入 RecursiveCharacterTextSplitter 类
# 该类用于将文本递归地按字符分割成较小的块
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(# 每个文本块的最大 token 数量为 300chunk_size=300, # 相邻文本块之间重叠的 token 数量为 50,这有助于保持上下文的连贯性chunk_overlap=50)# Make splits
# 调用 text_splitter 实例的 split_documents 方法
# 对 blog_docs 中的文档进行分割,返回分割后的文本块列表
splits = text_splitter.split_documents(blog_docs)from langchain_community.embeddings import DashScopeEmbeddings
from langchain_chroma import Chroma# 确保正确初始化 embedding 模型
embd = DashScopeEmbeddings(model="text-embedding-v4",dashscope_api_key=os.getenv("DASHSCOPE_API_KEY")  # 显式传递 API 密钥
)
# vectorstore = Chroma.from_documents(documents=splits, 
#                                     embedding=embd)
# # 调用 vectorstore 的 as_retriever 方法,将向量数据库转换为检索器
# # 检索器可以根据输入的查询向量,从向量数据库中检索出相关的文档
# retriever = vectorstore.as_retriever()
# 创建空的 Chroma 向量库
vectorstore = Chroma(embedding_function=embd)
# 手动分批次添加文档,每次最多 10 个
for i in range(0, len(splits), 10):batch = splits[i:i + 10]vectorstore.add_documents(documents=batch)# 调用 vectorstore 的 as_retriever 方法,将向量数据库转换为检索器
# 检索器可以根据输入的查询向量,从向量数据库中检索出相关的文档
retriever = vectorstore.as_retriever()

当给出一个同样经过嵌入处理的问题时,索引会执行相似性搜索,并返回与该问题相似的文档片段。我们可以想象这些向量有三个维度,每个文档片段处理的文档都被映射到三维空间的某个点上,这些点的位置是由不同的文本语义决定的,位于空间中相似位置的文档,其包含的语义也是相似的,这是许多现代向量存储中搜索与检索的基石。同样的将问题嵌入后进行搜索,就和围绕问题开展局部领域搜索一样如下图中黄点是问题,周围的红点是我们的目标文档。

在这里插入图片描述

总之,从文档切片到文本嵌入向量化保存到数据库,然后再将问题嵌入向量后进行搜索,获取我们需要的一个或者多个我们需要的文档片段,这是一个完整的流程如下图:
在这里插入图片描述

langchain中我们可以找到很多不同的嵌入模型,多样的索引方式,丰富的文档加载器和分割器,我们可以自由组合测试不同的索引和检测方法,来完成这一过程
在这里插入图片描述

Retrievel 索引代码

# Index
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
# vectorstore = Chroma.from_documents(documents=splits, 
#                                     embedding=embd)
vectorstore = Chroma(embedding_function=embd)
# 手动分批次添加文档,每次最多 10 个
for i in range(0, len(splits), 10):batch = splits[i:i + 10]vectorstore.add_documents(documents=batch)#参数k决定了检索过程中要获取的最近邻的数量
retriever = vectorstore.as_retriever(search_kwargs={"k": 2})
# docs = retriever.get_relevant_documents("What is Task Decomposition?")
docs = retriever.invoke("What is Task Decomposition?")
print(docs)
len(docs)Generation:
接下来我们讨论生成回答的过程
当我们使用KNN,或者k邻近算法从空间索引中寻找到问题相关的文档片段后,我们将这些文档片段整合到大模型的上下文窗口中,从而让大模型生成我们需要的答案,如下图:generation:
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate# Prompt
template = """Answer the question based only on the following context:
{context}Question: {question}
"""prompt = ChatPromptTemplate.from_template(template)
prompt# Chain
chain = prompt | llm# Run 用于生成回答内容
chain.invoke({"context":docs,"question":"What is Task Decomposition?"})from langchain import hub
prompt_hub_rag = hub.pull("rlm/rag-prompt")
prompt_hub_rag
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
#创建一个基于检索增强生成(RAG)的链式处理流程
# RAG 结合了检索和生成模型的能力,利用外部知识源来回答问题
rag_chain = (# 使用字典来组织输入数据# "context" 键对应的值为 retriever,意味着将输入问题通过检索器获取相关上下文# "question" 键对应的值为 RunnablePassthrough(),表示直接传递输入的问题{"context": retriever, "question": RunnablePassthrough()}# 将组织好的输入数据(包含上下文和问题)传递给提示模板 prompt# prompt 会根据上下文和问题生成适合大语言模型输入的提示文本| prompt
# 将生成好的提示文本传递给大语言模型 llm 进行推理,得到模型的输出| llm
# 使用 StrOutputParser() 对大语言模型的输出进行解析# 该解析器会将模型的输出转换为字符串类型| StrOutputParser()
)rag_chain.invoke("What is Task Decomposition?")

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

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

相关文章

OpenCV学习探秘之一 :了解opencv技术及架构解析、数据结构与内存管理​等基础

​一、OpenCV概述与技术演进​ 1.1技术历史​ OpenCV(Open Source Computer Vision Library)是由Intel于1999年发起创建的开源计算机视觉库,后来交由OpenCV开源社区维护,旨在为计算机视觉应用提供通用基础设施。经历20余年发展&…

什么是JUC

摘要 Java并发工具包JUC是JDK5.0引入的重要并发编程工具,提供了更高级、灵活的并发控制机制。JUC包含锁与同步器(如ReentrantLock、Semaphore等)、线程安全队列(BlockingQueue)、原子变量(AtomicInteger等…

零基础学后端-PHP语言(第二期-PHP基础语法)(通过php内置服务器运行php文件)

经过上期的配置,我们已经有了php的开发环境,编辑器我们继续使用VScode,如果是新来的朋友可以看这期文章来配置VScode 零基础学前端-传统前端开发(第一期-开发软件介绍与本系列目标)(VScode安装教程&#x…

扩散模型逆向过程详解:如何从噪声中恢复数据?

在扩散模型中,逆向过程的目标是从噪声数据逐步恢复出原始数据。本文将详细解析逆向条件分布 q(zt−1∣zt,x)q(\mathbf{z}_{t-1} \mid \mathbf{z}_t, \mathbf{x})q(zt−1​∣zt​,x)的推导过程,揭示扩散模型如何通过高斯分布实现数据重建。1. 核心问题 在…

2025年7月份实时最新获取地图边界数据方法,省市区县街道多级联动【文末附实时geoJson数据下载】

动态生成最新行政区划 GeoJSON 数据并结合 ECharts 实现地图下钻功能 在开发基于地图的数据可视化应用时,一个常见的挑战是获取准确且最新的行政区划边界数据(GeoJSON)。许多现有的在线资源可能数据陈旧,无法反映最新的行政区划调…

Spark实现WorldCount执行流程图

spark可以分区并行执行,同时并行执行也可以基于内存完成迭代代码对于大部分spark程序来说都是以driver开始driver结束,中间都是executor分布式运行

编程与数学 03-002 计算机网络 02_网络体系结构与协议

编程与数学 03-002 计算机网络 02_网络体系结构与协议一、网络体系结构的基本概念(一)分层体系结构的优点(二)协议、接口与服务的概念二、OSI参考模型(一)七层模型的层次划分及功能(二&#xff…

Flutter 提取图像主色调 ColorScheme.fromImageProvider

从图像中提取主色调,用于动态适配颜色主题或者界面颜色。之前在 Flutter 应用里一直用的 palette_generator 插件,可以分析图像颜色,从中提取一系列主要的色调。最近发现这个谷歌官方的插件竟然不维护了,后续没有更新计划了。 查找…

51c自动驾驶~合集8

自己的原文哦~ https://blog.51cto.com/whaosoft/11618683 #Hierarchical BEV BEV进入定制化时代!清华Hierarchical BEV:创新多模块学习框架,无痛落地无缝量产!​ 论文思路 自动驾驶指通过传感器计算设备、信息通信、自…

Excel——重复值处理

识别重复行的三种方法方法1:COUNTIF公式法在E2单元格输入公式:COUNTIF($B$2:$B2,B2)>1下拉填充至所有数据行结果为TRUE的即为重复行(会标出第二次及以后出现的重复项)方法2:排序IF公式法按商机号排序(数…

华普微Matter模块HM-MT7201,打破智能家居生态孤岛

随着智能家居渗透率与认可度的持续提升,消费者对于智能家居的功能诉求正从具备联网控制、远程控制与语音遥控等基础交互能力,升级为能通过单一的家居生态平台APP无缝控制所有的品牌设备,从而实现真正意义上的统一调度。这种从“单一设备联网控…

如何使用 minio 完成OceanBase社区版的归档和备份

自OceanBase社区版4.2.1BP7版本起,OceanBase的归档与备份功能开始兼容AWS S3及S3协议的对象存储服务,因此,许多用户选择采用 MinIO 作为其备份存储介质。因为 MinIO 兼容AWS S3云存储服务接口,成为了一个轻便的服务选项。 本文将…

Nacos-服务注册,服务发现(二)

Nacos健康检查 两种健康检查机制 Nacos作为注册中⼼, 需要感知服务的健康状态, 才能为服务调⽤⽅提供良好的服务。 Nacos 中提供了两种健康检查机制: 客⼾端主动上报机制: 客⼾端通过⼼跳上报⽅式告知服务端(nacos注册中⼼)健康状态, 默认⼼跳间隔5…

手写PPO_clip(FrozenLake环境)

参考:白话PPO训练 成功截图 算法组件 四大部分 同A2C相比,PPO算法额外引入了一个old_actor_model. 在PPO的训练中,首先使用old_actor_model与环境进行交互得到经验,然后利用一批经验优化actor_model,最后再将actor_m…

人形机器人指南(八)操作

八、环境交互与操作能力——人形机器人的“灵巧双手”环境交互与操作能力是人形机器人区别于移动平台的核心能力标志。通过仿生学设计的运动链与智能控制算法,机器人得以在非结构化环境中执行抓取、操纵、装配等复杂任务。本章将系统解析机械臂运动学架构、灵巧手设…

管理 GitHub Pages 站点的自定义域(Windows)

管理 GitHub Pages 站点的自定义域(Windows) 你可以设置或更新某些 DNS 记录和存储库设置,以将 GitHub Pages 站点的默认域指向自定义域。 谁可以使用此功能? GitHub Pages 在公共存储库中提供 GitHub Free 和 GitHub Free for organizations,在公共和私有存储库中提供 Gi…

【PCIe 总线及设备入门学习专栏 5.1.3 -- PCIe PERST# 时序要求】

文章目录 Overview 什么是PERST# 第一条要求 术语解释 要求含义 第二条要求 术语解释 要求含义 Perst 示例说明 过程如下 总结 Overview 首先我们看下 PCIe x协议对 PERST 的要求: A component must enter the LTSSM Detect state within 20 rms of the end of Fundamental R…

图像认知与OpenCV——图像预处理

目录 一、颜色加法 颜色加法 颜色加权加法 示例 二、颜色空间转换 RGB转Gray(灰度) RGB转HSV HSV转RGB 示例 三、灰度化 最大值法 平均值法 加权平均值法 四、图像二值化处理 阈值法 反阈值法 截断阈值法 低阈值零处理 超阈值法 OTSU…

Vue 3 组件通信全解析:从 Props 到 Pinia 的深入实践

引言 Vue 3 作为现代前端框架的代表之一,以其灵活性和高效性受到开发者的广泛喜爱。在 Vue 3 中,组件是构建用户界面的核心单元,而组件之间的通信则是实现动态交互和数据流动的关键环节。无论是简单的父子组件通信,还是复杂的跨组…

CodeBuddy IDE实战:用AI全栈能力快速搭建课程表网页

声明:本文仅是实践测评,并非广告 1.前言 在数字化开发的浪潮中,工具的革新往往是效率跃迁的起点。腾讯云 CodeBuddy IDE 是 “全球首个产设研一体 AI 全栈开发平台” ,它不仅打破了产品、设计与研发的职能壁垒,更重新…