|
| 1 | +## spring-ai-vector-milvus |
| 2 | + |
| 3 | +该工程模块主要是集成 Milvus 的向量存储功能,提供了一个使用 Milvus 存储向量并执行相似性搜索的简单示例。 |
| 4 | + |
| 5 | +### 1、Milvus 数据库部署与初始化 |
| 6 | + |
| 7 | +#### Milvus 安装 |
| 8 | + |
| 9 | +Milvus 是一个开源的向量数据库,用于存储和检索高维向量数据。本项目是使用 Docker 来运行 Milvus,当然你也可以选择其他方式安装 Milvus或者使用已经部署好的 Milvus 服务。 |
| 10 | + |
| 11 | +> PS: 如果你不运行 spring-ai-rag 模块和 spring-ai-embedding 模块,可以跳过此步骤。 |
| 12 | +
|
| 13 | +这个项目使用的 milvus 版本是 2.5.0 版本,安装方式见:[Install Milvus in Docker](https://milvus.io/docs/install_standalone-docker.md)。 |
| 14 | + |
| 15 | +> ⚠️本人的电脑是 Mac Air M2 芯片,使用官方文档中的 docker-compose 文件启动 Milvus 时,遇到 `milvus-standalone` 镜像不匹配问题。 |
| 16 | +
|
| 17 | +#### 创建 Collection(向量集合) |
| 18 | + |
| 19 | +> 注意:embedding 维度需与模型一致,否则会报错。 |
| 20 | +
|
| 21 | +* 创建 Collection 的 curl 示例 |
| 22 | +```bash |
| 23 | +curl -X POST "http://localhost:19530/v2/vectordb/collections/create" \ |
| 24 | + -H "Authorization: Bearer root:Milvus" \ |
| 25 | + -H "Content-Type: application/json" \ |
| 26 | + -d '{ |
| 27 | + "collectionName": "vector_store", |
| 28 | + "schema": { |
| 29 | + "fields": [ |
| 30 | + { "fieldName": "embedding", "dataType": "FloatVector", "elementTypeParams": { "dim": "2048" } }, |
| 31 | + { "fieldName": "content", "dataType": "VarChar", "elementTypeParams": { "max_length": 512000 } }, |
| 32 | + { "fieldName": "metadata", "dataType": "JSON" }, |
| 33 | + { "fieldName": "doc_id", "dataType": "VarChar", "isPrimary": true, "elementTypeParams": { "max_length": 512 } } |
| 34 | + ] |
| 35 | + }, |
| 36 | + "indexParams": [ |
| 37 | + { "fieldName": "embedding", "metricType": "COSINE", "indexName": "embedding_index", "indexType": "AUTOINDEX" }, |
| 38 | + { "fieldName": "doc_id", "indexName": "doc_id_index", "indexType": "AUTOINDEX" } |
| 39 | + ] |
| 40 | + }' |
| 41 | +``` |
| 42 | +* 加载集合(load collection) |
| 43 | + |
| 44 | +```bash |
| 45 | +curl -X POST "http://localhost:19530/v2/vectordb/collections/load" \ |
| 46 | + -H "Content-Type: application/json" \ |
| 47 | + -d '{ |
| 48 | + "collectionName": "vector_store" |
| 49 | + }' |
| 50 | +``` |
| 51 | + |
| 52 | +* 删除 Collection |
| 53 | + |
| 54 | +```bash |
| 55 | +POST /v2/vectordb/collections/drop HTTP/1.1 |
| 56 | +Authorization: Bearer root:Milvus |
| 57 | +Content-Length: 38 |
| 58 | +Content-Type: application/json |
| 59 | +Host: localhost:19530 |
| 60 | +User-Agent: HTTPie |
| 61 | +{ |
| 62 | + "collectionName": "vector_store" |
| 63 | +} |
| 64 | +``` |
| 65 | + |
| 66 | +### 2、引入依赖和配置 |
| 67 | + |
| 68 | +* 依赖 |
| 69 | + |
| 70 | +```xml |
| 71 | + <dependency> |
| 72 | + <groupId>org.springframework.ai</groupId> |
| 73 | + <artifactId>spring-ai-starter-vector-store-milvus</artifactId> |
| 74 | +</dependency> |
| 75 | +``` |
| 76 | + |
| 77 | +* 配置 |
| 78 | + |
| 79 | +```properties |
| 80 | +# embedding model |
| 81 | +# 这里替换成你自己的 api-key |
| 82 | +spring.ai.openai.api-key=${spring.ai.openai.api-key} |
| 83 | +spring.ai.openai.embedding.base-url=https://ark.cn-beijing.volces.com/api/v3 |
| 84 | +spring.ai.openai.embedding.embeddings-path=/embeddings |
| 85 | +spring.ai.openai.embedding.options.model=ep-20250506170049-dzjj7 |
| 86 | + |
| 87 | +spring.ai.vectorstore.milvus.client.host=localhost |
| 88 | +spring.ai.vectorstore.milvus.client.port=19530 |
| 89 | +#spring.ai.vectorstore.milvus.client.username=root |
| 90 | +#spring.ai.vectorstore.milvus.client.password=Milvus |
| 91 | +#spring.ai.vectorstore.milvus.databaseName="default" |
| 92 | +spring.ai.vectorstore.milvus.collection.name=vector_store |
| 93 | +``` |
| 94 | + |
| 95 | +#### 文档数据初始化与嵌入 |
| 96 | + |
| 97 | +按照示例,你可以将你需要存储的文件放在 `src/main/resources/files` 目录下,然后使用 `LangChainTextSplitter` 类来读取文件内容,切分为小块,并将其嵌入到 Milvus 向量库中。 |
| 98 | + |
| 99 | +核心代码如下: |
| 100 | + |
| 101 | +```java |
| 102 | +// LangChainTextSplitter.java |
| 103 | +/** |
| 104 | + * 读取本地 markdown 文档,切分为小块后写入 Milvus 向量库 |
| 105 | + */ |
| 106 | +public void embedding() { |
| 107 | + try { |
| 108 | + // 1. 创建文本切分器 |
| 109 | + TextSplitter splitter = new TokenTextSplitter(); |
| 110 | + // 2. 读取本地 markdown 文件内容 |
| 111 | + URL path = LangChainTextSplitter.class.getClassLoader().getResource("classpath:files/LLM-infer.md"); |
| 112 | + String mdContent = Files.readString(Paths.get(path.toURI()), StandardCharsets.UTF_8); |
| 113 | + // 3. 构造 Document 对象 |
| 114 | + Document doc = new Document(mdContent); |
| 115 | + // 4. 切分为小块 |
| 116 | + List<Document> docs = splitter.split(doc); |
| 117 | + // 5. 写入向量库 |
| 118 | + this.vectorStore.add(docs); |
| 119 | + } catch (Exception e) { |
| 120 | + // 异常信息 |
| 121 | + } |
| 122 | +} |
| 123 | +``` |
| 124 | + |
| 125 | +* controller 代码如下 |
| 126 | +```java |
| 127 | +// 初始化嵌入数据 |
| 128 | +/** |
| 129 | + * 触发文档嵌入,将本地文档内容写入向量库 |
| 130 | + */ |
| 131 | +@GetMapping("embedding_test") |
| 132 | +public String embedding() { |
| 133 | + langChainTextSplitter.embedding(); |
| 134 | + return "Embedding completed successfully."; |
| 135 | +} |
| 136 | + |
| 137 | +// RAG 聊天接口 |
| 138 | +/** |
| 139 | + * 基于用户输入,检索相关文档并拼接到系统提示词,实现 RAG 问答 |
| 140 | + * @param userInput 用户输入 |
| 141 | + * @return LLM 生成的答案 |
| 142 | + */ |
| 143 | +@GetMapping("/chat") |
| 144 | +public String prompt(@RequestParam String userInput) { |
| 145 | + // 1. 构造用户消息 |
| 146 | + Message userMessage = new UserMessage(userInput); |
| 147 | + // 2. 检索相似文档 |
| 148 | + List<Document> similarDocuments = vectorStore.similaritySearch(userInput); |
| 149 | + // 3. 拼接检索到的内容 |
| 150 | + String tncString = similarDocuments.stream().map(Document::getFormattedContent).collect(Collectors.joining("\n")); |
| 151 | + // 4. 构造系统提示词 |
| 152 | + Message systemMessage = new SystemPromptTemplate("You are a helpful assistant. Here are some relevant documents:\n\n {documents}") |
| 153 | + .createMessage(Map.of("documents", tncString)); |
| 154 | + // 5. 构造 Prompt |
| 155 | + Prompt prompt = new Prompt(List.of(systemMessage, userMessage)); |
| 156 | + // 6. 调用 LLM 生成答案 |
| 157 | + return chatClient.prompt(prompt).call().content(); |
| 158 | +} |
| 159 | +``` |
| 160 | + |
| 161 | +* ChatClient 配置(Advisor API 自动拼接) |
| 162 | +```java |
| 163 | +@Bean |
| 164 | +public ChatClient chatClient(OpenAiChatModel chatModel, VectorStore vectorStore) { |
| 165 | + return ChatClient.builder(chatModel) |
| 166 | + .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore)) |
| 167 | + .defaultSystem("You are a friendly chat bot that answers question with json always") |
| 168 | + .build(); |
| 169 | +} |
| 170 | +``` |
| 171 | + |
| 172 | +#### 效果验证 |
| 173 | + |
| 174 | +1. 启动 spring-ai-rag 服务(确保 Milvus 已启动并初始化好集合) |
| 175 | +2. 先访问 `/api/qwen/embedding_test` 完成文档嵌入 |
| 176 | +3. 再访问 `/api/qwen/chat?userInput=你的问题`,可检索并返回文档相关内容 |
0 commit comments