RAG 系统从零搭建指南:向量检索、重排策略对比、踩坑记录(附完整代码)
从零搭建 RAG 系统,我踩了 30 个坑后总结出这份指南
想做一个「上传 PDF → 智能问答」的系统,搜了一圈发现教程太浅、方案太重、踩坑太多。这篇文章把从零到生产的完整路径整理清楚了。
前言
最近想做一个 RAG(Retrieval-Augmented Generation)系统,需求很简单:上传 PDF,然后对文档提问。
搜了一圈教程,发现几个问题:
-
教程太浅:只讲
langchain.load_qa_chain(),生产环境根本不能用 -
方案太重:上来就要 Milvus + Kubernetes,个人项目扛不住
-
缺少对比:向量检索 vs 关键词检索 vs 混合检索,到底选哪个?
-
踩坑太多:embedding 维度不匹配、中文分词断裂、reranker 超时……
花了两周时间摸索后,我把所有经验整理成了一个开源仓库:rag-system-building,每一步都有代码、有对比、有踩坑记录。
一、架构设计
RAG 系统由两条管道组成:
Ingestion: 文档 → 解析 → 分块 → 向量化 → 存储 Query: 问题 → 向量化 → 检索 → 重排 → LLM 生成
按依赖顺序构建,每个模块只依赖前面的模块:
config.py → pdf_parser → text_splitter → embeddings → vector_store → retriever → reranker → llm → orchestrator → app
二、技术选型
向量数据库:FAISS 是最佳起点
| 数据库 | 适用规模 | 推荐场景 |
|---|---|---|
| FAISS | <50 万条 | 本地开发、小项目 |
| ChromaDB | <50 万条 | 需要元数据过滤 |
| Milvus | >100 万条 | 生产环境 |
FAISS 零依赖、速度最快,50 万条以内完全够用。不要一上来就上 Milvus。
Embedding 模型:bge-small-zh
中文场景推荐 BAAI/bge-small-zh-v1.5:
-
512 维,本地推理,CPU 也能跑
-
~1000 条/秒的编码速度
-
注意:需要加检索前缀
为这个句子生成表示以用于检索:
LLM:qwen-turbo 性价比最高
from openai import OpenAI client = OpenAI( api_key="sk-xxx", base_url="https://dashscope.aliyuncs.com/compatible-mode/v1" )
通义千问 qwen-turbo 是国内最便宜的选择,RAG 场景下质量足够。
三、重排策略对比(核心干货)
向量检索返回的 top-K 结果不一定按相关性排序,需要重排(Reranking)。
我对比了三种方案的实际性能(10 个文档块,Apple M4):
| 方案 | 延迟 | 准确率提升 | 推荐场景 |
|---|---|---|---|
| 无重排 | 0ms | 基线 | 延迟敏感 |
| TF-IDF | 3ms | +12% | 默认推荐 |
| CrossEncoder | 450ms | +18% | 精度优先 |
| LLM Reranker | 5.5s | +22% | 不推荐 |
结论:TF-IDF 是最佳默认选择。
3ms 延迟,+12% 准确率,零外部依赖。CrossEncoder 精度更高但慢 150 倍。LLM Reranker 性价比最低——每次调用 O(N) 次 API,DeepSeek 从国内调用还经常超时。
端到端优化效果:
优化前:CrossEncoder + qwen-plus + top_k=10 → 4558ms 优化后:TF-IDF + qwen-turbo + top_k=5 → 595ms(87% 提升)
四、关键词兜底检索(踩坑重点)
当向量检索分数过低(< 0.5)时,需要关键词兜底。这里有一个中文关键词提取的大坑:
# ❌ 错误:提取整句话作为关键词
re.findall(r'[\u4e00-\u9fa5]{2,}', "武汉力源信息技术股份有限公司组织结构图")
# → ["武汉力源信息技术股份有限公司组织结构图"] → 搜索时零匹配
# ✅ 正确:提取 2-4 字短词
re.findall(r'[\u4e00-\u9fa5]{2,4}', "武汉力源信息技术股份有限公司组织结构图")
# → ["武汉", "力源", "信息", "技术", "股份", "有限", "公司", "组织", "结构"]
正则 [\u4e00-\u9fa5]{2,} 会把整句话当做一个关键词,导致搜索时永远匹配不到。必须限制为 2-4 字。
五、常见踩坑记录
仓库里整理了 10+ 个踩坑记录,这里挑几个最典型的:
1. Embedding 维度不匹配
换了 embedding 模型但没有重建索引。FAISS 索引的维度是创建时固定的,换了模型必须删掉旧索引重建。
2. BGE 模型缺少检索前缀
# ❌ 错误 vectors = model.encode(["什么是RAG?"]) # ✅ 正确 query = "为这个句子生成表示以用于检索:什么是RAG?" vectors = model.encode([query])
3. Streamlit 工作目录问题
Streamlit 的工作目录是 streamlit run 命令执行的目录,不是脚本所在目录。用 Path(__file__).parent 获取正确路径。
4. LLM 回答 "文档中未找到相关信息"
排查步骤:
-
确认 chunks 数量 > 0
-
确认检索有结果(分数 > 阈值)
-
确认 LLM 收到了上下文(打印 prompt)
常见原因:分数阈值设太高(> 0.8),实际最高分只有 0.6。
六、仓库里有什么
rag-system-building/ ├── docs/ │ ├── architecture.md # 架构设计 + 技术选型决策树 │ ├── document-parsing.md # PDF/DOCX/TXT 解析方案 │ ├── vector-search.md # 向量检索方案对比 │ ├── reranking.md # 重排策略对比 + 性能基准 │ ├── llm-integration.md # LLM 接入方案 │ ├── performance.md # 性能优化指南 │ └── common-pitfalls.md # 10+ 踩坑记录 ├── templates/ │ ├── rag-pipeline.py # 完整 RAG 管道(可直接运行) │ ├── streamlit-ui.py # Streamlit 前端 │ └── config.py # 配置模板 └── benchmarks/ └── reranking-comparison.md # 重排性能基准测试
总结
搭建 RAG 系统,核心就几句话:
-
FAISS 够用就别上 Milvus
-
TF-IDF 重排是最佳默认选择
-
qwen-turbo 性价比最高
-
中文关键词提取用 2-4 字短词
-
embedding 模型换了必须重建索引
完整方案、代码和踩坑记录都在 GitHub 仓库里:
如果对你有帮助,给个 Star 吧 ⭐
本文同步发布于 GitHub
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)