news 2026/10/4 8:05:35

【Spring AI 实战 · 阶段三·篇3】检索质量工程:五阶段混合检索管线

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Spring AI 实战 · 阶段三·篇3】检索质量工程:五阶段混合检索管线

📌系列说明:一个 Java 后端视角的 Spring AI 渐进式实战教程,载体为开源项目「劳小司 · 智能法律助手」。

  • 序章:技术栈全景与 AI 学习指南
  • 阶段一 · 流式对话内核:篇1 SSE 流式·停止·思考可见化 / 篇2 会话记忆压缩与滚动体验
  • 阶段二 · 工具调用:篇1 Function Calling 与法律计算器 / 篇2 联网搜索与工具预算
  • 阶段三 · RAG 知识库:篇1 起步与底账化 / 篇2 Agentic RAG 与引用可信度 /篇3 检索质量工程与知识库体验(本文)
  • 阶段四 · 多模型路由:篇1 五路级联路由
  • 阶段五 · 安全与质量门:篇1 安全层与强制检索 / 篇2 质量门与评估门禁 / 篇3 指代消解与阻塞隔离
  • 阶段六 · 产品化与用户体系:篇1 认证·配额·门禁 / 篇2 前端·移动端·身份 / 篇3 劳动法专精与多模态
  • 阶段七 · 存储演进与部署:篇1 存储迁移 / 篇2 部署契约

📦本篇涉及:rag/KnowledgeRetrievalService.java、rag/RerankService.java、rag/LawArticleBrowseService.java。


一、单纯向量检索的天花板

篇1、篇2 打通了"检索得到、同步得起、引用可信",但一个核心问题还没解决:检索准不准?

单靠向量 Top-K 有两个天花板:

  1. Bi-Encoder 精度有限:向量检索是 query 和 doc各自独立编码成向量再算余弦,无法建模词级交互(比如"合同解除"和"解除合同"语义近,但"未解除合同"和"解除合同"向量可能很近,语义却相反);
  2. 离散符号命中差:用户问"第四十七条",向量检索对这种精确编号反而不如关键词匹配。

一个直觉的错误做法是"把 topK 调大点,多召回一些总没错"。大错特错——噪声条目会稀释模型注意力、撑爆上下文、还会诱发模型"从一堆半相关条文里硬凑依据"的幻觉。召回率和精度不能靠一个 topK 硬扛。

工业界的标准解是两阶段:宽进粗召回 + 窄出精排。本项目落地为五阶段管线。

二、五阶段检索管线

问题(Planner 改写后) ① 稠密路粗召回 pgvector KNN,recallTopK=12,similarity-threshold=0.55 截断噪声 ② 稀疏路召回 pg_trgm GIN(ILIKE),bm25TopK=10,补"第四十七条"类离散编号精确匹配 ③ RRF 融合 score = Σ 1/(60+rank),两路互补,条文级去重 ④ rerank 精排 百炼 gte-rerank-v2 Cross-Encoder 取 topK=5(失败降级融合序) ⑤ Small-to-Big chunk 子块命中 → 按 articleId 还原整条全文注入

对应retrieveCitations()的主干:稠密召回 → 稀疏召回 → RRF 融合 → rerank → 整条还原。下面逐个拆。

① 稠密路:向量 KNN 粗召回

SearchRequest.builder().query(query).topK(recallTopK)// 12,宽进.similarityThreshold(similarityThreshold)// 0.55,低于此视为未命中.build();

recallTopK=12是"宽进"——先多召回一些保召回率;similarity-threshold=0.55是降噪闸门,低于阈值的直接砍掉,同时激活"检索落空"分支(落空时提示模型换词或转通用知识)。可选category过滤(劳动/民事…)缩小检索域。

② 稀疏路:pg_trgm 补精确匹配

向量不擅长的"精确编号/术语",交给稀疏检索。本项目用 PostgreSQL 的pg_trgm GIN 索引(三元组 + ILIKE)近似 BM25,命中"第四十七条"这种离散符号。它是向量路的互补,不是替代。

③ RRF 融合:两路合成一个序

Reciprocal Rank Fusion,公式极简:score(d) = Σ 1/(k + rank),k=60(工业经验值)。

scores.merge(key,1.0/(RRF_K+i+1),Double::sum);// rank 从 1 起

妙处在于:只用名次不用原始分数,天然规避了"向量余弦分"和"BM25 分"不同量纲无法直接相加的问题。同一条文的多个 chunk 按lawName|articleNo条文级去重,保留融合分最高的。

④ rerank 精排:Cross-Encoder 的关键一跃

前一步的融合还是 Bi-Encoder 级别的排序。这里上Cross-Encoder:把 query 和每个候选 doc拼在一起送进gte-rerank-v2,模型输出精确的相关性分数,重排取 topK=5。

Map<String,Object>body=Map.of("model","gte-rerank-v2","input",Map.of("query",query,"documents",docs),"parameters",Map.of("top_n",topN,"return_documents",false));

精度高但慢、贵,所以只用在"窄出"这一层(对 12 条候选精排,不是对全库)。和联网搜索一样,Spring AI 没有 rerank 自动装配,手写 RestClient 直调。精排的relevance_score回写进citation.score——前端引用卡上的TOP 0.xx就是 Cross-Encoder 的真实分数(注意:它和 ① 的余弦相似度阈值不是同一个量表)。

⑤ Small-to-Big:命中小块,注入整条

篇1 分块时把长条文拆成了子块(检索粒度小、保精度)。命中子块后,按articleId回 PG 底账还原整条全文再注入 Prompt(注入粒度大、保上下文完整):

if(!c.chunked()||c.articleId()==null)returnc;LawArticleEntitya=lawArticleMapper.findById(c.articleId());returnnewLawCitation(a.getId(),...,a.toEmbeddingText(),false);// 整条还原

检索用小块、注入用整条——这就是 Small-to-Big 的精髓。

三、全链路降级信条

五阶段里每一阶段失败都不阻断,只降级:

阶段失败降级
分类过滤检索失败去掉过滤重搜
BM25 稀疏路失败(索引未建)退纯稠密路
rerank 精排失败退 RRF 融合序截断
Small-to-Big 还原失败退 chunk 子块文本
整条向量库不可用返回空列表,对话退化为纯 LLM

检索是"锦上添花"的能力,绝不能因为精排服务抖动就把用户的对话带崩。

四、知识库前端体验

检索质量之外,法条库浏览页(LawArticleBrowseService)也做了体验精修:faceted 高级检索(按法名/领域/条号分面过滤)、分页栏固定底部、法条正文的字体与标签样式优化。这部分偏前端,核心是"把后端检索能力如实、清晰地呈现给用户",与引用可信度一脉相承。

五、踩坑备忘

① topK 不是越大越好。调大 topK 看似提高召回,实则引入噪声、稀释注意力、诱发硬凑幻觉。要召回率和精度兼得,靠的是"宽进(recallTopK)+ 窄出(rerank topK)"两阶段,不是单点调参。

② pg_trgm 索引要先建。稀疏路依赖pg_trgmGIN 索引,扩展没装或索引没建时keywordSearch会抛异常——代码里 catch 住降级为纯稠密路,不阻断。上线前记得确认扩展和索引就位。

③ rerank 分数 ≠ 向量相似度。两个量表别混:引用卡TOP 0.xx是 Cross-Encoder 相关性分,similarity-threshold=0.55是余弦相似度阈值。给用户的文案别写错。

④ RRF 融合用名次不用分数。千万别试图把余弦分和 BM25 分加权平均——量纲不同,加出来是错的。RRF 只取名次,是这个场景的标准解。

六、小结

阶段作用
稠密粗召回向量 KNN,宽进保召回
稀疏召回pg_trgm 补精确编号
RRF 融合名次合成,跨量纲
rerank 精排Cross-Encoder 窄出提精度
Small-to-Big小块命中、整条注入

七、下篇预告

到这里,RAG 知识库这条线(起步→底账化→Agentic→引用可信→检索质量)完整闭环了。但一直用同一个模型跑所有问题,成本和质量的平衡没解决——闲聊用贵模型浪费、复杂推理用便宜模型降质。阶段四,我们做五路模型级联路由。


🌐源码与体验:Gitee(国内快)https://gitee.com/spaserby/laoxiaosi.git | GitHub https://github.com/spaserby/laoxiaosi.git
🖥 在线演示:https://laoxiaosi.noctisblue.com
本系列全套代码皆开源,觉得这篇有帮助,欢迎顺手点颗 ⭐

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/4 8:04:13

QClaw停运后迁移WorkBuddy实战:Skill编排与1000积分使用指南

1. 从QClaw停运说起&#xff1a;一次被迫的迁移决策QClaw停运的消息出来那天&#xff0c;我的工作群里炸了锅。不是因为这个工具本身有多不可替代&#xff0c;而是因为太多人的日常工作流已经深度绑定了它——自动化脚本、定时任务、数据抓取、消息推送&#xff0c;甚至一些团队…

作者头像 李华
网站建设 2026/10/4 8:03:01

MATLAB信号处理实战:从滤波FFT到雷达仿真与深度学习

做信号处理这些年&#xff0c;我电脑里装得最勤、卸了又装的工具就是 MATLAB。别误会&#xff0c;我不是说它难用&#xff0c;恰恰相反——当你手里有一堆 ADC 采集回来的数据&#xff0c;要做滤波、频谱分析、特征提取&#xff0c;或者想快速验证一个算法思路时&#xff0c;MA…

作者头像 李华
网站建设 2026/10/4 8:02:57

GEO优化实战:向量数据库、AI大模型与GPU算力集群的工程化落地

1. GEO优化到底在优化什么&#xff1a;从搜索引擎到生成式引擎的范式转移GEO这个词这两年出现的频率越来越高&#xff0c;但很多人第一次听到会以为是地理相关的缩写。它真正的全称是Generative Engine Optimization&#xff0c;翻译过来叫生成式引擎优化。传统SEO的目标是让网…

作者头像 李华
网站建设 2026/10/4 7:58:00

MRAM替代Flash和EEPROM:PIC18F55K42与MR25H40CDF工业存储方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华