第15篇:《检索效果太烂?我用Query改写和重排序把准确率翻了一倍》

承上 :上一篇我们用 QuestionAnswerAdvisor 一行代码跑通了RAG,但是在测试过程中发现一堆Bad Case:问"怎么退钱"搜出来的是"退款到账时间",问"加班费怎么算"搜出来的是"加班调休规定"。今天,我们要解决RAG最头疼的问题------检索不准

1. 问题诊断:为什么检索不准?

1.1. 三个典型Bad Case

sql 复制代码
Case 1:口语化表达 vs 文档术语
用户问:"怎么退钱?"
文档写:"退款流程"
检索结果:"退款到账时间为3-5个工作日"(不相关)

Case 2:模糊问题 vs 精确关键词
用户问:"加班费怎么算?"
文档写:"加班调休"
检索结果:"加班2小时以上可申请调休"(部分相关,但不是用户想要的)

Case 3:多义词
用户问:"苹果最新款"
文档里既有"苹果公司产品"又有"苹果水果"
检索结果:混在一起(50%不相关)

1.2. 根本原因

向量检索的本质是语义相似度匹配 ,但"口语化表达"和"文档术语"之间存在语义鸿沟

markdown 复制代码
用户语言:怎么退钱?        → 口语化
文档语言:退款申请流程        → 书面化
           ↓
向量距离可能很远,检索不到!

2. 优化一:Query改写

2.1. 核心思想

在检索之前,先用AI把用户的问题"翻译"成文档中可能出现的表达方式。

markdown 复制代码
用户问:"怎么退钱?"
    ↓ AI改写
改写后:"退款申请流程 退款条件 退款方式"
    ↓ 用改写后的Query去检索
检索结果:"第三章 退款流程:用户可在订单完成后7天内申请退款..."

2.2. 实现QueryTransformer

在2.0版本中,Spring AI提供了一套完整的 预检索(Pre-Retrieval) 优化体系,核心接口是 QueryTransformer

QueryTransformer 是Spring AI 2.0中专门用于在检索之前对用户查询进行变换 的接口。它在 RetrievalAugmentationAdvisor 内部自动执行,不需要我们手动管理Advisor顺序。

arduino 复制代码
用户查询:"怎么退钱?"
    │
    ▼
QueryTransformer.transform("怎么退钱?")
    │
    ▼
改写后查询:"退款申请流程 退款条件 退款方式"
    │
    ▼
用改写后的查询去向量数据库检索
    │
    ▼
精准命中文档块!
实现类 功能 适用场景
RewriteQueryTransformer 用AI改写用户查询 口语化→书面化,推荐首选
CompressionQueryTransformer 结合历史对话压缩查询 多轮对话场景
TranslationQueryTransformer 多语言翻译 跨语言检索

本篇重点讲最常用的 RewriteQueryTransformer

2.3. 配置RewriteQueryTransformer

ini 复制代码
package com.yunxi.ai.config;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.rag.advisor.RetrievalAugmentationAdvisor;
import org.springframework.ai.rag.preretrieval.query.transformation.QueryTransformer;
import org.springframework.ai.rag.preretrieval.query.transformation.RewriteQueryTransformer;
import org.springframework.ai.rag.retrieval.search.VectorStoreDocumentRetriever;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class QueryTransformerConfig {

    /**
     * 配置 RewriteQueryTransformer Bean
     * 该组件用于将用户模糊、口语化的查询重写为更适合向量检索的结构化查询
     */
    @Bean
    public QueryTransformer rewriteQueryTransformer(ChatModel chatModel) {
        // 构建 ChatClient,建议设置较低的 temperature 以保证重写结果的稳定性
        ChatClient.Builder builder = ChatClient.builder(chatModel);
        // 定义严格的提示词模板
        String rewritePrompt = """
            你是一个查询优化助手。你的任务是将用户的原始查询重写为更适合向量数据库检索的形式。
            规则:
            1. 严禁改变原意:必须严格保留用户查询中的核心关键词和意图。
            2. 严禁回答问题:不要提供答案,只输出重写后的查询语句。
            3. 去噪与规范化:去除语气词、冗余字符,将口语转化为书面语。
            4. 补全上下文:如果查询指代不明(如"它是什么"),请根据常识补全主语,但不要引入无关信息。
            5. 输出格式:只返回重写后的查询文本,不要包含任何解释或前缀
            原始查询: {query}
            重写后的查询: {target}
            """;
        PromptTemplate promptTemplate = new PromptTemplate(rewritePrompt);

        return RewriteQueryTransformer.builder()
                .chatClientBuilder(builder)
                .promptTemplate(promptTemplate) // 使用自定义提示词
                .build();
    }

    @Bean
    public RetrievalAugmentationAdvisor ragAdvisor(VectorStore vectorStore, QueryTransformer queryTransformer) {
        // 构建文档检索器
        VectorStoreDocumentRetriever retriever = VectorStoreDocumentRetriever.builder()
                .vectorStore(vectorStore)
                .similarityThreshold(0.5)
                .topK(3)
                .build();
        // 2. 构建 Advisor 并注入 Transformer
        return RetrievalAugmentationAdvisor.builder()
                .documentRetriever(retriever)
                .queryTransformers(queryTransformer) // 关键:应用查询转换器
                .build();
    }
}

配置解读:

scss 复制代码
RetrievalAugmentationAdvisor 执行流程:
    │
    ├── 1. queryTransformers.transform(原始查询)
    │      → "怎么退钱?" → "退款申请流程 退款条件 退款方式"
    │
    ├── 2. documentRetriever.retrieve(改写后查询)
    │      → 向量检索 Top 3
    │
    └── 3. 注入Prompt → AI生成回答

2.4. 增强Service

arduino 复制代码
package com.yunxi.ai.service;

import com.yunxi.ai.config.MyLoggerAdvisor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.rag.advisor.RetrievalAugmentationAdvisor;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class ChatV2Service {

    private final ChatClient chatClient;

    public ChatV2Service(ChatClient.Builder builder, ChatMemory chatMemory, RetrievalAugmentationAdvisor retrievalAugmentationAdvisor) {
        this.chatClient = builder
                .defaultAdvisors(new MyLoggerAdvisor())
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
                .defaultAdvisors(retrievalAugmentationAdvisor)
                .build();
    }
    
    /**
     * 同步接口
     * @param message
     * @return
     */
    public String chat(String message) {
        return chatClient.prompt()
                .system("你是一个专业的助手,可以回答用户的问题")
                .user(message)
                .advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, "TEST"))
                .call()
                .content();
    }
}

测试结果单测:

ini 复制代码
Query originalQuery = new Query(prompt);
Query transformedQuery = queryTransformer.transform(originalQuery);
transformedQuery.text();

2.5. Query改写效果对比

改写前

改写后

3. 优化二:结果重排序(Reranker)

在 Spring AI 的 RAG(检索增强生成)应用中,结果重排序(Reranker) 是一个关键的优化环节。它像一位"精筛师",在系统从向量数据库初步召回大量可能相关的文档后,通过一个专用的重排序模型(Rerank Model) ,对这些文档进行更精细的相关性打分、过滤和重新排序,最终把最相关的内容传递给大模型,从而提升最终答案的质量。

Spring AI Alibaba 提供的一个开箱即用的解决方案------RetrievalRerankAdvisor,就显得非常方便。

它是一个"顾问(Advisor) ",可以被注册到 ChatClient 中,自动在调用大模型前完成"检索→重排→过滤→组装"的整条链路。

核心组件

  • VectorStore: 负责第一步的"粗召回",从向量数据库中找出候选文档。
  • RerankModel: 重排序模型接口,负责对候选文档进行打分,Spring AI Alibaba 默认集成了阿里云的 DashScope(百炼) 重排序模型。
  • RetrievalRerankAdvisor: 作为胶水,将上述流程串联起来。

重排的实现在后面的SpringAIAlibab章节再详细写吧。

4. 优化三:混合检索(关键词 + 向量)

4.1. 向量检索的盲区

向量检索擅长找"语义相似",但对于精确匹配(如订单号、产品型号)表现差。

复制代码
用户问:"ORD001状态"
向量检索:把ORD001当成普通词汇,可能搜出"订单状态说明"
关键词检索:精确匹配ORD001,直接找到该订单

4.2. 混合检索思路

markdown 复制代码
用户查询
    │
    ├──→ 向量检索(语义匹配)→ 得分:语义相似度
    │
    └──→ 关键词检索(精确匹配)→ 得分:BM25/关键词命中数
            │
            ▼
    融合两个分数,取加权平均
            │
            ▼
    最终排序结果

4.3. Spring AI中的混合检索

如果使用Elasticsearch作为向量数据库,Spring AI的ElasticsearchVectorStore原生支持混合检索。Redis Stack目前以向量检索为主,关键词检索需要单独配置。

限于篇幅,混合检索的完整代码不在本篇展开,会在知识库实战中结合实际场景演示。

5. 三种优化对比

优化方式 解决问题 成本 效果
Query改写 口语化 vs 书面化 多一次AI调用 ⭐⭐⭐⭐
Reranker重排序 粗排精度不够 多N次AI调用 ⭐⭐⭐⭐⭐
混合检索 精确匹配盲区 需搜索引擎配合 ⭐⭐⭐⭐

组合使用

css 复制代码
Query改写 → 混合检索(粗排)→ Reranker(精排)→ Top 3 → 注入Prompt

6. 完整优化架构

css 复制代码
用户问题:"怎么退钱?"
     │
     ▼
┌─────────────────────┐
│  QueryTransformer   │ → "退款申请流程 退款条件"
│   (Query改写)       │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│  混合检索(可选)     │ → 向量检索 + 关键词检索
│  (粗排:召回Top 10) │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│  RerankerAdvisor    │ → 对Top 5逐个AI打分 → 保留Top 3
│   (精排:重排序)    │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│QuestionAnswerAdvisor│ → 注入Prompt → 生成回答
│   (RAG问答)        │
└─────────────────────┘

7. 本篇小结

这一篇我们解决了RAG的核心痛点------检索不准:

优化方式 原理 效果
Query改写 用AI把口语化查询转成文档术语 解决表达方式不匹配
Reranker重排序 对候选文档重新AI打分 解决粗排精度不够
混合检索 关键词+向量双路召回 解决精确匹配盲区

核心心法

erlang 复制代码
RAG的80%问题都出在检索环节,而不是生成环节。

检索不准 → AI看到的是不相关的资料 → 回答必然不准
检索精准 → AI看到的就是答案本身 → 回答自然准确

优化检索的投入产出比,远高于优化Prompt。

下一篇,我们要把前面学的所有RAG知识真正落地------面试知识库实战:我把自己收集的八股文喂给了AI,让它帮我模拟面试。


本文与DeepSeek协作完成

相关推荐
机器之心2 小时前
Kimi K3竟是GPT-2的22580倍,博主「肝」48小时发现:七年进化大模型不只是参数暴涨
人工智能·openai
旋生万物3 小时前
五条几何公理重构元素周期表:从量子力学到螺旋拓扑的降维(AI 编程时代的微观启示)
重构·ai编程·代码审计·cursor·ai agent·mcp协议·claudecode
ServBay4 小时前
AI Gateway 与直连 LLM API 的应该怎么选,一篇文章说明白
aigc·ai编程
JavaGuide4 小时前
GitHub 9.8 万 Star!把整个代码仓库变成知识图谱,这个 AI Coding 工具太适合 Claude Code / Codex 了
前端·后端·ai编程
Staticy4 小时前
Claude Code 接国产模型不能识图?一个 MCP 让纯文本模型也能看图
人工智能·ai编程·全栈
东小西5 小时前
第14篇:《公司制度问答机器人上线:老板问"能加薪吗",AI回答"请看第三章第四条"》
openai·ai编程
程序员鱼皮5 小时前
Claude Opus 5 全新发布,7 大项目实测,夯还是拉?半价吊打 Fable 5?
前端·后端·ai编程
众人皆醒我独醉6 小时前
为什么 AI 每次回答不一样?—— 温度参数是 AI 的"创意调节旋钮"
面试·ai编程
MomentYY6 小时前
RAG 建库:资料是怎么存进去的?
人工智能·agent·ai编程