RAG 全链路深度解析:从 Chunking 到生产落地的工程实践
RAG 全链路深度解析:从 Chunking 到生产落地的工程实践
大模型很强,但这个"强"是有边界的。你的知识截止在训练的那一天,你无法访问公司的内部文档,还时不时编造一些听起来很合理但完全是虚构的东西。这就是为什么我们需要 RAG(Retrieval-Augmented Generation)——给大模型配一个外挂知识库,让它每次回答前先去查资料。
RAG 的技术栈并不复杂,但要把每个环节都做到工程可用,里面隐藏着大量的权衡与取舍。本文从原理到工程,系统梳理 RAG 全链路的技术细节。
1. 为什么需要 RAG
LLM 天生有三个硬伤,而 RAG 恰好是它们的解药。
| 问题 | 说明 | RAG 怎么解决 |
|---|---|---|
| 知识截止 | 模型训练数据有时间限制 | 检索实时/最新数据 |
| 幻觉 | 编造不存在的事实 | 基于真实检索结果回答 |
| 领域知识不足 | 对内部文档、专业知识了解有限 | 接入私有知识库 |
但这三个问题并不是在所有场景下都需要解决。如果你想用 LLM 做数学推理、代码生成或创意写作,RAG 的意义不大——这些任务不需要外部知识,反而需要模型自身的推理能力。同样,如果是风格统一、术语固定的场景(比如客服话术模板),用 Fine-tuning 固化比每次检索更高效。
小结: RAG 解决的是"事实性知识"的问题,不是所有问题。选不选 RAG,取决于你的场景是否需要、以及能否获取到可信的外部信息。
2. RAG 还是 Fine-tuning:一个架构选择题
这是面试里最高频的问题之一,但在工程实践中也是一个真实存在的选型困境。
| 维度 | RAG | Fine-tuning |
|---|---|---|
| 更新成本 | 低,改数据库即可 | 高,需重新训练 |
| 知识范围 | 大,可到 TB 级 | 受模型容量限制 |
| 适合什么 | 事实、文档、可枚举知识 | 风格、任务范式、领域语言 |
| 可解释性 | 高,有引用可追溯 | 低,黑盒 |
| 延迟 | 多一步检索,延迟略高 | 纯生成,延迟更低 |
关键判断标准:如果知识需要频繁更新、或者知识量很大,RAG 是更好的选择。如果是想教会模型某种写作风格或推理模式,Fine-tuning 更合适。当然,两者可以结合——先用 RAG 召回事实,再用 Fine-tuned 模型以特定风格组织回答。
小结: RAG 管事实,Fine-tuning 管风格。这不是二选一,而是可以根据场景组合使用的两种工具。
3. RAG 全链路概览
RAG 系统分为离线(数据准备)和在线(检索生成)两个阶段。
离线阶段:
原始文档 → 清洗 → 切分(chunking)→ 向量化(embedding)→ 存入向量库
在线阶段:
用户问题 → 查询改写 → 向量化 → 向量检索 + 关键词检索
↓
重排(rerank)
↓
Top-K 文档 → 拼接 prompt → LLM 生成
离线阶段做一次、在线阶段每次请求都做。离线决定知识的上限,在线决定响应的质量。
小结: 理解清楚这两个阶段的职责划分,是搭建 RAG 系统的第一步。离线做得好,在线阶段才能有好的素材可用。
4. 数据基石:Chunking 切分策略
文档切分是整个 RAG 系统里"看似简单但影响深远"的一步。切得太粗,相关信息在长文本里被稀释,检索不准;切得太细,单块信息太少,LLM 拿不到完整上下文。
主流切分策略
| 策略 | 说明 | 适用场景 |
|---|---|---|
| 固定字符切分 | 每 N 个字符一刀切 | 粗糙原型,不推荐生产 |
| 按段落 | 按空行切 | 结构化文档 |
| 按句子 | NLP 工具切句 | 问答型文档 |
| 递归切分(Recursive) | 先按大边界切,超长再按小边界 | 默认推荐 |
| 语义切分(Semantic) | 用 Embedding 相似度找断点 | 高质量要求场景 |
| 按 Markdown 结构 | 按标题层级切 | 技术文档、Wiki |
生产环境中最常用的是递归切分:先尝试按段落切,如果段落太长再按句子切,句子还长就按固定长度截断。这样能在语义完整性和长度控制之间取得良好的平衡。
关键参数:chunk_size 与 chunk_overlap
- chunk_size:200-1500 token。短查询、精准问答用 300-500;需要长篇上下文理解的用 1000+。
- chunk_overlap:10-20% of chunk_size。避免相关信息刚好被切到边界上,代价是存储和检索成本略有增加。
有一个小技巧值得单独说:Parent-Child Chunking。把文档切成两级,小 chunk 用于检索(更精准),命中后返回对应的大 chunk 喂给 LLM(信息更完整)。这样既保证了召回精度,又保证了生成质量。
小结: 没有"最好"的 chunk 大小,只有"最适合当前文档和查询"的大小。parent-child 是兼顾检索精度和上下文完整性的实用方案。
5. 语义编码:Embedding 模型选型
Chunk 准备好了,下一步是把文字转换成向量。Embedding 模型的选择直接影响检索质量。
常用模型对比
| 模型 | 维度 | 特点 |
|---|---|---|
| text-embedding-3-small | 1536 | 效果均衡,成本低 |
| text-embedding-3-large | 3072 | 效果强,成本适中 |
| BGE (BAAI/bge-*) | 768/1024 | 开源,中文支持好 |
| M3E | 768 | 中文领先的开源模型 |
选型时关注三个点:
- 语言覆盖:中文场景 BGE / M3E 往往优于 OpenAI 的模型
- 领域适配:通用模型在医疗、法律、代码等专业领域可能不够,可以针对领域做 fine-tune
- 维度与成本:高维度效果更好但存储和计算成本更高。Matryoshka 式嵌入(可降维)是兼顾两者的新趋势
工程上还需要注意:批量处理能降低 5-10 倍 API 成本;相同文本重复嵌入浪费钱,加一层哈希缓存;换 Embedding 模型需要重建索引,保留旧索引直到新索引验证完毕。
小结: Embedding 模型是 RAG 的"翻译官",把人类语言翻译成计算机能检索的向量。选一个好翻译官,比后面花太多功夫调优检索策略更重要。
6. 向量存储:索引算法与数据库选型
数据量大了以后,暴力搜索太慢——遍历 1000 万条向量显然不现实。这时就需要 ANN(Approximate Nearest Neighbor)近似最近邻算法。
索引算法对比
| 算法 | 原理 | 特点 |
|---|---|---|
| Flat(暴力) | 遍历所有向量 | 100% 精度,慢 |
| IVF | 聚类 + 桶内搜索 | 快,精度中等 |
| HNSW | 分层图结构 | 最常用,快且精度高 |
| PQ | 向量压缩 | 省存储,精度略降 |
HNSW 是工业界的首选。 它的核心思想类似跳表(skip list):从稀疏的高层图快速定位到目标区域,再到密集的低层精细搜索。关键参数是 M(每个节点的连接数)和 efSearch(检索时搜索宽度)。
向量数据库选型
| 数据库 | 特点 | 适用规模 |
|---|---|---|
| Pinecone | 托管 SaaS,免运维 | 中小到大型 |
| Weaviate | 功能全,支持混合检索 | 中型 |
| Qdrant | 性能好,Rust 实现 | 中小到大型 |
| Milvus | 大规模、高性能 | 亿级+ |
| Chroma | 轻量,嵌入式 | 原型/小型 |
| pgvector | PostgreSQL 扩展 | 中小型,统一管理 |
规模估算:1000 万条 chunk、1536 维 float32 需要约 60GB 存储,用 PQ 压缩可以降到约 6GB,精度损失 5% 以内。
小结: HNSW + Qdrant 是目前中小规模场景的黄金组合。大规模场景可以考虑 Milvus 搭配 PQ 压缩。
7. 检索策略:从单路到混合
向量检索(Dense Retrieval)
用 Embedding 把 query 转成向量,在向量索引里找最近的 K 个。能捕捉语义——"笔记本电脑"和"笔记本"是相近的。但对关键词精确匹配不敏感,"iPhone 15"和"iPhone 14"在向量空间里可能很接近。
关键词检索(Sparse Retrieval)
BM25 是经典方案——按词频和逆文档频率打分。优点是对关键词、专有名词、数字、代码能做精准匹配。缺点是不理解同义词。
混合检索(Hybrid Retrieval)
生产级的 RAG 系统基本都用混合检索——两条路并行,结果合并。
合并方法:
- RRF(Reciprocal Rank Fusion):
score = Σ 1/(k + rank_i),最常用,不需要分数归一化 - 加权分数:
final = α · vec_score + (1-α) · bm25_score,需要归一化 - Rerank:两路 top-N 合并后统一重排
Top-K 的选择也有讲究。太小(K=3)召回率低,太大(K=20+)会稀释上下文且成本高。推荐策略是粗召回 K=20-50,经过 rerank 后取 top 5-10。
小结: 纯向量检索就像只靠感觉找东西,纯关键词检索就像只靠目录找东西。混合检索把两者结合起来,才是生产级的做法。
8. Rerank:粗召回后的精过滤
向量检索用的双塔模型(query 和 doc 分别编码,算相似度),速度快但精度有限。Rerank 用交叉编码器——把 query 和 doc 拼在一起喂给模型,直接输出相关度分数,精度更高但慢得多。
阶段 1:向量检索 → top-50(快但粗)
阶段 2:Rerank → top-5(慢但准)
常用 Reranker:
- Cohere Rerank:API,效果好,多语言
- BGE Reranker:开源,中文强
- LLM as Reranker:用 GPT-4 打分,效果最好但最贵
工业界的经验:加 Rerank 可以把精确率再提 10-30%,尤其在 top-3 指标上提升明显。代价是增加 100-500ms 的延迟。
当然,并不是所有场景都需要 Rerank。如果检索数据量小(几百条)、精度要求不高、或者有严格的延迟预算,可以跳过 Rerank。
小结: Rerank 是用时间来换精度的经典策略。精召回在前,粗筛选在后,两阶段配合才能兼顾速度和准确率。
9. Query 改写:让问题更精准
用户的问题往往不太讲究——简短、含代词、缺上下文。直接拿原问题去检索效果可能很差。Query 改写就是解决这个问题的。
几种常见方法
Query Expansion / Rewriting: 让 LLM 把模糊的问题补全。例如"它怎么用?"改写为"LangChain 的 RunnableSequence 怎么使用?"
HyDE(Hypothetical Document Embeddings): 一个很有意思的技巧——让 LLM 先假装回答这个问题,生成一段假想答案,然后用这段假想答案去检索。原理是:在向量空间里,"答案与相关文档的距离"通常比"问题与相关文档的距离"更近。
Step-Back Prompting: 问题太具体时,先抽象成更宏观的问题再检索。例如"梅西 1987 年 6 月 24 日出生那天是星期几?"先退回一步查"梅西出生在哪一天",查到出生日再推算。
问题分解(Decomposition): 复杂问题拆成子问题分别检索。例如"比较 LangChain 和 LlamaIndex 在 RAG 上的优劣"拆成三个子问题分别检索后再综合回答。
小结: Query 改写是最容易被忽视的优化点。用户提问随意,但检索需要精准。把随意变成精准,是 Rerank 之前最值得投入的优化之一。
10. 评估体系:衡量 RAG 好坏的标尺
没有评估就没有优化。RAG 的评估分两个层面:
检索层面
| 指标 | 关心什么 | 一句话理解 |
|---|---|---|
| Recall@K | 有没有漏 | 相关文档召回到多少 |
| Precision@K | 有没有错 | 召回的结果里多少是相关的 |
| MRR | 第一次对有多快 | 第一个正确答案排第几 |
| NDCG@K | 排序质量 | 相关的是否排在前面 |
生成层面
| 指标 | 评估什么 |
|---|---|
| Faithfulness | 答案是否基于检索内容,没有幻觉 |
| Answer Relevance | 答案是否回答用户的问题 |
| Context Precision | 检索回来的内容里有多少真正用上了 |
| Context Recall | 生成好答案所需的信息是否都检索到了 |
常用评估框架:RAGAS 专为 RAG 设计,开箱即用,覆盖上面四个生成层面指标。LangSmith 和 Langfuse 则提供了更完整的追踪和评估平台。
评估时一个比较实际的做法是:构建一套标准问答对并标注好相关文档,然后用 LLM-as-judge 打分,加人工抽检。这样可以定期跑指标,跟踪系统的退化或改进。
小结: 没有评估的 RAG 系统就像没有仪表盘的汽车——你在开车,但不知道速度、油量和方向。分层评估、定期运行、追踪趋势,是持续优化 RAG 系统的基础设施。
总结与思考
RAG 本质上是给大模型配了一个"外挂大脑"。但把这个外挂做好,需要理解从数据准备到检索生成的每一个环节:
- 数据层面:Chunk 怎么切、Embedding 用什么模型、向量索引怎么建
- 检索层面:混合检索召回、Rerank 精筛、Query 改写补全
- 评估层面:分层指标追踪、持续迭代优化
最容易被忽略的一点是:RAG 是个系统工程,不是加一个向量数据库就完事了。 每个环节的调优都会影响最终效果,而真正生产可用的 RAG 系统需要把这些环节串起来,配合可观测性、A/B 测试和持续评估,才能稳定迭代。