RAG系统:知识检索增强 - 核心内容总结
1. 核心概念
什么是RAG?
检索增强生成(Retrieval-Augmented Generation, RAG) 是一种将信息检索与文本生成相结合的技术。其核心原理是:
- 检索:从外部知识库查询相关内容
- 增强:将检索结果整合到提示词中辅助模型生成
- 生成:输出准确且透明的答案
核心价值
RAG解决了大语言模型的两个基本限制:
- 静态、有限的知识:模型有训练数据截止日期,无法访问实时信息
- 缺乏领域专长:通用模型在专业领域可能缺乏深入知识
- 幻觉问题:检索验证有助于减少模型幻觉
- 可解释性:提供信息来源以增强答案可信度
2. 发展历程
第一阶段:Naive RAG (2020-2021)
- 检索方法:传统关键词匹配算法(TF-IDF、BM25)
- 生成模式:直接将检索到的文档拼接进提示词上下文
- 特点:简单的”检索-阅读”模式,适合字面匹配但语义理解有限
第二阶段:Advanced RAG (2022-2023)
- 检索方法:转向基于密度的语义检索
- 生成模式:引入查询重写、文档分块、重排序等优化技术
- 进展:模型能理解超越关键词的语义相似性
第三阶段:Modular RAG (2023至今)
- 检索方法:混合检索、多查询扩展、假设文档嵌入
- 生成模式:链式思维推理、自我反思和修正
- 特点:模块化、可插拔、可组合的独立组件,适应多样化场景
3. 基本工作流程
数据准备阶段
- 数据提取:从知识源提取内容
- 文本分割:将文档切分为可管理的块
- 向量化:将文本块转换为高维向量
- 数据库构建:构建可检索的知识数据库
应用阶段
- 用户查询:接收用户问题
- 检索:从数据库中搜索相关信息
- 提示词注入:将检索结果整合到上下文中
- 答案生成:使用大语言模型生成准确响应
4. 系统架构
Hello-Agents的RAG系统采用**“五层七步”**设计模式:
用户层:RAGTool统一接口
↓
应用层:智能问答、搜索、管理
↓
处理层:文档解析、分块、向量化
↓
存储层:向量数据库、文档存储
↓
基础设施层:嵌入模型、大语言模型、数据库
完整处理流水线
任意格式文档 → MarkItDown转换 → Markdown文本
→ 智能分块 → 向量化 → 存储与检索
各层详细解析
1. 用户层:RAGTool统一接口
作用:对外暴露的API,用户直接交互的界面
具体职责:
- 提供简单易用的方法调用
- 隐藏底层复杂性
- 参数验证和错误提示
示例:
# 用户代码示例
rag = RAGTool(knowledge_base="./knowledge")
# 添加文档
rag.add_document("report.pdf")
# 提问
answer = rag.ask("报告讲了什么?")
# 搜索
results = rag.search("Python教程", top_k=5)类比:像餐厅的前台服务员,接收你的订单并转给后厨处理
2. 应用层:智能问答、搜索、管理
作用:业务逻辑层,决定如何响应用户请求
具体职责:
- 判断请求类型(问答、搜索、管理)
- 整合多个模块(RAG + Memory)
- 执行复杂的业务流程
- 生成报告和统计数据
示例:
class PDFLearningAssistant:
def handle_user_request(self, user_input):
# 判断请求类型
if is_question(user_input):
# 执行问答流程
return self.handle_question(user_input)
elif is_document_upload(user_input):
# 执行文档处理流程
return self.handle_upload(user_input)
elif is_summary_request(user_input):
# 整合RAG和Memory生成学习报告
return self.generate_summary()类比:餐厅的厨师团队,根据订单决定做什么菜、用什么食材
3. 处理层:文档解析、分块、向量化 ⭐核心转换层
作用:将原始文档转换为机器可理解的形式
为什么需要这一层:
- 原始文档(PDF)大语言模型看不懂
- 文档太大,需要分割成小块
- 文字需要转换成向量才能”理解”语义
具体操作:
-
文档解析:PDF → Markdown(用MarkItDown工具)
# 输入:PDF文件 "report.pdf" (二进制格式) # MarkItDown转换 markdown_text = markitdown.convert("report.pdf") # 输出:结构化Markdown """ # 技术报告 本章介绍RAG系统... ## 核心概念 RAG是一种... """ -
智能分块:Markdown → 文本块(按标题、段落分割)
# 输入:Markdown文本 markdown_text = "# 标题\n段落1\n段落2\n..." # 按语义分割 chunks = smart_chunk(markdown_text) # 输出:文本块 [ "# 标题\n段落1", "段落2\n段落3", ... ] -
向量化:文本块 → 高维数字向量(用嵌入模型)
# 输入:文本块 chunk = "RAG是一种检索增强生成的技术" # 嵌入模型转换 embedding = embed_model.encode(chunk) # 输出:高维向量 [0.1, 0.5, -0.2, 0.8, ...] # 通常有1024或1536维
类比:食材加工环节,把生肉洗净、切片、调味,让后厨能烹饪
4. 存储层:向量数据库、文档存储
作用:持久化存储,管理数据
为什么需要分离存储:
- 向量需要快速检索(找相似的)
- 文档需要展示给用户
- 元数据需要用于过滤和统计
具体存储内容:
-
向量数据库(Qdrant):存储向量(处理层的输出)
# 存储结构 { "id": "doc1_chunk_0", "vector": [0.1, 0.5, -0.2, ...], # 向量 "payload": { "text": "RAG是一种检索增强生成的技术", # 原始文本 "source": "report.pdf", # 来源 "timestamp": "2025-01-30" # 时间 } } -
文档存储:存储原始Markdown文本
# 用于展示给用户或重新处理 /knowledge_base/documents/ ├── report.pdf ├── report.md └── metadata.json -
元数据:存储来源、时间、标签等信息
{ "document_id": "report_001", "chunks": 15, "total_tokens": 12500, "created_at": "2025-01-30T10:00:00", "tags": ["技术", "RAG", "AI"] }
类比:仓库,分类存放加工好的食材和原材料,方便快速取用
5. 基础层:嵌入模型、LLM、数据库
作用:提供底层技术能力
为什么独立成层:
- 这些是通用的AI能力,可以复用
- 可以随时替换(换不同的模型)
- 可以统一管理和监控
具体组件:
-
嵌入模型(text-embedding-v3)
# 负责把文字变成向量 embedding_model = TextEmbeddingV3() # 可以替换成其他模型 # embedding_model = SentenceTransformer("all-MiniLM-L6-v2") -
大语言模型(GPT-4等)
# 负责生成回答 llm = GPT4Model() # 可以替换成其他模型 # llm = Claude3Model() -
数据库引擎
# 提供查询能力 vector_db = QdrantClient()
类比:厨房的基础设施(炉灶、刀具、水电),可以灵活更换升级
为什么需要分层?
问题1:为什么处理层、存储层、基础层要分开?
| 层级 | 职责 | 数据状态 | 可替换性 | 示例 |
|---|---|---|---|---|
| 基础层 | 提供AI能力 | 模型本身 | 高(可换不同模型) | 换嵌入模型 |
| 处理层 | 数据转换 | 变换过程中 | 中(可改算法) | 改分块策略 |
| 存储层 | 数据管理 | 存储状态 | 高(可换数据库) | 换向量数据库 |
实际例子:
- 想用更好的嵌入模型?只需修改基础层,不用动其他层
- 想改进分块算法?只需修改处理层,存储和基础层不变
- 想换成其他向量数据库?只需修改存储层,其他层不受影响
完整数据流转示例
场景1:添加文档
用户代码:
rag.add_document("report.pdf")
用户层:
↓ 调用 RAGTool.add_document()
应用层:
↓ 判断这是文档上传请求
↓ 调用处理层
处理层:
↓ 1. 文档解析
"report.pdf" → MarkItDown → "报告内容.md"
↓ 2. 智能分块
"报告内容.md" → ["段落1", "段落2", "段落3"]
↓ 3. 向量化(调用基础层的嵌入模型)
"段落1" → 嵌入模型 → [0.1, 0.5, -0.2, ...]
"段落2" → 嵌入模型 → [0.3, -0.1, 0.7, ...]
"段落3" → 嵌入模型 → [-0.2, 0.6, 0.4, ...]
存储层:
↓ 存储向量到Qdrant
存储原始文本和元数据
返回:"文档添加成功,共3个文本块"
场景2:问答
用户代码:
answer = rag.ask("报告讲了什么?")
用户层:
↓ 调用 RAGTool.ask()
应用层:
↓ 判断这是问答请求
↓ 应用层整合RAG检索 + LLM生成
处理层(MQE扩展):
↓ 原始查询:"报告讲了什么?"
↓ 扩展查询(MQE):
- "报告的主要内容"
- "报告的核心观点"
基础层(嵌入模型):
↓ 将扩展查询转向量
"报告的主要内容" → [0.2, 0.4, ...]
"报告的核心观点" → [0.1, 0.3, ...]
存储层(Qdrant检索):
↓ 向量相似度搜索
↓ 找到最相似的3个向量
↓ 返回对应的文本块:
[
"RAG系统是一种检索增强生成的技术...",
"本章介绍了RAG的核心概念...",
"RAG的主要优势包括..."
]
应用层:
↓ 将检索结果整合到提示词
prompt = """
上下文:[RAG系统是一种检索增强生成的技术...]
问题:报告讲了什么?
"""
基础层(LLM):
↓ 生成最终答案
"报告主要介绍了RAG系统的概念、发展历程和核心技术..."
返回给用户层
代码示例:完整的RAGTool使用
# ==================== 用户层使用 ====================
from hello_agents.tools import RAGTool
# 初始化
rag = RAGTool(
knowledge_base="./my_knowledge",
chunk_size=1000,
chunk_overlap=200
)
# ==================== 场景1:添加文档 ====================
# 用户只需简单调用,内部自动走完整流程
rag.add_document("technical_report.pdf")
# 内部执行:
# 1. 处理层:PDF → Markdown → 分块 → 向量
# 2. 存储层:存储到Qdrant
# 3. 基础层:调用嵌入模型
# ==================== 场景2:智能问答 ====================
# 用户提问
answer = rag.ask(
"什么是RAG?",
enable_mqe=True, # 启用多查询扩展
enable_hyde=True, # 启用假设文档嵌入
top_k=3 # 返回最相关的3个片段
)
# 内部执行:
# 1. 处理层:MQE扩展查询,HyDE生成假设答案
# 2. 基础层:将查询转向量
# 3. 存储层:从Qdrant检索相似文档
# 4. 应用层:整合上下文和问题
# 5. 基础层:LLM生成最终答案
print(answer)
# 输出:"RAG(检索增强生成)是一种将信息检索与文本生成相结合的技术..."
# ==================== 场景3:搜索文档 ====================
# 只搜索,不生成答案
results = rag.search(
"Python教程",
top_k=5,
score_threshold=0.7 # 只返回相似度>0.7的
)
# 内部执行:
# 1. 基础层:查询转向量
# 2. 存储层:Qdrant检索
# 3. 返回原始文本块
for result in results:
print(f"相似度: {result.score:.2f}")
print(f"内容: {result.text}")
print(f"来源: {result.source}")
print("---")
# ==================== 场景4:统计信息 ====================
stats = rag.stats()
print(f"文档数: {stats.total_documents}")
print(f"文本块数: {stats.total_chunks}")
print(f"总token数: {stats.total_tokens}")架构对比:不分层 vs 分层
❌ 不分层的缺点
# 所有逻辑混在一起
def add_document(file):
# 混合了:文件处理、分块、向量化、存储
pdf_text = read_pdf(file) # 处理逻辑
chunks = split_text(pdf_text) # 处理逻辑
vectors = embed(chunks) # 基础层调用
save_to_qdrant(vectors) # 存储逻辑问题:
- 难以测试(想测试分块逻辑,必须连带嵌入和存储)
- 难以复用(其他项目想用分块功能,无法单独提取)
- 难以维护(修改嵌入模型,影响整个函数)
✅ 分层的优势
# 用户层
class RAGTool:
def add_document(self, file):
chunks = self.processor.process(file) # 处理层
self.storage.save(chunks) # 存储层
# 处理层
class DocumentProcessor:
def process(self, file):
text = self.parser.parse(file) # 解析
chunks = self.chunker.split(text) # 分块
vectors = self.embedder.encode(chunks) # 基础层
return chunks, vectors
# 存储层
class VectorStorage:
def save(self, chunks, vectors):
self.qdrant.upsert(chunks, vectors)优势:
- 每层独立测试
- 每层独立优化
- 每层可独立替换
5. 实现细节
5.1 多模态文档加载
MarkItDown作为通用文档转换引擎,支持:
- 文档:PDF、Word、Excel、PowerPoint
- 图像:JPG、PNG、GIF(通过OCR)
- 音频:MP3、WAV、M4A(通过转录)
- 文本:TXT、CSV、JSON、XML、HTML
- 代码:Python、JavaScript、Java等
关键特性:
- 统一转换为结构化Markdown格式
- 增强的PDF处理,带有后备机制
- 错误处理和优雅降级
5.2 智能分块策略
Markdown感知分割
系统利用Markdown结构进行精确的语义分割:
标准Markdown文本 → 标题层级解析 → 段落语义分割
→ 基于Token的分块 → 重叠策略优化 → 准备向量化
处理过程:
双语Token估算
支持中英文混合文本:
- CJK字符:每个计为1个token
- 非CJK字符:基于空格分割计数
5.3 统一嵌入和向量存储
嵌入模型选项:
- DashScope API(主要):基于云的嵌入服务(text-embedding-v3)
- 本地Transformer(后备):sentence-transformers/all-MiniLM-L6-v2
- TF-IDF(最后手段):轻量级关键词方法
关键实现:
- 批处理,可配置的批大小(默认:64)
- 向量维度验证和归一化
- 不匹配向量的自动维度调整
- 带重试机制的错误处理
- 针对更好嵌入质量的Markdown预处理
向量存储:
- 使用Qdrant向量数据库,带有命名空间隔离
- 支持元数据过滤(memory_type、data_source、rag_namespace)
- 启用带分数阈值的相似性搜索
6. 高级检索策略
6.1 多查询扩展(MQE)
概念:生成语义等价的多样化查询以提高召回率
优势:
- 解决查询与文档之间的词汇不匹配
- 自动理解多种可能的解释
- 对模糊查询或专业术语特别有效
实现示例:
# 使用大语言模型生成多样化的查询扩展
原始查询:"如何学习Python?"
扩展查询:
- "Python入门教程"
- "Python学习方法"
- "Python编程指南"影响:将召回率提高30-50%
6.2 假设文档嵌入(HyDE)
概念:“用答案找答案” - 先生成假设的答案段落,然后用它们检索真实文档
核心洞察:问题和答案存在于不同的语义分布中
- 问题:疑问句
- 文档:陈述句
- 假设答案:桥接语义差距
优势:
- 假设答案在语义上更接近真实答案
- 即使内容不准确,关键术语和概念也能指导准确检索
- 显著提高领域特定查询的精确度
实现示例:
# 生成假设答案段落
查询:"什么是大语言模型?"
假设答案:"大语言模型是在海量文本数据上训练的AI系统。它们能够理解和生成类似人类的文本..."6.3 统一扩展检索框架
三步流程:
- 扩展:生成多个扩展查询(MQE + HyDE)
- 检索:对每个扩展查询并行向量搜索
- 合并:去重并对所有结果排序以返回top-k文档
关键参数:
enable_mqe:启用/禁用多查询扩展mqe_expansions:扩展查询数量(默认:2)enable_hyde:启用/禁用假设文档嵌入candidate_pool_multiplier:扩展候选池(默认:4)score_threshold:最小相似度分数
使用建议:
- 一般查询:仅启用MQE
- 领域特定查询:同时启用MQE和HyDE
- 性能敏感场景:使用基本检索或仅MQE
7. RAGTool API
支持的操作
- add_text:添加文本知识到知识库
- add_document:添加多格式文档(PDF、Office、图像、音频)
- search:使用可配置参数搜索知识库
- ask:大语言模型增强的智能问答
- stats:知识库统计信息
关键参数
knowledge_base_path:知识库目录路径collection_name:Qdrant集合名称(默认:“rag_knowledge_base”)rag_namespace:数据隔离的命名空间(默认:“default”)chunk_size:每块最大token数(默认:1000)chunk_overlap:块之间重叠token数(默认:200)
8. 实际应用:智能文档问答助手
本章演示了结合RAGTool和MemoryTool的完整实现:
系统组件
- PDFLearningAssistant:封装RAG和Memory逻辑的核心助手类
- Gradio Web界面:用户友好的Web应用程序
- 核心功能:
- 文档加载和处理
- 高级检索的智能问答
- 笔记记录和学习回顾
- 统计和报告生成
工作流程
- 加载文档:MarkItDown转换 → 智能分块 → 向量化 → 存储
- 提问:MQE + HyDE高级检索 → 上下文感知的答案生成
- 记录学习:将问答历史和笔记存储到记忆系统
- 生成报告:汇总统计、学习指标和摘要
记忆集成
- 工作记忆:当前学习任务和上下文
- 情景记忆:学习事件和查询历史
- 语义记忆:概念知识和理解
- 感知记忆:文档特征和多模态信息
9. 关键技术亮点
9.1 优势
- 通用文档支持:MarkItDown将任何格式转换为统一Markdown
- 语义感知分块:利用Markdown结构进行精确分割
- 多策略检索:结合MQE、HyDE和混合方法
- 统一嵌入:支持多种嵌入模型,自动后备
- 命名空间隔离:为不同用户/应用分离知识库
- 记忆集成:与记忆系统无缝集成以增强能力
9.2 设计原则
- 模块化:每层可以独立优化和替换
- 可重用性:处理流水线代码完全可重用
- 可扩展性:易于添加新的检索策略或文档处理器
- 性能:批处理、缓存和高效向量操作
- 鲁棒性:全面的错误处理和后备机制
10. 未来方向
本章建议了几个探索领域:
- 多模态RAG:扩展到文本+图像或跨模态场景
- 高级检索:探索融合检索、学习排序等其他技术
- 知识图谱集成:结合向量检索与知识图谱推理
- 优化:针对特定任务评估不同的嵌入模型和检索策略
- 生产部署:可扩展性、多用户支持和性能优化
11. 学习建议
对于实践者:
- 动手实现:从零构建基本记忆模块并迭代
- 模型评估:比较不同嵌入模型和检索策略
- 真实项目:将记忆和RAG系统应用于实际个人项目
- 开源贡献:参与Hello-Agents开源项目
- 跨学科思维:将认知科学理论应用于工程解决方案
总结
RAG系统代表了增强大语言模型外部知识的先进方法,结合了文档处理、语义检索和智能生成的先进技术,以创建强大的知识赋能AI应用。通过多模态文档处理、智能分块、高级检索策略和记忆系统的集成,RAG为构建智能问答系统提供了完整的解决方案框架。
核心要点:
- RAG解决了大语言模型的知识限制和幻觉问题
- 从Naive RAG发展到Modular RAG,技术不断演进
- 高级检索策略(MQE、HyDE)显著提升检索效果
- 模块化设计确保了系统的可扩展性和可维护性
- 与记忆系统的集成为Agent提供了强大的知识基础