ACP(四):RAG工作流程及如何创建一个RAG应用

RAG的工作原理

你在考试的时候有可能会因为忘记某个概念或公式而失去分数,但考试如果是开卷形式,那么你只需要找到与考题最相关的知识点,并加上你的理解就可以进行回答了。

对于大模型来说也是如此,在训练过程中由于没有见过某个知识点(比如你们公司的制度文件),因此直接向它提问相关问题会得到不准确的答案;如果在大模型生成内容时,像开卷考试一样将相关知识提供给它作为参考,那么大模型回答的质量也就会大幅提高了。
这引出了之前提到的一个核心理念:上下文工程(Context Engineering) ,专注于为大模型“上下文窗口”填充恰到好处的信息,以引导其完成特定任务。如果信息太少,模型会“不知道”;如果信息太多或无关,模型的性能会下降,成本也会增加。
而我们即将学习的RAG(Retrieval Augmented Generation,检索增强生成),是上下文工程中最重要、最有效的技术这一,专门解决大模型“知识不足”的问题,RAG应用通常包含建立索引检索生成两部分。

建立索引

你可能会在考试前对参考资料做标记,来帮助你在考试时更容易地找到相关信息。类似的,RAG应用往往也会在回答前就已经做好了标记,这一过程叫做建立索引,建立索引包括四个步骤:

  1. 文档解析
    就像你会将书上看到的视觉信息理解为文字信息一样,RAG应用也需要首先将知识库文档进行加载并解析为大模型能够理解的文字形式。
  2. 文本分段
    你通常不会在某道题时把整本书都翻阅一遍,而是去查找与问题最相关的几个段落,因此你会先把参考资料做一个大致的分段。类似的,RAG应用也会在文档解析后对文本进行分段,以便于在后续能够快速找到与提问最相关的内容
  3. 文本向量化
    在开卷考试时,你通常会先在参考资料中寻找与问题最相关的段落,再去进行作答。在RAG应用,通常需要借助嵌入(embedding)模型分别对段落与问题进行数字化表示,在进行相似度比较后找出最相关的段落,数字化表示的过程就叫做文本向量化
  4. 存储索引
    存储索引将向量化后的段落存储为向量数据库,这样RAG应用就无需在每次进行回复时都重复以上步骤,从而可以增加响应速度。
    在这里插入图片描述

检索生成

检索、生成分别对应着RAG名字中的RetrievalGeneration两阶段。检索就开卷考试时去查找资料的过程,生成则是在找到资料后,根据参考资料与问题进行作答的过程。

  1. 检索生成
    检索阶段会召回与问题最相关的文本段。通过embedding模型对问题进行文本向量化,并与向量数据库的段落进行语义相似度的比较,找出最相关的段落。检索是RAG应用中最重要的环节,你可以想象如果考试的时候找到了错误的资料,那么回答一定是不准确的。这个步骤完美诠释了上下文工程的精髓:从海量知识中“精准地选择相关信息”来填充上下文。找到最匹配的内容,是保证后续生成质量的第一步。为了提高检索准确性,除了使用性能强大的embedding模型,也可以做重排(Rerank)、句子窗口检索等方法。

  2. 生成
    在检索到相关的文本段后,RAG应用会将问题与文本段通过提示词模板生成最终的提示词,由大模型生成回复,这个阶段更多是利用大模型的总结能力,而不是大模型本身具有的知识。这个提示词模板的设计,是上下文工程的另一个关键环节。我们不仅要提供检索到的“资料”,还要明确地“指导”模型如何使用这些资料来回答问题

一个典型的提示词模板为:请根据以下信息回答用户的问题:{召回文本段}。用户的问题是:{question}。
在这里插入图片描述
它的整体流程图为:
在这里插入图片描述

创建RAG应用

一个简单的RAG应用

# 导入依赖
from llama_index.embeddings.dashscope import DashScopeEmbedding,DashScopeTextEmbeddingModels
from llama_index.core import SimpleDirectoryReader,VectorStoreIndex
from llama_index.llms.openai_like import OpenAILike# 这两行代码是用于消除 WARNING 警告信息,避免干扰阅读学习,生产环境中建议根据需要来设置日志级别
import logging
logging.basicConfig(level=logging.ERROR)print("正在解析文件...")
# LlamaIndex提供了SimpleDirectoryReader方法,可以直接将指定文件夹中的文件加载为document对象,对应着解析过程
documents = SimpleDirectoryReader('./docs').load_data()print("正在创建索引...")
# from_documents方法包含切片与建立索引步骤
index = VectorStoreIndex.from_documents(documents,# 指定embedding 模型embed_model=DashScopeEmbedding(# 你也可以使用阿里云提供的其它embedding模型:https://help.aliyun.com/zh/model-studio/getting-started/models#3383780daf8hwmodel_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V2))
print("正在创建提问引擎...")
query_engine = index.as_query_engine(# 设置为流式输出streaming=True,# 此处使用qwen-plus模型,你也可以使用阿里云提供的其它qwen的文本生成模型:https://help.aliyun.com/zh/model-studio/getting-started/models#9f8890ce29g5ullm=OpenAILike(model="qwen-plus",api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",api_key=os.getenv("DASHSCOPE_API_KEY"),is_chat_model=True))
print("正在生成回复...")
streaming_response = query_engine.query('我们公司项目管理应该用什么工具')
print("回答是:")
# 采用流式输出
streaming_response.print_response_stream()
正在解析文件...
正在创建索引...
正在创建提问引擎...
正在生成回复...
回答是:
项目管理应使用项目管理软件,如Jira、Trello。这些工具可用于需求沟通、任务分配和进度跟踪,配合协作工具如Slack或Microsoft Teams,能够有效提升团队协作效率。

保存与加载索引

你可能会发现,创建索引消耗的时间比较长。如果能够将索引保存到本地,并在需要使用的时候直接加载,而不是重新建立索引,那就可以大幅提升回复的速度,LlamaIndex提供了简单易实现的保存与加载索引的方法

# 将索引保存为本地文件
index.storage_context.persist("knowledge_base/test")
print("索引文件保存到了knowledge_base/test")

存在目录列表会出现以下几个文件:
在这里插入图片描述

文件名作用说明
default__vector.json文本向量存储存储所有文本 chunk 的向量(embedding)及其对应的节点 ID。检索时会用这些向量做相似度搜索。
image__vector.json图片向量存储如果你的文档中有图片,或者你用过图片 embedding,这里会存储图片的向量。没有图片时可能为空或不存在。
docstore.json文档存储存储原始的 Document 对象(包括文本内容、元数据等),用于在检索到节点后还原原文。
graph_store.json图结构存储存储索引的图结构信息(节点之间的关系、父子节点等),主要用于分层索引(TreeIndex、GraphIndex)等。
index_store.json索引元信息存储存储索引的元数据(索引类型、版本、向量存储的引用等),加载索引时会先读取这个文件来恢复结构。
index_store.json├── 引用 → default__vector.json  (文本向量)├── 引用 → image__vector.json    (图片向量)├── 引用 → docstore.json         (原始文档)└── 引用 → graph_store.json      (节点关系)

🤔LlamaIndex 在 persist() 保存索引时,不同模型(Embedding 模型、LLM 模型)生成的文件会不会不一样?

  1. 核心结论
    文件的种类和结构主要取决于 索引类型(VectorStoreIndex、TreeIndex、ListIndex 等)和数据类型(文本、图片、音频等),不是直接由模型决定的。
    文件的内容会因为你用的模型不同而不同(尤加粗样式其是向量文件),但文件名和整体结构大体是一样的。
  2. 为什么文件名大体固定
    LlamaIndex 的持久化是基于 StorageContext 的,它会把不同类型的数据存到不同的 Store 里:
    DocStore → docstore.json(原始文档)
    VectorStore → default__vector.json / image__vector.json(向量数据)
    GraphStore → graph_store.json(节点关系)
    IndexStore → index_store.json(索引元信息)
    这些 Store 的名字是固定的,所以文件名也基本固定。
  3. 模型不同,变化在哪里?
    Embedding 模型不同 → default__vector.json(或 image__vector.json)里的向量值会不同,因为不同模型生成的向量维度、数值分布不一样。
    例如:
    OpenAI: text-embedding-3-small → 1536 维向量
    阿里云: DashScope TEXT_EMBEDDING_V2 → 1024 维向量
    硅基流动: text-embedding-3-large → 3072 维向量
    维度不同会直接影响向量文件的内容和大小。
    是否有图片/多模态数据 → 决定是否生成 image__vector.json。
    索引类型不同 → 决定是否生成 graph_store.json(比如 TreeIndex 会用到)。
    LLM 模型不同 → 不会直接影响这些持久化文件,因为 LLM 主要在查询阶段用,不会存到索引文件里(除非你把 LLM 生成的内容当作文档再存)。
# 将本地索引文件加载为索引
from llama_index.core import StorageContext,load_index_from_storage
storage_context = StorageContext.from_defaults(persist_dir="knowledge_base/test")
index = load_index_from_storage(storage_context,embed_model=DashScopeEmbedding(model_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V2))
print("成功从knowledge_base/test路径加载索引")

LlamaIndex 会:
读取 index_store.json → 确定索引类型和引用的文件。
加载 default__vector.json(或 image__vector.json)→ 获取向量和元数据。
加载 docstore.json → 获取原文。
如果有 graph_store.json → 加载节点关系。

从本地加载索引后,你可以再次进行提问测试是否可以正常工作。

print("正在创建提问引擎...")
query_engine = index.as_query_engine(# 设置为流式输出streaming=True,# 此处使用qwen-plus模型,你也可以使用阿里云提供的其它qwen的文本生成模型:https://help.aliyun.com/zh/model-studio/getting-started/models#9f8890ce29g5ullm=OpenAILike(model="qwen-plus",api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",api_key=os.getenv("DASHSCOPE_API_KEY"),is_chat_model=True))
print("正在生成回复...")
streaming_response = query_engine.query('我们公司项目管理应该用什么工具')
print("回答是:")
streaming_response.print_response_stream()

你可以将上述代码进行封装,以便在后续持续迭代时快速使用。

from chatbot import rag# 引文在前面的步骤中已经建立了索引,因此这里可以直接加载索引。如果需要重建索引,可以增加一行代码:rag.indexing()
index = rag.load_index(persist_path='./knowledge_base/test')
query_engine = rag.create_query_engine(index=index)rag.ask('我们公司项目管理应该用什么工具', query_engine=query_engine)

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

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

相关文章

宇视设备视频平台EasyCVR视频设备轨迹回放平台监控摄像头故障根因剖析

监控摄像头的类型繁多,市场上提供了广泛的选择。然而,在使用监控摄像头的过程中,用户可能会遇到云台在很短的时间内出现运转不灵或完全无法转动的问题。这里,我们将对这一常见问题进行深入分析。一、具体的原因: 1、距…

【Uni-App+SSM 宠物项目实战】Day15:购物车添加

大家好!今天是学习路线的第15天,我们正式进入订单与购物车核心模块。昨天完成了商家服务列表的分页加载,今天聚焦“购物车添加”功能——这是连接“商品浏览”与“订单提交”的关键环节,用户可将宠物用品(如粮食、玩具)加入购物车,后续统一结算。 为什么学这个? 购物车…

Java 黑马程序员学习笔记(进阶篇6)

常用的 API1. 正则表达式(1) 题目:贪婪爬取和非贪婪爬取① 贪婪爬取:爬取数据的时候尽可能的多获取数据 ② 非贪婪爬取:爬取数据的时候尽可能的少获取数据 ③ Java中默认的是贪婪爬取 ④ 后面加上 ? 可以转变为非贪婪爬取(2) 捕获分组捕获分…

计算机网络---数据链路层上

文章目录1. 数据链路层的功能2. 组帧2.1 字符填充法2.2 字节填充法2.3 零比特填充法2.4 违规编码2.5 总结3. 差错控制3.1 检错编码3.1.1 奇偶校验3.1.2 循环冗余校验码(CRC)3.1.3 总结3.2 纠错编码(海明校验码)3.3 总结4. 流量控制…

机器学习实战项目中,回归与分类模型中该如何科学定义目标变量Y?

前言 在机器学习项目里,目标变量 (Y) 的定义决定了你能解答什么问题,以及模型能给业务带来什么价值。选择不当不仅可能导致模型误差大、偏差严重,还可能让业务决策方向偏离。 本文分两大场景: 供应链项目中的 销量预测&#xff08…

【 C/C++ 算法】入门动态规划-----一维动态规划基础(以练代学式)

每日激励:“不设限和自我肯定的心态:I can do all things。 — Stephen Curry” 绪论​: 本章是动态规划算法的基础入门篇,我将通过三道简单题 一道中等难度的一维动态规划题来带你对动态规划有个初认识,并基本了解动…

深入对比Tomcat与Netty:HTTP请求从网卡到Controller的全链路追踪

我们日常用Spring Boot写的RestController,感觉上就是一个简单的方法,但它背后其实有一套复杂的网络服务在支撑。一个HTTP请求到底是怎么从用户的浏览器,穿过层层网络,最终抵达我们代码里的Controller方法的?理解这个过…

GO学习记录十——发包

记录下不同平台的发包操作和期间遇到的问题 1.命令: $env:GOOSlinux $env:GOARCHamd64 go build -o release/HTTPServices-linux第一行,配置平台,linux、windows 第二行,配置部署服务器的处理器架构 第三行,输出目标文…

贪心算法与动态规划

1. 什么是贪心算法? 贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法。 核心思想:“每步都贪心地选择眼前最好的,不去考虑整个未来的长…

学会“读网页”:生成式 AI 在足球赛事信息整理中的实战

逐步教程(Step-by-Step) — 适合初学者与教学类文章 背景(为什么要这样做) 对于足球迷、资讯编辑与数据分析师来说,最快、最准确把握一场比赛的核心信息至关重要:比分、关键事件(进球、点球、红…

BM3D 图像降噪快速算法的 MATLAB 实现

BM3D 图像降噪快速算法的 MATLAB 实现1. 快速 BM3D 算法流程(概述)步骤操作加速技巧① 分组块匹配 堆叠FFT 互相关② 协同滤波3D 变换 硬阈值FFT 沿第三维③ 聚合加权平均稀疏矩阵累加 2. 核心函数(单文件版) 保存为 bm3d_fast.…

Go的schedt调度(runtime/proc.go)

1. 创建go的入口函数// Create a new g running fn. // Put it on the queue of gs waiting to run. // The compiler turns a go statement into a call to this. func newproc(fn *funcval) {gp : getg()pc : sys.GetCallerPC()systemstack(func() {newg : newproc1(fn, gp, …

Ubuntu 服务器配置转发网络访问

配置文档:Ubuntu 服务器转发网络访问 一、网络拓扑以以下网络拓扑为示例Ubuntu 服务器(两个网卡) eth1 10.66.71.222 (接入内网)eno1 192.168.2.100 (直连相机) 相机ip 192.168.2.1 Windows 客…

为什么企业需要高防IP

1. 抵御日益猖獗的DDoS攻击 现代DDoS攻击规模已突破Tbps级别 传统防火墙无法应对大规模流量攻击 高防IP采用分布式清洗中心,可轻松抵御300Gbps以上的攻击流量 2. 保障业务连续性 网络中断1小时可能造成数百万损失 高防IP确保服务99.99%可用性 智能切换机制实…

CSS基础 - 选择器备忘录 --笔记5

目录基础选择器组合器伪类选择器属性选择器选择器可以选中页面上的特定元素并为其指定样式。 CSS有多种选择器。 基础选择器 标签选择器 – tagname:匹配目标元素的标签名。优先级是0,0,1。如:p、h1、div类选择器 – .class:匹配class属性中…

自动驾驶中的传感器技术46——Radar(7)

卫星雷达(又称为分布式雷达)主要讲当前雷达的雷达信号处理计算以及雷达目标相关的一些感知算法都迁移到中央域控进行,雷达端基本只负责数据采集,这样做的影响如下: 雷达端成本与功耗降低; 雷达端采样得到的…

【论文阅读】Diff-Privacy: Diffusion-based Face Privacy Protection

基于扩散模型的人脸隐私保护方法——DiffPrivacy,解决了两类人脸隐私任务:匿名化(anonymization)和视觉身份信息隐藏(visual identity information hiding)。1. 研究背景随着人工智能和大数据技术的普及&am…

React 原理篇 - 深入理解虚拟 DOM

一、什么是虚拟 DOM? 在前端开发中,“虚拟 DOM” 是一个高频出现的术语,尤其在 React 生态中被广泛讨论。但很多开发者对它的理解往往停留在 “JS 对象” 这个表层认知上。 实际上,虚拟 DOM 是一种编程概念—— 在这个概念里&…

对汇编的初理解

此处是一个简单的函数,里面将调用了一个函数add()函数这里是函数的原型这里是调用lcd函数产生的汇编语言,翻译过来就是r11,r0cnt(r4cnt,前文有提及),然后调用add函数,此处BL是指会回到指令的下一…

《Python 自动化实战:从零构建一个文件同步工具》

《Python 自动化实战:从零构建一个文件同步工具》 一、开篇引入:为什么我们需要文件同步? 你是否有过这样的困扰: 公司电脑和家里电脑上都有工作项目,每次更新都要手动复制? U 盘频繁传输文件,不仅麻烦还容易出错? 项目文件夹动辄几 G,每次同步都耗时长、效率低? 在…