Spring AI 携手 Milvus 构建 RAG 智能问答框架:从环境搭建到实战部署
Spring AI 携手 Milvus 构建 RAG 智能问答系统:从环境搭建到实战部署
一、技术选型与核心价值:为何选择 Spring AI + Milvus
RAG(检索增强生成)技术通过 “检索相关文档 + 大模型生成” 的模式,解决了大模型知识时效性差、幻觉输出等问题。在这一架构中,Spring AI 与 Milvus 的组合展现出独特优势:
- Spring AI:作为 Spring 生态的 AI 集成框架,提供统一的向量嵌入、大模型调用接口,简化了与主流 AI 模型(如 OpenAI、通义千问)的对接,同时与 Spring Boot 无缝融合,降低企业级应用开发门槛。
- Milvus:开源向量数据库,专为海量向量检索设计,支持毫秒级查询亿级向量数据,其分布式架构和丰富的索引类型(如 IVF_FLAT、HNSW),完美适配 RAG 场景中 “文档向量快速匹配” 的核心需求。
两者结合实现了 “数据接入 - 向量存储 - 检索增强 - 生成回答” 的全流程闭环,让开发者无需深入 AI 底层技术,即可快速构建生产级智能问答系统。
二、环境搭建:基础组件与依赖配置
1. 核心依赖引入
在 Spring Boot 项目的pom.xml中添加以下依赖:
<!-- Spring AI 核心依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.8.0</version>
</dependency>
<!-- Milvus 客户端 -->
<dependency>
<groupId>io.milvus</groupId>
<artifactId>milvus-sdk-java</artifactId>
<version>2.3.4</version>
</dependency>
<!-- 文档处理工具 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-document-reader-tika</artifactId>
<version>0.8.0</version>
</dependency>
2. 配置文件设置
在application.yml中配置大模型、Milvus 连接信息:
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
embedding:
model: text-embedding-ada-002
chat:
model: gpt-3.5-turbo
milvus:
host: localhost
port: 19530
collection-name: rag_documents
dimension: 1536 # 与embedding模型维度一致(ada-002为1536)
三、核心实现步骤:从文档处理到智能问答
1. 文档加载与向量嵌入
首先需将原始文档(如 PDF、TXT)转换为向量并存储。Spring AI 的EmbeddingClient可简化向量生成过程:
@Service
public class DocumentService {
private final EmbeddingClient embeddingClient;
private final MilvusServiceClient milvusClient;
private final String collectionName;
// 构造函数注入依赖(省略)
// 处理文档并存储向量
public void processAndStoreDocument(MultipartFile file) throws IOException {
// 1. 加载文档(使用Tika解析多种格式)
TikaDocumentReader reader = new TikaDocumentReader(file.getInputStream());
List<Document> documents = reader.get();
// 2. 文档分块(避免超过模型上下文限制)
List<Document> chunks = new RecursiveCharacterTextSplitter(
500, // 块大小
100 // 重叠字符数
).split(documents);
// 3. 生成向量并存储
for (Document chunk : chunks) {
// 生成向量
List<Double> embedding = embeddingClient.embed(chunk.getContent());
// 转换为Milvus兼容的Float数组
float[] vector = embedding.stream()
.mapToFloat(Double::floatValue)
.toArray();
// 存储到Milvus(具体实现见下文)
saveToMilvus(chunk.getContent(), vector);
}
}
}
2. Milvus 向量存储实现
需创建集合、定义字段并实现向量插入。Milvus 的 Java SDK 提供了完整的操作接口:
private void saveToMilvus(String content, float[] vector) {
// 1. 检查集合是否存在,不存在则创建
if (!collectionExists()) {
createCollection();
}
// 2. 构造插入数据
List<Long> ids = Collections.singletonList(System.currentTimeMillis());
List<String> contents = Collections.singletonList(content);
List<float[]> vectors = Collections.singletonList(vector);
// 3. 插入数据
InsertParam insertParam = InsertParam.newBuilder()
.withCollectionName(collectionName)
.addField("id", ids)
.addField("content", contents)
.addField("vector", vectors)
.build();
milvusClient.insert(insertParam);
// 刷新集合使数据可见
milvusClient.flush(FlushParam.newBuilder()
.addCollectionName(collectionName)
.build());
}
// 创建集合(字段:id、content、vector)
private void createCollection() {
FieldType idField = FieldType.newBuilder()
.withName("id")
.withDataType(DataType.Int64)
.withPrimaryKey(true)
.withAutoID(false)
.build();
FieldType contentField = FieldType.newBuilder()
.withName("content")
.withDataType(DataType.VarChar)
.withMaxLength(2000)
.build();
FieldType vectorField = FieldType.newBuilder()
.withName("vector")
.withDataType(DataType.FloatVector)
.withDimension(dimension)
.build();
milvusClient.createCollection(CreateCollectionParam.newBuilder()
.withCollectionName(collectionName)
.addFieldType(idField)
.addFieldType(contentField)
.addFieldType(vectorField)
.withShardsNum(2)
.build());
// 创建向量索引(提升查询性能)

milvusClient.createIndex(CreateIndexParam.newBuilder()
.withCollectionName(collectionName)
.withFieldName("vector")
.withIndexType(IndexType.IVF_FLAT)
.withMetricType(MetricType.L2)
.withExtraParam("{\"nlist\": 1024}")
.build());
}
3. 检索增强生成(RAG)问答实现
核心逻辑是:用户提问→生成问题向量→检索相似文档→拼接上下文→调用大模型生成回答:
@Service
public class QAService {
private final EmbeddingClient embeddingClient;
private final MilvusServiceClient milvusClient;
private final ChatClient chatClient;
private final String collectionName;
// 构造函数注入依赖(省略)
public String answer(String question) {
// 1. 生成问题向量
List<Double> questionEmbedding = embeddingClient.embed(question);
float[] queryVector = questionEmbedding.stream()
.mapToFloat(Double::floatValue)
.toArray();
// 2. 检索相似文档(topK=3)
List<String> contexts = searchSimilarDocuments(queryVector, 3);
// 3. 构建提示词(上下文+问题)
String prompt = String.format("""
基于以下信息回答问题,不要编造内容:
%s
问题:%s
""", String.join("\n", contexts), question);
// 4. 调用大模型生成回答
return chatClient.call(
new Prompt(new UserMessage(prompt))
).getResult().getOutput().getContent();
}
// 检索相似文档
private List<String> searchSimilarDocuments(float[] queryVector, int topK) {
SearchParam searchParam = SearchParam.newBuilder()
.withCollectionName(collectionName)
.withMetricType(MetricType.L2)
.withVectorFieldName("vector")
.withVectors(List.of(queryVector))
.withLimit(topK)
.withOutputFields(List.of("content")) // 只返回内容字段
.build();
SearchResults results = milvusClient.search(searchParam);
// 解析结果并返回文档内容
return results.getResults().getScoredDocuments().stream()
.map(doc -> doc.getFields().get("content").toString())
.collect(Collectors.toList());
}
}
4. 接口层实现
通过 Controller 暴露 HTTP 接口,便于前端调用:
@RestController
@RequestMapping("/api/qa")
public class QAController {
private final DocumentService documentService;
private final QAService qaService;
@PostMapping("/upload")
public ResponseEntity<String> uploadDocument(@RequestParam("file") MultipartFile file) {
try {
documentService.processAndStoreDocument(file);
return ResponseEntity.ok("文档处理完成");
} catch (Exception e) {
return ResponseEntity.status(500).body("处理失败:" + e.getMessage());
}
}
@GetMapping("/answer")
public ResponseEntity<String> getAnswer(@RequestParam String question) {
return ResponseEntity.ok(qaService.answer(question));
}
}
四、优化策略与性能调优
- 文档分块优化:根据文档类型调整块大小(如技术文档用 800 字符,普通文本用 500 字符),平衡检索精度与召回率。
- Milvus 索引优化:百万级数据量推荐 HNSW 索引(查询速度快),亿级数据可结合 IVF_FLAT 与分区策略。
- 向量缓存:对高频查询的文档向量进行本地缓存(如使用 Caffeine),减少 Milvus 访问压力。
- 大模型参数调优:通过temperature=0.3降低随机性,maxTokens=1000控制回答长度。
- 异步处理:文档上传和向量生成采用异步任务(@Async),避免前端超时。
五、部署与扩展建议
- Milvus 部署:生产环境建议使用 Milvus 集群(至少 3 节点),配置对象存储(如 MinIO)持久化数据。
- 监控告警:集成 Prometheus 监控 Milvus 的 QPS、延迟,以及 Spring 应用的内存使用情况。
- 多源数据接入:扩展DocumentService支持数据库、API 接口等多种数据源,实现知识自动更新。
- 权限控制:通过 Spring Security 对文档上传和问答接口进行权限管理,确保数据安全。
六、总结
Spring AI 与 Milvus 的组合为 RAG 智能问答系统提供了高效的实现路径:Spring AI 简化了 AI 模型集成与流程编排,Milvus 则解决了向量高效存储与检索的难题。通过本文的实战步骤,开发者可在 1-2 天内搭建起基础系统,再结合业务场景进行优化,即可满足企业级知识问答、智能客服等需求。
随着大模型技术的发展,RAG 架构将成为连接私有数据与通用 AI 能力的核心范式,而 Spring AI 与 Milvus 的技术栈,无疑是这一领域值得投入的实用方案。

浙公网安备 33010602011771号