1. SpringAI RAG核心组件概述
在Spring生态系统中,SpringAI框架为开发者提供了强大的AI集成能力,其中检索增强生成(RAG)功能尤为突出。RAG通过结合信息检索和生成模型,有效解决了大模型幻觉问题,显著提升了回答的准确性和可靠性。
SpringAI的RAG实现基于模块化设计,主要包含以下核心组件:
- QuestionAnswerAdvisor:基础RAG实现,提供开箱即用的问答能力
- RetrievalAugmentationAdvisor:高级RAG框架,支持灵活组合各类处理模块
- 查询转换器(QueryTransformer):优化用户查询,提升检索效果
- 文档检索器(DocumentRetriever):从向量存储中获取相关文档
- 文档后处理器(DocumentPostProcessor):对检索结果进行过滤和优化
这种架构设计使得开发者可以根据具体需求,自由组合不同组件,实现从简单到复杂的各种RAG场景。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础使用:QuestionAnswerAdvisor
2.1 环境准备与初始化
首先需要添加SpringAI相关依赖:
xml复制<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency>
初始化向量存储和ChatClient的示例代码:
java复制@Slf4j
@RestController
@RequestMapping("/rag")
public class ReadController {
private final VectorStore vectorStore;
private final ChatClient chatClient;
public ReadController(@Autowired OllamaEmbeddingModel ollamaEmbeddingModel,
@Autowired OllamaChatModel ollamaChatModel) {
this.vectorStore = SimpleVectorStore.builder(ollamaEmbeddingModel).build();
this.chatClient = ChatClient.builder(ollamaChatModel)
.defaultSystem("你是一个知识问答助手,请根据提供的文档进行回答。请输出中文")
.defaultAdvisors(
SimpleLoggerAdvisor.builder().build(),
QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder()
.similarityThreshold(0.1d)
.topK(2)
.build())
.build())
.build();
// 初始化示例文档
Document doc1 = new Document(
"Spring AI 是 Spring 生态下的 AI 开发框架,支持 RAG、向量存储、大模型集成等功能。",
Map.of("category", "framework", "author", "Spring Team")
);
Document doc2 = new Document(
"检索增强生成(RAG)通过检索外部文档提升大模型回答的准确性,避免幻觉。",
Map.of("category", "technology", "author", "AI Researcher")
);
vectorStore.add(List.of(doc1, doc2));
}
}
2.2 基础问答实现
最简单的问答接口实现:
java复制@GetMapping("test1")
public String test1() {
return chatClient.prompt()
.user("什么是 Spring AI?")
.call().content();
}
2.3 自定义提示模板
通过自定义提示模板可以更好地控制回答格式:
java复制@GetMapping("test2")
public String test2() {
PromptTemplate customPromptTemplate = new PromptTemplate("""
请根据以下上下文信息,用简洁的中文回答问题。
问题:{query}
上下文:
---------------------
{question_answer_context}
---------------------
要求:只基于上下文回答,不要添加额外信息;如果上下文无相关内容,回复"无相关信息"。
""");
QuestionAnswerAdvisor questionAnswerAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder()
.similarityThreshold(0.7d)
.topK(2)
.build())
.promptTemplate(customPromptTemplate)
.build();
return chatClient.prompt()
.user("RAG 能解决什么问题?")
.advisors(questionAnswerAdvisor)
.call().content();
}
2.4 元数据过滤
可以根据文档元数据进行过滤查询:
java复制@GetMapping("test3")
public String test3() {
return chatClient.prompt()
.user("总结一下内容?")
.advisors(a -> a.param(QuestionAnswerAdvisor.FILTER_EXPRESSION, "category == 'framework'"))
.call().content();
}
3. 高级功能:RetrievalAugmentationAdvisor
3.1 查询转换器(QueryTransformer)
3.1.1 对话压缩转换
将对话历史压缩为独立查询:
java复制@GetMapping("QueryTransformer")
public String QueryTransformer() {
Query query = Query.builder()
.text("它的第二大城市是哪?")
.history(new UserMessage("中国的首都在哪?"),
new AssistantMessage("中国的首都是北京?"))
.build();
QueryTransformer queryTransformer = CompressionQueryTransformer.builder()
.chatClientBuilder(chatClient.mutate())
.build();
Query transformedQuery = queryTransformer.transform(query);
log.info("转换后的查询 {}", transformedQuery);
return transformedQuery.text();
}
3.1.2 查询重写转换
优化冗长或模糊的查询:
java复制@GetMapping("RewriteQueryTransformer")
public String RewriteQueryTransformer() {
Query query = new Query("今天天气很好,我想要学习一下AI相关知识。最近正在学习AI。什么是LLM?");
QueryTransformer queryTransformer = RewriteQueryTransformer.builder()
.chatClientBuilder(chatClient.mutate())
.build();
Query transformedQuery = queryTransformer.transform(query);
log.info("转换后的查询 {}", transformedQuery);
return transformedQuery.text();
}
3.1.3 查询翻译转换
解决查询语言与嵌入模型支持语言不一致的问题:
java复制@GetMapping("TranslationQueryTransformer")
public String TranslationQueryTransformer() {
Query query = new Query("美国的首都是哪?");
QueryTransformer queryTransformer = TranslationQueryTransformer.builder()
.chatClientBuilder(chatClient.mutate())
.targetLanguage("English")
.build();
Query transformedQuery = queryTransformer.transform(query);
log.info("转换后的查询 {}", transformedQuery);
return transformedQuery.text();
}
3.2 多查询扩展
生成多个语义相似的查询变体,提高召回率:
java复制@GetMapping("MultiQueryExpander")
public String MultiQueryExpander() {
MultiQueryExpander queryExpander = MultiQueryExpander.builder()
.chatClientBuilder(chatClient.mutate())
.includeOriginal(true)
.numberOfQueries(3)
.build();
List<Query> queries = queryExpander.expand(new Query("如何运行Spring Boot应用程序?"));
queries.forEach(query -> log.info("转换后的查询 {}", query.text()));
return queries.stream().map(Query::text).collect(Collectors.joining("\n"));
}
3.3 文档检索与合并
从多个向量存储检索并合并文档:
java复制@GetMapping("ConcatenationDocumentJoiner")
public String ConcatenationDocumentJoiner() {
// 创建多个向量存储
VectorStore database1 = SimpleVectorStore.builder(ollamaEmbeddingModel).build();
database1.add(List.of(Document.builder().id("1").text("Spring AI 的 RAG 组件支持模块化配置").build()));
VectorStore database2 = SimpleVectorStore.builder(ollamaEmbeddingModel).build();
database2.add(List.of(Document.builder().id("1").text("Spring AI 的 RAG 组件支持模块化配置").build()));
database2.add(List.of(Document.builder().id("2").text("RetrievalAugmentationAdvisor 是核心组件").build()));
// 执行查询
Query query = Query.builder().text("Spring AI 里面的 RAG 组件有哪些?").build();
SearchRequest searchRequest = SearchRequest.builder()
.topK(10)
.similarityThreshold(0.1d)
.query(query.text())
.build();
// 从不同存储检索
List<Document> documents1 = database1.similaritySearch(searchRequest);
List<Document> documents2 = database2.similaritySearch(searchRequest);
// 合并结果
Map<Query, List<List<Document>>> documentsForQuery = Map.of(query, List.of(documents1, documents2));
DocumentJoiner documentJoiner = new ConcatenationDocumentJoiner();
List<Document> documents = documentJoiner.join(documentsForQuery);
documents.forEach(document -> log.info("合并后的文档: {}", document.getText()));
return "ok";
}
3.4 查询增强与文档后处理
java复制@GetMapping("DocumentPostProcessor")
public String DocumentPostProcessor() {
VectorStore database1 = SimpleVectorStore.builder(ollamaEmbeddingModel).build();
database1.add(List.of(
Document.builder().id("1").text("Spring AI 的 RAG 组件").metadata("category", "Spring AI").build(),
Document.builder().id("2").text("RetrievalAugmentationAdvisor 是核心组件").metadata("category", "RAG").build()
));
List<Document> documentList = database1.similaritySearch(
SearchRequest.builder().query("查询RAG相关文档?").topK(10).similarityThreshold(0.1d).build());
// 自定义后处理器:按元数据过滤
DocumentPostProcessor documentPostProcessor = (query, documents) -> {
documents.forEach(document -> log.info("原始文档 {}", document.getText()));
return documents.stream()
.filter(document -> document.getMetadata().containsKey("category")
&& "RAG".equals(document.getMetadata().get("category")))
.collect(Collectors.toList());
};
List<Document> filtered = documentPostProcessor.apply(
Query.builder().text("查询RAG相关文档?").build(), documentList);
filtered.forEach(document -> log.info("过滤后的文档 {}", document.getText()));
return "ok";
}
4. 完整集成示例
将各组件整合使用的完整示例:
java复制@GetMapping("RetrievalAugmentationAdvisor")
public String RetrievalAugmentationAdvisor() {
// 初始化向量存储
VectorStore database1 = SimpleVectorStore.builder(ollamaEmbeddingModel).build();
database1.add(List.of(
Document.builder().id("1").text("Spring AI 的 RAG 组件支持模块化配置").metadata("category", "Spring AI").build(),
Document.builder().id("2").text("RetrievalAugmentationAdvisor 是核心组件").metadata("category", "RAG").build()
));
VectorStore database2 = SimpleVectorStore.builder(ollamaEmbeddingModel).build();
database2.add(List.of(
Document.builder().id("3").text("RetrievalAugmentationAdvisor 支持组合各种组件").build()
));
// 构建完整RAG流程
RetrievalAugmentationAdvisor ragAdvisor = RetrievalAugmentationAdvisor.builder()
.queryTransformers(RewriteQueryTransformer.builder()
.chatClientBuilder(chatClient.mutate())
.build())
.queryExpander(MultiQueryExpander.builder()
.chatClientBuilder(chatClient.mutate())
.includeOriginal(true)
.numberOfQueries(3)
.build())
.documentRetriever(VectorStoreDocumentRetriever.builder()
.vectorStore(database1)
.similarityThreshold(0.4d)
.topK(5)
.build())
.documentRetriever(VectorStoreDocumentRetriever.builder()
.vectorStore(database2)
.similarityThreshold(0.4d)
.topK(5)
.build())
.documentJoiner(new ConcatenationDocumentJoiner())
.documentPostProcessors((query, documents) -> {
documents.forEach(document -> log.info("查询出来的文档:{}", document.getText()));
return documents;
})
.build();
// 执行查询
String response = chatClient.prompt()
.user("Spring AI 的 RAG 模块有哪些")
.advisors(ragAdvisor)
.advisors(SimpleLoggerAdvisor.builder().build())
.call().content();
log.info("回答结果:{}", response);
return response;
}
5. 实践经验与优化建议
在实际项目中使用SpringAI RAG组件时,以下几点经验值得注意:
-
查询转换器的选择:
- 对于对话式应用,优先使用CompressionQueryTransformer
- 当用户输入冗长或不规范时,使用RewriteQueryTransformer
- 多语言场景下,TranslationQueryTransformer能显著提升效果
-
向量存储优化:
- 合理设置similarityThreshold(0.3-0.6通常较好)
- 根据文档数量调整topK参数(5-10是常用范围)
- 为文档添加丰富的元数据便于过滤
-
性能考量:
- 复杂流程会增加延迟,建议异步处理耗时操作
- 对高频查询考虑缓存机制
- 监控各组件耗时,针对性优化
-
错误处理:
- 为ContextualQueryAugmenter设置友好的空结果提示
- 记录转换前后的查询,便于分析问题
- 对关键操作添加重试机制
-
效果评估:
- 建立测试集评估不同配置的效果
- 关注召回率和准确率的平衡
- 定期人工抽样检查回答质量
通过合理组合这些组件和优化策略,可以构建出既灵活又高效的RAG应用,满足各种业务场景的需求。
