9-检索增强生成RAG详解

文章目录

简介

**RAG (Retrieval-Augmented Generation,检索增强生成) **是目前大模型应用的核心技术。

简单来说,它的作用是为大模型挂载一个"外部知识库"。如果把大模型比作一名正在参加闭卷考试的学霸,那么 RAG 就是允许这位学霸在遇到不懂的问题时,可以翻阅一本指定的参考书再回答。

**大模型(如 GPT-4, Gemini)**虽然博学,但存在三个致命弱点:

  • 幻觉问题: 面对不知道的知识,模型会"一本正经地胡说八道"。
  • 时效性滞后: 模型的知识停留在训练数据截止的那一刻,无法了解最新的新闻或私有文档。
  • 数据安全: 企业不希望将敏感的内部文档发送给大模型进行训练。

**解决方案1:**每次问答都把所有文档(资料)+问题都输入给模型,保证模型获取最大知识量。

**分析:**很显然这种方案是不可行的,原因在于:(1) 上下文窗口限制;(2) 成本昂贵;(3) 响应速度变慢。

解决方案2:在模型回答之前,先检索资料,再基于检索到的资料生成答案。也就是RAG

设计理念:RAG技术就像给AI大模型装上了「实时百科大脑」,为了让大模型获取足够的上下文,以便获得更加广泛的信息源,通过先查资料后回答的机制,让AI摆脱传统模型的"知识遗忘和幻觉回复"的困境

RAG文本处理核心知识

LangChain组件

LangChain组件 作用 常用组件类
文档加载器 对各种格式的文档信息进行加载转为Document对象,如下: page_content: 文档内容 metadata:元数据 + Document(文档组件) + CSVLoader(csv) + JSONLoader(json) + BHTML(html) + UnstructuredPDFLoader(PDF) + UnstructuredFileLoader(文件文档) + UnstructuredMarkdownLoader(markdown文档)
文档分割器 将加载的文档分割成文档片段 + RecursiveCharacterTextSplitter(递归字符文本分割器) + CharacterTextSplitter(按指定字符分割文本) + MarkdownTextSplitter(按md标题分割文本) + PythonCodeTextSplitter(专门分割Python代码) + TokenTextSplitter(按Token数量分割)
文本嵌入模型 将文本信息向量化 + OpenAIEmbeddings + HuggingFaceEmbeddings
向量数据库 将向量和元数据信息保存到向量数据库 + VectorStore(向量数据库)
文本检索器 根据用户提问在向量数据库中进行检索 + VectorStoreRetriever(向量数据库检索器)

文档加载器:

核心方法:

load():Load data into Document objects

lazy_load():A lazy loader for Document

文档分割器:

核心方法:

split_text():将文本字符串分割成字符串列表

split_documents():将Document对象列表分割成更小文本片段的Document对象列表

create_documents():通过字符串列表创建Document对象

RecursiveCharacterTextSplitter核心参数

参数名 核心含义 详细说明
chunk_size 文本块最大长度 单文本块最大字符数,可通过 length_function 自定义计数,适配模型上下文窗口(如 GPT-3.5 设 3000 )。
chunk_overlap 文本块重叠长度 相邻块重叠字符数,保留上下文,需小于 chunk_size,建议为其 10%-20%。
separators 递归拆分分隔符 按优先级拆分,超尺寸则用下一个分隔符,最后强制拆分,可自定义领域分隔符。
length_function 长度计算函数 默认按字符计数,可自定义(如 tiktoken 按 token 计数,适配大模型)。
keep_separator 是否保留分隔符 默认 False 丢弃;True 保留于块末尾,助力保留原格式。

标准流程

数据准备

将长文档切成小块(Chunks),通过 Embedding 模型转换成高维向量,存储在向量数据库 中。RAG效果好不好,80% 取决于数据准备阶段

  1. 文档加载与清洗 (Load & Clean)

核心:处理原始垃圾,从 PDF、网页或数据库等企业内部文档中提取文本。

关键任务是去除噪声(删除页眉页脚、广告条、乱码以及不相关的格式字符),涉及数据清洗、OCR文档识别等技术。

  1. 文档切分 (Chunking)

核心:确定颗粒度,将长文章按章节、段落、固定字符等粗略切成小块(Chunks)。

难点在于chunk若太大会包含太多噪声,太小会丢失上下文。通常采用 "固定长度 + 重叠(Overlap)" 的策略,确保相邻块之间保留语义连续性。

  1. 文档向量化 (Embedding)

核心:文字转数字

通过 Embedding 模型将文本块转换为一串数字(高维向量)。相似意思的文本在数学空间里的距离会更近。

  1. 构建索引(Indexing)

核心:建立搜索目录

选择适合的算法(如 HNSW 或 IVF)来组织这些向量,确保在数百万条数据中能以毫秒级速度找到最相关的项。

  1. 入库(storage)

核心:存入向量数据库。

将向量与其原始文本、元数据(如文件名、页码、日期)一起存入向量数据库(如 Milvus 或 Pinecone)。

用户检索

当用户提问时,系统将问题也转化为向量,在数据库中搜索最相似的 top-k 个文档块。

  1. 用户提问:用户输入问题(例如:"张三是谁?")。
  2. 问题改写 (Query Rewrite):将模糊的问题转化为更具体的描述(如:"详细介绍张三的性格与年龄")。
  3. 向量化:将问题转化为向量。
  4. 相似度检索 (Similarity Search)
    • 计算问题向量与数据库中各块向量的距离(如余弦相似度、欧氏距离)。
    • 混合检索 (Hybrid Search):结合"关键词检索"与"向量相似度检索"以提高准确率。
  5. 召回 (Recall):取出 Top-K 个最相关的文本块。
  6. 重排 (Rerank):对召回的文本块进行二次排序,精选出最相关的部分。

生成回答

  1. 构建 Prompt :将 [用户问题] + [检索到的参考文档] + [提示词] 组合在一起。
  2. LLM 生成:模型参考背景资料,输出最终答案(Response),有效缓解幻觉问题,提高逻辑性与准确性。

优化技巧

在实际落地中,需要关注以下优化方向:

  • 数据清洗与 OCR:提高源文件质量。
  • Chunk 策略:块太大会引入噪音,太小会导致语义丢失。
  • 多路召回与重排:优化检索的相关度。
  • 开源部署:针对私有化部署场景的优化。

案例代码

案例一

bash 复制代码
# 加载TXT文件
from langchain_community.document_loaders import TextLoader

file_path = "assets/sample.txt"

txt = TextLoader(
    file_path=file_path,
    encoding="utf-8",
).load()

print(txt)  # [Document(metadata={'source': 'assets/sample.txt'}, page_content='Hello world!追加内容.')]

# 加载PDF文件
from langchain_community.document_loaders import PyPDFLoader

file_path = "assets/sample.pdf"

pdf = PyPDFLoader(
    file_path=file_path,
    # plain 提取文本
    # layout 按布局提取
    extraction_mode="plain"
).load()

print(pdf)

案例二

bash 复制代码
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 1.分割文本内容
content = """
    单文本块最大字符数,可通过 length_function 自定义计数,适配模型上下文窗口(如 GPT-3.5 设 3000 左右),相邻块重叠字符数,保留上下文,需小于 chunk_size,建议为其 10%-20%。,
    按优先级拆分,超尺寸则用下一个分隔符,最后强制拆分,可自定义领域分隔符,默认按字符计数,可自定义(如 tiktoken 按 token 计数,适配大模型)。
"""

# 2.定义递归按字符文本分割器
# 遵循"重叠后向前取有效内容,且不生成小碎片"的核心分割逻辑,不会让最后一个片段的有效内容只剩扣除重叠后的少量字符
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=100,  # 文本块最大长度,是「最大限制」,不是精确值,当文本结构不符合分隔符时,会出现不均匀分割
    chunk_overlap=10,  # 文本块重叠长度
    length_function=len,  # 长度计算函数
)

# 3.分割文本,将原始大文本分割成多个文本块
splitter_texts = text_splitter.split_text(content)

# 4.转换为文档对象
splitter_documents = text_splitter.create_documents(splitter_texts)
print(f'原始文本大小: {len(content)}')
print(f'分割文本数量: {len(splitter_texts)}')
print(f'分割文档数量: {len(splitter_documents)}')
for splitter_document in splitter_documents:
    print(f'文档片段大小: {len(splitter_document.page_content)}, 文档内容: {splitter_document.page_content}')

案例三

bash 复制代码
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 1.TXT文档加载器
txt_loader = TextLoader(
    file_path="assets/sample.txt",
    encoding="utf-8",
).load()

# 2.定义递归文本分割器
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=100,
    chunk_overlap=10,
    length_function=len
)

# 3.分割文本
splitter_documents = text_splitter.split_documents(documents=txt_loader)

print(f"分割文本数量: {len(splitter_documents)}")
for document in splitter_documents:
    print(f'文档片段: {document.page_content}')
    print(f'文档片段大小: {len(document.page_content)}')
相关推荐
shehuiyuelaiyuehao1 小时前
算法27,二维前缀和
开发语言·python·算法
码界筑梦坊1 小时前
125-基于Flask的客户购物偏好数据可视化分析系统
python·信息可视化·flask·毕业设计
测试秃头怪2 小时前
接口测试与常用接口测试工具详解
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·接口测试
坐吃山猪2 小时前
【Nanobot】README03_LEVEL2_工具系统架构
python·源码·agent·nanobot
怪祝浙2 小时前
AI学习-LangChain实战-多模态识别agent
人工智能·学习·langchain
Soari2 小时前
挑战 100ms 延迟极限:深度拆解 dograh,构建企业级开源 WebRTC 实时语音智能体平台
开源·大模型·webrtc·实时音视频·voiceagent·语音智能体·dograh
河阿里2 小时前
Python容器:特性、区别和使用场景
开发语言·python
AC赳赳老秦2 小时前
OpenClaw与思维导图工具联动:自动生成工作规划脑图、拆解任务节点,适配职场管理
java·大数据·服务器·数据库·python·php·openclaw
才兄说2 小时前
机器人二次开发机器人动作定制?数据优化迁移
python