检索增强生成
检索增强生成(Retrieval Augmented Generation, RAG)是一种技术,用于克服大型语言模型在处理长文本内容、事实准确性以及上下文感知方面的局限性。
Spring AI 通过提供模块化架构来支持 RAG(Retrieval-Augmented Generation),允许您自己构建自定义的 RAG 流程,或者使用 Advisor API 提供的开箱即用的 RAG 流程。
在概念部分了解更多关于检索增强生成(Retrieval Augmented Generation)的内容。
顾问
Spring AI 提供了对常见 RAG(Retrieval-Augmented Generation)流程的开箱即用支持,使用 Advisor API。
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();
在这个示例中,QuestionAnswerAdvisor 将对 Vector Database 中的所有文档执行相似性搜索。为了限制搜索的文档类型,SearchRequest 接受一个类似 SQL 的过滤表达式,该表达式在所有 VectorStores 中都是可移植的。
此过滤器表达式可以在创建 QuestionAnswerAdvisor 时进行配置,因此它将始终应用于所有 ChatClient 请求,也可以在每次请求时在运行时提供。
以下是创建一个 QuestionAnswerAdvisor 实例的示例,其中阈值为 0.8,并返回前 6 个结果。
var qaAdvisor = new QuestionAnswerAdvisor(this.vectorStore,
        SearchRequest.builder().similarityThreshold(0.8d).topK(6).build());
动态过滤表达式
在运行时使用 FILTER_EXPRESSION 顾问上下文参数更新 SearchRequest 的过滤器表达式:
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.builder().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 参数允许你根据提供的表达式动态过滤搜索结果。
检索增强顾问(孵化中)
Spring AI 包含一个 RAG 模块库,你可以使用它来构建自己的 RAG 流程。RetrievalAugmentationAdvisor 是一个实验性的 Advisor,基于模块化架构,为最常见的 RAG 流程提供了开箱即用的实现。
RetrievalAugmentationAdvisor 是一个实验性功能,可能会在未来的版本中发生变化。
顺序 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();
高级 RAG
Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
        .queryTransformer(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();
模块
Spring AI 实现了模块化 RAG 架构,该架构灵感来源于论文《Modular RAG: Transforming RAG Systems into LEGO-like Reconfigurable Frameworks》中详细描述的模块化概念。
警告
模块化 RAG 是一个实验性功能,在未来的版本中可能会有所变化。
预检索
预检索模块负责处理用户查询,以实现最佳的检索结果。
查询转换
一个用于转换输入查询的组件,旨在使查询更有效地用于检索任务,解决诸如查询格式不佳、术语模糊、词汇复杂或语言不受支持等挑战。
CompressionQueryTransformer
CompressionQueryTransformer 使用大型语言模型将对话历史和后续查询压缩成一个独立的查询,该查询能够捕捉对话的精华。
当对话历史较长且后续查询与对话上下文相关时,此 transformer 非常有用。
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 = expander.expand(new Query("How to run a Spring Boot app?"));
默认情况下,MultiQueryExpander 会将原始查询包含在扩展查询列表中。你可以通过构建器中的 includeOriginal 方法来禁用此行为。
MultiQueryExpander queryExpander = MultiQueryExpander.builder()
    .chatClientBuilder(chatClientBuilder)
    .includeOriginal(false)
    .build();
该组件所使用的提示词(prompt)可以通过构建器中提供的 promptTemplate() 方法进行自定义。
检索
检索模块负责查询数据系统,如向量存储,并检索最相关的文档。
文档搜索
负责从底层数据源(如搜索引擎、向量存储、数据库或知识图谱)中检索 Documents 的组件。
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?"));
文档连接
一个用于将基于多个查询和从多个数据源检索到的文档合并为单一文档集合的组件。在合并过程中,它还可以处理重复文档和互惠排名策略。
连接文档合并器
ConcatenationDocumentJoiner 通过将基于多个查询和来自多个数据源检索到的文档连接成一个单一的文档集合来组合它们。在出现重复文档的情况下,保留第一个出现的文档。每个文档的分数保持不变。
Map<Query, List<List<Document>>> documentsForQuery = ...
DocumentJoiner documentJoiner = new ConcatenationDocumentJoiner();
List<Document> documents = documentJoiner.join(documentsForQuery);
检索后
后检索模块(Post-Retrieval modules)负责处理检索到的文档,以实现最佳的生成结果。
文档排序
一个用于根据文档与查询的相关性对文档进行排序和排名的组件,旨在将最相关的文档置于列表顶部,解决诸如中间迷失等挑战。
与 DocumentSelector 不同,该组件不会从列表中移除整个文档,而是改变列表中文档的顺序/分数。与 DocumentCompressor 不同,该组件不会更改文档的内容。
文档选择
一个用于从检索到的文档列表中移除不相关或冗余文档的组件,旨在解决诸如迷失在中间(lost-in-the-middle)以及模型上下文长度限制等挑战。
与 DocumentRanker 不同,该组件不会改变列表中文档的顺序/分数,而是删除不相关或冗余的文档。与 DocumentCompressor 不同,该组件不会改变文档的内容,而是删除整个文档。
文档压缩
一个用于压缩每个文档内容的组件,以减少检索信息中的噪音和冗余,解决诸如迷失在中间(lost-in-the-middle)和模型上下文长度限制等挑战。
与 DocumentSelector 不同,该组件不会从列表中移除整个文档,而是会修改文档的内容。与 DocumentRanker 不同,该组件不会改变列表中文档的顺序/评分。
生成
生成模块负责基于用户查询和检索到的文档生成最终响应。
查询增强
一个用于通过附加数据增强输入查询的组件,有助于为大型语言模型提供必要的上下文以回答用户查询。
ContextualQueryAugmenter
ContextualQueryAugmenter 通过从提供的文档内容中提取上下文数据来增强用户查询。
QueryAugmenter queryAugmenter = ContextualQueryAugmenter.builder().build();
默认情况下,ContextualQueryAugmenter 不允许检索到的上下文为空。当这种情况发生时,它会指示模型不要回答用户查询。
你可以启用 allowEmptyContext 选项,以允许模型在检索到的上下文为空时生成响应。
QueryAugmenter queryAugmenter = ContextualQueryAugmenter.builder()
        .allowEmptyContext(true)
        .build();
此组件使用的提示可以通过构建器中可用的 promptTemplate() 和 emptyContextPromptTemplate() 方法进行自定义。