检索增强生成

本站(springdoc.cn)中的内容来源于 spring.io ,原始版权归属于 spring.io。由 springdoc.cn 进行翻译,整理。可供个人学习、研究,未经许可,不得进行任何转载、商用或与之相关的行为。 商标声明:Spring 是 Pivotal Software, Inc. 在美国以及其他国家的商标。

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

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

更多关于检索增强生成的细节请参阅 概念 章节。

Advisor

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

要使用 QuestionAnswerAdvisorRetrievalAugmentationAdvisor,需添加 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(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();

动态过滤器表达式

通过 FILTER_EXPRESSION Advisor 上下文参数在运行时更新 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 参数允许根据提供的表达式动态过滤搜索结果。

自定义模板

QuestionAnswerAdvisor 使用默认模板通过检索到的文档增强用户问题。你可通过 .promptTemplate() Builder 方法提供自定义 PromptTemplate 对象来改写该行为。

此处提供的 PromptTemplate 用于定制 Advisor 如何将检索到的上下文与用户查询合并,这与在 ChatClient 上配置 TemplateRenderer(通过 .templateRenderer())不同 — 后者影响 Advisor 运行前初始用户/系统提示内容的渲染。详见 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();
QuestionAnswerAdvisor.Builder.userTextAdvise() 方法已弃用,推荐使用 .promptTemplate() 实现更灵活的定制。

RetrievalAugmentationAdvisor

Spring AI 包含 RAG 模块库 供你构建自定义 RAG 流程。RetrievalAugmentationAdvisor 作为 Advisor,基于模块化架构为最常见 RAG 流程提供开箱即用的实现。

顺序式 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 以根据元数据过滤搜索结果。你可在实例化时提供过滤表达式,或通过 FILTER_EXPRESSION Advisor 上下文参数在运行时按请求动态指定。

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 系统转化为乐高式可重构框架 中阐述的模块化理念。

检索前处理

检索前模块负责处理用户查询以获得最佳检索结果。

查询转换

一个用于转换输入查询的组件,使其更有效地完成检索任务,以应对查询格式不佳、术语含糊、词汇复杂或不支持语言等挑战。

使用 QueryTransformer 时,建议将 ChatClient.Builder 的温度值(temperature)设低(如 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);

该组件使用的提示词可通过 Builder 的 promptTemplate() 方法自定义。

RewriteQueryTransformer

RewriteQueryTransformer 利用大语言模型重写用户查询,从而在查询向量数据库或搜索引擎等目标系统时获得更好结果。

当用户查询冗长、含歧义或包含可能影响搜索结果质量的无关信息时,该转换器(Transformer)特别有效。

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

该组件使用的提示词可通过 Builder 的 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);

该组件使用的提示词可通过 Builder 的 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 会在扩展查询列表中包含原始查询。你可通过 Builder 的 includeOriginal() 方法禁用该行为。

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

该组件使用的提示词可通过 Builder 的 promptTemplate() 方法自定义。

检索

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

文档检索

该组件负责从底层数据源(如搜索引擎、向量数据库、传统数据库或知识图谱)检索 Document 对象。

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

检索后处理

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

文档后处理

该组件基于查询对检索到的文档进行后处理,解决诸如 “中间迷失”、模型上下文长度限制等问题,并降低检索信息中的噪声和冗余。

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

生成阶段

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

查询增强

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

ContextualQueryAugmenter

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

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

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

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

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

该组件使用的提示词可通过 Builder 的 promptTemplate()emptyContextPromptTemplate() 方法自定义。