SpringAI快速入门(7) RAG 实战(三):检索增强配置与重排序实战

文章目录

本文整理自公众号「攻城狮学习营」原创文章《SpringAI快速入门(9) RAG 实战(三):检索增强配置与重排序实战》(原文链接),聚焦进阶 RAG 配置与重排序(Rerank)实战。

本文是「Spring AI 向量检索与重排序全解析」系列的第三篇。在第一篇中我们了解了 RAG 基础概念和快速入门,在第二篇中深入了 ETL 全流程。本篇将聚焦于进阶的 RAG 配置------使用 RetrievalAugmentationAdvisor 实现精细化控制,并引入重排序(Rerank)技术来显著提升检索结果的排序质量。

一、进阶 RAG 配置:RetrievalAugmentationAdvisor

RetrievalAugmentationAdvisor 是 Spring AI 提供的更强大、更灵活的检索增强顾问,它允许开发者对 RAG 流程的各个环节进行精细化控制。相比于前面介绍的 QuestionAnswerAdvisor,它支持查询改写、空结果处理、文档后处理等高级功能。

下面逐一介绍其核心配置项,并给出一个完整的复杂流水线示例。

1. 查询空时扩展策略(Query Augmenter)

当向量检索返回的结果为空(即没有找到相似文档)时,可以通过 queryAugmenter 配置自定义的降级策略,避免将空上下文发送给模型导致错误回答。

java 复制代码
.queryAugmenter(ContextualQueryAugmenter.builder()
    .allowEmptyContext(false)   // 不允许空上下文
    .emptyContextPromptTemplate(PromptTemplate.builder()
        .template("用户查询位于知识库之外。礼貌地告知用户您无法回答")
        .build())
    .build())
  • allowEmptyContext(false):当检索结果为空时,不将空上下文传给模型,而是使用自定义提示模板。
  • emptyContextPromptTemplate:定义在无检索结果时,向模型发送的提示内容,可引导模型礼貌地拒绝回答。

2. 查询检索器与提示词重写(Query Transformers)

用户原始查询可能存在表述模糊、语义不完整、关键词缺失等问题。通过 queryTransformers 可以对查询进行改写,优化检索效果。Spring AI 提供了多种内置转换器:

(1)检索提示词重写(RewriteQueryTransformer)

使用大模型将用户查询改写为更适合向量检索的表述,提升召回精度。

java 复制代码
.queryTransformers(RewriteQueryTransformer.builder()
    .chatClientBuilder(ChatClient.builder(dashScopeChatModel))
    .targetSearchSystem("航空票务助手")  // 目标检索系统的名称,用于指导改写
    .build())
(2)翻译重写(TranslationQueryTransformer)

如果用户使用非目标语言提问,可先翻译成系统支持的语言,再进行检索。

java 复制代码
.queryTransformers(TranslationQueryTransformer.builder()
    .chatClientBuilder(ChatClient.builder(dashScopeChatModel))
    .targetLanguage("中文")   // 将用户查询翻译为中文
    .build())

多个转换器可以组合使用,按顺序执行。

3. 后置处理器(Document Post Processors)

检索到的文档列表在返回给模型之前,可以进行额外的处理,例如过滤、排序、截断、日志监控等。通过 documentPostProcessors 添加自定义处理逻辑。

java 复制代码
.documentPostProcessors((query, documents) -> {
    System.out.println("Original query: " + query.text());
    System.out.println("Retrieved documents: " + documents.size());
    // 可在此对 documents 进行过滤、重排等操作
    return documents;
})

该处理器接收当前查询和检索到的文档列表,并返回处理后的文档列表(可修改)。

4. 完整复杂流水线示例

下面的示例综合了上述所有配置,并结合 SimpleLoggerAdvisor 打印日志,演示了如何构建一个完整的 RAG 流水线:

java 复制代码
@Test
public void testRag3(@Autowired VectorStore vectorStore,
                     @Autowired DashScopeChatModel dashScopeChatModel) {

    ChatClient chatClient = ChatClient.builder(dashScopeChatModel)
            .defaultAdvisors(SimpleLoggerAdvisor.builder().build())
            .build();

    // 构建 RetrievalAugmentationAdvisor
    Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
            // 1. 配置文档检索器(相当于 QuestionAnswerAdvisor 的检索部分)
            .documentRetriever(VectorStoreDocumentRetriever.builder()
                    .similarityThreshold(0.50)
                    .vectorStore(vectorStore)
                    .build())
            // 2. 检索为空时的扩展策略(可选,此处注释掉)
            /*.queryAugmenter(ContextualQueryAugmenter.builder()
                    .allowEmptyContext(false)
                    .emptyContextPromptTemplate(PromptTemplate.builder()
                            .template("用户查询位于知识库之外。礼貌地告知用户您无法回答")
                            .build())
                    .build())*/
            // 3. 查询改写(可选,此处注释掉)
            /*.queryTransformers(RewriteQueryTransformer.builder()
                    .chatClientBuilder(ChatClient.builder(dashScopeChatModel))
                    .targetSearchSystem("航空票务助手")
                    .build())*/
            // 4. 检索后文档监控与操作(可选)
            .documentPostProcessors((query, documents) -> {
                System.out.println("Original query: " + query.text());
                System.out.println("Retrieved documents: " + documents.size());
                return documents;
            })
            .build();

    String answer = chatClient.prompt()
            .advisors(retrievalAugmentationAdvisor)
            .user("退一张票大概要多少费用?希望别扣太多啊")
            .call()
            .content();

    System.out.println(answer);
}

// 配置类(用于提供 VectorStore Bean)
@TestConfiguration
static class TestConfig {
    @Bean
    public VectorStore vectorStore(DashScopeEmbeddingModel embeddingModel) {
        return SimpleVectorStore.builder(embeddingModel).build();
    }
}

代码说明:

  • documentRetriever:指定了向量存储和相似度阈值,是检索的核心。
  • queryAugmenter:可配置空结果时的降级提示,本例中注释掉以便演示其他功能。
  • queryTransformers:可配置一个或多个查询改写器,本例注释掉,实际可开启。
  • documentPostProcessors:添加了一个简单的日志处理器,打印查询内容和召回文档数量,便于调试。
  • 最终通过 chatClient.prompt().advisors(retrievalAugmentationAdvisor).user(...).call().content() 执行完整流程。

该配置方式比 QuestionAnswerAdvisor 更加灵活,适合复杂业务场景下的 RAG 定制。

二、提升检索精度------重排序(Rerank)

为什么需要重排序?

在实际应用中,单纯依赖向量相似度排序存在以下问题:

  • 语义相似度局限:向量相似不等于内容绝对相关。两个文档可能在向量空间中距离很近,但实际内容却并不匹配用户意图。
  • 排序质量不佳:初始检索排序非最优,尤其当向量模型性能较差时,排序结果往往不够理想。
  • 上下文理解缺失:向量检索缺乏对查询和文档完整上下文的深度理解,无法精确判断文档与查询之间的真实相关性。

重排序方案(二阶段架构)

推荐采用"粗排 + 精排"的二阶段架构:

  1. 粗排(第一阶段):向量检索快速召回 Top-K(如 200)候选文档,保证召回率。
  2. 精排(第二阶段) :使用专用重排序模型(如 gte-rerank-hybrid)对候选文档重新评分,并按相关性降序输出,可设置分数阈值过滤低质量文档。

代码示例(使用 DashScopeRerankModel)

在 Spring AI 中,重排序通过 RetrievalRerankAdvisor 实现。以下是一个完整的端到端示例:

测试配置(使用 Ollama 较低性能向量模型,凸显 rerank 效果):

java 复制代码
@TestConfiguration
static class TestConfig {
    @Bean
    public VectorStore vectorStore(OllamaEmbeddingModel embeddingModel) {
        return SimpleVectorStore.builder(embeddingModel).build();
    }
}

重排序测试:

java 复制代码
@SpringBootTest
public class RerankTest {

    @BeforeEach
    public void init(@Autowired VectorStore vectorStore,
                     @Value("classpath:rag/terms-of-service.txt") Resource resource) {
        TextReader reader = new TextReader(resource);
        reader.getCustomMetadata().put("filename", resource.getFilename());
        List<Document> docs = reader.read();

        ChineseTokenTextSplitter splitter = new ChineseTokenTextSplitter(80, 10, 5, 10000, true);
        List<Document> chunks = splitter.apply(docs);

        vectorStore.add(chunks);
    }

    @Test
    public void testRerank(@Autowired DashScopeChatModel chatModel,
                           @Autowired VectorStore vectorStore,
                           @Autowired DashScopeRerankModel rerankModel) {
        ChatClient chatClient = ChatClient.builder(chatModel).build();

        // 粗排 TopK=200,精排默认 TopN=5
        RetrievalRerankAdvisor advisor = new RetrievalRerankAdvisor(
            vectorStore, rerankModel,
            SearchRequest.builder().topK(200).build()
        );

        String content = chatClient.prompt()
            .user("退票费用")
            .advisors(advisor)
            .call()
            .content();

        System.out.println(content);
    }
}

效果对比:

  • 重排前,排名第一的文档与"退费"无关(如预订航班说明)。
  • 重排后,排名第一的文档直接包含取消预订费用信息(经济舱 75 美元等)。

重排序的核心价值

重排序技术为 RAG 系统带来了以下关键收益:

  1. 弥补向量模型不足:即使使用较低性能的向量模型(如 Ollama 的 nomic-embed-text),通过重排序仍能获得高质量的检索结果。
  2. 提升 Top-K 精确率:粗排阶段可以设置较大的 TopK(如 200),保证召回率;精排阶段从这 200 条中选出最相关的 Top-N(默认 5),保证精确率。
  3. 降低 Token 消耗:通过精排筛选出最相关的文档,减少发送给大模型的无关上下文,降低 Token 消耗和费用。
  4. 灵活可配置:可以根据业务场景独立调整粗排和精排的参数,实现检索质量与性能的最佳平衡。

系列总结:Spring AI RAG 实战要点回顾

  • 向量模型 决定语义表征质量,建议选择 text-embedding-v4 等高性能模型
  • 分块策略需结合业务场景,可尝试语义分块或递归分块,并配合重叠(overlap)机制
  • ETL 流程是 RAG 的数据底座,涵盖读取、分块、格式转换、元数据增强、向量化存储全链路
  • RetrievalAugmentationAdvisor 提供比 QuestionAnswerAdvisor 更灵活的精细化控制
  • 重排序能显著改善检索排序,尤其当向量模型较弱时效果明显,采用"粗排+精排"二阶段架构
  • 始终以**"业务召回效果"**为最终标准,而非追求固定阈值
相关推荐
#六脉神剑14 分钟前
myBuilder 11 月新版本:AI辅助设计
ide·人工智能·低代码·ai·mybuilder
codigger4 小时前
AI 把代码写得飞快,然后呢?聊聊我验收生成代码的一套流程
ai·程序员·编程·#人工智能
秋饼20 小时前
MCP 2026-07-28 无状态协议大改版:Spring AI 2.1 多轮往返请求与网关路由落地
java·ai·技术分享·后端开发
进击的雷神1 天前
我用 second-brain-skills 把 Claude Code 升级成了“第二大脑”:PPT、品牌手册、SOP 一人搞定
ai·claude·ppt·sop
Alluxio1 天前
Alluxio + OCI:为 AI 工作负载实现亚毫秒访问延迟与可扩展吞吐
运维·数据库·人工智能·分布式·缓存·ai·语言模型
wflynn1 天前
Genie 4新增原生音频,实时世界模型加入声音生成
ai·技术
吴名氏.1 天前
Java转AI Agent教程(第十二周)检索与切分
java·开发语言·人工智能·ai·ai agent
效率工作实验室1 天前
企业级AI Agent平台评测:核心指标、能力对比与选型路径
ai·企业级ai·agent选型·agent平台评测·agent平台对比
不吃紫菜2 天前
LangGraph 体系实战(下):中断恢复、流式、子图与设计模式
ai