检索增强生成 (RAG)

检索增强生成(RAG)是一种有效技术,旨在克服大型语言模型在处理长篇内容、事实准确性及上下文意识方面的局限。

Spring AI 通过提供模块化架构支持 RAG,让您能够自行构建定制化的 RAG 流程,或借助 Advisor API 直接使用开箱即用的 RAG 流程。

##顾问(Advisors )

Spring AI 为常见的 RAG 流程提供了开箱即用的支持,通过 Advisor API 实现。

要使用 QuestionAnswerAdvisorVectorStoreChatMemoryAdvisor,您需要将 spring-ai-advisors-vector-store 依赖项添加到项目中:

<dependency>
   <groupId>org.springframework.ai</groupId>
   <artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency>

QuestionAnswerAdvisor

向量数据库存储着 AI 模型未知的数据。当用户问题发送至 AI 模型时,问题回答顾问会查询向量数据库,寻找与用户问题相关的文档。

向量数据库的响应被附加到用户文本中,为 AI 模型生成回答提供上下文。

假设您已将数据加载到 VectorStore 中,您可以通过向 ChatClient 提供一个 QuestionAnswerAdvisor 实例来执行检索增强生成(RAG)。

ChatResponse response = ChatClient.builder(chatModel)
        .build().prompt()
        .advisors(new QuestionAnswerAdvisor(vectorStore))
        .user(userText)
        .call()
        .chatResponse();

在此示例中,QuestionAnswerAdvisor 将对向量数据库中的所有文档执行最近邻搜索。为了限制搜索的文档类型,SearchRequest 采用了一种类似于 SQL 的过滤表达式,这种表达式在所有 VectorStores 中都是可移植的。

在创建 QuestionAnswerAdvisor 时,可配置此过滤器表达式,从而使其始终应用于所有 ChatClient 请求,或在每次请求时动态提供。

以下是创建一个 QuestionAnswerAdvisor 实例的方法,其中阈值设为 0.8,并返回前 6 个结果。

var qaAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
        .searchRequest(SearchRequest.builder().similarityThreshold(0.8d).topK(6).build())
        .build();

动态过滤表达式(Dynamic Filter Expressions)

在运行时使用 FILTER_EXPRESSION 顾问上下文参数更新 SearchRequest 的筛选表达式:

ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(QuestionAnswerAdvisor.builder(vectorStore)
        .searchRequest(SearchRequest.builder().build())
        .build())
    .build();

// Update filter expression at runtime
String content = this.chatClient.prompt()
    .user("Please answer my question XYZ")
    .advisors(a -> a.param(QuestionAnswerAdvisor.FILTER_EXPRESSION, "type == 'Spring'"))
    .call()
    .content();

FILTER_EXPRESSION参数允许您根据提供的表达式动态过滤搜索结果。

自定义模板(Custom Template)

QuestionAnswerAdvisor 采用默认模板,将检索到的文档与用户问题结合以增强效果。您可以通过.promptTemplate ()构建方法提供自定义的 PromptTemplate 对象,来调整这一行为。

此处提供的 PromptTemplate 定制了顾问如何将检索到的上下文与用户查询合并。这与在 ChatClient 自身配置 TemplateRenderer(通过.templateRenderer ())不同,后者影响的是在顾问运行之前初始用户 / 系统提示内容的渲染方式。有关客户端级别模板渲染的更多详情,请参阅 ChatClient 提示模板 。

自定义的 PromptTemplate 可以使用任何 TemplateRenderer 实现(默认情况下,它基于 StringTemplate 引擎使用 StPromptTemplate)。关键要求是模板必须包含以下两个占位符:

  • query: 用于接收用户问题的查询占位符。
  • question_answer_context : 用于接收检索到的上下文的占位符
PromptTemplate customPromptTemplate = PromptTemplate.builder()
    .renderer(StTemplateRenderer.builder().startDelimiterToken('<').endDelimiterToken('>').build())
    .template("""
            <query>

            Context information is below.

            ---------------------
            <question_answer_context>
            ---------------------

            Given the context information and no prior knowledge, answer the query.

            Follow these rules:

            1. If the answer is not in the context, just say that you don't know.
            2. Avoid statements like "Based on the context..." or "The provided information...".
            """)
    .build();

    String question = "Where does the adventure of Anacletus and Birba take place?";

    QuestionAnswerAdvisor qaAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
        .promptTemplate(customPromptTemplate)
        .build();

    String response = ChatClient.builder(chatModel).build()
        .prompt(question)
        .advisors(qaAdvisor)
        .call()
        .content();

RetrievalAugmentationAdvisor

Spring AI 包含一个 RAG 模块库 ,您可以使用它来构建自己的 RAG 流程。 检索增强顾问(RetrievalAugmentationAdvisor)是一种顾问,为最常见的 RAG 流程提供了开箱即用的实现, 基于模块化架构。

要使用 RetrievalAugmentationAdvisor,您需要在项目中添加 spring-ai-rag 依赖项:

<dependency>
   <groupId>org.springframework.ai</groupId>
   <artifactId>spring-ai-rag</artifactId>
</dependency>

顺序 RAG 流程(Sequential RAG Flows)

Naive RAG

Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
        .documentRetriever(VectorStoreDocumentRetriever.builder()
                .similarityThreshold(0.50)
                .vectorStore(vectorStore)
                .build())
        .build();

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

默认情况下,RetrievalAugmentationAdvisor 不允许检索到的上下文为空。当这种情况发生时,它会指示模型不回答用户查询。您可以通过以下方式允许空上下文。

Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
        .documentRetriever(VectorStoreDocumentRetriever.builder()
                .similarityThreshold(0.50)
                .vectorStore(vectorStore)
                .build())
        .queryAugmenter(ContextualQueryAugmenter.builder()
                .allowEmptyContext(true)
                .build())
        .build();

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

VectorStoreDocumentRetriever 接受一个 FilterExpression(过滤表达式),用于根据元数据筛选搜索结果。您可以在实例化 VectorStoreDocumentRetriever 时提供此表达式,或在每次请求时通过 FILTER_EXPRESSION 顾问上下文参数动态指定。

Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
        .documentRetriever(VectorStoreDocumentRetriever.builder()
                .similarityThreshold(0.50)
                .vectorStore(vectorStore)
                .build())
        .build();

String answer = chatClient.prompt()
        .advisors(retrievalAugmentationAdvisor)
        .advisors(a -> a.param(VectorStoreDocumentRetriever.FILTER_EXPRESSION, "type == 'Spring'"))
        .user(question)
        .call()
        .content();

高级 RAG(Advanced RAG)

Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
        .queryTransformers(RewriteQueryTransformer.builder()
                .chatClientBuilder(chatClientBuilder.build().mutate())
                .build())
        .documentRetriever(VectorStoreDocumentRetriever.builder()
                .similarityThreshold(0.50)
                .vectorStore(vectorStore)
                .build())
        .build();

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

您还可以利用 DocumentPostProcessor API 对检索到的文档进行后处理,再将其传递给模型。例如,通过此接口,您可以基于文档与查询的相关性对其进行重新排序,剔除不相关或冗余的文档,或是压缩每个文档的内容以减少噪声和冗余信息。

模块(Modules)

Spring AI 采用了一种模块化 RAG 架构,这一设计灵感来源于论文《 模块化 RAG:将 RAG 系统转变为类似乐高的可重构框架 》中详述的模块化理念。

预检索(Pre-Retrieval)

预检索模块负责处理用户查询,以实现最佳的检索结果。

查询转换(Query Transformation)

一种组件,用于转换输入查询,使其在检索任务中更加高效,解决诸如查询表述不当、术语模糊、词汇复杂或语言不受支持等挑战。

使用 QueryTransformer 时,建议将 ChatClient.Builder 配置为较低的温度值(例如 0.0),以确保结果更具确定性和准确性,从而提升检索质量。大多数聊天模型的默认温度通常过高,不利于实现最佳查询转换效果,会导致检索效率降低。

压缩查询转换器(CompressionQueryTransformer)

压缩查询转换器利用大型语言模型,将对话历史记录和后续查询压缩为一个独立的查询,从而精准捕捉对话的核心要义。

当对话历史较长且后续查询与对话上下文相关时,此转换器尤为有用。

Query query = Query.builder()
        .text("And what is its second largest city?")
        .history(new UserMessage("What is the capital of Denmark?"),
                new AssistantMessage("Copenhagen is the capital of Denmark."))
        .build();

QueryTransformer queryTransformer = CompressionQueryTransformer.builder()
        .chatClientBuilder(chatClientBuilder)
        .build();

Query transformedQuery = queryTransformer.transform(query);

该组件使用的提示可以通过构建器中的 promptTemplate () 方法进行自定义。

重写查询转换器利用大型语言模型来改写用户查询,以便在查询目标系统(如向量存储库或网页搜索引擎)时提供更优的结果。

当用户查询冗长、含糊或包含可能影响搜索结果质量的无相关信息时,此转换器尤为有用。

Query query = new Query("I'm studying machine learning. What is an LLM?");

QueryTransformer queryTransformer = RewriteQueryTransformer.builder()
        .chatClientBuilder(chatClientBuilder)
        .build();

Query transformedQuery = queryTransformer.transform(query);

该组件使用的提示可以通过构建器中的 promptTemplate () 方法进行自定义。

翻译查询转换器(TranslationQueryTransformer)

TranslationQueryTransformer 利用大型语言模型将查询翻译为目标语言,该语言需与生成文档嵌入的嵌入模型所支持的语言一致。若查询已为目标语言,则直接返回原查询;若查询语言未知,同样保持原样返回。

当嵌入模型针对特定语言进行训练,而用户查询使用的是另一种语言时,该转换器便十分有用。

Query query = new Query("Hvad er Danmarks hovedstad?");

QueryTransformer queryTransformer = TranslationQueryTransformer.builder()
        .chatClientBuilder(chatClientBuilder)
        .targetLanguage("english")
        .build();

Query transformedQuery = queryTransformer.transform(query);

该组件使用的提示可以通过构建器中的 promptTemplate () 方法进行自定义。

查询扩展(Query Expansion)

一个用于将输入查询扩展为查询列表的组件,通过提供替代查询表述来解决诸如查询构建不当等挑战,或将复杂问题分解为更简单的子查询。

多查询扩展器(MultiQueryExpander)

MultiQueryExpander 利用大型语言模型将单一查询扩展为多个语义多样化的变体,以捕捉不同视角,有助于检索更多上下文信息,并提高找到相关结果的可能性。

MultiQueryExpander queryExpander = MultiQueryExpander.builder()
    .chatClientBuilder(chatClientBuilder)
    .numberOfQueries(3)
    .build();
List<Query> queries = queryExpander.expand(new Query("How to run a Spring Boot app?"));

默认情况下,MultiQueryExpander 会在扩展查询列表中包含原始查询。您可以通过构建器中的 includeOriginal 方法来禁用此行为。

MultiQueryExpander queryExpander = MultiQueryExpander.builder()
    .chatClientBuilder(chatClientBuilder)
    .includeOriginal(false)
    .build();

该组件使用的提示可以通过构建器中的 promptTemplate () 方法进行自定义。

检索(Retrieval)

检索模块负责查询向量存储等数据系统,并获取最相关的文档。

文档检索(Document Search)

负责从底层数据源(如搜索引擎、向量存储、数据库或知识图谱)检索文档的组件。

向量存储文档检索器(VectorStoreDocumentRetriever)

VectorStoreDocumentRetriever 从向量存储中检索与输入查询语义相似的文档。它支持基于元数据、相似度阈值和 top-k 结果的筛选。

DocumentRetriever retriever = VectorStoreDocumentRetriever.builder()
    .vectorStore(vectorStore)
    .similarityThreshold(0.73)
    .topK(5)
    .filterExpression(new FilterExpressionBuilder()
        .eq("genre", "fairytale")
        .build())
    .build();
List<Document> documents = retriever.retrieve(new Query("What is the main character of the story?"));

过滤表达式可以是静态的或动态的。对于动态过滤表达式,您可以传递一个 Supplier。

DocumentRetriever retriever = VectorStoreDocumentRetriever.builder()
    .vectorStore(vectorStore)
    .filterExpression(() -> new FilterExpressionBuilder()
        .eq("tenant", TenantContextHolder.getTenantIdentifier())
        .build())
    .build();
List<Document> documents = retriever.retrieve(new Query("What are the KPIs for the next semester?"));

您还可以通过查询 API 使用 FILTER_EXPRESSION 参数提供特定于请求的过滤表达式。如果同时提供了特定于请求和特定于检索器的过滤表达式,则以特定于请求的过滤表达式为准。

Query query = Query.builder()
    .text("Who is Anacletus?")
    .context(Map.of(VectorStoreDocumentRetriever.FILTER_EXPRESSION, "location == 'Whispering Woods'"))
    .build();
List<Document> retrievedDocuments = documentRetriever.retrieve(query);

文档合并(Document Join)

一个组件,用于将基于多个查询和从多个数据源检索到的文档合并为单一文档集合。在合并过程中,它还能处理重复文档及实施互惠排名策略。

文档连接器(ConcatenationDocumentJoiner)

串联文档合并器通过将基于多个查询并从多个数据源检索到的文档连接起来,合并成一个单一的文档集合。对于重复文档,保留首次出现的版本。每个文档的评分保持不变。

Map<Query, List<List<Document>>> documentsForQuery = ...
DocumentJoiner documentJoiner = new ConcatenationDocumentJoiner();
List<Document> documents = documentJoiner.join(documentsForQuery);

后检索处理(Post-Retrieval)

后检索模块负责对检索到的文档进行处理,以实现最佳的生成效果。

文档后处理(Document Post-Processing)

一个用于根据查询对检索到的文档进行后处理的组件,旨在解决诸如中间信息丢失、模型上下文长度限制以及减少检索信息中的噪声和冗余等挑战。

例如,它可以根据文档与查询的相关性对其进行排序,移除不相关或冗余的文档,或者压缩每个文档的内容以减少噪声和冗余。

生成(Generation)

生成模块负责根据用户查询和检索到的文档生成最终响应。

查询增强(Query Augmentation)

一个用于通过附加数据增强输入查询的组件,有助于为大型语言模型提供回答用户查询所需的上下文。

上下文查询增强器(ContextualQueryAugmenter)

上下文查询增强器利用提供文档的内容,将用户查询与上下文数据相结合进行增强。

QueryAugmenter queryAugmenter = ContextualQueryAugmenter.builder().build();

默认情况下,ContextualQueryAugmenter 不允许检索到的上下文为空。当这种情况发生时,它会指示模型不回答用户查询。

您可以启用 allowEmptyContext 选项,以允许模型在检索到的上下文为空时仍能生成响应。

QueryAugmenter queryAugmenter = ContextualQueryAugmenter.builder()
        .allowEmptyContext(true)
        .build();

该组件的提示信息可通过构建器中提供的‘promptTemplate ()‘和‘emptyContextPromptTemplate ()‘方法进行自定义。

作者:Jeebiz  创建时间:2025-08-03 21:13
最后编辑:Jeebiz  更新时间:2025-08-31 23:07