RAG 应用开发实战:从文档切分到向量检索的完整链路

为什么 RAG 链路质量比大模型本身更重要

很多团队第一次做 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/

(0)
上一篇 5天前
下一篇 5天前

相关推荐