Spring AI 2.0 Modular RAG:接入 Rerank 重排模型

各位朋友大家好,我是一言,继续更新 Spring AI 2.0 系列。

前两篇文章分别讲了查询扩展和多轮改写。这次来看检索之后:结果已经按照相关性排好了,为什么还要调用另外一个模型再排一次?

Rerank(重排)就是给候选资料重新打分,再选出更符合当前问题的几份。

两次评分有什么不同

向量检索时,Embedding 模型把资料和问题分别编码成向量,再比较相似度。资料在入库时编码,问题在提问时编码。

Qwen3-Reranker 会把当前问题和每份候选原文一起输入模型,重新给这对文本打分。

比如两份资料都谈退款,一份讲正常到账,一份讲退款失败。用户问"退款失败后怎么办",就需要优先选出后一份规则。

实测用了三个模型:

模型类型 选用模型 输入 → 输出
Embedding Qwen3-Embedding 0.6B 文本 → 向量
Reranker Qwen3-Reranker 0.6B 问题+候选原文 → 相关性分数
Chat Model Qwen2.5 3B 问题+选中资料 → 最终回答

选中的资料会和问题一起进入 Prompt,作为 Chat Model 回答的依据。

看知识库里面有什么

我准备了 20 份售后演示规则,作为 Document 存入 SimpleVectorStore。其中三份原文如下:

我固定了 6 个问题,跑两组对照:

  • 直接检索:向量检索前 3 份,用来回答。
  • 检索加重排:先检索 10 份,重排取前 3 份,用来回答。

两组共用知识库、回答模型和提示词。三个模型均在本地运行,6 组问答用例和 3 个后处理器测试通过。

一份关键规则,从第 6 位变成了第 1 位

订单显示退款失败,我继续等几天就会到账吗?

《退款失败后的处理》原本排在第 6 位,不能进入前 3。重排之后,它到了第一位:

直接检索的时候,模型回答:

退款失败的状态下,需要联系商家审核退款,审核通过后才会进入银行处理中阶段。

它把"商家待处理"的办法用到了"退款失败"上面。

重排之后的完整回答是:

退款状态为退款失败时,请先查看失败原因并联系客服。客服核实收款账户状态和渠道返回码,修复原因后重新提交退款。当前失败状态下,单纯继续等待不会自动重新发起退款。

"退款失败"的规则进入回答上下文后,模型有了查原因、修复后重新提交的依据。

六道题,重排改变了什么

下表比较的是每道题需要的关键资料排在第几位:

问题 重排前 重排后
银行卡退款超过 3 个工作日未到账 第 1 位 第 1 位
周末算不算退款处理时间 第 1 位 第 1 位
原付款银行卡已经注销 第 1 位 第 1 位
退款仍显示商家待处理 第 1 位 第 1 位
余额付款,银行卡没收到退款 第 1 位 第 1 位
退款失败,继续等会到账吗 第 6 位 第 1 位

另外 5 题的关键资料一直排在第 1 位,重排后没有变化。"周末怎么算"和"银行卡注销"两题的答案也没有变化。

第一题重排后,还多了一句没必要的"缺少具体的退款流水号信息"。所以,重排也不保证每道题都答得更好。

直接把全部 10 份资料交给回答模型,也是另外一种做法,本次没有测试。

看一次 Reranker 的输入和输出

我用 Python 在本地加载 Qwen3-Reranker,封装成 HTTP 服务,Java 通过自定义的 HttpRerankClient 调用它。

再用 Postman 单独发一次请求:query 是问题,documents 是向量检索出的 10 份候选全文。

服务返回每份资料的索引和 relevance_score,按分数从高到低排列:

这一步输出分数,后面的 Chat Model 负责生成答案。

在 Spring AI 中接哪一层

Spring AI 提供了 DocumentPostProcessor 接口,在检索和合并之后、组装上下文之前对资料进行处理。

java 复制代码
List<Document> process(Query query, List<Document> documents);

运行环境为 Spring AI 2.0.1、Spring Boot 4.0.8、Java 21。这个接口从 1.0.0 开始就已经提供。

自定义 RerankDocumentPostProcessor 实现该接口。process() 核心如下,省略空列表及返回值校验:

java 复制代码
// 1. 把问题和候选全文交给重排模型
var scores = client.rerank(query.text(),
        documents.stream().map(Document::getText).toList());

// 2. 按重排分数从高到低排列
var ranked = new ArrayList<>(scores);
ranked.sort(Comparator.comparingDouble(
        RerankClient.RankedIndex::relevance_score).reversed());

// 3. 按索引取回原文档,保留前 topN 份
return ranked.stream().limit(topN).map(item -> {
    Document original = documents.get(item.index());
    var metadata = new HashMap<>(original.getMetadata());
    metadata.put("rerank_score", item.relevance_score());
    return original.mutate().metadata(metadata).build();
}).toList();

重排分数存入 rerank_score,保留原文档信息。它不代表回答准确率,也不能与向量得分混排。

通过 documentPostProcessors(...) 接入 Advisor,下面省略模型、向量库和提示词组件的初始化:

java 复制代码
var retriever = VectorStoreDocumentRetriever.builder()
        .vectorStore(vectorStore)
        .similarityThreshold(0.0)
        .topK(10) // 先召回 10 份候选
        .build();

var reranker = new RerankDocumentPostProcessor(
        new HttpRerankClient(
                URI.create("http://127.0.0.1:18765/rerank")),
        3); // 重排后保留 3 份

var advisor = RetrievalAugmentationAdvisor.builder()
        .documentRetriever(retriever)
        .documentPostProcessors(reranker)
        .queryAugmenter(queryAugmenter)
        .build();

String answer = chatClient.prompt()
        .user(question)
        .advisors(advisor)
        .call()
        .content();

其中,ContextualQueryAugmenter 把资料填入 {context}、问题填入 {query},组装成回答模型的 Prompt。

多出这一步,得到的是什么

"退款失败"这题,总耗时从 4.71 秒增加到 6.63 秒 ,其中重排 HTTP 请求约 1.78 秒。

两组各调用一次 Chat Model,重排组多一次 Rerank 请求。耗时均为本机单次值。

要不要加,可以根据答错的记录来决定:

  • 正确资料已经搜到,但是排名靠后:可以尝试重排。
  • 候选中没有正确资料:先检查知识库、切分和检索。
相关推荐
YDS8295 小时前
Small Spring IOC篇:作用域和FactoryBean
java·后端·spring
学到老活到老6 小时前
第 22 集:Filter、Interceptor、AOP 都能拦截,为什么不能互换?
java·后端·spring
郭小李6 小时前
spring 中的 @EnableWebMvc注解和WebMvcConfigurer接口
java·后端·spring
CoderYanger7 小时前
Java EE 进阶:5. Spring IoC&DI(第二弹):Spring IoC
java·开发语言·spring boot·spring·面试·职场和发展·java-ee
霸道流氓气质8 小时前
AI 翻译插件:翻译记忆级联复用、术语混合检索与结构化质量门禁的 Vue3 + Spring AI 生产级实战
java·人工智能·spring
霸道流氓气质10 小时前
智能表单生成器:结构化输出契约、JSON Schema 驱动渲染与 EAV/JSONB 混合存储的 Vue3 + Spring AI 生产级实战
人工智能·spring·json
java_nnnn11 小时前
spring web mvc图书管理小案例
前端·spring·mvc
步行cgn1 天前
StringHttpMessageConverter 详解:Spring MVC 中字符串读写的默认转换器
java·spring·mvc
步行cgn1 天前
@ResponseBody 详解:Spring MVC 中把返回值直接写入响应体
java·spring·mvc