【从零理解 Spring AI · 14】RAG 的四个环节:加载、切分、向量化、检索
写在开头
上一篇的最小闭环里,"把文档导入向量库"被一句话带过了。真到落地,你会发现RAG 项目的坑 80% 都埋在导入环节:PDF 解析乱码、段落切得稀碎、检索回来的内容答非所问。
这篇把从"一堆 PDF"到"能被语义检索的知识"的完整流水线讲清楚------四个环节、每个环节在解决什么问题、取舍在哪里。

四段流水线,每段都有取舍
一、① 加载:把各种格式统一成"文档对象"
企业知识散落在 PDF、Word、网页、Markdown、数据库里。第一步是把它们读进来,抹平格式差异,统一成框架里的 Document 对象(内容 + 元数据)。
Spring AI 的 ETL 管线提供了各类 DocumentReader:PDF、文本、Tika(通用格式)等,读进来就是统一的 Document 流。
这个环节最常见的坑 :PDF 解析质量。扫描版 PDF(图片)读出来是空的或乱码,表格会被打碎------垃圾进,垃圾出,后三个环节再好也救不回来。导入后抽检解析结果,是 RAG 项目的第一课。
二、② 切分:RAG 质量的第一决定因素
长文档不能整篇塞给模型------既超上下文,检索也不准。要切成小段,每段独立成为检索的单元。
切分的两难:
| 切得太碎 | 切得太粗 |
|---|---|
| 一段话被拦腰斩断,语义不完整 | 一个段落混了多个主题 |
| 检索回来的片段缺上下文,模型答不全 | 相似度被稀释,召回不准 |
实践中的做法 (Spring AI 的 TokenTextSplitter 支持):
- 按固定大小切 + 重叠:每段约几百个 token,相邻段重叠一部分------被切断的句子在相邻段里还有完整版本;
- 按结构切优先:有标题、章节的文档沿结构边界切,天然保住语义完整性;
- 切分时带上元数据:来源文件名、章节标题、页码------检索命中后能溯源,还能当过滤条件用。
没有万能参数 ,不同文档类型的最优粒度不一样,要靠"导入 → 测试一批典型问题 → 调整"的循环来磨。
三、③ 向量化:把"语义"变成"坐标"
切好的段落要变成向量(Embedding)------一串数字,代表这段文本的"语义坐标"。
它为什么能工作? 向量模型(Embedding Model)在海量文本上训练过,学会了把意思相近的文本映射到相近的坐标:
"退货政策是什么" ──→ [0.82, 0.11, 0.43, ...]
"怎么申请退换货" ──→ [0.80, 0.13, 0.41, ...] ← 很近!
"双十一促销活动" ──→ [0.12, 0.75, 0.66, ...] ← 很远
这就是语义搜索和关键词搜索的本质区别:用户问"怎么退",关键词搜索找不到只写了"退货"的段落;语义搜索能------因为两个意思在坐标空间里是邻居。
Spring AI 里 EmbeddingModel 接口统一了各家向量模型,VectorStore 负责存储与检索(支持的实现:PGvector、Redis、Milvus、Elasticsearch 等几十种------都是同一套接口,换库不改代码)。
四、④ 检索:在坐标空间里找邻居
查询时,用户的问题也做同样的向量化,然后在向量库里找"坐标最近的 K 个段落":
java
// 检索支持相似度门槛、条数、元数据过滤
DocumentRetriever retriever = VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.similarityThreshold(0.73) // 太不像的不要
.topK(5) // 最多 5 段
.build();
召回的片段会拼进上下文 ,交给模型生成回答(上一篇的 QuestionAnswerAdvisor 干的就是这件事------它内部就是"检索 + 拼装")。
检索环节的实用参数:
- similarityThreshold:门槛太低会召回不相关的噪声,太高会漏掉正确答案------从 0.7 左右起步调;
- topK:塞给模型的段落数量------多了贵且噪声大,少了可能漏;
- 元数据过滤 :
type == '产品文档'这类条件能把检索范围缩到指定类别,多知识库隔离的标配。
五、一个高频陷阱:多轮对话下的检索失效
把 RAG 挂进多轮对话后,马上会遇到这个场景:
用户:丹麦的首都是哪?
模型:哥本哈根。
用户:那它第二大城市呢? ← "它"指丹麦
第二轮拿"那它第二大城市呢 "这句话去检索------这句话里根本没有"丹麦",检索结果一塌糊涂。
解法:检索前先把问题改写成独立完整的问句 ("丹麦的第二大城市是什么?")。Spring AI 的 CompressionQueryTransformer 就是干这个的:拿对话历史 + 追问,用模型压成一个自包含的查询。
这是多轮 RAG 的标配环节,缺了它,用户聊到第二轮就开始收到答非所问的结果。
六、这篇的四个要点
- 四环节流水线------加载(统一格式)→ 切分(定检索单元)→ 向量化(语义坐标)→ 检索(找最近邻)。
- 切分是第一坑------太碎丢上下文,太粗稀释相似度;固定大小 + 重叠、沿结构切,参数靠测试循环磨。
- 向量化 = 语义坐标------意思相近坐标相近,这是语义搜索能命中"换一种说法"的原因。
- 多轮对话必须做查询改写------把带指代的追问压成独立问句,否则第二轮检索就失效。
延伸思考
四个环节都做到位之后,还会有一种不满意的场景:召回的 5 段里,真正回答问题的只有 1 段,其余是"话题相近但没用"的噪声------塞给模型徒增成本和干扰。
这时候要上的就是"重排序"------先宽召回、再精排。下一讲展开。
下一篇
《让检索更准:先宽召回,再精排序》------向量检索的"够快但不够准"、重排序补的是哪一刀、以及什么时候值得给管线加这一层。
本专栏按 Spring AI 当前版本讲解。