第二章 RAG构建概述
小编参考:
两个项目,初步了解RAG构建方法,在此聊一聊自己的看法。这里强推一波datawhale的所有AI教程,牛!!!
一、RAG简介
RAG简述
RAG是什么?RAG是一种解决大模型知识局限的方法技术,由于大模型的训练机制,在训练完成之后知识仅仅局限于训练过程中的知识,对于未知问题,模型会一本正经的胡说八道,由此诞生了RAG。
RAG说到底就是(Retrieval-Augmented Generation)检索-增强-生成,本质上可以理解为一个数据库,将检索到的内容提供给大模型,大模型从而做出有理有据的回复。
其实,构建一个RAG非常简单,利用市面上现有的大模型(豆包、kimi等),输入“请帮我生成一个RAG项目”的提示词就可以快速构建一个demo。但是可靠吗?构建项目只是起点,真正的目的构建一个可靠实用的RAG系统,这才是难点。
技术原理
RAG核心包含两个点:
- 1.检索阶段,寻找外部知识
- 知识向量化:通过嵌入模型(embedding model)将外部知识库编码成向量索引,存入向量数据库
- 语义召回,用户发起查询时,检索模块利用同样的嵌入模型将问题向量化,通过相似度搜索,从海量数据中精确查找与问题相关的文档片段。
- 2.生成阶段,融合外部知识
- 上下文整合:生成模块接受检索阶段送来的文档片段以及用户的原始问题
- 指令引导:该模块遵循预设的Prompt指令,将上下文于问题进行整合,并引导LLM进行可控、有理有据的文本生成,从而给出满意的回答
技术演进过程
RAG的技术架构经历了大致三个阶段:
初级RAG---高级RAG---模块化RAG

为什么使用RAG?
对于大模型一般的技术选型顺序是:提示词工程-RAG-模型微调
其中提示词工程门槛最低,通过精细构建提示词控制模型能力和输出,由此诞生了一大批的“提示词工程师”。但是提示词设计的再精妙也补充不了外部知识,所以不适用。
RAG,通过检索外部知识库,将相关内容联合提示词一起送给LLM,从而进行生成,基于LLM原本的语义理解能力,搭配上真实的数据,可以生成令用户满意的结果。
对于微调而言,确实可以将模型未知的知识融入到模型中,效果很好,但是代价较大,不能及时将知识融入到模型中去,需要定期微调。
相较而言,RAG比较适用这个场景,可以提供准确真实的数据,新的数据也可以及时放入数据库中。
四步构建最小可行系统(MVP)
-
数据准备与清洗 这是系统的地基。我们需要将 PDF、Word 等多源异构数据标准化,并采用合理的分块策略(如按语义段落切分而非固定字符数),避免信息在切割中支离破碎。
-
索引构建 将切分好的文本通过嵌入模型转化为向量,并存入数据库。可以在此阶段关联元数据(如来源、页码),这对后续的精确引用很有帮助。
-
检索策略优化 不要依赖单一的向量搜索。可以采用混合检索(向量+关键词)等方式来提升召回率,并引入重排序模型对检索结果进行二次精选,确保 LLM 看到的都是精华。
-
生成与提示工程 最后,设计一套清晰的 Prompt 模板,引导 LLM 基于检索到的上下文回答用户问题,并明确要求模型“不知道就说不知道”,防止幻觉。
二、构建RAG
数据加载
在RAG系统中,数据加载是整个流水线的第一步,也是不可或缺的一步。文档加载器负责将各种格式的非结构化文档(如PDF、Word、Markdown、HTML等)转换为程序可以处理的结构化数据。数据加载的质量会直接影响后续的索引构建、检索效果和最终的生成质量。
当前有很多文档加载工具:
PyMuPDF4LLM、TextLoader、DirectoryLoader、Unstructured、MinerU
使用文档加载工具,不要单纯的依赖其功能,需要把控质量。上面提及的工具有些是库,有些是OCR。个人感觉在面对结构清晰整洁的数据时(json、markdown)使用库函数可以有很好的效果,但是对于毫无结构的数据(图片或者pdf这种)时使用OCR工具,但是OCR也是一种大模型(相对来说 没有那么大比如MinerU运行起来需要10G的显存、或者paddleOCR等模型)所以数据加载过程要综合考量。
数据切分
将加载的长篇文档加载后的长文档切分成更小,更容易处理的基本单位。
为什么要数据切分?主要模型上下文限制和检索生成性能需求
- 嵌入模型有严格的上下文限制,超出长度将被截断,丢失有用信息
- 大模型有上下文窗口限制,检索到的所有文本块和用户问题以及提示词都将被放入窗口中,如果单个文本块过大,只能容纳少数几个块,限制LLM回答问题的参考
- 文本转化为向量的过程其实是一个信息压缩的过程,比如一段长文本转换成786位的向量这个过程就会丢失很多信息。
-
生成过程的“大海捞针”即使将检索到的多个大块文本都塞进LLM的长上下文窗口中,也会出现关键信息被“淹没”在大量无关内容里的问题。有研究表明,当LLM处理非常长的、充满大量信息的上下文时,它倾向于更好地记住开头和结尾的信息,而忽略中间部分的内容。
基础的分块策略,LangChain 提供了丰富且易于使用的文本分割器(Text Splitters)。
from langchain.text_splitter import CharacterTextSplitter
from langchain_community.document_loaders import TextLoader
loader = TextLoader("../../data/C2/txt/蜂医.txt")
docs = loader.load()
text_splitter = CharacterTextSplitter(
chunk_size=200, # 每个块的目标大小为100个字符
chunk_overlap=10 # 每个块之间重叠10个字符,以缓解语义割裂
)
chunks = text_splitter.split_documents(docs)
print(f"文本被切分为 {len(chunks)} 个块。\n")
print("--- 前5个块内容示例 ---")
for i, chunk in enumerate(chunks[:5]):
print("=" * 60)
# chunk 是一个 Document 对象,需要访问它的 .page_content 属性来获取文本
print(f'块 {i+1} (长度: {len(chunk.page_content)}): "{chunk.page_content}"')
向量嵌入
向量嵌入(Embedding)是一种将真实世界中复杂、高维的数据对象(如文本、图像、音频、视频等)转换为数学上易于处理的、低维、稠密的连续数值向量的技术。Embedding 的真正意义在于,它产生的向量不是随机数值的堆砌,而是对数据语义的数学编码。
- 核心原则:在 Embedding 构建的向量空间中,语义上相似的对象,其对应的向量在空间中的距离会更近;而语义上不相关的对象,它们的向量距离会更远。
- 关键度量:我们通常使用以下数学方法来衡量向量间的“距离”或“相似度”:
- 余弦相似度 (Cosine Similarity) :计算两个向量夹角的余弦值。值越接近 1,代表方向越一致,语义越相似。这是最常用的度量方式。
- 点积 (Dot Product) :计算两个向量的乘积和。在向量归一化后,点积等价于余弦相似度。
- 欧氏距离 (Euclidean Distance) :计算两个向量在空间中的直线距离。距离越小,语义越相似。
向量数据库
向量数据库的核心价值在于其高效处理海量高维向量的能力。其主要功能可以概括为以下几点:
- 高效的相似性搜索:这是向量数据库最重要的功能。它利用专门的索引技术(如 HNSW, IVF),能够在数十亿级别的向量中实现毫秒级的近似最近邻(ANN)查询,快速找到与给定查询最相似的数据。
- 高维数据存储与管理:专门为存储高维向量(通常维度成百上千)而优化,支持对向量数据进行增、删、改、查等基本操作。
- 丰富的查询能力:除了基本的相似性搜索,还支持按标量字段过滤查询(例如,在搜索相似图片的同时,指定
年份 > 2023)、范围查询和聚类分析等,满足复杂业务需求。 - 可扩展与高可用:现代向量数据库通常采用分布式架构,具备良好的水平扩展能力和容错性,能够通过增加节点来应对数据量的增长,并确保服务的稳定可靠。
- 数据与模型生态集成:与主流的 AI 框架(如 LangChain, LlamaIndex)和机器学习工作流无缝集成,简化了从模型训练到向量检索的应用开发流程。
混合检索
混合检索(Hybrid Search)是一种结合了 稀疏向量(Sparse Vectors) 和 密集向量(Dense Vectors) 优势的先进搜索技术。旨在同时利用稀疏向量的关键词精确匹配能力和密集向量的语义理解能力,以克服单一向量检索的局限性,从而在各种搜索场景下提供更准确、更鲁棒的检索结果。
稀疏向量,也常被称为“词法向量”,是基于词频统计的传统信息检索方法的数学表示。它通常是一个维度极高(与词汇表大小相当)但绝大多数元素为零的向量。
密集向量,也常被称为“语义向量”,是通过深度学习模型学习到的数据(如文本、图像)的低维、稠密的浮点数表示。这些向量旨在将原始数据映射到一个连续的、充满意义的“语义空间”中来捕捉“语义”或“概念”。
可以看出稀疏向量和密集向量各有千秋,那么将它们结合起来,实现优势互补,就成了一个不错的选择。混合检索便是基于这个思路,通过结合多种搜索算法(最常见的是稀疏与密集检索)来提升搜索结果相关性和召回率。
先写到这里吧,还有一些来不及写了,后面有空再补充。
更多推荐


所有评论(0)