Spring AI 2.0 RAG 工程优化:从 Chunking、混合检索到 Rerank

Spring AI 2.0.1 本身已经把 RAG 拆成 Query Transformation、Query Expansion、Retrieval、Post-Retrieval 和 Generation 等模块,并提供 VectorStoreDocumentRetrieverRewriteQueryTransformerMultiQueryExpanderDocumentPostProcessor 等接口,因此可以直接用这套结构建立完整心智模型。(Home)

RAG 的核心目标可以概括成一句话:在模型回答之前,把当前问题真正需要的证据找出来,并以尽量少的噪声送入 Context。

因此,一个真正的 RAG 系统远比"连接一个向量数据库"复杂。完整过程通常经历:

bash 复制代码
原始资料
   ↓
解析 / 清洗
   ↓
Chunking
   ↓
Embedding
   ↓
建立索引
================
用户问题
   ↓
Query Rewrite / Multi-Query
   ↓
Metadata Filter
   ↓
Vector Search + Keyword Search
   ↓
Hybrid Search
   ↓
Top-K 候选
   ↓
Rerank / 去重 / 压缩
   ↓
最终证据
   ↓
Context
   ↓
LLM
   ↓
回答 + 引用

理解这张图以后,绝大多数 RAG 术语都可以确定自己处在哪个位置。

一、Chunking:先决定"拿什么东西去检索"

Chunking 就是把原始长文档切成较小的文本块。Spring AI 的 ETL Pipeline 中,DocumentReader 负责读取资料,DocumentTransformer 负责转换,其中 TokenTextSplitter 就是常用的切块工具。Spring AI 2.0.1 默认按照 token 数量切分 ,并支持自定义中文句号、问号等边界。(Home)

为什么不能直接把一本 100 页 PDF 当成一个 Document?因为用户问:

"项目申请需要哪些材料?"

真正有用的可能只有其中一段。如果整个 PDF 作为一个向量,Embedding 会把大量主题压进一个向量中,检索精度很差。

于是可以:

java 复制代码
// 创建 TokenTextSplitter,按 token 数量切分文本
TokenTextSplitter splitter = TokenTextSplitter.builder()
        .withChunkSize(800)          // 每个 Chunk 的最大 token 数
        .withMinChunkSizeChars(350)  // 最小字符数,避免切出过短的碎片
        .withPunctuationMarks(       // 指定中文标点作为切分边界
                List.of('。', '?', '!', '\n')
        )
        .build();
// 对原始文档执行切块,得到多个 Document
List<Document> chunks =
splitter.apply(documents);

这里没有一个适合所有项目的最佳 chunkSize。真正要理解的是两个方向。

bash 复制代码
Chunk 太大
→ 一个块里包含多个主题
→ 检索到了相关内容,同时带进大量噪声
Chunk 太小
→ 单独一句话缺乏前后文
→ 检索虽然命中,但模型无法正确理解

例如原文是:

bash 复制代码
申请对象为注册满两年的科技企业。
申请截止日期为9月30日。
企业还需提交上一年度审计报告。

如果切成三个完全独立的小块,用户问"谁需要提交审计报告"时,第三句话本身可能已经失去了"科技企业"的主体。

因此实际项目还经常出现 Overlap、Structure-aware Chunking、Semantic Chunking、Parent-Child Retrieval 等术语。

Overlap 指相邻 Chunk 保留少量重复上下文;Structure-aware Chunking 指按照标题、段落、表格、代码方法等自然结构切分;Semantic Chunking 则尽量在语义主题变化的位置切开;Parent-Child Retrieval 是检索较小的 Child Chunk,提高命中精度,最终把它所属的较大 Parent 内容交给模型,从而同时兼顾"找得准"和"上下文完整"。

面试时如果被问 Chunking,可以回答:切块首先影响检索单元的粒度。过大会增加噪声,过小会破坏语义,因此通常根据文档结构、模型 Context 和真实查询进行实验,而不是机械固定一个长度。

二、Embedding:把"语义相似"变成可以计算的距离

Embedding 是把文本转换成一个高维数字向量。例如:

bash 复制代码
"如何办理退款"
        ↓
Embedding Model
        ↓
[0.18, -0.42, 0.73, ...]

"退款申请步骤"虽然没有完全相同的关键词,但生成的向量通常会在语义空间中比较接近。向量数据库随后通过 cosine similarity、dot product(点积) 等方式寻找相似向量。Spring AI 的 VectorStore 对不同向量数据库提供统一接口。(Home)

因此,当出现:

bash 复制代码
用户问:
"离职以后还能报销吗?"
知识库写:
"劳动关系终止后不再享受费用报销资格"

纯关键词搜索可能匹配得很差,Embedding 检索则有机会通过语义关系找到它。

【重点】****Embedding 出问题时通常表现为"明明知识库里有,语义检索却总找不到"这时需要检查 Embedding 模型是否适合当前语言和领域、文档与查询是否使用同一套兼容模型、Chunk 是否包含足够语义,以及专业缩写、编号等信息是否更适合关键词检索当前有研究证明,Embedding对代码标识符的语义理解几乎失效。****【尤其是在代码搜索的关键词里,95%都是标识符。所以如果大篇幅都是代码,Embedding语义检索可能不及grep精确查询效果好】

PS:可观测性首先发现"绿色问题"------知识库明明有内容,但检索结果里长期找不到。如果记录得足够细,它还能继续往下定位"红色原因" :查看 Query 和 Chunk 的 Embedding、相似度分数、Top-K 排名、切块结果、模型版本等,从而判断更可能是模型不适配、Chunk 语义不足,还是查询表达问题。所以它的定位精度取决于"观测了多少中间过程"基础可观测性只能告诉你"检索失败了";完善的 Trace + Metrics + Eval 可以把问题缩小到 Embedding、Chunk、Query 等具体环节,但通常不会自动告诉你"就是某一个参数错了"。

Embedding 后通常还会听到一个词:ANN,Approximate Nearest Neighbor,近似最近邻搜索 。它解决的是向量规模非常大以后,如何快速找到最相似向量。HNSW【图索引、跳表算法,从顶层开始,沿着长距离边快速靠近目标区域】、IVF【先K-Means聚类,找到最近的簇,只在该簇内搜索,避免全量比对】 等属于底层索引算法。应用开发初期知道它们属于"向量检索性能层"即可,没有必要把它们与 RAG 业务逻辑混在一起。

图片来自于小红书博主:近似最近邻搜索(ANN)算法:HNSW 和 IVF ✅ HNSW 分... xhslink.cn/o/9rsMHWtQI... 存下链接,去【小红书】阅读全文~

三、Top-K 与 Similarity Threshold:取多少结果

向量检索通常不会只返回一个结果,而是返回最相近的前 K 个:

java 复制代码
// 构建向量检索器,配置 Top-K 和相似度阈值
VectorStoreDocumentRetriever retriever =
        VectorStoreDocumentRetriever.builder()
                .vectorStore(vectorStore)          // 指定向量存储
                .topK(5)                           // 最多返回 5 个候选 Chunk
                .similarityThreshold(0.70)         // 过滤相似度低于 0.70 的结果
                .build();

Spring AI 的 VectorStoreDocumentRetriever 原生支持 topK、相似度阈值以及 Metadata Filter。(Home)

Top-K = 5 表示最多返回五个候选 Chunk;similarityThreshold 则用于过滤相似度过低的内容

这里同样存在取舍:

bash 复制代码
Top-K 太小
→ 正确证据可能排在第6名
→ 漏召回
Top-K 太大
→ 无关材料大量进入 Context
→ 成本增加,模型受到干扰

因此常见工程策略是:检索阶段适当扩大召回,再通过 Rerank 缩小最终进入 Context 的数量【广撒网,收大鱼】。

例如:

bash 复制代码
第一阶段召回 Top 20
        ↓
Rerank
        ↓
最终保留 Top 5

这也是 Recall(召回率)和 Precision(精确率)之间的典型平衡。

四、Metadata Filter:先限定"在哪些资料里找"

这是企业 RAG 中非常重要、同时经常被初学者忽略的一层。【辅助检索:在检索阶段给request加入结构化Metadata约束(即限定场景),便于和文档的Metadata进行快速、准确匹配。】

假设知识库里同时有:

bash 复制代码
2024年退款政策
2025年退款政策
2026年退款政策
广东政策
海南政策
北京政策
普通用户制度
VIP制度
内部员工制度

用户问:

"2026年海南地区企业用户申请条件是什么?"

单纯做向量相似度搜索,很可能检索到语义非常相似的 2025 年文件。

Metadata Filter 会在检索时加入明确的结构化约束

bash 复制代码
year == 2026
AND region == "海南"
AND userType == "enterprise"

Spring AI 提供可移植的 SQL-like Metadata Filter【过滤器表达式,过滤出新的空间】

java 复制代码
// 构建检索请求,加入 Metadata 过滤条件
SearchRequest request = SearchRequest.builder()
        .query(question)                          // 用户查询
        .topK(10)                                 // 召回 10 个候选
        .filterExpression("""                     // SQL-like 过滤表达式
            region == '海南'
            && year == 2026
            && documentType == 'policy'
        """)
        .build();

这些表达式可以由支持过滤能力的 VectorStore 转换为底层数据库自己的查询格式。(Home)

因此,在入库阶段就应该认真设计文档的 Metadata

java 复制代码
// 创建 Document,并附带结构化 Metadata 元数据
Document document = new Document(
        text,                                     // 文档正文内容
        Map.of(                                   // 元数据键值对
            "source", "海南省政府网站",            // 来源
            "region", "海南",                      // 地区
            "year", 2026,                         // 年份
            "documentType", "policy",             // 文档类型
            "authorityLevel", "government",       // 权威等级
            "publishDate", "2026-05-12"           // 发布日期
        )
);

这也是为什么高质量 RAG 的 Document 不能只有正文。

Metadata 可以承担:

bash 复制代码
来源
发布日期
地区
部门
文档类型
权限范围
项目 ID
租户 ID
版本
权威等级

因此面试时可以把 Metadata Filter 概括为:向量检索回答"语义上像不像",Metadata Filter 回答"这个文档有没有资格参与这次检索"。

五、Hybrid Search:为什么只做向量检索还不够

Hybrid Search,即混合检索,是实际 RAG 中非常重要的一项技术

向量检索擅长理解:

bash 复制代码
"怎么办理费用报销"
≈
"费用 reimbursement 的申请流程"

但如果用户查询:

bash 复制代码
琼府〔2026〕18号
MGC-2026-001
Spring AI 2.0.1
GB/T 7714-2015

这些编号、专有名词、产品型号和缩写通常更需要精确关键词匹配。

传统全文检索常使用 BM25,它非常擅长:

bash 复制代码
关键词
编号
人名
机构名
专业术语

因此 Hybrid Search 会同时执行:

bash 复制代码
用户 Query
   ↓
┌──────────────┐
│              │
Vector Search  BM25 Search
语义检索        关键词检索
│              │
└──────┬───────┘
       ↓
结果融合
       ↓
候选文档

例如用户问:

"琼府〔2026〕18号里关于红树林项目申报的要求是什么?"

Vector Search 负责找到"红树林项目申报要求"这一语义主题,BM25 则可以精确抓住"琼府〔2026〕18号"。

两路结果还需要进行融合 ,常见方法包括 Weighted Score 和 RRF(Reciprocal Rank Fusion,倒数排名融合)。RRF 不强依赖两套检索分数处于相同量纲,而是根据各自排名进行融合,因此在混合检索中非常常见。

图片来自小红书博主:快star大模型二面:什么是RRF? ... xhslink.cn/o/3awxgPTiD... 存下口令,跳转【小红书】阅读~

Spring AI 的统一 VectorStore API 主要抽象向量相似度检索;真正的 Hybrid Search 能力往往与底层搜索引擎相关,例如 Spring AI 官方文档明确指出 OpenSearch 同时支持 lexical、vector 和 hybrid search。(Home)

面试时可以这样回答:

向量检索解决语义匹配,BM25 解决关键词和精确实体匹配。业务资料中经常同时存在自然语言描述与政策编号、产品型号、专有名词,因此生产 RAG 常采用 Hybrid Search,再对两路结果进行融合。

六、Query Rewrite 与 Multi-Query:用户的问题本身可能就不好搜

RAG 检索失败还有一种常见原因:问题本身写得不好。

例如多轮对话:

bash 复制代码
用户:海南企业补贴最高多少?
AI:......
用户:那深圳呢?

第二个 Query 如果直接拿:

bash 复制代码
"那深圳呢?"

去向量库搜索,语义严重不足。

Query Rewrite 会把它改写成:

bash 复制代码
"深圳企业补贴最高金额是多少?"

Spring AI 已经提供 RewriteQueryTransformer,专门用于把冗长、模糊或者带有无关信息的 Query 重写成更适合检索的查询 。(Home)

还有一种方法叫 Multi-Query。例如:

bash 复制代码
原问题:
企业申请蓝碳项目需要什么条件?
扩展:
蓝碳项目企业申报资格
企业申请蓝碳项目准入条件
蓝碳项目申请主体要求

然后分别检索,再合并结果。这样可以增加召回率,降低用户某一种表达方式恰好与文档表达差异过大的影响 。Spring AI 提供 MultiQueryExpander,可以用 LLM 生成多个语义不同的查询版本。(Home)

继续深入还会看到 Query DecompositionHyDE前者把复杂问题拆成多个子问题(问题解压);HyDE 会先让模型生成一个假想答案,再使用假想答案去进行语义检索。初级项目不需要一开始全部使用,它们主要解决"原始 Query 难以直接召回正确证据"的问题。

七、Rerank:召回以后重新认真排一次

检索阶段通常追求"别漏掉正确答案",因此可能先召回:

bash 复制代码
20 个 Chunk

但最终给模型 20 个 Chunk 很可能噪声太大。

Rerank 的作用就是:

bash 复制代码
20个候选
   ↓
更加精细地比较
Query 与每个 Document 的相关性
   ↓
重新排序
   ↓
只留下最好的5个

第一阶段的向量搜索需要在几十万甚至几百万文档中快速搜索,因此强调速度。Reranker 只需要处理已经召回的几十个候选,可以使用计算量更大的 Cross-Encoder 或专门的 Rerank Model,更细致地判断 Query 与 Document 的真实相关性。【速度和精度的平衡:Rerank】

例如第一次检索得到:

简单向量相似度可能把第 3 项排在第三。

Reranker结合整个 Query 和候选内容重新判断后,可以得到:

Spring AI 的高级 RAG API 提供 DocumentPostProcessor 接口,官方明确将 reranking、去除无关或重复文档以及内容压缩列为 Post-Retrieval 阶段的典型用途。具体 Rerank 模型通常需要结合实际供应商或搜索后端实现。(Home)

因此可以记住:

bash 复制代码
Retrieval
→ 先把正确答案捞进候选集
Rerank
→ 再把真正最相关的排到前面

八、还有哪些常见 RAG 词需要认识

到了这里,再看到很多术语就容易定位了。

术语 解决的问题
Chunk Overlap 切块边界导致上下文断裂
Semantic Chunking 固定长度切分破坏完整语义
Parent-Child Retrieval 小块好检索,大块更适合阅读
Similarity Threshold 过滤明显不相关的召回结果
Top-K 控制候选数量
Metadata Filter 限定地区、时间、权限、来源等检索范围
BM25 精确关键词和实体检索
Hybrid Search 融合语义检索与关键词检索
RRF 融合多种检索结果排名
Query Rewrite 原始问题模糊、不完整
Multi-Query 单一表达可能漏召回
Query Decomposition 一个问题包含多个子问题
Rerank 初次召回排序不够准确
Deduplication(去重) 多个 Chunk 内容高度重复
Context Compression(内容压缩) Chunk 有效信息少、噪声多
Citation / Provenance 回答需要返回原始证据来源
Freshness 避免过期资料覆盖新资料

这些技术没有必要全部默认开启。工程上应该根据 Badcase 判断真正的问题发生在哪里

九、RAG 最后一定要做 Retrieval Eval

RAG 最容易出现一种错觉:模型最终回答看起来不错,于是认为检索系统也很好。

真正评价 RAG 时,应当单独检查检索阶段

例如准备一个问题:

bash 复制代码
Q:
2026年海南某项目申报截止日期是什么?
标准证据:
policy_2026_hainan.pdf 第18页

然后检查:

bash 复制代码
Top 5 检索结果中
是否出现标准证据?

这就是最基础的 Recall@K 思想。更正式一点,如果一个问题对应若干条真正相关的证据,那么:

bash 复制代码
Recall@K = Top K 中找回的相关结果数​/所有应该找回的相关结果数

例如标准答案一共有 2 个相关 Chunk,Top 5 找到了其中 1 个,那么 Recall@5 = 1/2 = 50%。

为什么 RAG 特别重视 Recall?因为前面的检索阶段如果漏掉正确证据,后面的 Rerank 和大模型基本没有机会补救 。相反,如果 Top 5 里面多混进两三个无关 Chunk,后面的重排序和模型还有机会把它们过滤掉。因此第一阶段检索通常优先保证"别漏"

这也能帮助我们理解 Precision@K。假设 Top 5 返回了 5 条,其中只有 2 条相关:

bash 复制代码
Precision@5=\frac{2}{5}=40%

Precision 更关注"你拿回来的东西干不干净",Recall 更关注"该拿回来的东西有没有漏掉"。

还可以继续关注:

bash 复制代码
Precision@K
→ 返回的 K 个结果里有多少是真的相关
MRR
→ 第一个正确结果排得有多靠前
nDCG
→ 整体排序质量怎么样

对于企业知识库,还应该增加业务指标,例如:

bash 复制代码
权威来源是否优先?
最新版本是否优先?
引用能否返回原文?
跨租户资料是否被严格隔离?
没有证据时是否明确回答"无法确认"?

这一步非常重要。一个高质量 RAG 系统的目标不是"无论什么问题都生成一段像答案的话",而是尽可能找到正确证据,并且在缺少可靠证据时保持可控。

十、用问题驱动的方法选择 RAG 技术

最后把整篇压缩成一套面试时真正有用的判断路径:

bash 复制代码
知识库里根本没有答案
→ 补充数据源
资料有,但总搜不到
→ 检查 Chunking / Embedding / Query Rewrite
同义表达搜不到
→ Embedding / Multi-Query
政策编号、型号、专有名词搜不到
→ BM25 + Hybrid Search
总搜到旧政策或错误地区
→ Metadata Filter + Freshness
正确文档在候选里,但排名很靠后
→ Rerank
召回结果太多、噪声很大
→ Threshold / Top-K / Rerank / Compression
Chunk 命中了,但上下文残缺
→ Overlap / Parent-Child / 更合理 Chunking
单问题过于复杂
→ Query Decomposition
最终答案无法核查
→ Citation / Provenance
不知道优化以后有没有变好
→ Retrieval Eval + Badcase + 回归测试

因此,RAG 工程真正需要掌握的核心思路可以概括为:

bash 复制代码
先把资料组织好
        ↓
尽可能召回正确证据
        ↓
通过过滤缩小搜索空间
        ↓
结合关键词与语义检索
        ↓
重新排序候选证据
        ↓
只把高质量证据交给 LLM
        ↓
保留来源并进行 Eval

Spring AI 在这里提供的是一套模块化接口。DocumentReader → TokenTextSplitter → VectorStore → QueryTransformer / QueryExpander → DocumentRetriever → DocumentPostProcessor → RetrievalAugmentationAdvisor 基本已经对应了完整 RAG 工程的主要阶段。(Home)

真正到了项目和面试场景,你需要表达的重点也不再是"我接了一个向量数据库",而应该能够说明:遇到了什么检索问题,问题发生在 RAG 的哪一层,为什么选择 Chunking、Metadata Filter、Hybrid Search 或 Rerank,以及最终用什么 Eval 指标证明检索确实变好了。

相关推荐
她的男孩2 小时前
做了"异步导出",用户点了还是卡 3 分钟:扒完 4223 行源码,@Async 压根没生效
人工智能·后端·程序员
wno7042 小时前
Spring Security添加图形验证码
java·后端·spring
ewbok2 小时前
Springboot整合mqtt实现软硬件通信
后端
CRZZX2 小时前
阶段 0.3:为 AI Agent 建立输入、工具、限流与前端渲染安全边界
后端
鱼弦2 小时前
实时性能力的边界:大模型能否胜任毫秒级响应的交互?
后端
葡萄城技术团队3 小时前
买来的设备管理系统总"不合脚"?——"管理+感知+智能"三层框架与分期落地路径
后端
程序员天天困3 小时前
RustFS 1.0.0 深度解析:GA 之后,它值得替代 MinIO 吗?
后端·云原生·rust
葡萄城技术团队3 小时前
GcExcel V9.2 新特性揭秘:注音文字,随正文一起进 PDF
后端
海阔天空3673 小时前
1538 万行大表从 SQL Server 搬到 MySQL:一个支持断点续传的迁移工具设计与踩坑实录
后端