当前位置: 首页 > news >正文

Spring AI RAG RetrievalAugmentationAdvisor源码分析

RAG

在现代大型语言模型(LLM)应用中,RAG(Retrieval-Augmented Generation) 是一种常用策略:通过检索外部知识库来增强生成结果的准确性
在 Spring AI 框架中,RetrievalAugmentationAdvisor 扮演着 RAG (检索增强生成) 流程的指挥官角色。
它负责编排从用户查询到最终上下文准备的整个过程,确保 LLM 接收到的 Prompt 是经过多层优化和增强的高质量输入。
本文将基于其内部的六个核心属性,详细阐述 RAG 流程中每一个组件的作用、典型应用场景,以及它们如何协同工作,共同提升问答系统的性能。

一、 QuestionAnswerAdvisor

研究RetrievalAugmentationAdvisor之前,我们先分析下QuestionAnswerAdvisor.context

两者都属于 Spring AI 提供的 Advisor API,用于 RAG(Retrieval-Augmented Generation)场景
QuestionAnswerAdvisory用于实现 简单、快速入门的 RAG 流。 

  • 它接收用户问题,从向量存储(vector store)中检索相关文档,然后把这些文档拼接到用户的问题里,给 LLM 生成答案。 
  • 自定义能力较有限 —— 查询扩展、重写等定制较弱。

源码分析

public class QuestionAnswerAdvisor implements BaseAdvisor {public static final String RETRIEVED_DOCUMENTS = "qa_retrieved_documents";public static final String FILTER_EXPRESSION = "qa_filter_expression";/*** query: 原始prompt查询* question_answer_context: vector查询结果*/ private static final PromptTemplate DEFAULT_PROMPT_TEMPLATE = new PromptTemplate("{query}\n\nContext information is below, surrounded by ---------------------\n\n---------------------\n{question_answer_context}\n---------------------\n\nGiven the context and provided history information and not prior knowledge,\nreply to the user comment. If the answer is not in the context, inform\nthe user that you can't answer the question.\n");private final VectorStore vectorStore;private final PromptTemplate promptTemplate;private final SearchRequest searchRequest;public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) {//1. build search requstSearchRequest searchRequestToUse = SearchRequest.from(this.searchRequest).query(chatClientRequest.prompt().getUserMessage().getText())//1.1 用chatClientRequest 替换searchRequest.query.filterExpression(this.doGetFilterExpression(chatClientRequest.context()))//1.2 使用searchRequest.filterExpression.build();//2. 相似度查询											List<Document> documents = this.vectorStore.similaritySearch(searchRequestToUse);//3. enrich contextMap<String, Object> context = new HashMap(chatClientRequest.context());context.put("qa_retrieved_documents", documents);//4. build new prompt String documentContext = documents == null ? "" : (String)documents.stream().map(Document::getText).collect(Collectors.joining(System.lineSeparator()));UserMessage userMessage = chatClientRequest.prompt().getUserMessage();String augmentedUserText = this.promptTemplate.render(Map.of("query", userMessage.getText(), "question_answer_context", documentContext));//5. execute promptreturn chatClientRequest.mutate().prompt(chatClientRequest.prompt().augmentUserMessage(augmentedUserText)).context(context).build();}public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {ChatResponse.Builder chatResponseBuilder;if (chatClientResponse.chatResponse() == null) {chatResponseBuilder = ChatResponse.builder();} else {chatResponseBuilder = ChatResponse.builder().from(chatClientResponse.chatResponse());}//9. 将rag_document_context 放到repoonse中chatResponseBuilder.metadata("qa_retrieved_documents", chatClientResponse.context().get("qa_retrieved_documents"));return ChatClientResponse.builder().chatResponse(chatResponseBuilder.build()).context(chatClientResponse.context()).build();}
}

一、RetrievalAugmentationAdvisor

RetrievalAugmentationAdvisor 的核心职责

RetrievalAugmentationAdvisor 实现了 BaseAdvisor 接口,它的核心职责是:

  • 查询优化: 预处理用户查询,使其更适合检索。
  • 文档检索: 调用检索器获取相关文档。
  • 文档精炼: 对检索结果进行过滤、重排和摘要。
  • 上下文组装: 将精炼后的文档和原始/增强查询整合,形成最终的 Prompt 上下文(Context)。

通过链式调用其内部的组件,该 Advisor 实现了 RAG 流程的自动化和模块化。

源码分析


public final class RetrievalAugmentationAdvisor implements BaseAdvisor {public static final String DOCUMENT_CONTEXT = "rag_document_context";private final List<QueryTransformer> queryTransformers;@Nullableprivate final QueryExpander queryExpander;private final DocumentRetriever documentRetriever;private final DocumentJoiner documentJoiner;private final List<DocumentPostProcessor> documentPostProcessors;private final QueryAugmenter queryAugmenter;public ChatClientRequest before(ChatClientRequest chatClientRequest, @Nullable AdvisorChain advisorChain) {Map<String, Object> context = new HashMap(chatClientRequest.context());Query originalQuery = Query.builder().text(chatClientRequest.prompt().getUserMessage().getText()).history(chatClientRequest.prompt().getInstructions()).context(context).build();Query transformedQuery = originalQuery;//1. queryTransformer(查询转换):				---  对原始用户问题进行标准化或重写,使其易被检索和模型理解。for(QueryTransformer queryTransformer : this.queryTransformers) {transformedQuery = queryTransformer.apply(transformedQuery);}//2. QueryExpander(查询扩展): 				--- 生成多个语义相关查询,提高检索覆盖率。   1 -> nList<Query> expandedQueries = this.queryExpander != null ? this.queryExpander.expand(transformedQuery) : List.of(transformedQuery);//3.Retriever(文档检索): 					---	从向量数据库或知识库中检索相关文档。	Map<Query, List<List<Document>>> documentsForQuery = (Map)expandedQueries.stream().map((query) -> CompletableFuture.supplyAsync(() -> this.getDocumentsForQuery(query), this.taskExecutor)).toList().stream().map(CompletableFuture::join).collect(Collectors.toMap(Map.Entry::getKey, (entry) -> List.of((List)entry.getValue())));//4. documentJoiner(文档合并): List<Document> documents = this.documentJoiner.join(documentsForQuery);//5.DocumentPostProcessors(文档后处理):    		---- 清洗、去重、摘要或重排检索结果,提高文档质量。for(DocumentPostProcessor documentPostProcessor : this.documentPostProcessors) {documents = documentPostProcessor.process(originalQuery, documents);}//6. queryAugmenter(查询增强):   	--- 在不改变语义的前提下加入上下文或元数据。context.put("rag_document_context", documents); //6.1 将document存放至contextQuery augmentedQuery = this.queryAugmenter.augment(originalQuery, documents); //6.2 使用queryAugmenter 查询originalQuery//7.最终执行增强queryreturn chatClientRequest.mutate().prompt(chatClientRequest.prompt().augmentUserMessage(augmentedQuery.text())).context(context).build();}//3.从vector-store retrieve documentprivate Map.Entry<Query, List<Document>> getDocumentsForQuery(Query query) {List<Document> documents = this.documentRetriever.retrieve(query);return Map.entry(query, documents);}public ChatClientResponse after(ChatClientResponse chatClientResponse, @Nullable AdvisorChain advisorChain) {ChatResponse.Builder chatResponseBuilder;if (chatClientResponse.chatResponse() == null) {chatResponseBuilder = ChatResponse.builder();} else {chatResponseBuilder = ChatResponse.builder().from(chatClientResponse.chatResponse());}//9. 将rag_document_context 放到repoonse中chatResponseBuilder.metadata("rag_document_context", chatClientResponse.context().get("rag_document_context"));return ChatClientResponse.builder().chatResponse(chatResponseBuilder.build()).context(chatClientResponse.context()).build();}}

三、核心组件解析

1. queryTransformers: List<QueryTransformer> (查询转换器)

  • 作用: 在执行检索之前,对原始用户查询进行修改或转换。这是 RAG 流程的第一道优化关卡。
  • 典型应用:
    • 上下文感知重写 (Context-Aware Rewrite): 将多轮对话中的代词(如“它”、“这个”)替换成具体名词,使查询更独立。
    • 多语言翻译 (Translation):
      • 将用户非目标语言的查询翻译成知识库文档的语言(如将中文查询转为英文)。
      • 对原始用户查询进行语义层面的“重写或改写”,使其更适合被向量检索或关键字搜索使用
  • Spring AI 组件: RewriteQueryTransformer, TranslationQueryTransformer 等。
demo
    @Testpublic void test_RewriteQueryTransformer(){Query query = Query.builder().text("今天天气挺好的,你在看什么?在看西游记啊, 你能告诉我孙悟空在花果山的朋友都有哪些人吗?").build();QueryTransformer queryTransformer = RewriteQueryTransformer.builder().chatClientBuilder(ChatClient.builder(openAiChatModel)).targetSearchSystem("孙悟空").build();Query transformedQuery = queryTransformer.transform(query);System.out.printf("Transformed Query: %s%n", transformedQuery.text()); //孙悟空在花果山的朋友有哪些?}@Testpublic void test_TranslationQueryTransformer(){Query query = Query.builder().text("今天天气挺好的,你在看什么?在看西游记啊, 你能告诉我孙悟空在花果山的朋友都有哪些人吗?").build();QueryTransformer queryTransformer = TranslationQueryTransformer.builder().chatClientBuilder(ChatClient.builder(openAiChatModel)).targetLanguage("Japanese").build();Query transformedQuery = queryTransformer.transform(query);System.out.printf("Transformed Query: %s%n", transformedQuery.text()); //今天天気はとても良いですね、何を見ていますか?『西遊記』を見ていますか、孫悟空の花果山の友達は誰か教えてもらえますか?}

2. queryExpander: QueryExpander (查询扩展器)

  • 作用: 基于原始查询,生成多个语义相关的替代查询,用于并行或多次检索,以提高文档召回率(Recall)。
  • 典型应用:
    • 同义词扩展 (Synonym Expansion): 将“薪水”扩展为“工资”、“报酬”。
    • 假设文档生成 (HyDE - Hypothetical Document Embedding): 利用 LLM 为查询生成一个虚拟的、可能的答案,然后对这个虚拟答案进行 Embedding 检索,这能更好地捕获查询的深层语义。
  • Spring AI 组件: MultiQueryExpander
demo
@Testpublic void test_QueryExpend(){Query query = Query.builder().text("齐天大圣?").build();List<Query> expendQueries = MultiQueryExpander.builder().chatClientBuilder(ChatClient.builder(openAiChatModel)).includeOriginal(true) //是否包含原始查询.numberOfQueries(5) //生成查询数量.build().expand(query);System.out.printf("Expend Queries: %s%n", expendQueries.size()); //6expendQueries.forEach(expendQuery -> System.out.printf("Expend Query: %s%n", expendQuery.text()));/**Expend Query: 齐天大圣?Expend Query: 齐天大圣的历史背景Expend Query: 齐天大圣的传说故事Expend Query: 齐天大圣在中国文化中的象征意义Expend Query: 关于齐天大圣的影视作品推荐Expend Query: 齐天大圣的武器和神通介绍*/}

3. documentRetriever: DocumentRetriever (文档检索器)

  • 作用: 接收经过转换和扩展的查询,从配置的知识库(如向量数据库、搜索引擎)中检索出最相关的一批文档片段。
  • 典型应用:
    • 向量检索 (Vector Store Retrieval): 从 Chroma, Milvus, Redis 等向量库中基于余弦相似度检索。
    • 混合检索 (Hybrid Search): 同时利用关键词(如 Elasticsearch)和向量检索,以获得更高的精确度和召回率。
  • Spring AI 组件: VectorStoreRetriever, ElasticSearchRetriever

4. documentPostProcessors: List<DocumentPostProcessor> (文档后处理器)

  • 作用:documentRetriever 检索到的文档列表进行二次处理和精炼,以提高文档质量和降低 LLM 的输入噪声。
  • 典型应用:
    • 重排 (Re-Ranking): 使用 Reranker 模型(通常是 Cross-Encoder)对文档进行评分和重新排序,将最相关的文档放在前面,对抗 LLM 的位置偏差 (Positional Bias)。
    • 过滤 (Filtering): 基于元数据(如权限、时效性)或相似度得分阈值,移除低质量或不应公开的文档。
    • 摘要/压缩 (Summarization/Compression): 对过长的文档片段生成简洁摘要,确保其在 LLM 的上下文窗口内。

5. documentJoiner: DocumentJoiner (文档连接器)

  • 作用: 负责将所有经过后处理的文档片段,按照特定的格式和分隔符连接起来,形成一个完整的上下文字符串,供 LLM 使用。
  • 典型应用:
    • 标准 Markdown 格式: 使用 \n\n---\n\n 或 Markdown 标题分隔符连接。
    • JSON/XML 结构化格式: 将每个文档片段及其元数据封装成结构化数据,以便 LLM 更好地解析。
  • Spring AI 组件: ConcatenationDocumentJoiner

6. queryAugmenter: QueryAugmenter (查询增强器)

  • 作用: 在检索和后处理完成后,将最终的上下文字符串系统角色等非检索信息注入到原始查询中,准备构建发送给 LLM 的最终 Prompt。
  • 典型应用:
    • 系统指令注入: 添加如“你是一个友好的金融顾问,请基于上下文回答问题”的指令。
    • 最终 Prompt 组装:documentJoiner 生成的上下文字符串放入一个 Prompt 模板的特定占位符中。
  • Spring AI 组件: ContextQueryAugmenter, MetadataQueryAugmenter
ContextQueryAugmenter

ContextualQueryAugmenter:基于上下文增强查询。默认情况下 allowEmptyContext = false

  • 如果为false,即不允许在没有检索到任何相关内容时,emptyContextPromptTemplate 作为回应返回给用户。
  • 如果为true,即允许在没有检索到任何相关内容时,LLM将仅基于用户的查询生成回答。

四、 综合应用案例:高性能混合 RAG 流程

本案例展示如何组合上述组件,实现一个高性能、多语言支持的混合 RAG 系统

代码结构(核心逻辑)

  Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()// 1. 查询转换 (QueryTransformer):.queryTransformers(//1.1  中文 -> 英文TranslationQueryTransformer.builder().chatClientBuilder(ChatClient.builder(openAiChatModel)).targetLanguage("English").build(),//2. 提取关键词 Monkey KingRewriteQueryTransformer.builder().chatClientBuilder(ChatClient.builder(openAiChatModel)).targetSearchSystem("Monkey King").build())//2. 前查 ==  多重查询扩展.queryExpander(MultiQueryExpander.builder().chatClientBuilder(ChatClient.builder(openAiChatModel)).includeOriginal(true) //是否包含原始查询.numberOfQueries(5) //生成查询数量.build())//3. retriever.documentRetriever(VectorStoreDocumentRetriever.builder().similarityThreshold(0.1).topK(3)
//                        .filterExpression().vectorStore(vectorStore).build())//4. 文档后处理 (DocumentPostProcessors).documentPostProcessors(//mock reranking(query, documents) -> {System.out.printf("Re-ranker - original query:", query.text());//简单的基于关键字year 重排示例List<Document> rerankedDocs = documents.stream().sorted((doc1, doc2) -> {double count1 = Optional.ofNullable(doc1.getMetadata().get("year")).map(Object::toString).map(Double::parseDouble).orElse(0.0);double count2 = Optional.ofNullable(doc2.getMetadata().get("year")).map(Object::toString).map(Double::parseDouble).orElse(0.0);return Double.compare(count2,count1); //降序}).collect(Collectors.toList());System.out.printf("Re-ranker - re-ranked %d documents.%n", rerankedDocs.size());rerankedDocs.forEach(doc -> System.out.printf("Re-ranker - re-ranked document: %s%n", doc.getMetadata()));return rerankedDocs;},//mock filtering(query, documents) -> {documents.removeIf(doc -> doc.getScore() < 0.75);return documents;})//5.文档连接 (DocumentJoiner).documentJoiner((documents) -> {/*** todo* MarkdownDocumentJoiner: 将多个文档合并为一个Markdown格式的文档,适用于需要结构化内容的场景。* List<List<Document>>>  -> List<Document(markdown)>>*/return null;})/*** 6. 查询增强 (QueryAugmenter):* - ContextualQueryAugmenter:基于上下文增强查询。默认情况下 allowEmptyContext = false*  - 如果为false,即不允许在没有检索到任何相关内容时,emptyContextPromptTemplate 作为回应返回给用户。*  - 如果为true,即允许在没有检索到任何相关内容时,LLM将仅基于用户的查询生成回答。*/.queryAugmenter(ContextualQueryAugmenter.builder().allowEmptyContext(true) //允许空上下文,否则没有检索到内容回复: 无法处理.emptyContextPromptTemplate(PromptTemplate.builder().template("用户查询之于知识库之外,请礼貌的告诉他: 您的问题, 超出了我的知识范围,建议您咨询相关领域的专家。").renderer(StTemplateRenderer.builder().startDelimiterToken('<').endDelimiterToken('>').build()).build()).build()).build();

http://www.dtcms.com/a/610982.html

相关文章:

  • 做的好的奥运会网站织梦的网站模板免费吗
  • 自己做的网站怎么打开电影爱好网
  • PyTorch-混合精度训练(amp)
  • 集约化网站群建设网站建设的公司上海
  • 每日两题day43
  • JVM入门知识点
  • 悬镜安全获评2024年度北京市科学技术奖
  • 用yershop做网站win7搭建网站服务器
  • 网站空间国外那个好网站推广代理
  • 云南手机网站建设英文网站建设需求
  • P11096 体育课 top51goc CSP2025模拟第二次第二题题解​ ​
  • Structured Output Parser in LangChain
  • 解释性语言和编译型语言的区别与不同 | 深入了解两者的特点与应用
  • asp网站镜像代码网站建设分金手指专业十六
  • 建设一个网站首先需要什么滕州做网站哪家好
  • 多场景服务机器人代理品牌深度解析
  • 建设网站答辩情况wordpress 搜索 范围
  • “干活”机器人“教练”登场:宇树机器人推出首款轮式机器人G1-D
  • 素马网站制作开发天猫交易购买平台
  • 增强AI编程助手效能:使用开源Litho(deepwiki-rs)深度上下文赋能iFlow
  • 郑州网站建设找三牛天津建设工程信息网评标专家 终审
  • LTE和5G NR中的PDSCH和PUSCH
  • 【HarmonyOS 6】静态和动态添加应用快捷方式详解
  • 手机网站模板更换方法设计上海网站建设
  • 手机网站竞价莆田外贸自建网站
  • 让设备 “开口说话”:设备间通信艺术与以太网温湿度传感器通信实现
  • 宝安做小程序有推荐吗wordpress 百度优化
  • HTML 页面跳转实现
  • 网站登录注册做验证码的目地开一家公司需要具备什么条件
  • CS8389、CS8390:防破音,AB/D,2×6.6W立体音频功率放大器