为什么 RAG 链路质量比大模型本身更重要
很多团队第一次做 RAG 的时候,注意力都放在大模型选型上——用什么模型、多大参数量、支不支持中文。但真正上线跑起来后会发现,用户问的问题答非所问,或者答案里掺了大量无关内容,根本原因往往不在模型端,而在于检索回来的东西质量太差。
RAG 的本质是”先搜后生成”,检索阶段决定了喂给大模型的上下文质量。用一句行业里常说的话:垃圾进,垃圾出。如果检索阶段召回的文档片段跟用户问题语义不相关,或者关键信息在切分时被截断了,再强的 LLM 也无法凭空补回来。
所以整条链路里,真正需要花精力打磨的是从文档处理到向量检索这一段。这篇文章就把这条链路拆开来讲:文档怎么切、Embedding 怎么选、向量怎么存、检索怎么做、常见坑在哪。
文档切分:RAG 性能的第一道门槛
切分质量决定了系统上限
文档切分(Chunking)是整个 RAG 流程的起点,也是最容易低估的一个环节。切得好,检索召回率和准确率都会有明显提升;切得不好,后续 Embedding 和检索做得再精致也白搭。
这里有一个很多团队踩过的坑:直接用 LangChain 默认的 RecursiveCharacterTextSplitter,设个 chunk_size=500、overlap=50 就上线了。跑通 demo 没问题,但真实知识库的文档结构千差万别——技术文档有标题层级,法律合同有条款编号,API 手册有代码示例和表格,统一按字符数硬切,很容易把一段完整的逻辑拆得支离破碎。
几种主流切分策略对比
| 切分策略 | 核心思路 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 固定长度切分 | 按字符/Token 数直接切割 | 实现简单、速度快 | 极易破坏语义边界 | 结构弱的纯文本、基线方案 |
| 递归字符切分 | 按标题→段落→换行→字符逐层递归 | 兼顾语义与块大小控制 | 对表格、代码效果一般 | 通用知识库、说明文档 |
| 结构感知切分 | 利用文档标题层级作为切分边界 | 逻辑清晰、可追溯性强 | 依赖文档结构解析质量 | Markdown/HTML 技术文档 |
| 语义切分 | 用 Embedding 判断相邻句子语义相似度来聚合 | 最大程度保留主题连贯性 | 计算成本高、依赖模型质量 | 长文、资讯、研究报告 |
| 父子索引 | 小块检索、大块喂给 LLM | 平衡检索精度与上下文丰富度 | 需要额外维护父子映射 | 对上下文连续性要求高的场景 |
实际项目里,很少只用一种策略。比较常见的做法是:以递归字符切分为基底,叠加结构感知逻辑——即先用标题、段落等结构性分隔符做粗分,再在超长段落下用递归切分做细分,同时设置 10%~20% 的重叠来缓解边界断裂。
有一个场景特别值得注意:当知识库包含大量表格或代码块时,纯文本切分会把表格拆成几段无意义的数据行。这种情况下,切分逻辑需要对表格、代码块做特殊保护——要么整块保留,要么用结构感知策略按表格单元切分,而不是简单地按行切割。
chunk_size 和 overlap 怎么调
这两个参数没有标准答案,但有一些经验性的判断框架:
- 中文文档:chunk_size 建议 300~800 字,overlap 设为 chunk_size 的 10%~20%
- FAQ 类短文本:chunk_size 可以更小,200~400 字即可,overlap 可以适当缩小
- 技术文档:按章节结构切,单块可以到 1000~1500 字,但要保证块内逻辑完整
- 如果检索结果经常缺乏上下文,说明 chunk 太小;如果召回噪声太大,说明 chunk 太大
下面是一段基于 LangChain 的递归字符切分示例,也是工程中最常用的基线配置:
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=80,
separators=["
", "
", "。", "!", "?", ";", ",", " ", ""],
keep_separator=True
)
chunks = splitter.split_text(document_text)
separators 的顺序很关键。中文场景下把句号、分号等标点加进去,可以让切分尽量落在句子边界,避免一句话被拦腰截断。keep_separator=True 是为了保留分隔符本身——尤其是标题行,有时候标题本身就携带了重要的语义信号。
Embedding 模型:把文本变成可计算的向量
切分完成后,下一步是把每个 chunk 转成向量。Embedding 模型的选择直接决定了向量空间里”语义相似”这件事到底靠不靠谱。
这里有个常见的误区:认为维度越高、模型越大,检索效果越好。实际上,Embedding 模型的效果更多取决于训练数据的覆盖面和你的领域匹配度,而不是单纯的模型规模。一个在通用语料上训练的大模型,未必比一个在你的垂直领域上做过微调的中小模型效果好。
选型时关注什么
| 模型 | 维度 | 中文支持 | 特点 | 适用场景 |
|---|---|---|---|---|
| BGE-large-zh | 1024 | 原生支持 | 开源、中文效果好、社区活跃 | 中文知识库、本地部署 |
| text-embedding-3-large (OpenAI) | 3072(可降维) | 支持 | 多语言能力强、API 调用 | 多语言场景、不想自建模型 |
| m3e-base | 768 | 原生支持 | 轻量、速度快 | 资源受限环境、快速验证 |
| GTE-large-zh | 1024 | 原生支持 | 在 C-MTEB 上表现稳定 | 对检索质量要求高的场景 |
一个容易被忽视的工程细节:离线索引和在线检索必须用同一个 Embedding 模型。这听起来是常识,但在实际项目中,团队迭代模型版本时经常出问题——用新模型重新生成了一批 chunk 的向量,但忘了更新历史数据,导致向量空间不一致,检索效果突然下降。
另一个坑是短文本的 Embedding 质量。有些模型对很长的文档向量效果很好,但对短查询(比如用户只输入三五个字)的向量化质量不行。做选型评测时,一定不要只看文档侧的召回效果,要同时测 Query 侧——用户真实输入的 query 通常很短、很口语化,跟知识库文档的书面表达差异很大。
向量存储与索引:选对方案比选对产品更重要
向量存到哪里,这个问题表面上是在选产品——Milvus、Pinecone、Qdrant、Weaviate,还是直接用 PostgreSQL 的 pgvector 插件。但本质上要回答的是:你的数据规模多大、检索延迟要求多高、需不需要元数据过滤、团队能不能维护独立组件。
一个典型的误判:团队只有几万条向量,数据增长也不快,却一上来就引入了 Milvus 集群。结果维护成本远超收益——Milvus 本身的部署、版本升级、配置调优都有不少坑,小规模场景下 pgvector 完全够用,而且直接挂在业务库里,不需要额外维护一套基础设施。
| 方案 | 适合规模 | 元数据过滤 | 运维复杂度 | 典型适用阶段 |
|---|---|---|---|---|
| pgvector (PostgreSQL) | 10万级以下 | 原生 SQL 支持 | 低 | MVP 验证、中小知识库 |
| Qdrant | 百万级 | payload 过滤 | 中 | 中等规模、需要灵活过滤 |
| Milvus | 千万级以上 | 标量字段过滤 | 高 | 大规模生产环境 |
| Pinecone | 弹性扩展 | metadata 过滤 | 低(全托管) | 不想维护基础设施 |
关于索引算法,大部分向量库默认使用 HNSW(Hierarchical Navigable Small World),它在召回率和查询延迟之间取得了不错的平衡。如果你的数据量特别大、对延迟要求没那么极端,IVF_FLAT 或 IVF_PQ 也是可选项——内存占用更低,但需要提前训练聚类中心,而且参数(nlist、nprobe)需要根据数据分布反复调。
检索策略:别只靠向量相似度
纯向量检索的局限
很多团队上线后发现一个尴尬的问题:用户搜”Q3 销售数据”,向量检索返回的是一段”Q2 的营销策略分析”——语义上确实相关,但用户要的是 Q3 的数据。纯向量检索擅长捕捉语义相似性,但对精确匹配(比如编号、日期、专有名词)并不敏感。
这就是为什么实际工程中,纯向量检索很少单独使用,混合检索(Hybrid Search)几乎成了标配。
混合检索的工程实现
混合检索的核心思路是:向量检索抓语义相似,关键词检索(BM25)抓精确匹配,两路结果融合后做统一排序。用 RRF(Reciprocal Rank Fusion)做融合是最常见的方式,因为它不需要调权重,对不同打分量纲天然鲁棒。
下面是一个简化的混合检索伪代码:
# 向量检索:ANN Top-K
vec_results = vector_store.search(query_embedding, top_k=20)
# 关键词检索:BM25 Top-K
bm25_results = bm25_index.search(query_text, top_k=20)
# RRF 融合排序
def rrf_fusion(rank_lists, k=60):
scores = {}
for rank_list in rank_lists:
for rank, doc in enumerate(rank_list):
scores[doc.id] = scores.get(doc.id, 0) + 1 / (k + rank + 1)
return sorted(scores.items(), key=lambda x: -x[1])
fused = rrf_fusion([vec_results, bm25_results])
top_k_final = fused[:10]
这里 k 是 RRF 的平滑常数,通常取 60 就够用了。融合之后取 Top-10 或 Top-20 送入下一步——Rerank 精排。
Rerank:用小成本换大提升
Rerank 模型(如 bge-reranker、Cohere Rerank)的作用是对召回的候选集做精细化排序。它跟 Embedding 模型的区别在于:Embedding 是把 query 和 document 各自独立编码成向量,而 Rerank 模型是把 query 和 document 拼在一起做交叉编码,能捕捉两者之间的深层交互关系,精度远高于向量相似度。
代价是计算成本高——Rerank 是逐对计算的,不能预计算。所以正确的用法是:先用向量检索 + BM25 做粗召回,拿到一个稍大的候选集(比如 Top-50),再用 Rerank 模型精选 Top-5~10,最后把这几条喂给 LLM。
一个实战中的判断:如果你的知识库文档质量高、切分做得好,Rerank 带来的提升可能不那么明显;但如果文档来源杂、噪声大、切分不够理想,Rerank 往往能把准确率拉升一个台阶。所以它更像是一层”保险”,而不是必需品——但有了它,你的容错空间会大很多。
Query 改写:别让用户口语化的提问拖累检索
真实场景中,用户的提问很少是结构清晰、关键词精准的检索语句。更多时候是”那个报销流程是啥来着”或者”上个月说的那个方案”。这种口语化、模糊、省略上下文的 query,直接拿去做向量检索,效果通常很差。
Query Rewrite 的做法是:先用 LLM 把用户的原始问题改写成更规范、更适合检索的形式。比如把”那个很火的手机”改写成”2024 年销量最高的智能手机型号”。也可以做 Query Decomposition——把一个复杂问题拆成多个子问题分别检索,再做合并。
这一步的成本是增加一次 LLM 调用,会让响应时间多 200~500 毫秒。对于实时性要求高的对话场景,可以考虑用小模型(比如 7B 级别的模型)做 Query Rewrite,而不是调大模型——改写任务对推理能力要求不高,小模型足够胜任。
真实落地中的典型问题与建议
元数据过滤被忽视
很多团队的知识库是分文档、分版本的。用户问”V2.0 的 API 文档”,如果检索时没有做版本过滤,很可能召回的是 V1.0 的旧文档,导致答案错误。在存入向量时把文档版本、创建时间、文档类型等元数据一并存好,检索时加上 metadata filter,这一步做起来不难但效果明显。
更新策略缺失
知识库不是静态的。文档更新了,对应的 chunk 和向量也要同步更新。比较常见的问题有两种:一是增量更新时只重新生成了变更文档的向量,但没有删除旧版本的向量,导致新旧版本共存、检索时随机命中;二是全量重建时没有做平滑切换,重建期间检索结果为空或者不完整。
建议的做法是:用文档 ID + 版本号作为向量记录的主键,更新时先写入新版本、再标记删除旧版本,或者用软删除 + 双版本灰度切换的方式过渡。
评估体系不到位
RAG 系统最大的工程难点不在于搭建,而在于评估和持续优化。很多团队上线后只能靠人工抽检来判断效果好坏,缺少系统化的评估手段。
比较实用的评估方式:
- 构建一个评估集:人工标注 100~200 个高频问题及其对应的正确文档片段
- 关注两个核心指标:召回率(Recall@K)——正确文档是否在 Top-K 里;准确率(Precision@K)——Top-K 里有多少是相关的
- 对生成端:用 LLM 做自动评估,判断答案是否忠实于检索内容、是否完整回答了用户问题
- 做 A/B 对比:每次改切分策略、换 Embedding 模型、调整 Rerank 参数,都用同一个评估集跑一遍,用数据说话
整条链路的取舍逻辑
回过头来看整条链路,每个环节都有多种方案可选,没有放之四海而皆准的最优解。但有一个贯穿始终的判断原则:先跑通最小闭环,再逐环节优化。
具体来说,建议的演进路径是:先用递归字符切分 + 开源 Embedding 模型 + pgvector 做通整个链路,确保数据能流通、能检索、能生成。然后根据实际效果定位瓶颈——如果是召回率低,优先优化切分策略和 Embedding 模型;如果是准确率低,加 BM25 混合检索和 Rerank;如果 query 质量差,引入 Query Rewrite。每次只改一个变量,用评估集验证效果。
RAG 的工程价值不在于某个单点技术多先进,而在于整条链路的每个环节都做到 80 分以上,才能组合出一个真正可用的系统。比起追新技术,把切分、检索、排序这三个环节做扎实,往往能带来最显著的收益。
原创文章,作者:,如若转载,请注明出处:https://fudengji.cn/article/276/