一、准备工作

经过前期(必不可少的)铺垫学习,现在要进入到RAG实战环节了,我们通过rag_basic工程的调试带大家深入了解每一个核心部分,在自己跑出结果,调整参数后观察变化后,相信大家能够有更深的理解。

请先确保以下工作就绪:

  1. 正确clone了工程源码

  2. samples\rag_basic为根目录,单独兴建一个工程,并用uv和venv配置独立的依赖(工程配置参考quick_start,不赘述了)

  3. 本地配置ollama服务,并下载all-minilm:latest向量(可省略,但embedder效果无法和本地对比了)

  4. 正确配置了环境变量: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%

调优方法

  1. 运行chunker.py查看切片结果

  2. 检查output/chunks.json中的切片质量

  3. 调整参数后重新运行,对比效果

运行方式

python chunker.py

输出

  • output/chunks_simple.json - 简单切片结果

  • output/chunks.json - LangChain切片结果(推荐使用)


4.3 embedder - 向量化

📖 过程说明

什么是向量化?

向量化是将文本转换为固定长度的数值向量(数组)的过程。

为什么需要向量化?

  • 计算机无法直接理解文本语义

  • 向量化后可以用数学方法计算相似度

  • 实现"语义匹配"而非"关键词匹配"

🔍 向量化过程

输入:文本片段(如"如何更换电池?")

处理

  1. 文本通过Embedding模型

  2. 模型理解文本语义

  3. 输出固定长度的向量(如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 - 向量存储

📖 过程说明

目标:将向量化的文本存储到向量数据库中,便于后续检索

为什么需要向量数据库?

  • 向量数量可能很大(几千到几百万)

  • 需要快速检索相似向量

  • 传统数据库无法高效处理向量检索

🔍 存储过程
  1. 加载切片:读取chunks.json中的文本片段

  2. 向量化:对每个片段进行向量化(或使用已有向量)

  3. 存储:将向量和元数据存入Chroma数据库

  4. 持久化:保存到磁盘,下次可直接加载

⚙️ 向量数据库选择
数据库 特点 适用场景
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+ 上下文完整 检索精度下降 长文档、技术文档

调优方法

  1. 运行chunker.py生成不同size的切片

  2. 查看output/chunks.json中的切片质量

  3. 运行llm_with_rag.py测试检索效果

  4. 根据效果调整参数


5.2 检索参数调优

top_k的影响

top_k 优点 缺点 适用场景
1-3 答案简洁 可能信息不足 简单问题
5-10 平衡 - 大多数问题(推荐)
10+ 信息丰富 可能引入噪声 复杂问题

调优方法

  1. 运行llm_with_rag.py --k 3测试不同k值

  2. 观察检索到的片段是否相关

  3. 检查最终答案质量

  4. 根据效果调整k值


5.3 常见问题排查

问题1:检索不到相关内容

可能原因

  • 切片质量差(chunk_size不合适)

  • 问题表述与文档差异大

  • Embedding模型选择不当

解决方法

  1. 检查切片结果(output/chunks.json

  2. 尝试调整chunk_size和overlap

  3. 使用更好的Embedding模型

问题2:检索到但不相关

可能原因

  • top_k太大,引入了噪声

  • 向量化质量不够

解决方法

  1. 减小top_k值

  2. 使用重排序(Rerank)提升精度

  3. 优化问题表述


六、经验总结

6.1 核心收获

通过本工程,你应该已经:

  1. 理解了RAG的完整流程 - 从文档到答案的每一步

  2. 掌握了参数调优方法 - 知道如何调整参数提升效果

  3. 看到了RAG的优势 - 相比纯LLM的准确性和可追溯性

6.2 关键洞察

RAG系统的核心

如何让RAG正确检索到相关信息? 这是RAG系统的灵魂所在。

影响因素(按重要性排序)

  1. 文档质量(最重要)- 结构化、清晰的文档

  2. 切片策略 - chunk_size、overlap、separators

  3. 检索策略 - top_k、相似度阈值

  4. Embedding模型 - 影响语义理解能力

6.3 下一步学习

  • 📖 第7章:学习如何评估RAG系统

  • 🔧 第8章:学习性能优化和最佳实践

  • 第9章:查阅常见问题解答


七、个人感悟

"什么是人工智能?就是人工+智能"。这是笔者多年前入职AI公司时大家自嘲的话语,没想到现在仍然奏效。

纵观RAG整个流程,关乎质量的几大关键点中,最核心的其实还是人工部分。高质量的切片结果直接决定了后续检索召回的效果。

为什么明明文档清清楚楚告诉了标准答案,无法被准确召回?因为切片太差劲了,这一小段标准答案被一大段文字向量化后的数字所淹没。

而提高切片质量智能无法起到全自动作用,最终效果调优还是得靠人工!人工写自定义的extractor(比如正则表达式)、人工调整参数、甚至直接人工来暴力改切片结果。

通过"人工+智能"的模式,理论上可以构建一个100%正确的向量知识库,那么剩下的压力都可以给到"检索和召回"上了😄。


参考文献

相关文档

  1. Transformer架构详解 - Jay Alammar的可视化教程

  2. Chroma向量数据库使用教程 - Chroma中文教程

工具文档

代码参考

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐