检索增强生成(RAG)是一种技术,有助于克服大型语言模型在处理长篇内容、事实准确性和上下文感知方面的局限性。
Spring AI 通过提供模块化架构来支持 RAG,该架构允许自行构建自定义的 RAG 流程,或者使用 Advisor API 提供的现成 RAG 流程。
增强器(Advisors)
Spring AI 通过使用增强器 API(Advisor API)为常见的检索增强生成(RAG)流程提供了现成的支持。
若要使用问答增强器(QuestionAnswerAdvisor)或检索增强增强器(RetrievalAugmentationAdvisor),需要将 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)的实例来执行检索增强生成(Retrieval Augmented Generation,RAG)。
ChatResponse response = ChatClient.builder(chatModel).build().prompt().advisors(new QuestionAnswerAdvisor(vectorStore)).user(userText).call().chatResponse();
在此示例中,QuestionAnswerEnhancer(问答增强器)将在向量数据库(Vector Database)中的所有文档上执行相似性搜索。若要限制搜索的文档类型,SearchRequest(搜索请求)会采用一种类似 SQL 的过滤表达式,该表达式可在所有 VectorStores(向量存储库)中通用。
此过滤表达式可以在创建 QuestionAnswerEnhancer 时进行配置,这样一来它将始终适用于所有 ChatClient(聊天客户端)请求;或者也可以在每次请求运行时提供该表达式。
以下是如何创建一个 QuestionAnswerEnhancer 实例的方法,其中相似度阈值设为 0.8,并返回前 6 个结果。
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();// 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() 构建器方法提供自己的 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();
QuestionAnswerAdvisor.Builder.userTextAdvise() 方法已被弃用,建议改用 .promptTemplate() 方法以实现更灵活的自定义设置。
RetrievalAugmentationAdvisor
Spring AI 包含一个 RAG(检索增强生成)模块库,可以使用这些模块来构建自己的 RAG 工作流程。RetrievalAugmentationAdvisor(检索增强顾问)是一个基于模块化架构的顾问,它为最常见的 RAG 工作流程提供了现成的实现方案。
顺序 RAG 工作流程
朴素 RAG(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();
有关更多信息,请参阅 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 系统转变为类似乐高积木的可重构框架》(“Modular RAG: Transforming RAG Systems into LEGO-like Reconfigurable Frameworks”)中详细阐述的模块化概念
预检索(Pre-Retrieval)
预检索模块负责处理用户查询,以实现最佳的检索结果。查询转换(Query Transformation)
一个用于转换输入查询的组件,旨在使其更适用于检索任务,解决诸如格式不佳的查询、歧义术语、复杂词汇或不受支持的语言等挑战
在使用 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);
该组件所使用的提示词(prompt)可通过构建器(builder)中提供的 promptTemplate() 方法进行自定义。
查询重写转换器(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);
该组件所使用的提示词(prompt)可通过构建器(builder)中提供的 promptTemplate() 方法进行自定义。
查询翻译转换器(TranslationQueryTransformer)
查询翻译转换器会利用大型语言模型,将查询翻译为目标语言,该目标语言需是用于生成文档嵌入(document embeddings)的嵌入模型所支持的语言。如果查询已经是目标语言,则直接原样返回。如果查询的语言未知,同样也会原样返回。
当嵌入模型是针对特定语言进行训练,而用户查询却是用另一种语言提出时,这种转换器便十分有用
Query query = new Query("Hvad er Danmarks hovedstad?");QueryTransformer queryTransformer = TranslationQueryTransformer.builder().chatClientBuilder(chatClientBuilder).targetLanguage("english").build();Query transformedQuery = queryTransformer.transform(query);
该组件所使用的提示词(prompt)可通过构建器(builder)中提供的 promptTemplate() 方法进行自定义。
查询扩展(Query Expansion)
这是一个将输入查询扩展为一系列查询的组件,通过提供替代的查询表述方式,或通过将复杂问题分解为更简单的子查询,来应对诸如查询表述不当等挑战。
多查询扩展器(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();
该组件所使用的提示词(prompt)可通过构建器(builder)中提供的 promptTemplate() 方法进行自定义。
检索(Retrieval)
检索(Retrieval)模块负责查询数据系统(如向量存储库),并检索出最相关的文档。
文档搜索(Document Search
文档搜索(Document Search)组件负责从底层数据源(如搜索引擎、向量存储库、数据库或知识图谱)中检索文档。
向量存储文档检索器(VectorStoreDocumentRetriever)
向量存储文档检索器用于从向量存储库中检索与输入查询语义相似的文档。它支持基于元数据、相似度阈值以及前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)
检索后处理模块负责对检索到的文档进行处理,以实现最佳的生成结果。
文档后处理
一个用于根据查询对检索到的文档进行后处理的组件,旨在解决诸如信息遗漏(lost-in-the-middle)、模型上下文长度限制以及检索信息中噪声和冗余的减少等挑战。
例如,它可以基于文档与查询的相关性对文档进行排名,移除不相关或冗余的文档,或者压缩每篇文档的内容以减少噪声和冗余。
生成(Generation)
生成模块负责根据用户查询和检索到的文档生成最终响应。
查询增强(Query Augmentation)
一个用于通过附加数据增强输入查询的组件,有助于为大型语言模型提供回答用户查询所需的上下文。
上下文查询增强器(ContextualQueryAugmenter)
上下文查询增强器使用提供的文档内容中的上下文数据来增强用户查询。
QueryAugmenter queryAugmenter = ContextualQueryAugmenter.builder().build();
默认情况下,上下文查询增强器(ContextualQueryAugmenter)不允许检索到的上下文为空。当这种情况发生时,它会指示模型不要回答用户查询。
可以启用 allowEmptyContext 选项,以允许模型即使在检索到的上下文为空时也能生成响应。
QueryAugmenter queryAugmenter = ContextualQueryAugmenter.builder().allowEmptyContext(true).build();
该组件使用的提示词(prompts)可以通过构建器中提供的 promptTemplate() 和 emptyContextPromptTemplate() 方法进行自定义。