检索增强拦截器-查询转换器&翻译转化器&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. 查询转换器优化检索RewriteQueryTransformerTranslationQueryTransformer 可以提高检索准确性。

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

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

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

适用场景总结

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

最佳实践总结

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

技术栈总结

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

参考资料

相关推荐
sxgzzn2 小时前
无人机热成像+数字孪生:光伏运维的智能革命
运维·无人机
Suchadar2 小时前
Linux计划任务进程
linux·运维·服务器
食咗未2 小时前
Linux microcom工具的使用
linux·运维·服务器·驱动开发·串口调试
YYYing.2 小时前
【计算机网络 | 第四篇】路由与NAT技术
运维·服务器·网络·网络协议·计算机网络
感觉不怎么会2 小时前
ubuntu - 搭建TR069平台问题(Open ACS)
linux·运维·ubuntu
Xの哲學2 小时前
Linux Worklet 深入剖析: 轻量级延迟执行机制
linux·服务器·网络·数据结构·算法
oh,huoyuyan2 小时前
火语言RPA随机访问网址 + 随机时长停留 自动化循环案例分享
运维·自动化·rpa
宴之敖者、2 小时前
Linux——初始Linux系统
linux·运维·服务器
独自破碎E2 小时前
在Linux系统中如何使用ssh进行远程登录?
linux·运维·ssh