SpringAIalibaba +milvus本地化全链路知识库系统

本地化全链路知识库系统

结合 Spring AIAlibaba 本地 AI 模型 (如通义千问)和 Milvus 向量数据库 实现本地知识库,可构建一套数据本地化、检索高效、兼容 Spring 生态的方案。核心是利用 Milvus 存储向量,通义千问模型处理文本嵌入和问答,Spring AI 简化组件整合。以下是具体实现方案:

核心技术栈

环节 技术选型 说明
开发框架 Spring Boot 3.x + Spring AI 0.8.1+ 提供依赖注入、抽象接口(向量存储、嵌入等)
文档解析 Apache Tika + Alibaba EasyExcel 支持 PDF/Word/Excel/TXT 等格式解析
文本分割 Spring AI TextSplitter 按语义分割文本为 chunk(适配向量模型长度)
向量嵌入(Embedding) 通义千问 Embedding 本地模型 + DJL 阿里巴巴开源模型,本地生成文本向量
向量数据库 Milvus 2.3+(本地 Docker 部署) 高效向量检索,支持大规模向量存储
大语言模型(LLM) 通义千问 Qwen-7B(本地部署) + DJL 基于检索到的上下文生成回答

实现步骤

1. 环境准备

验证:Milvus 服务默认监听 localhost:19530

  • 模型下载

    • 通义千问 Embedding 模型(如 qwen-embedding-v1

    • 通义千问 LLM(如 qwen-7b-chat,建议量化版)

      下载地址:ModelScope(需遵守开源协议)。

2. 项目依赖配置(pom.xml)

核心依赖包括 Spring AI、Milvus SDK、DJL(模型运行)、文档解析工具:

复制代码
<!-- Spring Boot 核心 -->

<dependency>

   <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-starter-web</artifactId>

</dependency>

<!-- Spring AI 核心 -->

<dependency>

   <groupId>org.springframework.ai</groupId>

   <artifactId>spring-ai-core</artifactId>

   <version>0.8.1</version>

</dependency>

<!-- Milvus 向量数据库 SDK -->

<dependency>

   <groupId>io.milvus</groupId>

   <artifactId>milvus-sdk-java</artifactId>

   <version>2.3.4</version>

</dependency>

<!-- 文档解析 -->

<dependency>

   <groupId>org.apache.tika</groupId>

   <artifactId>tika-core</artifactId>

   <version>2.9.1</version>

</dependency>

<dependency>

   <groupId>com.alibaba</groupId>

   <artifactId>easyexcel</artifactId>

   <version>3.3.0</version> <!-- 处理Excel -->

</dependency>

<!-- 本地模型运行(DJL) -->

<dependency>

   <groupId>ai.djl</groupId>

   <artifactId>api</artifactId>

   <version>0.25.0</version>

</dependency>

<dependency>

   <groupId>ai.djl.pytorch</groupId>

   <artifactId>pytorch-native-auto</artifactId>

   <version>2.0.1</version>

   <scope>runtime</scope>

</dependency>
3. 核心组件实现
3.1 文档加载与分割

支持多格式文档解析,并分割为适合嵌入的文本块:

复制代码
import com.alibaba.excel.EasyExcel;

import org.apache.tika.Tika;

import org.springframework.ai.document.Document;

import org.springframework.ai.document.splitter.TextSplitter;

import org.springframework.stereotype.Component;

import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;

import java.util.List;

import java.util.stream.Collectors;

@Component

public class DocumentProcessor {

   private final Tika tika = new Tika();

   private final TextSplitter textSplitter = new TextSplitter(500, 50); // 500字符/块,重叠50字符

   public List<Document> process(MultipartFile file) throws Exception {

       String filename = file.getOriginalFilename();

       String content = "";

       // 按文件类型解析

       if (filename.endsWith(".xlsx") || filename.endsWith(".xls")) {

           // EasyExcel解析Excel

           InputStream is = file.getInputStream();

           List<ExcelData> dataList = EasyExcel.read(is).head(ExcelData.class).sheet().doReadSync();

           content = dataList.stream().map(ExcelData::getContent).collect(Collectors.joining("\n"));

       } else {

           // Tika解析其他格式(PDF/Word/TXT等)

           content = tika.parseToString(file.getInputStream());

       }

       // 分割为文档块

       return textSplitter.split(new Document(filename, content));

   }

   // Excel数据模型

   public static class ExcelData {

       private String content;

       // getter/setter

   }

}
3.2 通义千问 Embedding 客户端(实现 Spring AI 接口)

使用 DJL 加载本地 Embedding 模型,将文本转为向量:

复制代码
import ai.djl.inference.Predictor;

import ai.djl.modality.nlp.embedding.TextEmbedding;

import ai.djl.repository.zoo.Criteria;

import ai.djl.repository.zoo.ZooModel;

import org.springframework.ai.embedding.EmbeddingClient;

import org.springframework.ai.embedding.EmbeddingRequest;

import org.springframework.ai.embedding.EmbeddingResponse;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

import java.util.List;

import java.util.stream.Collectors;

@Component

public class QwenEmbeddingClient implements EmbeddingClient {

   @Value("\${qwen.embedding.model.path}")

   private String embeddingModelPath; // 本地模型路径(application.properties配置)

   private TextEmbedding embeddingModel;

   @PostConstruct

   public void init() throws Exception {

       // 加载通义千问Embedding模型

       Criteria<String, float\[]> criteria = Criteria.builder()

               .setTypes(String.class, float\[].class)

               .optModelUrls("file://" + embeddingModelPath)

               .build();

       ZooModel<String, float\[]> model = criteria.loadModel();

       this.embeddingModel = model.newPredictor();

   }

   @Override

   public EmbeddingResponse embed(EmbeddingRequest request) {

       // 生成向量

       List<float\[]> embeddings = request.getInputs().stream()

               .map(text -> embeddingModel.embedText(text))

               .collect(Collectors.toList());

       // 包装为Spring AI的响应格式

       return new EmbeddingResponse(

               embeddings.stream()

                       .map(emb -> new EmbeddingResponse.Embedding(emb))

                       .collect(Collectors.toList())

       );

   }

}
3.3 Milvus 向量存储(实现 Spring AI VectorStore 接口)

封装 Milvus 操作,实现向量的存储与检索:

复制代码
import io.milvus.client.MilvusClient;

import io.milvus.client.MilvusServiceClient;

import io.milvus.param.ConnectParam;

import io.milvus.param.collection.CreateCollectionParam;

import io.milvus.param.dml.InsertParam;

import io.milvus.param.dml.SearchParam;

import io.milvus.response.SearchResultsWrapper;

import org.springframework.ai.document.Document;

import org.springframework.ai.vectorstore.VectorStore;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

import java.util.ArrayList;

import java.util.List;

import java.util.UUID;

@Component

public class MilvusVectorStore implements VectorStore {

   @Value("\${milvus.host:localhost}")

   private String milvusHost;

   @Value("\${milvus.port:19530}")

   private int milvusPort;

   private final String collectionName = "local\_knowledge"; // 集合名(类似表)

   private final int vectorDimension = 768; // 通义千问Embedding向量维度

   private MilvusClient milvusClient;

   @PostConstruct

   public void init() {

       // 连接Milvus

       milvusClient = new MilvusServiceClient(

               ConnectParam.newBuilder()

                       .withHost(milvusHost)

                       .withPort(milvusPort)

                       .build()

       );

       // 创建集合(若不存在)

       milvusClient.createCollection(CreateCollectionParam.newBuilder()

               .withCollectionName(collectionName)

               .withDimension(vectorDimension)

               .build());

   }

   // 存储文档向量到Milvus

   @Override

   public void add(List<Document> documents) {

       for (Document doc : documents) {

           // 生成向量(通过QwenEmbeddingClient)

           float\[] vector = qwenEmbeddingClient.embed(doc.getContent()).getEmbeddings().get(0).getOutput();

           // 构造插入参数

           InsertParam insertParam = InsertParam.newBuilder()

                   .withCollectionName(collectionName)

                   .withFields(List.of(

                           UUID.randomUUID().toString(), // 文档ID

                           doc.getContent(), // 文本内容

                           vector // 向量

                   ))

                   .build();

           milvusClient.insert(insertParam);

       }

       // 插入后刷新集合(确保数据可查)

       milvusClient.flush(collectionName);

   }

   // 检索相似文档

   @Override

   public List<Document> similaritySearch(String query, int topK) {

       // 生成查询向量

       float\[] queryVector = qwenEmbeddingClient.embed(query).getEmbeddings().get(0).getOutput();

       // 构造检索参数(余弦相似度)

       SearchParam searchParam = SearchParam.newBuilder()

               .withCollectionName(collectionName)

               .withQueryVectors(List.of(queryVector))

               .withTopK(topK)

               .withMetricType(SearchParam.MetricType.COSINE) // 余弦相似度

               .build();

       // 执行检索

       SearchResultsWrapper results = new SearchResultsWrapper(

               milvusClient.search(searchParam).getData()

       );

       // 转换结果为Document列表

       List<Document> docs = new ArrayList<>();

       results.getFieldData("content", String.class).forEach(content -> {

           docs.add(new Document(UUID.randomUUID().toString(), content));

       });

       return docs;

   }

}
3.4 通义千问 LLM 问答服务

使用本地 Qwen-7B 模型生成回答:

复制代码
import ai.djl.modality.nlp.generate.TextGenerator;

import ai.djl.repository.zoo.ZooModel;

import org.springframework.ai.document.Document;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

import java.util.List;

@Service

public class QwenLlmService {

   @Value("\${qwen.llm.model.path}")

   private String llmModelPath; // 本地Qwen-7B模型路径

   private TextGenerator llmGenerator;

   @PostConstruct

   public void init() throws Exception {

       // 加载Qwen-7B模型

       ZooModel<String, String> model = Criteria.builder()

               .setTypes(String.class, String.class)

               .optModelUrls("file://" + llmModelPath)

               .build()

               .loadModel();

       this.llmGenerator = model.newPredictor();

   }

   // 结合上下文生成回答

   public String generateAnswer(String question, List<Document> contextDocs) {

       String context = contextDocs.stream()

               .map(Document::getContent)

               .collect(Collectors.joining("\n\n"));

       // 构建提示词

       String prompt = String.format("""

               基于以下上下文回答问题,仅使用上下文信息,不编造内容:

               上下文:%s

               问题:%s

               回答:

               """, context, question);

       return llmGenerator.generate(prompt);

   }

}
4. 知识库服务整合与接口

封装完整流程,提供文档上传和问答接口:

复制代码
import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.\*;

import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@RestController

@RequestMapping("/kb")

public class KnowledgeBaseController {

   private final DocumentProcessor documentProcessor;

   private final MilvusVectorStore vectorStore;

   private final QwenLlmService llmService;

   // 依赖注入

   public KnowledgeBaseController(DocumentProcessor documentProcessor,

                                  MilvusVectorStore vectorStore,

                                  QwenLlmService llmService) {

       this.documentProcessor = documentProcessor;

       this.vectorStore = vectorStore;

       this.llmService = llmService;

   }

   // 上传文档到知识库

   @PostMapping("/upload")

   public ResponseEntity<String> upload(@RequestParam("file") MultipartFile file) throws Exception {

       List<Document> docs = documentProcessor.process(file);

       vectorStore.add(docs);

       return ResponseEntity.ok("文档处理完成,已添加 " + docs.size() + " 个片段到知识库");

   }

   // 基于知识库问答

   @GetMapping("/query")

   public ResponseEntity<String> query(@RequestParam String question) {

       List<Document> contextDocs = vectorStore.similaritySearch(question, 3); // 取top3相似文档

       String answer = llmService.generateAnswer(question, contextDocs);

       return ResponseEntity.ok(answer);

   }

}
5. 配置文件(application.properties)
复制代码
# 通义千问模型路径

qwen.embedding.model.path=/path/to/qwen-embedding-v1

qwen.llm.model.path=/path/to/qwen-7b-chat-int4  # 建议使用INT4量化版节省资源

# Milvus配置

milvus.host=localhost

milvus.port=19530

关键注意事项

  1. 模型性能优化
  • Qwen-7B 需至少 8GB 显存(INT4 量化版),建议使用 GPU 加速(需配置 DJL 支持 CUDA)。

  • 文本分割长度需与 Embedding 模型最大输入长度匹配(通义千问建议 ≤512 tokens)。

  1. Milvus 索引优化

    大规模数据时,为 Milvus 集合创建索引(如 IVF_FLAT)提升检索速度:

    // 在MilvusVectorStore初始化时添加索引

    milvusClient.createIndex(CreateIndexParam.newBuilder()

    复制代码
        .withCollectionName(collectionName)
    
        .withIndexType(IndexType.IVF\_FLAT)
    
        .withMetricType(MetricType.COSINE)
    
        .withParamsInJson("{\\"nlist\\": 1024}")
    
        .build());
  2. 错误处理

    增加模型加载超时、Milvus 连接失败的重试机制,确保服务稳定性。

通过以上方案,实现了 本地化全链路 的知识库系统:文档解析、向量生成、存储、检索、问答均在本地完成,结合 Spring AI 的抽象接口和 Milvus 的高效向量检索,兼顾开发便捷性与性能。

相关推荐
长存祈月心5 小时前
Rust 迭代器适配器
java·服务器·前端
胡耀超5 小时前
AI应用开发入门,docker部署 Milvus + GPUStack (Attu+MinIO)的基础入门!
人工智能·docker·ai·大模型·milvus·rag·gpustack
rengang665 小时前
103-Spring AI Alibaba Milvus RAG 示例
人工智能·spring·milvus·rag·spring ai·ai应用编程
Fuly10245 小时前
使用Milvus作为向量数据库
数据库·milvus
沐雨橙风ιε5 小时前
防止表单重复提交功能简单实现
java·spring boot·ajax·axios·spring mvc
熙客5 小时前
后端日志框架
java·开发语言·log4j·logback
全栈师5 小时前
LigerUI下frm与grid的交互
java·前端·数据库
钱彬 (Qian Bin)5 小时前
项目实践6—全球证件智能识别系统(Qt客户端开发+FastAPI后端人工智能服务开发)
人工智能·qt·fastapi·证件识别
CodeCraft Studio5 小时前
前端表格工具AG Grid 34.3 发布:重磅引入AI工具包,全面支持 React 19.2!
前端·人工智能·react.js·angular·ag grid·前端表格工具·透视分析