收藏本站,收获最前沿的人工智能与编程资讯!!

Spring AI 检索增强生成(RAG)

技术文档 19℃ 0

检索增强生成(RAG)是一项用于克服大语言模型局限性的技术,大语言模型在长文本内容生成、事实准确性和上下文感知方面存在短板。

Spring AI 通过提供模块化架构支持 RAG 技术,你可以自行构建自定义的 RAG 流程,也可以使用 Advisor API 开箱即用的 RAG 流程。

在概念章节中了解更多关于检索增强生成的信息。

顾问(Advisors)

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

若要使用 QuestionAnswerAdvisor 或 VectorStoreChatMemoryAdvisor,你需要在项目中添加 spring-ai-advisors-vector-store 依赖:

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

QuestionAnswerAdvisor

向量数据库用于存储 AI 模型未知的数据。当用户向 AI 模型发送问题时,QuestionAnswerAdvisor 会向向量数据库查询与用户问题相关的文档。

向量数据库返回的结果会追加到用户输入文本中,为 AI 模型生成回答提供上下文信息。

假设你已经将数据加载到 VectorStore 中,可通过为 ChatClient 提供 QuestionAnswerAdvisor 实例来实现检索增强生成(RAG)。

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

在本示例中,QuestionAnswerAdvisor 会对向量数据库中的所有文档执行相似度搜索。若要限制搜索的文档类型,SearchRequest 支持类 SQL 的过滤表达式,该表达式可在所有 VectorStore 中通用。

该过滤表达式可在创建 QuestionAnswerAdvisor 时配置(将应用于所有 ChatClient 请求),也可在每次请求运行时动态指定。

以下是创建相似度阈值为 0.8、返回前 6 条结果的 QuestionAnswerAdvisor 实例的方法:

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

动态过滤表达式

通过 FILTER_EXPRESSION 顾问上下文参数,在运行时更新 SearchRequest 过滤表达式:

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

// 在运行时更新过滤表达式
String content = this.chatClient.prompt()
    .user("Please answer my question XYZ")
    .advisors(a -> a.param(QuestionAnswerAdvisor.FILTER_EXPRESSION, "type == 'Spring'"))
    .call()
    .content();

FILTER_EXPRESSION 参数允许你根据指定的表达式动态过滤搜索结果。

自定义模板

QuestionAnswerAdvisor 使用默认模板将检索到的文档与用户问题结合。你可以通过构建器方法 .promptTemplate() 提供自定义的 PromptTemplate 对象来修改这一行为。

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

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

  • query 占位符:用于接收用户问题

  • question_answer_context 占位符:用于接收检索到的上下文

PromptTemplate customPromptTemplate = PromptTemplate.builder()
    .renderer(StPromptRenderer.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();

QuestionAnswerAdvisor.Builder.userTextAdvise() 方法已被弃用,推荐使用 .promptTemplate() 实现更灵活的自定义配置。

RetrievalAugmentationAdvisor

Spring AI 包含一套 RAG 模块库,你可用于构建自定义的 RAG 流程。RetrievalAugmentationAdvisor 是基于模块化架构、为最常用的 RAG 流程提供开箱即用实现的顾问组件。

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

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

顺序式 RAG 流程

基础 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();

更多信息请参阅 VectorStoreDocumentRetriever。

高级 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 在将检索到的文档传递给模型之前进行后处理。例如,你可以通过该接口根据文档与查询的相关性对文档进行重排序、移除无关或冗余的文档,或压缩每个文档的内容以减少噪声和冗余信息。

模块

Spring AI 实现了模块化 RAG 架构,其设计灵感来源于论文《模块化 RAG:将 RAG 系统转变为乐高式可重构框架》中详述的模块化概念。

检索前处理(Pre-Retrieval)

检索前处理模块负责处理用户查询,以获取最优的检索结果。

查询转换

用于转换输入查询的组件,使查询更适用于检索任务,解决查询语句不规范、术语模糊、词汇复杂或语言不支持等问题。

使用 QueryTransformer 时,建议为 ChatClient.Builder 配置低温度值(例如 0.0),以确保结果更具确定性和准确性,提升检索质量。大多数对话模型的默认温度值通常过高,无法实现最优的查询转换效果,会降低检索效率。

CompressionQueryTransformer

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() 方法自定义。

RewriteQueryTransformer

RewriteQueryTransformer 使用大语言模型重写用户查询,以在向目标系统(如向量库或网络搜索引擎)查询时获得更优结果。

当用户查询冗长、模糊或包含可能影响搜索结果质量的无关信息时,该转换器非常实用。

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() 方法自定义。

查询扩展

用于将输入查询扩展为多个查询的组件,通过提供替代查询语句或将复杂问题拆解为简单子查询,解决查询语句不规范等问题。

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)

检索模块负责查询向量库等数据系统,检索最相关的文档。

文档搜索

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

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?"));

你还可以通过 Query 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);
文档合并

用于将基于多个查询、从多个数据源检索到的文档合并为单个文档集合的组件。在合并过程中,该组件还可处理重复文档和互相关排序策略。

ConcatenationDocumentJoiner

ConcatenationDocumentJoiner 通过拼接的方式,将基于多个查询、从多个数据源检索到的文档合并为单个文档集合。如果存在重复文档,保留首次出现的文档,每个文档的分数保持不变。

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

检索后处理(Post-Retrieval)

检索后处理模块负责处理检索到的文档,以获取最优的生成结果。

文档后处理

基于查询对检索到的文档进行后处理的组件,解决中间信息丢失、模型上下文长度限制、减少检索信息中的噪声和冗余等问题。

例如,该组件可根据文档与查询的相关性排序、移除无关或冗余的文档,或压缩每个文档的内容以减少噪声和冗余。

生成(Generation)

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

查询增强

为输入查询补充额外数据的组件,用于为大语言模型提供回答用户查询所需的上下文。

ContextualQueryAugmenter

ContextualQueryAugmenter 使用提供的文档内容中的上下文数据增强用户查询。

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

默认情况下,ContextualQueryAugmenter 不允许检索到的上下文为空。若上下文为空,组件会指示模型不回答用户查询。

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

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

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

相关推荐