RAG 系列第二章:向量数据库与文本分块——两个关键决策
这是 RAG 系列第二章。上一章我们讲了”什么是 RAG”,这章讲”怎么搭 RAG”——向量数据库和文本分块,选对了事半功倍,选错了白折腾。
系列导航
本系列参考 DataWhale All-in-RAG 教程,聚焦实战落地。
| 章节 | 主题 | 状态 |
|---|---|---|
| 第一章 | RAG 简介:让 AI “查资料” 再回答 | ✅ 已发布 |
| 第二章 | 向量数据库与文本分块 | 📝 本篇 |
| 第三章 | RAG 实战:搭建企业问答系统 | 🔜 敬请期待 |
| 第四章 | 检索优化:混合检索、重排序 | 🔜 敬请期待 |
| 第五章 | RAG 进阶:GraphRAG、Agentic RAG | 🔜 敬请期待 |
一、很多团队搞 RAG,效果像”百度搜索但三成准确”
上周有个朋友找我吐槽:
“我们花了两周搭 RAG,问公司财报数据,AI 回答’营收增长15%’——但没说具体数字。问报销流程,返回的还是三年前的旧制度。”
问题在哪?不是 RAG 不行,是架构选错了。
RAG 有两个”命门”:
- 向量数据库选错了 → 成本爆炸 or 检索太慢
- 文本分块策略不对 → 答案不完整 or 噪音太大
这章专门讲这两个关键决策。
二、向量数据库:RAG 的”记忆中枢”
2.1 为什么需要向量数据库?
向量数据库是一种专门用于存储、管理和查询高维向量的数据库。
核心能力:
| 功能 | 说明 |
|---|---|
| 高效相似性搜索 | 利用 HNSW、IVF 等索引,在数十亿向量中毫秒级查询 |
| 高维数据存储 | 支持向量的增删改查 |
| 标量过滤 | 按时间、标签等字段过滤(如”只检索2024年的文档”) |
| 分布式扩展 | 支持水平扩展,应对大规模数据 |
2.2 向量数据库 vs 传统数据库
| 维度 | 向量数据库 | 传统数据库 |
|---|---|---|
| 核心数据类型 | 高维向量 | 结构化数据 |
| 查询方式 | 相似性搜索(ANN) | 精确匹配 |
| 索引机制 | HNSW、IVF、LSH | B-Tree、Hash |
| 应用场景 | AI应用、RAG、推荐系统 | 业务系统、金融交易 |
两者是互补关系:向量数据库负责语义检索,传统数据库负责结构化查询,通常结合使用。
2.3 主流向量数据库对比
| 数据库 | 类型 | 性能 | 部署难度 | 成本 | 适用场景 |
|---|---|---|---|---|---|
| Milvus | 开源 | ⭐⭐⭐⭐⭐ | 中等 | 低 | 大规模生产环境,支持GPU加速 |
| Chroma | 开源 | ⭐⭐⭐ | 简单 | 低 | 小项目、原型验证、零配置 |
| FAISS | 开源 | ⭐⭐⭐⭐ | 简单 | 低 | 本地实验、轻量高效 |
| Pinecone | 云服务 | ⭐⭐⭐⭐⭐ | 最简单 | 高 | 企业级、不想运维、Serverless |
| Weaviate | 开源/云 | ⭐⭐⭐⭐ | 中等 | 中 | 多模态支持、GraphQL查询 |
| Qdrant | 开源 | ⭐⭐⭐⭐ | 简单 | 低 | Rust 高性能、RPS>4000 |
2.4 选型决策:30秒搞定
问自己三个问题:
-
数据量多大?
- < 10万条 → Chroma 或 FAISS(够用)
- 10万~100万条 → Qdrant 或 Weaviate
-
100万条 → Milvus(分布式架构)
-
要不要运维?
- 不想运维 → Pinecone(贵但省心)
- 可以运维 → Milvus / Qdrant(开源省钱)
-
有没有特殊需求?
- 需要按时间/标签过滤 → Weaviate
- 需要GPU加速 → Milvus
- 只做个 demo → Chroma(零配置,5分钟搞定)
DataWhale 教程的建议:新手入门推荐 Chroma 或 FAISS;生产环境大规模应用推荐 Milvus、Weaviate 或 Pinecone。
2.5 成本对比(人民币)
假设 100万条向量,存储 10GB:
| 方案 | 月成本 | 说明 |
|---|---|---|
| Pinecone | ¥3500+ | 云服务计费,数据越多越贵 |
| Milvus(自建) | ¥350 | 一台云服务器就够了 |
| Chroma(本地) | ¥0 | 文件存储,零成本 |
结论:小项目用 Chroma,大项目用 Milvus,预算充足用 Pinecone。
三、文本分块:不是”切得越小越好”
3.1 为什么需要分块?
文本分块是将长文档切分成更小单元的过程,这些文本块是向量检索和模型处理的基本单位。
不分块会出问题:
| 问题 | 原因 | 影响 |
|---|---|---|
| 模型输入限制 | 嵌入模型有长度上限(如 bge-base-zh-v1.5 为512 token) | 长文档无法处理 |
| 信息稀释 | 768维向量需概括整个文本块,块越长语义越模糊 | 检索匹配失败 |
| “大海捞针” | LLM倾向记住开头结尾,忽略中间内容 | 答案遗漏关键信息 |
| 主题混杂 | 多主题混合在一个大块里 | 检索噪音大 |
3.2 分块大小的权衡
很多人以为:切得越小,检索越精准。
错。
- 切太小 → 上下文丢失,答案不完整
- 切太大 → 语义稀释,噪音太多
比如问”公司去年营收多少?”
- chunk_size=100,检索结果只有:”同比增长15%” —— 数字丢了
- chunk_size=2000,检索返回整篇文章 —— AI 找不到关键信息
3.3 四种主流分块策略
| 策略 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 固定大小 | 每500字切一段 | 简单可控 | 可能切断语义 |
| 递归字符 | 先段落再句子递归切 | 平衡语义和长度 | ✅ 推荐 |
| 语义分块 | 按语义相似度切分 | 语义最完整 | 需要Embedding计算 |
| 结构分块 | 按Markdown标题切分 | 结构清晰 | 仅适用结构化文档 |
3.4 推荐策略:递归字符分块
LangChain 默认策略,先按段落切,段落太大再按句子切,句子太大再按字切:
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
separators=["\n\n", "\n", "。", ";", ",", " ", ""],
chunk_size=500, # 每段最大500字
chunk_overlap=50, # 重叠50字(防止断句)
)
chunks = text_splitter.split_documents(docs)
3.5 参数选择指南
| 场景 | chunk_size | overlap | 说明 |
|---|---|---|---|
| 问答场景 | 300 | 50 | 检索精准,答案简短 |
| 长文档 | 600 | 100 | 保留上下文 |
| 法律/合同 | 800 | 150 | 必须完整 |
| 技术文档 | 400 | 80 | 代码片段完整 |
经验值:chunk_size ≥ 300,overlap ≥ 50,这是底线。
四、实战:10行代码搭建最小 RAG
结合 Chroma + LangChain,最小可用架构:
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Chroma
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA
# 1. 分块
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_documents(docs)
# 2. 向量化(用中文效果好的 BGE 模型)
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-large-zh")
vectorstore = Chroma.from_documents(chunks, embeddings)
# 3. 搭建 RAG 链
qa = RetrievalQA.from_chain_type(
llm=ChatOpenAI(model="gpt-4"),
retriever=vectorstore.as_retriever(search_kwargs={"k": 3})
)
# 4. 提问
answer = qa.run("公司去年营收是多少?")
架构流程:
文档 → 分块 → 向量化 → Chroma存储 → 检索 → 增强提示词 → LLM生成
五、进阶技巧:让准确率提升40%
基础架构搭好了,但检索可能还是不准。两个进阶技巧:
5.1 混合检索:向量 + 关键词
向量检索擅长语义匹配(”赚钱”→”营收”),关键词检索擅长精确匹配(”500亿”)。
from langchain.retrievers import EnsembleRetriever, BM25Retriever
# 向量检索
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
# 关键词检索(BM25)
bm25_retriever = BM25Retriever.from_documents(chunks)
bm25_retriever.k = 5
# 混合:各占50%
ensemble = EnsembleRetriever(
retrievers=[vector_retriever, bm25_retriever],
weights=[0.5, 0.5]
)
5.2 重排序:挑出最相关的3条
检索返回10条,用重排序模型筛选:
from langchain.retrievers.document_compressors import CrossEncoderReranker
reranker = CrossEncoderReranker(
model_name="BAAI/bge-reranker-large",
top_n=3 # 只给LLM最相关的3条
)
# 带重排序的检索
from langchain.retrievers import ContextualCompressionRetriever
final_retriever = ContextualCompressionRetriever(
base_retriever=ensemble,
base_compressor=reranker
)
实测效果:准确率提升 30%~40%。
六、避坑清单
| 坑 | 表现 | 解决方案 |
|---|---|---|
| 向量库选太贵 | Pinecone月费¥3500+ | 用Milvus/Chroma开源方案 |
| 切分太碎 | 答案不完整 | chunk_size≥300, overlap≥50 |
| 检索不重排序 | 返回噪音多 | 加 CrossEncoder 重排序 |
| 文档不清洗 | 检索全是广告 | 去除页脚、导航、无效内容 |
| 只用向量检索 | 精确匹配失败 | 混合检索:向量+关键词 |
七、本章小结
两个关键决策:
- 向量数据库:小项目用 Chroma,大项目用 Milvus
- 文本分块:递归字符分块,chunk_size=500,overlap=50
记住:架构选对了,RAG 才能跑;选错了,就是白折腾。
下章预告
第三章:RAG 实战:搭建企业问答系统
内容包括:
- 环境配置(API密钥、虚拟环境)
- 完整代码(可直接运行)
- 效果评测(如何衡量准确率)
参考资料
关于码孖 AI
码孖 AI,专注 AI 工程化落地。向量数据库选型、文本分块策略,我们帮你踩过坑了,你不用再踩。
关注我,下一章带你实战搭建企业问答系统。