Spring AI 集成 Milvus 实战:向量数据库从入门到 RAG 应用

📅 2026/7/14 17:04:11
Spring AI 集成 Milvus 实战:向量数据库从入门到 RAG 应用
一、先搞懂向量数据库是干什么的想象你去图书馆找书。传统数据库的工作方式像这样的对话你问有没有讲机器学习的书图书馆员只会找书名里带机器学习三个字的书。如果一本书叫《统计学习方法》即使内容全是机器学习他也找不到因为名字不一样。向量数据库就不一样了。它把人话转成一串数字叫向量然后比较这些数字的远近。机器学习和统计学习方法意思相近向量就离得近机器学习和红烧肉意思不相干向量就离得远。所以它能按意思搜索而不是按关键词。Milvus 就是目前最主流的开源向量数据库。Zilliz 公司开发2019 年开源Linux Foundation AI 基金会项目。十亿级数据几十毫秒就能找出最相似的内容。二、Spring AI 集成 Milvus 的整体流程Spring AI 对 Milvus 的支持走的是 VectorStore 接口——和 JDBC 一个思路统一接口切换底层实现不用改业务代码。整体链路是这样的1. Embedding 模型把文档转成向量2. Milvus 存这些向量Collection Index3. 用户提问时同样的 Embedding 模型把问题也转成向量4. Spring AI 调用 Milvus 做相似度搜索返回最相关的文档片段5. 把这些片段拼成 Prompt发给大模型得到最终回答三、实战搭一个 RAG 知识库3.1 引入依赖!-- Spring AI Milvus 向量存储 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-milvus-store/artifactId /dependency !-- Embedding 模型用通义千问 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-dashscope/artifactId /dependency3.2 配置 Milvus 连接spring: ai: dashscope: api-key: ${DASHSCOPE_API_KEY} chat: options: model: qwen3 vectorstore: milvus: uri: ${MILVUS_URI:http://localhost:19530} username: ${MILVUS_USERNAME:} password: ${MILVUS_PASSWORD:} database-name: default collection-name: knowledge_base index-type: HNSW metric-type: COSINE embedding-dimension: 768几个配置项的含义index-type索引类型。HNSW 精度高、速度快适合在线检索。如果是超大数据量图省内存可以用 IVF_FLAT。metric-type距离计算方式。文本搜索一般用 COSINE余弦相似度因为不受向量长度影响。embedding-dimension你用的 Embedding 模型输出的向量维度。bge-base-zh 是 768text-embedding-3 是 1536要和模型对齐。3.3 文档入库Spring AI 自动注入了 VectorStore Bean直接注入就能用Service public class KnowledgeService { private final VectorStore vectorStore; public KnowledgeService(VectorStore vectorStore) { this.vectorStore vectorStore; } /** * 把文档存进 Milvus * Spring AI 会自动调用 DashScope Embedding 模型把文本转成向量 */ public void addDocument(String content, String source) { Document doc new Document(content, Map.of(source, source, timestamp, Instant.now().toString())); vectorStore.add(List.of(doc)); } /** * 批量入库 */ Transactional public void batchAdd(ListString contents, String source) { ListDocument docs contents.stream() .map(c - new Document(c, Map.of(source, source))) .collect(Collectors.toList()); vectorStore.add(docs); } }3.4 语义搜索 RAG用户提一个问题先在 Milvus 里找到相关文档再送给大模型生成回答Service public class RagService { private final VectorStore vectorStore; private final ChatClient chatClient; public RagService(VectorStore vectorStore, ChatClient.Builder builder) { this.vectorStore vectorStore; this.chatClient builder.build(); } public String ask(String question) { // 1. 在 Milvus 中找到最相关的 3 个文档片段 ListDocument docs vectorStore .similaritySearch(SearchRequest.query(question) .withTopK(3)); // 2. 把文档片段拼成上下文 String context docs.stream() .map(d - 【 d.getMetadata().get(source) 】 d.getContent()) .collect(Collectors.joining(\n\n)); // 3. 发给大模型生成回答 return chatClient.prompt() .user(u - u.text( 基于以下知识回答问题。 如果知识不够直接说不知道。 知识库内容 {context} 问题{question} ) .param(context, context) .param(question, question)) .call() .content(); } }3.5 加一个 ControllerRestController RequestMapping(/knowledge) public class KnowledgeController { private final KnowledgeService knowledgeService; private final RagService ragService; PostMapping(/add) public Result add(RequestBody AddDocDTO dto) { knowledgeService.addDocument(dto.content(), dto.source()); return Result.success(); } PostMapping(/ask) public Result ask(RequestBody AskDTO dto) { String answer ragService.ask(dto.question()); return Result.success(answer); } }四、Milvus 部署与注意事项部署方式开发测试用 Milvus Standalone一个 docker-compose.yml 搞定# docker-compose.yml version: 3.5 services: etcd: image: quay.io/coreos/etcd:v3.5.16 environment: ETCD_AUTO_COMPACTION_MODE: revision ETCD_AUTO_COMPACTION_RETENTION: 1000 minio: image: minio/minio:RELEASE.2024-11-17T00-39-20Z standalone: image: milvusdb/milvus:v2.5.0 ports: - 19530:19530 # gRPC - 9091:9091 # HTTP depends_on: [etcd, minio]几个坑Embedding 维度要对齐配置文件里的 embedding-dimension 必须和实际使用的模型一致否则存进去搜不出来。索引要显式创建Spring AI 创建 Collection 时不会自动建索引需要先调 createIndex() 或等数据量够了再建。也可以在配置里把 index-type 配好Spring AI 会自动处理。内存规划向量数据在内存中检索最快。HNSW 索引的内存大约是数据量的 1.5-2 倍。如果是 10 亿条 768 维向量大约需要 800GB 内存——这时候就用 DiskANN 或者上 GPU。元数据过滤Milvus 支持标量字段过滤相当于 SQL 的 WHERE 条件对常用过滤字段建标量索引能大幅提升混合检索性能。五、进阶自定义 Embedding 模型如果你的场景需要特定的 Embedding 模型比如中文效果更好的 bge-large-zh-v1.5可以实现 EmbeddingModel 接口Configuration public class MilvusConfig { Bean public VectorStore milvusVectorStore(MilvusServiceClient milvusClient, EmbeddingModel embeddingModel) { return new MilvusVectorStore(milvusClient, embeddingModel, MilvusVectorStoreConfig.builder() .withCollectionName(knowledge_base) .withIndexType(IndexType.HNSW) .withMetricType(MetricType.COSINE) .build()); } }这样就能灵活切换不同的 Embedding 模型不受 starter 自动配置的限制。常见面试题与参考答案1. Milvus 为什么比 ES 更适合向量检索参考答案Elasticsearch 的向量检索插件如 dense_vector本质上还是暴力搜索或简单的 HNSW性能和可扩展性有限。Milvus 从底层就是为向量设计的支持多种 ANN 索引IVF、HNSW、DiskANN、支持 GPU 加速、有专门的调度和分片策略。在十亿级数据上Milvus 的检索速度比 ES 快 2-3 个数量级。但 ES 在全文搜索和日志分析场景仍然不可替代两个数据库通常是配合使用。2. Spring AI 的 VectorStore 接口怎么切换不同的向量数据库参考答案Spring AI 的 VectorStore 是顶层抽象接口定义了 add()、similaritySearch()、delete() 等方法。Milvus、Pinecone、Weaviate、Pgvector 等都有各自的实现类。切换时只需换依赖和配置业务代码不需要改动。比如从 Milvus 切到 Pgvector把 spring-ai-milvus-store 换成 spring-ai-pgvector-store修改 yml 中的连接配置代码里的 VectorStore 注入不用动。3. 向量检索的召回率Recall怎么保证参考答案向量检索本质是近似搜索ANN不是精确搜索所以召回率不可能 100%。影响召回率的关键因素索引类型HNSW 召回率高于 IVF、索引参数HNSW 的 efConstruction 和 ef、IVF 的 nprobe、向量质量Embedding 模型的好坏。提召回率的常用方法提高 ef/nprobe 参数但会降低速度、使用集成多索引投票、或者结合 BM25 关键词搜索做混合检索。