检索增强拦截器-查询转换器&翻译转化器&document后置处理器

📋 目录

  1. 方法代码分析
  2. 核心组件详解
  3. 执行流程与原理
  4. 使用场景
  5. 与其他方法的对比
  6. 最佳实践
  7. 常见问题

方法代码分析

示例代码

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

    // 1. 创建 ChatClient
    chatClient = ChatClient.builder(dashScopeChatModel)
            .defaultAdvisors(SimpleLoggerAdvisor.builder().build())
            .build();

    // 2. 构建 RetrievalAugmentationAdvisor(核心组件)
    Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
            // 2.1 文档检索器
            .documentRetriever(VectorStoreDocumentRetriever.builder()
                    .similarityThreshold(0.0)
                    .vectorStore(vectorStore)
                    .build())
            // 2.2 查询增强器(处理空上下文)
            .queryAugmenter(ContextualQueryAugmenter.builder()
                    .allowEmptyContext(false)
                    .emptyContextPromptTemplate(PromptTemplate.builder()
                            .template("用户查询位于知识库之外。礼貌地告知用户您无法回答")
                            .build())
                    .build())
            // 2.3 查询转换器 - 重写查询
            .queryTransformers(RewriteQueryTransformer.builder()
                    .chatClientBuilder(ChatClient.builder(dashScopeChatModel))
                    .targetSearchSystem("航空票务助手")
                    .build())
            // 2.4 查询转换器 - 翻译查询
            .queryTransformers(TranslationQueryTransformer.builder()
                    .chatClientBuilder(ChatClient.builder(dashScopeChatModel))
                    .targetLanguage("english")
                    .build())
            // 2.5 文档后处理器
            .documentPostProcessors((query, documents) -> {
                System.out.println("Original query: " + query.text());
                System.out.println("Retrieved documents: " + documents.size());
                return documents;
            })
            .build();

    // 3. 执行 RAG 查询
    String answer = chatClient.prompt()
            .advisors(retrievalAugmentationAdvisor)
            .user("我今天心情不好,不想去玩了,你能不能告诉我退票需要多少钱?")
            .call()
            .content();

    System.out.println(answer);
}

代码结构分析

复制代码
testRag3 方法
├── ChatClient 构建
│   └── 配置默认 Advisor(日志记录)
│
├── RetrievalAugmentationAdvisor 构建
│   ├── documentRetriever(文档检索器)
│   │   └── VectorStoreDocumentRetriever
│   │       ├── similarityThreshold(0.0)
│   │       └── vectorStore
│   │
│   ├── queryAugmenter(查询增强器)
│   │   └── ContextualQueryAugmenter
│   │       ├── allowEmptyContext(false)
│   │       └── emptyContextPromptTemplate
│   │
│   ├── queryTransformers(查询转换器链)
│   │   ├── RewriteQueryTransformer(重写查询)
│   │   └── TranslationQueryTransformer(翻译查询)
│   │
│   └── documentPostProcessors(文档后处理器)
│       └── Lambda 函数(监控、日志)
│
└── RAG 查询执行
    └── ChatClient.prompt().advisors().user().call()

核心组件详解

1. RetrievalAugmentationAdvisor

作用 :Spring AI 提供的高级 RAG Advisor,相比 QuestionAnswerAdvisor 提供了更多的定制化能力。

核心特性:

  • ✅ 支持查询转换(Query Transformation)
  • ✅ 支持查询增强(Query Augmentation)
  • ✅ 支持文档后处理(Document Post Processing)
  • ✅ 支持自定义文档检索器(Document Retriever)
  • ✅ 支持空上下文处理(Empty Context Handling)

与 QuestionAnswerAdvisor 的区别:

特性 QuestionAnswerAdvisor RetrievalAugmentationAdvisor
查询转换 ❌ 不支持 ✅ 支持多个转换器链
查询增强 ❌ 不支持 ✅ 支持 ContextualQueryAugmenter
文档后处理 ❌ 不支持 ✅ 支持自定义后处理器
配置复杂度 简单 复杂但灵活
适用场景 基础 RAG 高级 RAG、生产环境

2. VectorStoreDocumentRetriever

作用:从向量库中检索相关文档的检索器。

配置参数:

java 复制代码
VectorStoreDocumentRetriever.builder()
    .vectorStore(vectorStore)              // 向量库实例
    .similarityThreshold(0.0)              // 相似度阈值(0.0 表示不过滤)
    .topK(5)                                // 返回前 K 个结果(可选)
    .filterExpression(filterExpression)     // 元数据过滤表达式(可选)
    .build()

参数说明:

参数 类型 说明 默认值
vectorStore VectorStore 向量库实例 必填
similarityThreshold double 相似度阈值,低于此值的文档会被过滤 0.0
topK int 返回前 K 个最相似的文档 未设置
filterExpression FilterExpression 元数据过滤表达式 null

工作原理:

  1. 将用户查询转换为向量(使用 EmbeddingModel)
  2. 在向量库中搜索最相似的文档向量
  3. 根据 similarityThreshold 过滤低相似度文档
  4. 根据 topK 返回前 K 个结果
  5. 应用 filterExpression 进行元数据过滤(如果提供)

3. ContextualQueryAugmenter

作用:处理检索结果为空的情况,决定是否继续回答或返回提示信息。

配置参数:

java 复制代码
ContextualQueryAugmenter.builder()
    .allowEmptyContext(false)              // 是否允许空上下文
    .emptyContextPromptTemplate(template)   // 空上下文时的提示模板
    .build()

参数说明:

参数 类型 说明 默认值
allowEmptyContext boolean 是否允许在检索结果为空时继续回答 true
emptyContextPromptTemplate PromptTemplate 空上下文时的提示模板 null

行为模式:

allowEmptyContext 检索结果为空时的行为
true 继续使用 LLM 回答(可能产生幻觉)
false 使用 emptyContextPromptTemplate 返回提示信息

使用场景:

  • ✅ 严格知识库模式 :allowEmptyContext=false,确保只基于知识库回答
  • ✅ 宽松模式 :allowEmptyContext=true,允许 LLM 基于通用知识回答

4. RewriteQueryTransformer

作用:使用 LLM 重写用户查询,使其更适合检索系统。

配置参数:

java 复制代码
RewriteQueryTransformer.builder()
    .chatClientBuilder(ChatClient.builder(chatModel))  // ChatClient 构建器
    .targetSearchSystem("航空票务助手")                  // 目标检索系统描述
    .build()

工作原理:

  1. 接收原始用户查询
  2. 使用 LLM 根据 targetSearchSystem 重写查询
  3. 返回优化后的查询用于向量检索

示例:

复制代码
原始查询:"我今天心情不好,不想去玩了,你能不能告诉我退票需要多少钱?"

重写后查询:"退票费用是多少?"

优势:

  • ✅ 去除无关信息(如"心情不好")
  • ✅ 提取核心查询意图
  • ✅ 适配特定检索系统(如"航空票务助手")

5. TranslationQueryTransformer

作用:将用户查询翻译为目标语言,用于多语言知识库检索。

配置参数:

java 复制代码
TranslationQueryTransformer.builder()
    .chatClientBuilder(ChatClient.builder(chatModel))  // ChatClient 构建器
    .targetLanguage("english")                          // 目标语言
    .build()

参数说明:

参数 类型 说明 默认值
chatClientBuilder ChatClient.Builder ChatClient 构建器,用于调用 LLM 进行翻译 必填
targetLanguage String 目标语言(如 "english", "chinese", "spanish") 必填

工作原理:

  1. 接收原始用户查询(可能是中文)
  2. 使用 LLM 将查询翻译为目标语言(如英文)
  3. 使用翻译后的查询进行向量检索
  4. 返回检索到的文档(可能是英文文档)

示例:

复制代码
原始查询(中文):"退票需要多少费用?"

翻译后查询(英文):"What is the fee for ticket refund?"

检索结果:英文文档(如果知识库是英文的)

使用场景:

  • ✅ 多语言知识库:知识库是英文的,但用户用中文提问
  • ✅ 跨语言检索:支持不同语言之间的知识检索
  • ✅ 国际化应用:为不同语言用户提供统一的知识库访问

注意事项:

  • ⚠️ 翻译会增加 LLM 调用次数,可能影响响应时间
  • ⚠️ 翻译质量会影响检索准确性
  • ⚠️ 如果知识库和用户语言一致,不需要翻译

6. DocumentPostProcessors

作用:在检索到文档后、传递给 LLM 之前,对文档进行处理、过滤或转换。

配置方式:

java 复制代码
.documentPostProcessors((query, documents) -> {
    // 自定义处理逻辑
    System.out.println("Original query: " + query.text());
    System.out.println("Retrieved documents: " + documents.size());
    
    // 可以过滤、排序、转换文档
    return documents;  // 返回处理后的文档列表
})

参数说明:

参数 类型 说明
query Query 原始查询对象,包含查询文本
documents List<Document> 检索到的文档列表

常见用途:

1. 文档过滤
java 复制代码
.documentPostProcessors((query, documents) -> {
    // 过滤掉过短的文档
    return documents.stream()
        .filter(doc -> doc.getText().length() > 100)
        .collect(Collectors.toList());
})
2. 文档排序
java 复制代码
.documentPostProcessors((query, documents) -> {
    // 按元数据中的优先级排序
    return documents.stream()
        .sorted((d1, d2) -> {
            int p1 = (int) d1.getMetadata().getOrDefault("priority", 0);
            int p2 = (int) d2.getMetadata().getOrDefault("priority", 0);
            return Integer.compare(p2, p1);  // 降序
        })
        .collect(Collectors.toList());
})
3. 文档去重
java 复制代码
.documentPostProcessors((query, documents) -> {
    // 根据文档 ID 去重
    Set<String> seenIds = new HashSet<>();
    return documents.stream()
        .filter(doc -> {
            String id = doc.getId();
            return seenIds.add(id);
        })
        .collect(Collectors.toList());
})
4. 文档监控和日志
java 复制代码
.documentPostProcessors((query, documents) -> {
    // 记录检索统计信息
    log.info("Query: {}, Retrieved: {} documents", 
        query.text(), documents.size());
    
    // 记录文档详情
    documents.forEach(doc -> {
        log.debug("Document ID: {}, Similarity: {}", 
            doc.getId(), doc.getMetadata().get("similarity"));
    });
    
    return documents;
})
5. 文档增强
java 复制代码
.documentPostProcessors((query, documents) -> {
    // 为文档添加额外的元数据
    return documents.stream()
        .map(doc -> {
            Map<String, Object> metadata = new HashMap<>(doc.getMetadata());
            metadata.put("processed_at", LocalDateTime.now());
            metadata.put("query_text", query.text());
            return Document.builder()
                .from(doc)
                .metadata(metadata)
                .build();
        })
        .collect(Collectors.toList());
})

执行流程与原理

完整执行流程

复制代码
用户查询:"我今天心情不好,不想去玩了,你能不能告诉我退票需要多少钱?"
    ↓
[1] Query Transformers(查询转换器链)
    ├─ RewriteQueryTransformer
    │   └─ 重写查询:"退票费用是多少?"
    │
    └─ TranslationQueryTransformer
        └─ 翻译查询:"What is the fee for ticket refund?"
    ↓
[2] Document Retriever(文档检索器)
    ├─ 将查询转换为向量
    ├─ 在向量库中搜索相似文档
    ├─ 应用 similarityThreshold 过滤
    └─ 返回相关文档列表
    ↓
[3] Document Post Processors(文档后处理器)
    ├─ 记录日志
    ├─ 过滤/排序文档(可选)
    └─ 返回处理后的文档
    ↓
[4] Query Augmenter(查询增强器)
    ├─ 检查检索结果是否为空
    │   ├─ 如果为空且 allowEmptyContext=false
    │   │   └─ 返回 emptyContextPromptTemplate 的提示
    │   └─ 如果为空且 allowEmptyContext=true
    │       └─ 继续执行(使用 LLM 通用知识)
    └─ 如果有文档,构建增强后的 Prompt
    ↓
[5] LLM 生成回答
    ├─ 输入:用户查询 + 检索到的文档上下文
    ├─ 处理:LLM 基于上下文生成回答
    └─ 输出:最终答案
    ↓
返回答案:"根据我们的政策,退票费用取决于舱位类型:
          - 经济舱:75 美元
          - 豪华经济舱:50 美元
          - 商务舱:25 美元"

详细步骤说明

步骤 1:查询转换(Query Transformation)

目的:优化用户查询,使其更适合向量检索。

执行顺序:

  1. RewriteQueryTransformer 先执行

    • 输入:原始查询(包含无关信息)
    • 输出:重写后的查询(提取核心意图)
  2. TranslationQueryTransformer 后执行

    • 输入:重写后的查询
    • 输出:翻译后的查询

示例流程:

复制代码
原始查询:"我今天心情不好,不想去玩了,你能不能告诉我退票需要多少钱?"
    ↓ RewriteQueryTransformer
重写查询:"退票费用是多少?"
    ↓ TranslationQueryTransformer
翻译查询:"What is the fee for ticket refund?"
步骤 2:文档检索(Document Retrieval)

目的:从向量库中检索相关文档。

执行过程:

  1. 使用 EmbeddingModel 将查询文本转换为向量
  2. 在 VectorStore 中计算查询向量与文档向量的相似度
  3. 根据 similarityThreshold 过滤低相似度文档
  4. 根据 topK 返回前 K 个最相似的文档
  5. 应用 filterExpression 进行元数据过滤(如果提供)

相似度计算:

  • 使用余弦相似度(Cosine Similarity)或点积(Dot Product)
  • 相似度范围:-1, 1 或 0, 1
  • similarityThreshold=0.0 表示不过滤任何文档
步骤 3:文档后处理(Document Post Processing)

目的:对检索到的文档进行进一步处理。

执行时机:在文档检索之后、传递给 LLM 之前。

常见操作:

  • 文档过滤(去除不相关文档)
  • 文档排序(按优先级、相关性等)
  • 文档去重(避免重复内容)
  • 文档增强(添加元数据)
  • 监控和日志(记录检索统计)
步骤 4:查询增强(Query Augmentation)

目的:处理空上下文情况,构建最终的 Prompt。

执行逻辑:

java 复制代码
if (documents.isEmpty()) {
    if (allowEmptyContext == false) {
        // 返回空上下文提示
        return emptyContextPromptTemplate.render();
    } else {
        // 继续使用 LLM 回答(可能产生幻觉)
        return llm.call(userQuery);
    }
} else {
    // 构建增强后的 Prompt
    Prompt prompt = buildPrompt(userQuery, documents);
    return llm.call(prompt);
}

Prompt 构建:

复制代码
System Message: "你是一个航空票务助手,基于以下文档回答问题:"

User Message: 
"""
文档1:预订航班: 通过我们的网站或移动应用程序预订...

文档2:取消预订: 最晚在航班起飞前 48 小时取消...

用户问题:退票费用是多少?
"""
步骤 5:LLM 生成回答

目的:基于检索到的文档上下文生成最终答案。

执行过程:

  1. LLM 接收增强后的 Prompt(包含文档上下文和用户查询)
  2. LLM 分析文档内容,提取相关信息
  3. LLM 生成基于文档的回答
  4. 返回最终答案

使用场景

场景 1:企业知识库问答系统

需求:企业内部知识库,需要处理复杂的用户查询,支持查询优化和空上下文处理。

实现:

java 复制代码
RetrievalAugmentationAdvisor advisor = RetrievalAugmentationAdvisor.builder()
    .documentRetriever(VectorStoreDocumentRetriever.builder()
        .vectorStore(vectorStore)
        .similarityThreshold(0.6)
        .topK(5)
        .build())
    .queryAugmenter(ContextualQueryAugmenter.builder()
        .allowEmptyContext(false)  // 严格模式,只基于知识库回答
        .emptyContextPromptTemplate(PromptTemplate.builder()
            .template("抱歉,我无法在知识库中找到相关信息。请咨询相关部门。")
            .build())
        .build())
    .queryTransformers(RewriteQueryTransformer.builder()
        .chatClientBuilder(ChatClient.builder(chatModel))
        .targetSearchSystem("企业知识库系统")
        .build())
    .documentPostProcessors((query, documents) -> {
        // 记录检索日志
        log.info("Query: {}, Retrieved: {} documents", 
            query.text(), documents.size());
        return documents;
    })
    .build();

业务价值:

  • ✅ 提高查询准确性(查询重写)
  • ✅ 避免幻觉回答(空上下文处理)
  • ✅ 支持审计和监控(文档后处理)

场景 2:多语言知识库系统

需求:知识库是英文的,但用户可能用中文、日文等多种语言提问。

实现:

java 复制代码
RetrievalAugmentationAdvisor advisor = RetrievalAugmentationAdvisor.builder()
    .documentRetriever(VectorStoreDocumentRetriever.builder()
        .vectorStore(vectorStore)  // 英文知识库
        .build())
    .queryTransformers(
        // 先重写查询
        RewriteQueryTransformer.builder()
            .chatClientBuilder(ChatClient.builder(chatModel))
            .targetSearchSystem("Product Documentation")
            .build(),
        // 再翻译为英文
        TranslationQueryTransformer.builder()
            .chatClientBuilder(ChatClient.builder(chatModel))
            .targetLanguage("english")
            .build()
    )
    .build();

业务价值:

  • ✅ 支持多语言用户访问英文知识库
  • ✅ 提高跨语言检索准确性
  • ✅ 降低多语言知识库维护成本

场景 3:智能客服系统

需求:客服系统需要处理用户的情感化表达,提取核心问题,并确保回答基于知识库。

实现:

java 复制代码
RetrievalAugmentationAdvisor advisor = RetrievalAugmentationAdvisor.builder()
    .documentRetriever(VectorStoreDocumentRetriever.builder()
        .vectorStore(vectorStore)
        .similarityThreshold(0.5)
        .topK(3)
        .build())
    .queryAugmenter(ContextualQueryAugmenter.builder()
        .allowEmptyContext(false)
        .emptyContextPromptTemplate(PromptTemplate.builder()
            .template("抱歉,我无法找到相关信息。请转接人工客服。")
            .build())
        .build())
    .queryTransformers(RewriteQueryTransformer.builder()
        .chatClientBuilder(ChatClient.builder(chatModel))
        .targetSearchSystem("客服知识库")
        .build())
    .documentPostProcessors((query, documents) -> {
        // 过滤掉过时的文档
        return documents.stream()
            .filter(doc -> {
                String status = (String) doc.getMetadata().get("status");
                return "active".equals(status);
            })
            .collect(Collectors.toList());
    })
    .build();

业务价值:

  • ✅ 处理用户情感化表达(查询重写)
  • ✅ 确保回答准确性(空上下文处理)
  • ✅ 过滤过时信息(文档后处理)

场景 4:法律文档检索系统

需求:法律文档系统需要精确检索,支持查询优化和文档质量过滤。

实现:

java 复制代码
RetrievalAugmentationAdvisor advisor = RetrievalAugmentationAdvisor.builder()
    .documentRetriever(VectorStoreDocumentRetriever.builder()
        .vectorStore(vectorStore)
        .similarityThreshold(0.7)  // 高阈值,确保精确性
        .topK(5)
        .filterExpression(FilterExpressionBuilder.builder()
            .eq("status", "有效")
            .build())
        .build())
    .queryAugmenter(ContextualQueryAugmenter.builder()
        .allowEmptyContext(false)  // 严格模式
        .emptyContextPromptTemplate(PromptTemplate.builder()
            .template("未找到相关法律条文,请咨询专业律师。")
            .build())
        .build())
    .queryTransformers(RewriteQueryTransformer.builder()
        .chatClientBuilder(ChatClient.builder(chatModel))
        .targetSearchSystem("法律文档检索系统")
        .build())
    .documentPostProcessors((query, documents) -> {
        // 按发布时间排序,优先返回最新文档
        return documents.stream()
            .sorted((d1, d2) -> {
                String date1 = (String) d1.getMetadata().get("published_date");
                String date2 = (String) d2.getMetadata().get("published_date");
                return date2.compareTo(date1);  // 降序
            })
            .collect(Collectors.toList());
    })
    .build();

业务价值:

  • ✅ 确保法律条文的准确性
  • ✅ 优先返回最新、有效的法律条文
  • ✅ 避免引用过时或无效的法律条文

与其他方法的对比

java 复制代码
package com.xushu.springai.rag;

import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel;
import org.apache.el.lang.ExpressionBuilder;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.client.advisor.api.Advisor;
import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.document.Document;
import org.springframework.ai.model.transformer.KeywordMetadataEnricher;
import org.springframework.ai.rag.Query;
import org.springframework.ai.rag.advisor.RetrievalAugmentationAdvisor;
import org.springframework.ai.rag.generation.augmentation.ContextualQueryAugmenter;
import org.springframework.ai.rag.preretrieval.query.transformation.QueryTransformer;
import org.springframework.ai.rag.preretrieval.query.transformation.RewriteQueryTransformer;
import org.springframework.ai.rag.preretrieval.query.transformation.TranslationQueryTransformer;
import org.springframework.ai.rag.retrieval.search.VectorStoreDocumentRetriever;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;

import java.util.List;

@SpringBootTest
public class ChatClientRagTest {

    ChatClient chatClient;
    @BeforeEach
    public void init(
            @Autowired DashScopeChatModel chatModel,
            @Autowired VectorStore vectorStore) {
        Document doc = Document.builder()
                .text("""
                        预订航班:
                        - 通过我们的网站或移动应用程序预订。
                        - 预订时需要全额付款。
                        - 确保个人信息(姓名、ID 等)的准确性,因为更正可能会产生 25 的费用。
                        """)
                .build();
        Document doc2 = Document.builder()
                .text("""
                        取消预订:
                        - 最晚在航班起飞前 48 小时取消。
                        - 取消费用:经济舱 75 美元,豪华经济舱 50 美元,商务舱 25 美元。
                        - 退款将在 7 个工作日内处理。
                        """)
                .build();

        List<Document> documents = List.of(doc, doc2);


        // 存储向量(内部会自动向量化)
        vectorStore.add(documents);


    }


    @Test
    public void testRag(
            @Autowired DashScopeChatModel dashScopeChatModel,
            @Autowired VectorStore vectorStore) {


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

        String content = chatClient.prompt()
                .user("退票需要多少费用?")
                .advisors(
                        SimpleLoggerAdvisor.builder().build(),
                        QuestionAnswerAdvisor.builder(vectorStore)
                                .searchRequest(
                                       SearchRequest.builder()
                                               .topK(5)
                                               .similarityThreshold(0.6)
                                               .build()
                                ).build()
                )
                .call()
                .content();



        System.out.println(content);
    }



    @Test
    public void testRag2(
            @Autowired DashScopeChatModel dashScopeChatModel,
            @Autowired VectorStore vectorStore) {


        chatClient = ChatClient.builder(dashScopeChatModel)
                .defaultAdvisors(SimpleLoggerAdvisor.builder().build())
                .build();
        //FilterExpression基于元数据过滤搜索结果的参数
        String content = chatClient.prompt()
                .user("退票需要多少费用?")
                .advisors(
                        SimpleLoggerAdvisor.builder().build(),
                        QuestionAnswerAdvisor.builder(vectorStore)
                                .searchRequest(
                                        SearchRequest.builder()
                                                .topK(5)
                                                .similarityThreshold(0.1)
                                                //.filterExpression()
                                                .build()
                                ).build()
                )
                .call()
                .content();



        System.out.println(content);
    }


    @Test
    public void testRag3(@Autowired VectorStore vectorStore,
                        @Autowired DashScopeChatModel dashScopeChatModel) {


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

        // 增强多
        Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
                // 查 = QuestionAnswerAdvisor
                .documentRetriever(VectorStoreDocumentRetriever.builder()
                        .similarityThreshold(0.0)
//                        .topK()
//                        .filterExpression()
                        .vectorStore(vectorStore)
                        .build())
                // 检索为空时,allowEmptyContext=false返回提示   allowEmptyContext=true 正常回答
                .queryAugmenter(ContextualQueryAugmenter.builder()
                        .allowEmptyContext(false)
                        .emptyContextPromptTemplate(PromptTemplate.builder().template("用户查询位于知识库之外。礼貌地告知用户您无法回答").build())
                        .build())
                //  检索查询转换器
                // 重写检索查询转换器
                .queryTransformers(RewriteQueryTransformer.builder()
                        .chatClientBuilder(ChatClient.builder(dashScopeChatModel))
                        .targetSearchSystem("航空票务助手")
                        .build())
                // 翻译转换器
                .queryTransformers(TranslationQueryTransformer.builder()
                                    .chatClientBuilder(ChatClient.builder(dashScopeChatModel))
                                    .targetLanguage("english")
                                    .build())
                // 检索后文档监控、操作
                .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);
    }

    @TestConfiguration
    static class TestConfig {

        @Bean
        public VectorStore vectorStore(DashScopeEmbeddingModel embeddingModel) {
            return SimpleVectorStore.builder(embeddingModel).build();
        }
    }



}

testRag vs testRag2 vs testRag3

特性 testRag testRag2 testRag3
使用的 Advisor QuestionAnswerAdvisor QuestionAnswerAdvisor RetrievalAugmentationAdvisor
查询转换 ❌ ❌ ✅ RewriteQueryTransformer ✅ TranslationQueryTransformer
查询增强 ❌ ❌ ✅ ContextualQueryAugmenter
文档后处理 ❌ ❌ ✅ DocumentPostProcessors
空上下文处理 ❌ ❌ ✅ 支持自定义提示
配置复杂度 简单 简单 复杂
灵活性 低 低 高
适用场景 基础 RAG 基础 RAG + 元数据过滤 高级 RAG、生产环境

代码对比

testRag(基础 RAG)
java 复制代码
QuestionAnswerAdvisor.builder(vectorStore)
    .searchRequest(SearchRequest.builder()
        .topK(5)
        .similarityThreshold(0.6)
        .build())
    .build()

特点:

  • ✅ 简单易用
  • ❌ 不支持查询转换
  • ❌ 不支持空上下文处理
  • ❌ 不支持文档后处理
testRag2(基础 RAG + 元数据过滤)
java 复制代码
QuestionAnswerAdvisor.builder(vectorStore)
    .searchRequest(SearchRequest.builder()
        .topK(5)
        .similarityThreshold(0.1)
        .filterExpression(filterExpression)  // 可以添加元数据过滤
        .build())
    .build()

特点:

  • ✅ 支持元数据过滤
  • ❌ 不支持查询转换
  • ❌ 不支持空上下文处理
  • ❌ 不支持文档后处理
testRag3(高级 RAG)
java 复制代码
RetrievalAugmentationAdvisor.builder()
    .documentRetriever(VectorStoreDocumentRetriever.builder()...)
    .queryAugmenter(ContextualQueryAugmenter.builder()...)
    .queryTransformers(RewriteQueryTransformer.builder()...)
    .queryTransformers(TranslationQueryTransformer.builder()...)
    .documentPostProcessors((query, documents) -> {...})
    .build()

特点:

  • ✅ 支持查询转换(重写、翻译)
  • ✅ 支持空上下文处理
  • ✅ 支持文档后处理
  • ✅ 高度可定制
  • ⚠️ 配置较复杂
  • ⚠️ 需要更多 LLM 调用(查询转换)

选择建议

场景 推荐方法 原因
快速原型 testRag 简单易用,快速验证
基础生产环境 testRag2 支持元数据过滤,满足基本需求
高级生产环境 testRag3 支持查询优化、空上下文处理等高级功能
多语言知识库 testRag3 支持查询翻译
严格知识库模式 testRag3 支持空上下文处理,避免幻觉

最佳实践

1. 查询转换器的顺序

原则:先重写,再翻译。

java 复制代码
// ✅ 正确顺序
.queryTransformers(
    RewriteQueryTransformer.builder()...      // 先重写
        .build(),
    TranslationQueryTransformer.builder()...   // 再翻译
        .build()
)

// ❌ 错误顺序:先翻译再重写会导致翻译质量下降
.queryTransformers(
    TranslationQueryTransformer.builder()...   // 先翻译(不推荐)
        .build(),
    RewriteQueryTransformer.builder()...      // 再重写
        .build()
)

原因:

  • 重写可以去除无关信息,提高翻译质量
  • 翻译后的查询可能包含语法错误,影响重写效果

2. 空上下文处理策略

严格模式(推荐用于生产环境)
java 复制代码
.queryAugmenter(ContextualQueryAugmenter.builder()
    .allowEmptyContext(false)  // 严格模式
    .emptyContextPromptTemplate(PromptTemplate.builder()
        .template("抱歉,我无法在知识库中找到相关信息。请咨询相关部门或人工客服。")
        .build())
    .build())

适用场景:

  • ✅ 企业知识库(需要准确性)
  • ✅ 法律文档系统(避免错误引用)
  • ✅ 医疗知识系统(避免误导)
宽松模式(适用于通用场景)
java 复制代码
.queryAugmenter(ContextualQueryAugmenter.builder()
    .allowEmptyContext(true)  // 宽松模式
    .build())

适用场景:

  • ✅ 通用问答系统
  • ✅ 客服系统(允许基于通用知识回答)
  • ✅ 原型验证阶段

3. 相似度阈值设置

建议:

java 复制代码
// 根据业务场景选择合适的阈值
VectorStoreDocumentRetriever.builder()
    .similarityThreshold(
        // 严格场景(法律、医疗):0.7-0.8
        // 一般场景(客服、知识库):0.5-0.6
        // 宽松场景(探索性查询):0.3-0.4
        0.6  // 根据实际效果调整
    )
    .build()

调整策略:

  1. 从较低阈值开始(如 0.3)
  2. 观察检索结果的质量
  3. 逐步提高阈值,直到找到平衡点
  4. 考虑使用 topK 限制结果数量

4. 文档后处理的最佳实践

监控和日志
java 复制代码
.documentPostProcessors((query, documents) -> {
    // 记录检索统计
    log.info("Query: '{}', Retrieved: {} documents", 
        query.text(), documents.size());
    
    // 记录文档详情(DEBUG 级别)
    if (log.isDebugEnabled()) {
        documents.forEach(doc -> {
            log.debug("Document ID: {}, Similarity: {}, Text length: {}", 
                doc.getId(), 
                doc.getMetadata().get("similarity"),
                doc.getText().length());
        });
    }
    
    return documents;
})
文档质量过滤
java 复制代码
.documentPostProcessors((query, documents) -> {
    return documents.stream()
        .filter(doc -> {
            // 过滤过短的文档
            if (doc.getText().length() < 50) {
                return false;
            }
            
            // 过滤状态为"已删除"的文档
            String status = (String) doc.getMetadata().get("status");
            if ("deleted".equals(status)) {
                return false;
            }
            
            return true;
        })
        .collect(Collectors.toList());
})
文档排序
java 复制代码
.documentPostProcessors((query, documents) -> {
    return documents.stream()
        .sorted((d1, d2) -> {
            // 优先返回高优先级的文档
            int p1 = (int) d1.getMetadata().getOrDefault("priority", 0);
            int p2 = (int) d2.getMetadata().getOrDefault("priority", 0);
            if (p1 != p2) {
                return Integer.compare(p2, p1);  // 降序
            }
            
            // 如果优先级相同,按相似度排序
            double s1 = (double) d1.getMetadata().getOrDefault("similarity", 0.0);
            double s2 = (double) d2.getMetadata().getOrDefault("similarity", 0.0);
            return Double.compare(s2, s1);  // 降序
        })
        .collect(Collectors.toList());
})

5. 性能优化

减少 LLM 调用次数
java 复制代码
// ⚠️ 避免不必要的查询转换
// 如果用户查询已经是标准格式,不需要重写
if (needsRewrite(userQuery)) {
    .queryTransformers(RewriteQueryTransformer.builder()...)
}

// ⚠️ 如果知识库和用户语言一致,不需要翻译
if (!isSameLanguage(userQuery, knowledgeBaseLanguage)) {
    .queryTransformers(TranslationQueryTransformer.builder()...)
}
缓存查询转换结果
java 复制代码
// 缓存重写后的查询,避免重复调用 LLM
Map<String, String> rewriteCache = new ConcurrentHashMap<>();

.documentPostProcessors((query, documents) -> {
    String cachedRewrite = rewriteCache.get(query.text());
    if (cachedRewrite != null) {
        // 使用缓存的查询
    }
    return documents;
})
限制文档数量
java 复制代码
.documentPostProcessors((query, documents) -> {
    // 限制传递给 LLM 的文档数量,减少 token 消耗
    return documents.stream()
        .limit(3)  // 只保留前 3 个最相关的文档
        .collect(Collectors.toList());
})

6. 错误处理

处理查询转换失败
java 复制代码
try {
    RetrievalAugmentationAdvisor advisor = RetrievalAugmentationAdvisor.builder()
        .queryTransformers(RewriteQueryTransformer.builder()...)
        .build();
} catch (Exception e) {
    // 降级为不使用查询转换
    log.warn("Query transformer failed, falling back to direct query", e);
    RetrievalAugmentationAdvisor advisor = RetrievalAugmentationAdvisor.builder()
        .documentRetriever(...)
        .build();
}
处理空上下文
java 复制代码
.queryAugmenter(ContextualQueryAugmenter.builder()
    .allowEmptyContext(false)
    .emptyContextPromptTemplate(PromptTemplate.builder()
        .template("""
            抱歉,我无法在知识库中找到相关信息。
            
            建议:
            1. 尝试使用不同的关键词重新提问
            2. 联系相关部门获取帮助
            3. 查看知识库目录
            """)
        .build())
    .build())

常见问题

Q1: 为什么需要查询转换器?

问题:用户查询可能包含无关信息、情感化表达或不符合检索系统的格式。

解决方案 :使用 RewriteQueryTransformer 提取核心查询意图。

示例:

复制代码
原始查询:"我今天心情不好,不想去玩了,你能不能告诉我退票需要多少钱?"
    ↓ RewriteQueryTransformer
重写查询:"退票费用是多少?"

优势:

  • ✅ 提高检索准确性
  • ✅ 减少噪音文档
  • ✅ 适配特定检索系统

Q2: 什么时候需要 TranslationQueryTransformer?

适用场景:

  • ✅ 知识库是英文的,但用户用中文提问
  • ✅ 多语言知识库,需要统一检索语言
  • ✅ 国际化应用,支持跨语言检索

不需要的场景:

  • ❌ 知识库和用户语言一致
  • ❌ 向量模型支持多语言(如多语言 Embedding 模型)

Q3: allowEmptyContext 应该设置为 true 还是 false?

建议:

场景 推荐值 原因
企业知识库 false 确保只基于知识库回答,避免幻觉
法律文档系统 false 避免错误引用法律条文
医疗知识系统 false 避免误导性医疗建议
通用客服系统 true 允许基于通用知识回答常见问题
原型验证 true 快速验证功能,后续再调整

Q4: 查询转换器会增加多少延迟?

延迟分析:

转换器 LLM 调用次数 预估延迟(秒)
RewriteQueryTransformer 1 次 0.5-2.0
TranslationQueryTransformer 1 次 0.5-2.0
两者组合 2 次 1.0-4.0

优化建议:

  • ✅ 缓存转换结果
  • ✅ 异步执行转换(如果支持)
  • ✅ 只在必要时使用转换器

Q5: 如何调试查询转换过程?

调试方法:

java 复制代码
// 方法 1:在文档后处理器中记录
.documentPostProcessors((query, documents) -> {
    System.out.println("=== Query Transformation Debug ===");
    System.out.println("Original query: " + query.text());
    System.out.println("Retrieved documents: " + documents.size());
    documents.forEach(doc -> {
        System.out.println("Document: " + doc.getText().substring(0, 
            Math.min(100, doc.getText().length())));
    });
    return documents;
})

// 方法 2:使用 SimpleLoggerAdvisor
ChatClient.builder(chatModel)
    .defaultAdvisors(SimpleLoggerAdvisor.builder().build())
    .build()

Q6: 文档后处理器会影响性能吗?

性能影响:

  • 轻微影响:简单的过滤、排序操作(< 10ms)
  • 中等影响:复杂的文档处理(10-100ms)
  • 显著影响:涉及外部服务调用(> 100ms)

优化建议:

  • ✅ 避免在文档后处理器中进行耗时操作
  • ✅ 使用异步处理(如果支持)
  • ✅ 限制处理的文档数量

Q7: 如何选择合适的相似度阈值?

选择策略:

  1. 从低阈值开始(如 0.3)
  2. 观察检索结果 :
    • 如果返回太多不相关文档 → 提高阈值
    • 如果返回太少相关文档 → 降低阈值
  3. 根据业务需求调整 :
    • 严格场景(法律、医疗):0.7-0.8
    • 一般场景(客服、知识库):0.5-0.6
    • 宽松场景(探索性查询):0.3-0.4

Q8: RetrievalAugmentationAdvisor 和 QuestionAnswerAdvisor 如何选择?

选择建议:

需求 推荐 Advisor
快速原型 QuestionAnswerAdvisor
基础 RAG QuestionAnswerAdvisor
查询转换 RetrievalAugmentationAdvisor
空上下文处理 RetrievalAugmentationAdvisor
文档后处理 RetrievalAugmentationAdvisor
多语言支持 RetrievalAugmentationAdvisor
生产环境 RetrievalAugmentationAdvisor

总结

核心要点

  1. RetrievalAugmentationAdvisor 是高级 RAG 解决方案 :相比 QuestionAnswerAdvisor,提供了更多的定制化能力。

  2. 查询转换器优化检索 :RewriteQueryTransformer 和 TranslationQueryTransformer 可以提高检索准确性。

  3. 空上下文处理避免幻觉 :通过 ContextualQueryAugmenter 的 allowEmptyContext=false 确保只基于知识库回答。

  4. 文档后处理增强灵活性:支持文档过滤、排序、增强等操作。

  5. 配置复杂度与灵活性平衡 :testRag3 配置较复杂,但提供了最大的灵活性。

适用场景总结

场景 是否适合 testRag3
企业知识库 ✅ 非常适合
多语言知识库 ✅ 非常适合
智能客服系统 ✅ 非常适合
法律文档系统 ✅ 非常适合
医疗知识系统 ✅ 非常适合
快速原型验证 ⚠️ 建议使用 testRag
简单问答系统 ⚠️ 建议使用 testRag 或 testRag2

最佳实践总结

实践 说明
✅ 查询转换器顺序 先重写,再翻译
✅ 空上下文处理 生产环境使用严格模式
✅ 相似度阈值 根据业务场景调整(0.5-0.7)
✅ 文档后处理 用于监控、过滤、排序
✅ 性能优化 缓存、限制文档数量
✅ 错误处理 实现降级策略

技术栈总结

  • 核心组件 :RetrievalAugmentationAdvisor
  • 文档检索 :VectorStoreDocumentRetriever
  • 查询转换 :RewriteQueryTransformer、TranslationQueryTransformer
  • 查询增强 :ContextualQueryAugmenter
  • 文档处理 :DocumentPostProcessors

参考资料

相关推荐
姜鱼问生5 小时前
docker exec 排查容器问题:从日志到进程
运维·网络·数据库·docker·容器
qq_543447825 小时前
DNS解析怎么排查?故障处理与运维实用手册
运维·服务器·网络·php
DeviceHub5 小时前
AI服务器电源设计进阶:TLVR电感的应用解析与选型验证指南
运维·服务器·人工智能
Apipi*5 小时前
30天速通Linux 第七章进程间通信
linux·运维·服务器
迪康软件zz5 小时前
终端审批体系怎么搭?从模板配置到业务连续性
运维·安全·自动化运维
科技研学社5 小时前
中小服装工装厂自动化改造:产线节拍平衡与工位减负实施方案
运维·自动化
极光通讯5 小时前
服务器内存替代料选型方法论:主料号缺货时如何找到Pin-to-Pin兼容型号
运维·服务器
草根大哥5 小时前
08-横向扩容与突发高并发:加机器到底买到了什么
运维·webrtc·srt·横向扩容·whip·自建cdn
oicola6 小时前
【运维】CI/CD是什么,合格的规范应该怎么设置
运维·ci/cd
Dachui_11226 小时前
内网穿透如何限制访问地区?ZeroNews Geo Location 区域访问控制实践
运维·网络安全·内网穿透·访问控制·远程办公·api安全·ip白名单