Spring AI与RAG技术实战:构建企业级智能文档问答系统
引言
随着人工智能技术的快速发展,企业对于智能化文档处理的需求日益增长。传统的文档检索方式已经无法满足用户对于精准、智能问答的需求。Spring AI结合RAG(Retrieval-Augmented Generation)技术,为企业提供了一种全新的智能文档问答解决方案。本文将详细介绍如何使用Spring AI框架构建企业级智能文档问答系统。
技术栈概述
Spring AI框架
Spring AI是Spring生态系统中的AI扩展框架,提供了统一的API来集成各种AI模型和服务。它支持OpenAI、Google AI、Azure OpenAI等多种AI服务提供商。
RAG技术原理
RAG(检索增强生成)技术结合了信息检索和文本生成的优势:
- 检索阶段:从知识库中检索与问题相关的文档片段
- 增强阶段:将检索到的信息作为上下文提供给生成模型
- 生成阶段:基于检索到的上下文生成准确、相关的回答
系统架构设计
整体架构
用户界面层 → API网关层 → 业务逻辑层 → 数据访问层
↓
AI服务集成层
↓
向量数据库 + 文档存储
核心组件
1. 文档处理模块
@Component
public class DocumentProcessor {
@Autowired
private EmbeddingModel embeddingModel;
public List<DocumentChunk> processDocument(MultipartFile file) {
// 文档解析、分块、向量化处理
List<String> chunks = splitDocument(file);
List<Embedding> embeddings = embeddingModel.embed(chunks);
return createDocumentChunks(chunks, embeddings);
}
private List<String> splitDocument(MultipartFile file) {
// 实现文档分块逻辑
return Arrays.asList("chunk1", "chunk2", "chunk3");
}
}
2. 向量存储模块
@Repository
public class VectorStoreService {
@Autowired
private MilvusClient milvusClient;
public void storeDocuments(List<DocumentChunk> chunks) {
List<List<Float>> vectors = chunks.stream()
.map(DocumentChunk::getEmbedding)
.collect(Collectors.toList());
milvusClient.insert(vectors);
}
public List<DocumentChunk> searchSimilarDocuments(Embedding queryEmbedding, int topK) {
// 相似度搜索
return milvusClient.search(queryEmbedding, topK);
}
}
3. RAG问答引擎
@Service
public class RAGQuestionAnsweringService {
@Autowired
private ChatClient chatClient;
@Autowired
private VectorStoreService vectorStoreService;
@Autowired
private EmbeddingModel embeddingModel;
public String answerQuestion(String question) {
// 1. 将问题向量化
Embedding questionEmbedding = embeddingModel.embed(question);
// 2. 检索相关文档
List<DocumentChunk> relevantChunks =
vectorStoreService.searchSimilarDocuments(questionEmbedding, 5);
// 3. 构建提示词
String context = buildContext(relevantChunks);
String prompt = buildPrompt(question, context);
// 4. 生成回答
return chatClient.generate(prompt);
}
private String buildContext(List<DocumentChunk> chunks) {
return chunks.stream()
.map(DocumentChunk::getContent)
.collect(Collectors.joining("\n\n"));
}
private String buildPrompt(String question, String context) {
return String.format("""
基于以下上下文信息,请回答用户的问题。
如果上下文信息不足以回答问题,请如实告知。
上下文:
%s
问题:%s
回答:
""", context, question);
}
}
详细实现步骤
1. 环境配置
Maven依赖配置
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.8.1</version>
</dependency>
<dependency>
<groupId>io.milvus</groupId>
<artifactId>milvus-sdk-java</artifactId>
<version>2.3.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
应用配置
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-3.5-turbo
temperature: 0.7
milvus:
host: localhost
port: 19530
2. 文档处理实现
文档解析器
@Component
public class DocumentParser {
public List<String> parsePdf(MultipartFile file) throws IOException {
try (PDDocument document = PDDocument.load(file.getInputStream())) {
PDFTextStripper stripper = new PDFTextStripper();
String text = stripper.getText(document);
return splitTextIntoChunks(text);
}
}
public List<String> parseDocx(MultipartFile file) throws IOException {
XWPFDocument document = new XWPFDocument(file.getInputStream());
List<String> paragraphs = new ArrayList<>();
for (XWPFParagraph paragraph : document.getParagraphs()) {
String text = paragraph.getText();
if (!text.trim().isEmpty()) {
paragraphs.add(text);
}
}
return mergeSmallChunks(paragraphs);
}
private List<String> splitTextIntoChunks(String text, int chunkSize) {
// 实现文本分块逻辑
return TextSplitter.split(text, chunkSize);
}
}
3. 向量数据库集成
Milvus配置类
@Configuration
public class MilvusConfig {
@Value("${spring.milvus.host}")
private String host;
@Value("${spring.milvus.port}")
private int port;
@Bean
public MilvusServiceClient milvusClient() {
ConnectParam connectParam = ConnectParam.newBuilder()
.withHost(host)
.withPort(port)
.build();
return new MilvusServiceClient(connectParam);
}
@Bean
public DataType collectionSchema() {
return DataType.newBuilder()
.withName("document_chunks")
.addField(FieldType.newBuilder()
.withName("id")
.withDataType(DataType.Int64)
.withIsPrimaryKey(true)
.build())
.addField(FieldType.newBuilder()
.withName("embedding")
.withDataType(DataType.FloatVector)
.withDimension(1536) // OpenAI embedding dimension
.build())
.addField(FieldType.newBuilder()
.withName("content")
.withDataType(DataType.VarChar)
.withMaxLength(1000)
.build())
.build();
}
}
4. REST API设计
控制器层
@RestController
@RequestMapping("/api/rag")
public class RAGController {
@Autowired
private RAGQuestionAnsweringService ragService;
@Autowired
private DocumentProcessingService documentService;
@PostMapping("/upload")
public ResponseEntity<String> uploadDocument(@RequestParam("file") MultipartFile file) {
try {
documentService.processAndStoreDocument(file);
return ResponseEntity.ok("文档上传成功");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("文档处理失败: " + e.getMessage());
}
}
@PostMapping("/ask")
public ResponseEntity<AnswerResponse> askQuestion(@RequestBody QuestionRequest request) {
try {
String answer = ragService.answerQuestion(request.getQuestion());
return ResponseEntity.ok(new AnswerResponse(answer));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new AnswerResponse("系统繁忙,请稍后重试"));
}
}
@GetMapping("/documents")
public ResponseEntity<List<DocumentInfo>> listDocuments() {
List<DocumentInfo> documents = documentService.getAllDocuments();
return ResponseEntity.ok(documents);
}
}
@Data
class QuestionRequest {
private String question;
}
@Data
class AnswerResponse {
private String answer;
private LocalDateTime timestamp;
public AnswerResponse(String answer) {
this.answer = answer;
this.timestamp = LocalDateTime.now();
}
}
性能优化策略
1. 缓存优化
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(1000));
return cacheManager;
}
}
@Service
public class CachedRAGService {
@Autowired
private RAGQuestionAnsweringService ragService;
@Cacheable(value = "answers", key = "#question")
public String getCachedAnswer(String question) {
return ragService.answerQuestion(question);
}
}
2. 批量处理优化
@Service
public class BatchDocumentProcessor {
@Async
public CompletableFuture<Void> processDocumentsInBatch(List<MultipartFile> files) {
return CompletableFuture.runAsync(() -> {
files.parallelStream().forEach(file -> {
try {
processSingleDocument(file);
} catch (Exception e) {
log.error("处理文档失败: {}", file.getOriginalFilename(), e);
}
});
});
}
}
安全考虑
1. 输入验证
@Component
public class InputValidator {
private static final int MAX_QUESTION_LENGTH = 1000;
private static final Pattern MALICIOUS_PATTERN =
Pattern.compile("[<>{}]|script|javascript", Pattern.CASE_INSENSITIVE);
public void validateQuestion(String question) {
if (question == null || question.trim().isEmpty()) {
throw new IllegalArgumentException("问题不能为空");
}
if (question.length() > MAX_QUESTION_LENGTH) {
throw new IllegalArgumentException("问题长度超过限制");
}
if (MALICIOUS_PATTERN.matcher(question).find()) {
throw new SecurityException("检测到潜在恶意输入");
}
}
}
2. 速率限制
@Configuration
public class RateLimitConfig {
@Bean
public RateLimiter rateLimiter() {
return RateLimiter.create(10); // 10 requests per second
}
}
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(RateLimitExceededException.class)
public ResponseEntity<String> handleRateLimitExceeded() {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS)
.body("请求过于频繁,请稍后重试");
}
}
测试策略
单元测试
@SpringBootTest
class RAGServiceTest {
@Autowired
private RAGQuestionAnsweringService ragService;
@MockBean
private VectorStoreService vectorStoreService;
@Test
void testAnswerQuestionWithRelevantContext() {
// 模拟返回相关文档片段
when(vectorStoreService.searchSimilarDocuments(any(), anyInt()))
.thenReturn(createMockChunks());
String answer = ragService.answerQuestion("什么是Spring AI?");
assertNotNull(answer);
assertTrue(answer.contains("Spring"));
}
private List<DocumentChunk> createMockChunks() {
DocumentChunk chunk = new DocumentChunk();
chunk.setContent("Spring AI是Spring生态系统中的AI扩展框架...");
return Arrays.asList(chunk);
}
}
集成测试
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class RAGIntegrationTest {
@LocalServerPort
private int port;
@Test
void testCompleteWorkflow() {
// 上传文档
uploadTestDocument();
// 提问
String answer = askQuestion("测试问题");
assertNotNull(answer);
}
}
部署与监控
Docker部署配置
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/rag-system.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Prometheus监控配置
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
metrics:
export:
prometheus:
enabled: true
总结
本文详细介绍了如何使用Spring AI和RAG技术构建企业级智能文档问答系统。通过结合向量数据库、AI模型和Spring框架的优势,我们能够创建出高效、准确的文档问答解决方案。关键优势包括:
- 准确性提升:RAG技术显著减少了AI幻觉问题
- 可扩展性:基于Spring框架,易于扩展和维护
- 性能优化:通过缓存和批量处理提高系统性能
- 安全性:完善的输入验证和速率限制机制
这种架构不仅适用于文档问答,还可以扩展到客服系统、知识库管理等多个应用场景,为企业智能化转型提供强有力的技术支撑。
810

被折叠的 条评论
为什么被折叠?



