RAG(检索增强生成)
约 3594 字大约 12 分钟
2026-09-15
📌 原文地址:https://docs.spring.io/java-ai/reference/api/retrieval-augmented-generation.html
📌 原文版本:Java AI 2.0.1(Stable)
检索增强生成(RAG) 是一种有助于克服大语言模型在长篇内容、事实准确性和上下文感知方面局限性的技术。
Java AI 通过提供模块化架构来支持 RAG,允许你自行构建自定义 RAG 流程,或使用 Advisor API 开箱即用的 RAG 流程。
白话解读
RAG 的本质就一句话:让 LLM 先查资料再回答。它解决 LLM 的两个老毛病——一是知识有截止日期,二是容易"一本正经胡说八道"。Java AI 把 RAG 拆成四段:Pre-Retrieval(检索前整理问题)、Retrieval(去向量库捞资料)、Post-Retrieval(资料去噪排序)、Generation(带着资料生成答案)。你可以用现成的 Advisor,也可以自己拼。
一、Advisors(顾问)
Java AI 使用 Advisor API 为常见 RAG 流程提供开箱即用的支持。
要使用 QuestionAnswerAdvisor 或 VectorStoreChatMemoryAdvisor,需向项目添加 spring-ai-vector-store-advisor 依赖:
<!-- RAG 向量存储顾问依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-vector-store-advisor</artifactId>
</dependency>1.1 QuestionAnswerAdvisor
向量数据库存储 AI 模型感知不到的数据。当用户问题被发送给 AI 模型时,QuestionAnswerAdvisor 会查询向量数据库中与用户问题相关的文档,并将向量数据库的响应追加到用户文本后面,为 AI 模型生成回复提供上下文。
假设你已将数据加载到 VectorStore 中,可以通过向 ChatClient 提供一个 QuestionAnswerAdvisor 实例来执行 RAG:
// 基础用法:向 ChatClient 注册 QuestionAnswerAdvisor 执行 RAG
ChatResponse response = ChatClient.builder(chatModel)
.build().prompt()
.advisors(QuestionAnswerAdvisor.builder(vectorStore).build())
.user(userText) // 用户提问
.call()
.chatResponse();在此示例中,QuestionAnswerAdvisor 会对向量数据库中的所有文档执行相似度搜索。若要限制搜索的文档类型,SearchRequest 支持一种类 SQL 的过滤表达式,可在所有 VectorStore 之间移植。
该过滤表达式既可在创建 QuestionAnswerAdvisor 时配置(对所有 ChatClient 请求始终生效),也可在运行时按请求提供。
以下示例展示如何创建一个相似度阈值为 0.8、返回前 6 条结果的 QuestionAnswerAdvisor:
// 相似度阈值 0.8,返回 Top 6 结果
var qaAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder().similarityThreshold(0.8d).topK(6).build())
.build();1.2 Dynamic Filter Expressions(动态过滤表达式)
可在运行时通过 FILTER_EXPRESSION 顾问上下文参数更新 SearchRequest 的过滤表达式:
// 构建带默认 SearchRequest 的 ChatClient
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder().build())
.build())
.build();
// 运行时更新过滤表达式:仅搜索 type == 'Spring' 的文档
String content = this.chatClient.prompt()
.user("Please answer my question XYZ")
.advisors(a -> a.param(QuestionAnswerAdvisor.FILTER_EXPRESSION, "type == 'Spring'"))
.call()
.content();FILTER_EXPRESSION 参数允许你根据提供的表达式动态过滤搜索结果。
1.3 Custom Template(自定义模板)
QuestionAnswerAdvisor 使用默认模板将检索到的文档与用户问题合并。你可以通过 .promptTemplate() 构建器方法提供自己的 PromptTemplate 对象来自定义此行为。
⚠️ 注意区分:此处提供的
PromptTemplate自定义的是顾问如何将检索到的上下文与用户查询合并;这与在 ChatClient 本身上配置TemplateRenderer(使用.templateRenderer())不同——后者影响的是顾问运行之前初始用户/系统提示内容的渲染。详见 ChatClient Prompt Templates。
自定义 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
QuestionAnswerAdvisor qaAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
.promptTemplate(customPromptTemplate)
.build();
// 执行 RAG 请求
String response = ChatClient.builder(chatModel).build()
.prompt(question)
.advisors(qaAdvisor)
.call()
.content();📌
QuestionAnswerAdvisor.Builder.userTextAdvise()方法已弃用,请改用.promptTemplate()以获得更灵活的自定义能力。
二、RetrievalAugmentationAdvisor
Java AI 包含一个 RAG 模块库,可用于构建你自己的 RAG 流程。RetrievalAugmentationAdvisor 是一个 Advisor,基于模块化架构为最常见 RAG 流程提供开箱即用的实现。
要使用 RetrievalAugmentationAdvisor,需添加 spring-ai-rag 依赖:
<!-- RAG 模块库依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-rag</artifactId>
</dependency>2.1 Sequential RAG Flows(顺序 RAG 流程)
Naive RAG(朴素 RAG)
// 朴素 RAG:文档检索器 + 相似度阈值 0.50
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 以基于元数据过滤搜索结果。可在实例化时提供,也可在运行时按请求提供(使用 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(高级 RAG)
// 高级 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(模块架构)
Java AI 实现了模块化 RAG 架构,其模块化理念源自论文 "Modular RAG: Transforming RAG Systems into LEGO-like Reconfigurable Frameworks"(将 RAG 系统转变为乐高式可重构框架)。
模块分为四大阶段:
| 阶段 | 职责 |
|---|---|
| Pre-Retrieval(检索前) | 处理用户查询以获得最佳检索效果 |
| Retrieval(检索) | 查询数据系统(如向量存储)并检索最相关文档 |
| Post-Retrieval(检索后) | 处理检索到的文档以获得最佳生成效果 |
| Generation(生成) | 基于用户查询和检索到的文档生成最终回复 |
3.1 Pre-Retrieval(检索前)
3.1.1 Query Transformation(查询转换)
用于转换输入查询、使其更利于检索任务的组件,可解决查询格式不佳、术语含糊、词汇复杂或语言不受支持等问题。
💡 最佳实践:使用
QueryTransformer时,建议将ChatClient.Builder的温度配置为低值(如 0.0),以确保结果更具确定性和准确性,提升检索质量。多数聊天模型的默认温度对查询转换而言偏高,会降低检索效果。
① 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);
// 转换后的查询将变成类似:"What is the second largest city in Denmark?"该组件使用的提示词可通过构建器中的 promptTemplate() 方法自定义。
② RewriteQueryTransformer(重写查询转换器)
使用大语言模型重写用户查询,以便在查询目标系统(如向量存储或 Web 搜索引擎)时获得更好的结果。适用于用户查询冗长、含糊或包含可能影响搜索结果质量的不相关信息时。
// 原始查询冗长:"我在学习机器学习。什么是 LLM?"
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);
// 重写后:"What is a Large Language Model (LLM)?"该组件使用的提示词可通过构建器中的 promptTemplate() 方法自定义。
③ TranslationQueryTransformer(翻译查询转换器)
使用大语言模型将查询翻译为生成文档嵌入所用的嵌入模型支持的目标语言。若查询已是目标语言,则原样返回;若查询语言未知,同样原样返回。适用于嵌入模型基于特定语言训练、而用户查询使用其他语言的场景。
// 丹麦语查询:"丹麦的首都是什么?"
Query query = new Query("Hvad er Danmarks hovedstad?");
QueryTransformer queryTransformer = TranslationQueryTransformer.builder()
.chatClientBuilder(chatClientBuilder)
.targetLanguage("english") // 目标语言:英语
.build();
Query transformedQuery = queryTransformer.transform(query);该组件使用的提示词可通过构建器中的 promptTemplate() 方法自定义。
3.1.2 Query Expansion(查询扩展)
将输入查询扩展为查询列表的组件,通过提供替代查询表述或把复杂问题拆解为更简单的子查询,来应对查询格式不佳的问题。
MultiQueryExpander(多查询扩展器)
使用大语言模型将一个查询扩展为多个语义多样的变体以捕捉不同视角,有助于检索更多上下文信息并提高找到相关结果的概率。
// 将一个查询扩展为 3 个语义多样的变体
MultiQueryExpander queryExpander = MultiQueryExpander.builder()
.chatClientBuilder(chatClientBuilder)
.numberOfQueries(3) // 扩展出 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() 方法自定义。
3.2 Retrieval(检索)
3.2.1 Document Search(文档搜索)
负责从底层数据源(如搜索引擎、向量存储、数据库或知识图谱)检索 Document 的组件。
VectorStoreDocumentRetriever(向量存储文档检索器)
从向量存储中检索与输入查询语义相似的文档。支持基于元数据过滤、相似度阈值和 top-k 结果数。
// 从向量存储检索:阈值 0.73、Top 5、按 genre == fairytale 过滤
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:
// 动态过滤表达式:通过 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?"));也可以通过 Query API 使用 FILTER_EXPRESSION 参数提供请求级的过滤表达式。若同时提供了请求级与检索器级的过滤表达式,请求级表达式优先:
// 请求级过滤表达式:location == 'Whispering Woods'(优先于检索器级配置)
Query query = Query.builder()
.text("Who is Anacletus?")
.context(Map.of(VectorStoreDocumentRetriever.FILTER_EXPRESSION, "location == 'Whispering Woods'"))
.build();
List<Document> retrievedDocuments = documentRetriever.retrieve(query);3.2.2 Document Join(文档合并)
将基于多个查询并从多个数据源检索到的文档合并为单一文档集合的组件。在合并过程中,还可处理重复文档和倒数排名策略。
ConcatenationDocumentJoiner(拼接文档合并器)
通过拼接方式,将基于多个查询、来自多个数据源的检索文档合并为单一集合。遇到重复文档时,保留第一次出现的;每个文档的得分保持不变。
// 合并多个查询、多个数据源的检索结果
Map<Query, List<List<Document>>> documentsForQuery = ...
DocumentJoiner documentJoiner = new ConcatenationDocumentJoiner();
List<Document> documents = documentJoiner.join(documentsForQuery);3.3 Post-Retrieval(检索后)
Document Post-Processing(文档后处理)
基于查询对检索到的文档进行后处理的组件,可应对以下挑战:
- lost-in-the-middle(中间信息丢失)问题;
- 模型的上下文长度限制;
- 检索信息中噪声与冗余的削减需求。
例如,它可以:
- 基于文档与查询的相关性进行排序;
- 移除不相关或冗余的文档;
- 压缩每个文档的内容以减少噪声和冗余。
3.4 Generation(生成)
Query Augmentation(查询增强)
用额外数据增强输入查询的组件,用于向大语言模型提供回答用户查询所需的上下文。
ContextualQueryAugmenter(上下文查询增强器)
使用所提供文档内容中的上下文数据来增强用户查询。
// 默认构建:不允许空上下文
QueryAugmenter queryAugmenter = ContextualQueryAugmenter.builder().build();默认情况下,ContextualQueryAugmenter 不允许检索到的上下文为空——为空时会指示模型不回答用户查询。
可以启用 allowEmptyContext 选项,允许模型即使在检索上下文为空时也能生成回复:
// 允许空上下文:即使检索不到内容,模型也可作答
QueryAugmenter queryAugmenter = ContextualQueryAugmenter.builder()
.allowEmptyContext(true)
.build();该组件使用的提示词可通过构建器中的 promptTemplate() 与 emptyContextPromptTemplate() 方法自定义。
四、要点总结
- 两大开箱即用 Advisor:
QuestionAnswerAdvisor(简单场景)与RetrievalAugmentationAdvisor(模块化高级场景),分别依赖spring-ai-vector-store-advisor和spring-ai-rag。 - 过滤表达式三处可配:构建时(静态)、运行时(
FILTER_EXPRESSION上下文参数)、请求级(QueryAPI,优先级最高)。 - 查询转换器选型:长对话压缩用
CompressionQueryTransformer;冗长/含糊查询用RewriteQueryTransformer;跨语言嵌入用TranslationQueryTransformer;多视角检索用MultiQueryExpander。 - 温度建议:所有
QueryTransformer建议将 ChatClient 温度设为 0.0 以获得确定性结果。 - 空上下文策略:默认拒绝作答,可通过
allowEmptyContext(true)允许模型在无检索结果时仍生成回复。 - 模块化架构:Pre-Retrieval → Retrieval → Post-Retrieval → Generation 四阶段,源自《Modular RAG》论文的乐高式可重构设计。