Spring AI 之检索增强生成(Retrieval Augmented Generation)

检索增强生成(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)。重要的要求是,模板必须包含以下两个占位符:

  1. 一个query占位符,用于接收用户的问题。
  2. 一个 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() 方法进行自定义。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/83302.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端开源JavaScrip库

以下内容仍在持续完善中&#xff0c;如有遗漏或需要补充之处&#xff0c;欢迎在评论区指出。感谢支持&#xff0c;如果觉得有帮助&#xff0c;欢迎点赞鼓励。感谢支持 JavaScript 框架Vue.jsVue.js - 渐进式 JavaScript 框架 | Vue.jsReactReactAngularHome • AngularjQueryj…

什么是 CPU 缓存模型?

导语&#xff1a; CPU 缓存模型是后端性能调优、并发编程乃至分布式系统设计中一个绕不开的核心概念。它不仅关系到指令执行效率&#xff0c;还影响锁机制、内存可见性等多个面试高频点。本文将以资深面试官视角&#xff0c;详解缓存模型的原理、常见面试题及实战落地&#xff…

海外tk抓包简单暴力方式

将地址替换下面代码就可以 function hook_dlopen(module_name, fun) {var android_dlopen_ext Module.findExportByName(null, "android_dlopen_ext");if (android_dlopen_ext) {Interceptor.attach(android_dlopen_ext, {onEnter: function (args) {var pathptr …

多模态大语言模型arxiv论文略读(103)

Are Bigger Encoders Always Better in Vision Large Models? ➡️ 论文标题&#xff1a;Are Bigger Encoders Always Better in Vision Large Models? ➡️ 论文作者&#xff1a;Bozhou Li, Hao Liang, Zimo Meng, Wentao Zhang ➡️ 研究机构: 北京大学 ➡️ 问题背景&…

代码随想录算法训练营 Day61 图论ⅩⅠ Floyd A※ 最短路径算法

图论 题目 97. 小明逛公园 本题是经典的多源最短路问题。 在这之前我们讲解过&#xff0c;dijkstra朴素版、dijkstra堆优化、Bellman算法、Bellman队列优化&#xff08;SPFA&#xff09; 都是单源最短路&#xff0c;即只能有一个起点。 而本题是多源最短路&#xff0c;即求多…

【机器学习】集成学习与梯度提升决策树

目录 一、引言 二、自举聚合与随机森林 三、集成学习器 四、提升算法 五、Python代码实现集成学习与梯度提升决策树的实验 六、总结 一、引言 在机器学习的广阔领域中,集成学习(Ensemble Learning)犹如一座闪耀的明星,它通过组合多个基本学习器的力量,创造出…

yarn、pnpm、npm

非常好&#xff0c;这样从“问题驱动 → 工具诞生 → 优化演进”的角度来讲&#xff0c;更清晰易懂。下面我按时间线和动机&#xff0c;把 npm → yarn → pnpm 的演变脉络讲清楚。 &#x1f9e9; 一、npm 为什么一开始不够好&#xff1f; 早期&#xff08;npm v4 及之前&…

如何用AI写作?

过去半年&#xff0c;我如何用AI高效写作&#xff0c;节省数倍时间 过去六个月&#xff0c;我几乎所有文章都用AI辅助完成。我的朋友——大多是文字工作者&#xff0c;对语言极为敏感——都说看不出我的文章是AI写的还是亲手创作的。 我的AI写作灵感部分来自丘吉尔。这位英国…

什么是trace,分布式链路追踪(Distributed Tracing)

在你提到的 “个人免费版” 套餐中&#xff0c;“Trace 上报量&#xff1a;5 万条 / 月&#xff0c;存储 3 天” 里的 Trace 仍然是指 分布式链路追踪记录&#xff0c;但需要结合具体产品的场景来理解其含义和限制。以下是更贴近个人用户使用场景的解释&#xff1a; 一、这里的…

[免费]微信小程序网上花店系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序网上花店系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序网上花店系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…

PyTorch——DataLoader的使用

batch_size, drop_last 的用法 shuffle shuffleTrue 各批次训练的图像不一样 shuffleFalse 在第156step顺序一致

【Linux】基础文件IO

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;Linux 前言 无论是日常使用还是系统管理&#xff0c;文件是Linux系统中最核心的概念之一。对于初学者来说&#xff0c;理解文件是如何被创建、读取、写入以及存储…

【JAVA后端入门基础001】Tomcat 是什么?通俗易懂讲清楚!

&#x1f4da;博客主页&#xff1a;代码探秘者 ✨专栏&#xff1a;《JavaSe》 其他更新ing… ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;作者水平有限&#xff0c;欢迎各位大佬指点&…

TDengine 的 AI 应用实战——电力需求预测

作者&#xff1a; derekchen Demo数据集准备 我们使用公开的UTSD数据集里面的电力需求数据&#xff0c;作为预测算法的数据来源&#xff0c;基于历史数据预测未来若干小时的电力需求。数据集的采集频次为30分钟&#xff0c;单位与时间戳未提供。为了方便演示&#xff0c;按…

D2000平台上Centos使用mmap函数遇到的陷阱

----------原创不易&#xff0c;欢迎点赞收藏。广交嵌入式开发的朋友&#xff0c;讨论技术和产品------------- 在飞腾D2000平台上&#xff0c;安装了麒麟linux系统&#xff0c;我写了个GPIO点灯的程序&#xff0c;在应用层利用mmap函数将内核空间映射到用户态&#xff0c;然后…

深入了解linux系统—— 进程间通信之管道

前言 本篇博客所涉及到的代码一同步到本人gitee&#xff1a;testfifo 迟来的grown/linux - 码云 - 开源中国 一、进程间通信 什么是进程间通信 在之前的学习中&#xff0c;我们了解到了进程具有独立性&#xff0c;就算是父子进程&#xff0c;在修改数据时也会进行写时拷贝&…

设计模式——模版方法设计模式(行为型)

摘要 模版方法设计模式是一种行为型设计模式&#xff0c;定义了算法的步骤顺序和整体结构&#xff0c;将某些步骤的具体实现延迟到子类中。它通过抽象类定义模板方法&#xff0c;子类实现抽象步骤&#xff0c;实现代码复用和算法流程控制。该模式适用于有固定流程但部分步骤可…

Python使用

Python学习&#xff0c;从安装&#xff0c;到简单应用 前言 Python作为胶水语言在web开发&#xff0c;数据分析&#xff0c;网络爬虫等方向有着广泛的应用 一、Python入门 相关基础语法直接使用相关测试代码 Python编译器版本使用3以后&#xff0c;安装参考其他教程&#xf…

吴恩达机器学习笔记(1)—引言

目录 一、欢迎 二、机器学习是什么 三、监督学习 四、无监督学习 一、欢迎 机器学习是当前信息技术领域中最令人兴奋的方向之一。在这门课程中&#xff0c;你不仅会学习机器学习的前沿知识&#xff0c;还将亲手实现相关算法&#xff0c;从而深入理解其内部机理。 事实上&…

java笔记08

多线程&JUC 1.什么是多线程 1.什么是多线程&#xff1f;有了多线程&#xff0c;我们就可以让程序同时做多件事情 2.多线程的作用&#xff1f;提高效率 3.多线程的应用场景&#xff1f;只要你想让多个事情同时运行就需要用到多线程比如&#xff1a;软件中的耗时操作、所有…