Spring AI:检索增强生成(RAG)
检索增强生成(RAG)是一种有用的技术,可以克服大型语言模型在长篇内容、事实准确性和上下文感知方面的局限性。
Spring AI通过提供模块化架构来支持RAG,该架构允许您自己构建自定义RAG流,或使用Advisor API使用现成的RAG流。
在概念部分了解有关检索增强生成的更多信息。
Advisors
Spring AI使用Advisor API为常见RAG流提供了开箱即用的支持。
要使用QuestionAnswerAdvisor或RetrievalAugmentationAdvisor,您需要将spring-ai advisers向量存储依赖项添加到您的项目中:
<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请求,也可以在运行时为每个请求提供。
以下是如何创建阈值为0.8的QuestionAnswerAdvisor实例并返回前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(使用.templateRender())不同,后者会影响顾问运行前初始用户/系统提示内容的呈现。有关客户端级模板呈现的更多详细信息,请参阅ChatClient提示模板。
自定义PromptTemplate可以使用任何TemplateRenderer实现(默认情况下,它使用基于StringTemplate引擎的StPromptTemplate)。重要的要求是模板必须包含以下两个占位符:
查询占位符,用于接收用户问题。
一个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();
问答顾问。Builder.userTextAdvice()方法已被弃用,取而代之的是使用.promptTemplate()进行更灵活的自定义。
RetrievalAugmentationAdvisor
Spring AI包含一个RAG模块库,您可以使用它来构建自己的RAG流。RetrievalAugmentationAdvisor是一个Advisor,基于模块化架构,为最常见的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();
有关更多信息,请参阅VectorStoreDocumentRetriever。
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。使用低温(例如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()方法进行自定义。
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?"));
过滤器表达式可以是静态的或动态的。对于动态筛选表达式,您可以传递一个供应商。
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?"));
还可以使用filter_expression参数,通过查询API提供特定于请求的筛选器表达式。如果同时提供了特定于请求和特定于检索器的筛选器表达式,则优先使用特定于请求的筛选器表达式。
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
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
ContextualQueryAugmenter使用来自所提供文档内容的上下文数据来增强用户查询。
QueryAugmenter queryAugmenter = ContextualQueryAugmenter.builder().build();
默认情况下,ContextualQueryAugmenter不允许检索到的上下文为空。当这种情况发生时,它指示模型不要回答用户查询。
您可以启用allowEmptyContext选项,以允许模型生成响应,即使检索到的上下文为空。
QueryAugmenter queryAugmenter = ContextualQueryAugmenter.builder().allowEmptyContext(true).build();
此组件使用的提示可以通过构建器中提供的promptTemplate()和emptyContactPromptTemplate()方法进行自定义。