Spring AI实现RAG(检索增强生成)详解与实践
支持多种文件格式的文档加载实现高效的向量存储和检索构建智能的问答系统提供可追溯的知识服务RAG技术为AI应用提供了强大的知识增强能力,使得模型能够基于实际文档生成准确、可靠的回答。
·
Spring AI实现RAG(检索增强生成)详解与实践
一、什么是RAG?
RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合信息检索和文本生成的技术。它通过从外部知识库中检索相关信息,然后将这些信息作为上下文提供给生成模型,从而生成更准确、更相关的回答。
RAG的核心原理
- 检索(Retrieval):从知识库中检索与查询相关的文档片段
- 增强(Augmentation):将检索到的信息作为上下文增强提示
- 生成(Generation):基于增强后的提示生成最终回答
RAG的优势
- 知识更新:无需重新训练模型即可更新知识
- 准确性:基于实际文档生成,减少幻觉问题
- 可追溯性:可以追溯到信息来源
- 成本效益:比微调模型更经济
二、Spring AI中的RAG支持
Spring AI提供了完整的RAG实现框架,包括:
- 向量存储:支持多种向量数据库
- 文档加载:支持多种文件格式
- 文本分割:智能文档分块
- 向量化:文本向量嵌入
- 检索:相似度搜索
三、Spring AI实现RAG的步骤
1. 添加依赖
在pom.xml中添加Spring AI RAG相关依赖:
<dependencies>
<!-- Spring AI Core -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-core</artifactId>
<version>1.0.0</version>
</dependency>
<!-- Spring AI OpenAI -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
<!-- Spring AI Vector Store (支持多种向量数据库) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pgvector-store</artifactId>
<version>1.0.0</version>
</dependency>
<!-- 或者使用其他向量数据库 -->
<!-- <dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-chroma-store</artifactId>
<version>1.0.0</version>
</dependency> -->
<!-- 文档处理 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pdf-document-reader</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
2. 配置向量存储和模型
在application.yml中配置:
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4
temperature: 0.7
vectorstore:
pgvector:
index-type: HNSW
distance-type: COSINE_DISTANCE
dimensions: 1536
3. 创建向量存储服务
package com.example.rag.service;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class VectorStoreService {
@Autowired
private VectorStore vectorStore;
/**
* 添加文档到向量存储
*/
public void addDocuments(List<Document> documents) {
vectorStore.add(documents);
}
/**
* 根据查询检索相似文档
*/
public List<Document> search(String query, int topK) {
return vectorStore.similaritySearch(query, topK);
}
}
4. 实现RAG服务
package com.example.rag.service;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.document.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class RAGService {
@Autowired
private ChatClient chatClient;
@Autowired
private VectorStoreService vectorStoreService;
/**
* RAG查询:检索 + 生成
*/
public String query(String question) {
// 1. 检索相关文档
List<Document> relevantDocs = vectorStoreService.search(question, 5);
// 2. 构建增强提示
String context = relevantDocs.stream()
.map(Document::getContent)
.collect(Collectors.joining("\n\n"));
String enhancedPrompt = String.format(
"基于以下上下文信息回答问题。如果上下文中没有相关信息,请说明。\n\n" +
"上下文:\n%s\n\n" +
"问题:%s\n\n" +
"回答:",
context,
question
);
// 3. 生成回答
Prompt prompt = new Prompt(new UserMessage(enhancedPrompt));
return chatClient.call(prompt).getResult().getOutput().getContent();
}
}
5. 文档加载和向量化
package com.example.rag.service;
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.tika.TikaDocumentReader;
import org.springframework.ai.transformer.splitter.TextSplitter;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
@Service
public class DocumentService {
@Autowired
private VectorStoreService vectorStoreService;
/**
* 加载文档并存储到向量数据库
*/
public void loadDocument(MultipartFile file) throws IOException {
// 1. 读取文档
TikaDocumentReader reader = new TikaDocumentReader(file.getInputStream());
List<Document> documents = reader.get();
// 2. 文档分割
TextSplitter textSplitter = new TokenTextSplitter();
List<Document> splitDocuments = textSplitter.apply(documents);
// 3. 添加元数据
for (Document doc : splitDocuments) {
doc.getMetadata().put("source", file.getOriginalFilename());
doc.getMetadata().put("type", getFileType(file.getOriginalFilename()));
}
// 4. 存储到向量数据库
vectorStoreService.addDocuments(splitDocuments);
}
private String getFileType(String filename) {
if (filename.endsWith(".pdf")) return "PDF";
if (filename.endsWith(".docx")) return "Word";
if (filename.endsWith(".txt")) return "Text";
if (filename.endsWith(".md")) return "Markdown";
return "Unknown";
}
}
四、Spring AI RAG支持的文件类型
Spring AI通过Apache Tika支持多种文件格式:
1. 文档格式
- PDF (
.pdf):PDF文档 - Word (
.doc,.docx):Microsoft Word文档 - Excel (
.xls,.xlsx):Microsoft Excel表格 - PowerPoint (
.ppt,.pptx):Microsoft PowerPoint演示文稿 - RTF (
.rtf):富文本格式
2. 文本格式
- 纯文本 (
.txt):纯文本文件 - Markdown (
.md,.markdown):Markdown文档 - HTML (
.html,.htm):HTML网页 - XML (
.xml):XML文档 - JSON (
.json):JSON数据文件
3. 代码文件
- Java (
.java) - Python (
.py) - JavaScript (
.js) - TypeScript (
.ts) - C/C++ (
.c,.cpp,.h) - 其他编程语言
4. 配置文件
- YAML (
.yaml,.yml) - Properties (
.properties) - INI (
.ini) - CSV (
.csv)
5. 其他格式
- 图像 (
.jpg,.png,.gif):通过OCR提取文本 - 音频 (
.mp3,.wav):通过语音识别提取文本 - 视频 (
.mp4,.avi):提取字幕和元数据
五、完整RAG实现示例
1. 文档上传控制器
package com.example.rag.controller;
import com.example.rag.service.DocumentService;
import com.example.rag.service.RAGService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@RestController
@RequestMapping("/api/rag")
public class RAGController {
@Autowired
private DocumentService documentService;
@Autowired
private RAGService ragService;
/**
* 上传文档
*/
@PostMapping("/upload")
public ResponseEntity<String> uploadDocument(@RequestParam("file") MultipartFile file) {
try {
documentService.loadDocument(file);
return ResponseEntity.ok("文档上传成功");
} catch (Exception e) {
return ResponseEntity.badRequest().body("上传失败: " + e.getMessage());
}
}
/**
* RAG查询
*/
@PostMapping("/query")
public ResponseEntity<String> query(@RequestBody QueryRequest request) {
String answer = ragService.query(request.getQuestion());
return ResponseEntity.ok(answer);
}
}
2. 使用示例
package com.example.rag.example;
import com.example.rag.service.DocumentService;
import com.example.rag.service.RAGService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileInputStream;
import java.nio.file.Files;
@Component
public class RAGExample implements CommandLineRunner {
@Autowired
private DocumentService documentService;
@Autowired
private RAGService ragService;
@Autowired
private ResourceLoader resourceLoader;
@Override
public void run(String... args) throws Exception {
// 1. 加载文档
File pdfFile = resourceLoader.getResource("classpath:sample.pdf").getFile();
documentService.loadDocument(
new MockMultipartFile("sample.pdf", new FileInputStream(pdfFile))
);
// 2. 进行RAG查询
String question = "文档中提到了哪些关键技术?";
String answer = ragService.query(question);
System.out.println("问题:" + question);
System.out.println("回答:" + answer);
}
}
3. 高级RAG:带重排序
package com.example.rag.service;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.document.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class AdvancedRAGService {
@Autowired
private ChatClient chatClient;
@Autowired
private VectorStoreService vectorStoreService;
/**
* 带重排序的RAG查询
*/
public String queryWithReranking(String question, int topK, int rerankTopK) {
// 1. 初始检索(获取更多候选)
List<Document> candidates = vectorStoreService.search(question, topK);
// 2. 重排序(使用交叉编码器或LLM)
List<Document> reranked = rerankDocuments(question, candidates, rerankTopK);
// 3. 构建增强提示
String context = reranked.stream()
.map(Document::getContent)
.collect(Collectors.joining("\n\n"));
String enhancedPrompt = buildPrompt(context, question);
// 4. 生成回答
return chatClient.call(new Prompt(new UserMessage(enhancedPrompt)))
.getResult().getOutput().getContent();
}
/**
* 文档重排序
*/
private List<Document> rerankDocuments(String question,
List<Document> candidates,
int topK) {
// 使用LLM对文档进行相关性评分
return candidates.stream()
.map(doc -> {
double score = scoreRelevance(question, doc.getContent());
doc.getMetadata().put("rerank_score", score);
return doc;
})
.sorted(Comparator.comparing(doc ->
(Double) doc.getMetadata().get("rerank_score")).reversed())
.limit(topK)
.collect(Collectors.toList());
}
private double scoreRelevance(String question, String content) {
// 简化的相关性评分(实际可以使用交叉编码器)
String prompt = String.format(
"评估以下内容与问题的相关性(0-1分):\n" +
"问题:%s\n" +
"内容:%s\n" +
"只返回分数:",
question, content.substring(0, Math.min(500, content.length()))
);
String response = chatClient.call(new Prompt(new UserMessage(prompt)))
.getResult().getOutput().getContent();
try {
return Double.parseDouble(response.trim());
} catch (Exception e) {
return 0.5;
}
}
private String buildPrompt(String context, String question) {
return String.format(
"基于以下上下文信息回答问题。如果上下文中没有相关信息,请说明。\n\n" +
"上下文:\n%s\n\n" +
"问题:%s\n\n" +
"回答:",
context, question
);
}
}
六、RAG的最佳实践
1. 文档预处理
- 清理:移除无关内容、格式化文本
- 分块:合理设置分块大小(通常200-500 tokens)
- 重叠:分块之间保留一定重叠(10-20%)
2. 检索优化
- 混合检索:结合关键词检索和向量检索
- 重排序:使用交叉编码器提高精度
- 过滤:根据元数据过滤文档
3. 提示工程
- 明确指令:清晰说明如何使用上下文
- 格式要求:指定回答格式
- 引用来源:要求模型引用信息来源
七、总结
通过Spring AI实现RAG,我们可以:
- 支持多种文件格式的文档加载
- 实现高效的向量存储和检索
- 构建智能的问答系统
- 提供可追溯的知识服务
RAG技术为AI应用提供了强大的知识增强能力,使得模型能够基于实际文档生成准确、可靠的回答。
参考资料
更多推荐


所有评论(0)