RAG基础[6] - RAG核心流程
通过本工程,你应该已经:✅理解了RAG的完整流程- 从文档到答案的每一步✅掌握了参数调优方法- 知道如何调整参数提升效果✅看到了RAG的优势- 相比纯LLM的准确性和可追溯性。
一、准备工作
经过前期(必不可少的)铺垫学习,现在要进入到RAG实战环节了,我们通过rag_basic工程的调试带大家深入了解每一个核心部分,在自己跑出结果,调整参数后观察变化后,相信大家能够有更深的理解。
请先确保以下工作就绪:
-
正确clone了工程源码
-
以
samples\rag_basic为根目录,单独兴建一个工程,并用uv和venv配置独立的依赖(工程配置参考quick_start,不赘述了) -
本地配置ollama服务,并下载all-minilm:latest向量(可省略,但embedder效果无法和本地对比了)
-
正确配置了环境变量:
QWEN_API_KEY
二、总体流程
以一个实际的PDF文档检索为案例,step by step揭示了从PDF文档提取到切片、向量化、存储、检索召回、prompt生成与LLM回答的全部流程。
📋 完整流程
原始文档 (PDF/Word/Markdown) ↓ 【步骤1】文档提取 (extractor.py) ↓ 纯文本内容 ↓ 【步骤2】文本切片 (chunker.py) ↓ 文本片段 (chunks) ↓ 【步骤3】向量化 (embedder.py) ↓ 向量数组 (embeddings) ↓ 【步骤4】向量存储 (store_manager.py) ↓ 向量数据库 (chroma_db) ↓ 【步骤5】检索与生成 (llm_with_rag.py) ↓ 最终答案 + 引用来源
🎯 快速体验
方式1:一键对比(第一次)
python main.py
自动对比纯LLM和RAG的效果差异。
方式2:完整流程(调试)
# 按顺序运行以下脚本 python extractor.py # 提取文档 python chunker.py # 切片 python embedder.py # 向量化 python store_manager.py # 存储 python llm_with_rag.py # 问答
然后调整源码里的相关参数,不断观察output的输出
三、工程结构
只含主要流程,不含任何优化和工程化代码,主体内容如下:
rag_basic/ ├── extractor.py # 文档提取 ├── chunker.py # 切片 ├── embedder.py # 向量化 ├── store_manager.py # 向量存储 ├── llm_with_rag.py # 检索召回 ├── main.py # 🎯 一键对比脚本(推荐) ├── knowledge/ │ └── test_knowledge/ # 存放所有知识库原材料 ├── chroma_db/ # 向量数据库(自动创建) └── output/ # 输出结果目录(自动创建)
💡 提示:
所有脚本都可以单独运行
输出结果统一保存在
output/目录建议按顺序运行:extractor → chunker → embedder → store_manager → llm_with_rag
四、RAG核心流程详解
本工程提供了5个独立的脚本,每个脚本对应RAG流程的一个环节。建议按顺序运行,观察每个环节的输出,深入理解RAG的工作原理。
4.1 extractor - 文档提取
📖 过程说明
目标:从各种格式的文档中提取纯文本内容
支持格式:
-
PDF:使用pdfplumber提取文本
-
Word:使用python-docx提取段落和表格
-
Markdown:提取并清理Markdown标记
-
Excel/CSV:转换为文本格式
关键点:
-
不同格式需要不同的提取方法
-
提取后的文本需要清理格式标记
-
确保文本编码正确(UTF-8)
运行方式:
python extractor.py
输出:
-
output/pdf_content.txt- 提取的文本内容
4.2 chunker - 文本切片
📖 过程说明
为什么需要切片?
原始文档通常很长(几千到几万字),直接向量化会导致:
-
❌ 向量维度固定,长文本信息丢失
-
❌ 检索时匹配整个文档,精度低
-
❌ 无法精确定位相关信息
切片的作用:
-
✅ 将长文档切分成小块(chunk)
-
✅ 每个chunk独立向量化
-
✅ 检索时可以精确定位到相关片段
🔍 两种切片方法对比
方法1:简单切片(simple_chunk_text)
工作原理:
-
固定长度切分(如500字符)
-
设置重叠区域(如50字符)避免切断语义
特点:
-
✅ 实现简单,速度快
-
❌ 可能切断句子或段落
-
❌ 语义完整性较差
适用场景:结构简单的文档
方法2:LangChain智能切片(langchain_chunk_text)
工作原理:
-
按分隔符优先级切分(
\n\n→\n→。→!→?→ ) -
尽量在语义边界处切分
-
保持语义完整性
特点:
-
✅ 语义完整性好
-
✅ 不会切断句子
-
⚠️ 实现稍复杂
适用场景:大多数文档(推荐)
⚙️ 核心参数详解
| 参数 | 说明 | 推荐值 | 影响 |
|---|---|---|---|
| chunk_size | 切片大小(字符数) | 300-1000 | 太小:信息碎片化 太大:检索精度下降 |
| overlap | 重叠大小(字符数) | chunk_size的10-20% | 太小:可能丢失上下文 太大:冗余信息多 |
| separators | 分隔符列表 | ["\n\n", "\n", "。", "!", "?"] |
影响切片的语义边界 |
💡 参数调优建议
chunk_size选择:
-
技术文档:500-800字符
-
对话记录:200-400字符
-
长文章:800-1500字符
overlap设置:
-
一般设为chunk_size的10-20%
-
如果文档结构复杂,可以增加到20-30%
调优方法:
-
运行
chunker.py查看切片结果 -
检查
output/chunks.json中的切片质量 -
调整参数后重新运行,对比效果
运行方式:
python chunker.py
输出:
-
output/chunks_simple.json- 简单切片结果 -
output/chunks.json- LangChain切片结果(推荐使用)
4.3 embedder - 向量化
📖 过程说明
什么是向量化?
向量化是将文本转换为固定长度的数值向量(数组)的过程。
为什么需要向量化?
-
计算机无法直接理解文本语义
-
向量化后可以用数学方法计算相似度
-
实现"语义匹配"而非"关键词匹配"
🔍 向量化过程
输入:文本片段(如"如何更换电池?")
处理:
-
文本通过Embedding模型
-
模型理解文本语义
-
输出固定长度的向量(如1024维)
输出:向量数组(如[0.12, -0.45, 0.78, ...])
⚙️ 核心参数详解
| 参数 | 说明 | 推荐值 | 影响 |
|---|---|---|---|
| model_name | Embedding模型名称 | text-embedding-v4 | 影响向量质量和维度 |
| batch_size | 批量处理大小 | 10-32 | 影响处理速度和内存占用 |
| 向量维度 | 输出向量长度 | 384/768/1024 | 维度越高,语义理解越强 |
💡 模型选择建议
云端模型(推荐):
-
text-embedding-v4(Dashscope):1024维,中文优化 -
text-embedding-3-large(OpenAI):3072维,性能强
本地模型(可选):
-
all-minilm:latest(Ollama):384维,速度快但精度较低
选择原则:
-
生产环境:优先选择云端高质量模型
-
开发测试:可以使用本地模型降低成本
🔬 向量化效果
相同语义的文本 → 相似的向量
例如:
-
"如何更换电池?" 和 "电池怎么换?" → 向量相似度高
-
"如何更换电池?" 和 "今天天气怎么样?" → 向量相似度低
运行方式:
python embedder.py
输出:
-
output/embeddings.jsonl- 向量化结果(包含向量和文本预览)
4.4 store_manager - 向量存储
📖 过程说明
目标:将向量化的文本存储到向量数据库中,便于后续检索
为什么需要向量数据库?
-
向量数量可能很大(几千到几百万)
-
需要快速检索相似向量
-
传统数据库无法高效处理向量检索
🔍 存储过程
-
加载切片:读取
chunks.json中的文本片段 -
向量化:对每个片段进行向量化(或使用已有向量)
-
存储:将向量和元数据存入Chroma数据库
-
持久化:保存到磁盘,下次可直接加载
⚙️ 向量数据库选择
| 数据库 | 特点 | 适用场景 |
|---|---|---|
| Chroma | 轻量级、易用 | 中小规模(<100万向量) |
| FAISS | 极快检索速度 | 大规模、高性能需求 |
| Milvus | 生产级、高可用 | 企业级应用 |
本教程使用Chroma:简单易用,适合学习和中小规模应用。
💡 数据库管理
首次构建:
-
运行
store_manager.py自动创建数据库 -
数据库保存在
chroma_db/目录
更新数据库:
-
删除
chroma_db/目录 -
重新运行
store_manager.py重建
运行方式:
python store_manager.py
输出:
-
chroma_db/- 向量数据库目录
4.5 llm_with_rag - 检索与生成
📖 过程说明
这是RAG系统的核心环节,展示了从用户问题到最终答案的完整流程。
🔍 RAG检索流程
步骤1:加载向量数据库
-
从
chroma_db/加载已构建的向量库 -
初始化检索器(Retriever)
步骤2:问题向量化
-
将用户问题转换为向量
-
使用与存储时相同的Embedding模型
步骤3:相似度检索
-
计算问题向量与所有文档向量的相似度
-
返回top-k个最相似的文档片段
步骤4:构建Prompt
-
将检索到的片段拼接成上下文
-
添加指令,要求LLM基于这些片段回答
步骤5:LLM生成
-
将完整的Prompt发送给LLM
-
LLM基于检索到的内容生成答案
⚙️ 核心参数详解
| 参数 | 说明 | 推荐值 | 影响 |
|---|---|---|---|
| top_k | 检索片段数量 | 3-10 | 太少:信息不足 太多:引入噪声 |
| temperature | LLM生成温度 | 0.1-0.3 | RAG场景建议低温度,保证准确性 |
| max_tokens | 最大输出长度 | 200-500 | 控制答案长度和成本 |
💡 检索策略优化
top_k选择:
-
简单问题:3-5个片段足够
-
复杂问题:5-10个片段
-
可以先用较大值(如10),再通过重排序筛选
Prompt构建:
-
明确要求"只基于检索内容回答"
-
要求标注引用来源
-
如果无相关信息,明确回答"不知道"
运行方式:
# 使用默认问题 python llm_with_rag.py # 自定义问题 python llm_with_rag.py --query "你的问题"
输出:
-
output/rag_answer.txt- RAG回答结果(包含来源信息)
4.6 main.py - 一键对比
📖 功能说明
main.py脚本可以同时对比纯LLM和RAG的效果,直观展示RAG的优势。
运行方式:
python main.py
输出:
-
控制台:实时对比展示
-
output/comparison_result.txt- 对比结果文件
对比维度:
-
回答准确性
-
引用来源
-
响应时间
-
回答长度
五、参数调优实践
5.1 切片参数调优
chunk_size的影响:
| chunk_size | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 200-300 | 检索精度高 | 信息碎片化 | 短文档、FAQ |
| 500-800 | 平衡 | - | 大多数文档(推荐) |
| 1000+ | 上下文完整 | 检索精度下降 | 长文档、技术文档 |
调优方法:
-
运行
chunker.py生成不同size的切片 -
查看
output/chunks.json中的切片质量 -
运行
llm_with_rag.py测试检索效果 -
根据效果调整参数
5.2 检索参数调优
top_k的影响:
| top_k | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 1-3 | 答案简洁 | 可能信息不足 | 简单问题 |
| 5-10 | 平衡 | - | 大多数问题(推荐) |
| 10+ | 信息丰富 | 可能引入噪声 | 复杂问题 |
调优方法:
-
运行
llm_with_rag.py --k 3测试不同k值 -
观察检索到的片段是否相关
-
检查最终答案质量
-
根据效果调整k值
5.3 常见问题排查
问题1:检索不到相关内容
可能原因:
-
切片质量差(chunk_size不合适)
-
问题表述与文档差异大
-
Embedding模型选择不当
解决方法:
-
检查切片结果(
output/chunks.json) -
尝试调整chunk_size和overlap
-
使用更好的Embedding模型
问题2:检索到但不相关
可能原因:
-
top_k太大,引入了噪声
-
向量化质量不够
解决方法:
-
减小top_k值
-
使用重排序(Rerank)提升精度
-
优化问题表述
六、经验总结
6.1 核心收获
通过本工程,你应该已经:
-
✅ 理解了RAG的完整流程 - 从文档到答案的每一步
-
✅ 掌握了参数调优方法 - 知道如何调整参数提升效果
-
✅ 看到了RAG的优势 - 相比纯LLM的准确性和可追溯性
6.2 关键洞察
RAG系统的核心:
如何让RAG正确检索到相关信息? 这是RAG系统的灵魂所在。
影响因素(按重要性排序):
-
文档质量(最重要)- 结构化、清晰的文档
-
切片策略 - chunk_size、overlap、separators
-
检索策略 - top_k、相似度阈值
-
Embedding模型 - 影响语义理解能力
6.3 下一步学习
-
📖 第7章:学习如何评估RAG系统
-
🔧 第8章:学习性能优化和最佳实践
-
❓ 第9章:查阅常见问题解答
七、个人感悟
"什么是人工智能?就是人工+智能"。这是笔者多年前入职AI公司时大家自嘲的话语,没想到现在仍然奏效。
纵观RAG整个流程,关乎质量的几大关键点中,最核心的其实还是人工部分。高质量的切片结果直接决定了后续检索召回的效果。
为什么明明文档清清楚楚告诉了标准答案,无法被准确召回?因为切片太差劲了,这一小段标准答案被一大段文字向量化后的数字所淹没。
而提高切片质量智能无法起到全自动作用,最终效果调优还是得靠人工!人工写自定义的extractor(比如正则表达式)、人工调整参数、甚至直接人工来暴力改切片结果。
通过"人工+智能"的模式,理论上可以构建一个100%正确的向量知识库,那么剩下的压力都可以给到"检索和召回"上了😄。
参考文献
相关文档
-
Transformer架构详解 - Jay Alammar的可视化教程
-
Chroma向量数据库使用教程 - Chroma中文教程
工具文档
-
LangChain Text Splitters - 文本分割器文档
-
Chroma Documentation - Chroma官方文档
-
Dashscope Embeddings - 通义千问Embedding API
代码参考
-
RAG-项目源码 - 本教程配套代码
更多推荐
所有评论(0)