一、Spring AI 构建私有化语义内容检索
Spring AI
是 Spring
官方社区项目,旨在简化 Java AI
应用程序开发,让 Java
开发者像使用 Spring
开发普通应用一样开发 AI
应用。
上篇文章我们基于 Spring AI MCP
+ DeepSeek R1
快速实现了数据库 ChatBI
能力。在AIGC
领域,向量语义能力同样也有着十分重要的地位。因此本篇文章探索 Spring AI + bge-large-zh-v1.5 + Milvus
搭建一套私有化的内容语义检索能力方案。
其中 bge-large-zh-v1.5
本地私有化部署采用 vLLM
进行部署和优化。Spring AI
采用 1.0.0-SNAPSHOT
版本。
处理过程大致如下图所示:
实验前需要提前部署好 Milvus
向量库,如果不清楚可参考下面文章的介绍。
二、vLLM 部署 bge-large-zh-v1.5 模型
如果不了解 vLLM
,可参考下面这篇文章中的介绍和使用过程:
首先使用 modelscope
下载模型:
modelscope download --model="BAAI/bge-large-zh-v1.5" --local_dir bge-large-zh-v1.5
加载模型,启动服务:
vllm serve bge-large-zh-v1.5 \
--port 8070 \
--dtype auto \
--task embed \
--trust-remote-code \
--api-key token-abc123
使用 curl
测试服务接口是否正常:
curl http://localhost:8070/v1/embeddings \
-H "Content-Type: application/json" \
-H "Authorization: Bearer token-abc123" \
-d '{
"model": "bge-large-zh-v1.5",
"input": [
"你好,小毕超"
]
}'
三、Spring AI 集成 OpenAI 格式向量模型
Spring AI
关于集成 OpenAI
格式向量模型的文档介绍如下:
新建 SpringBoot
项目,在 pom
中修改如下依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<groupId>com.example</groupId>
<modelVersion>4.0.0</modelVersion>
<artifactId>embedding</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>embedding</name>
<description>embedding</description>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>3.3.0</spring-boot.version>
<spring-ai.version>1.0.0-SNAPSHOT</spring-ai.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<name>Central Portal Snapshots</name>
<id>central-portal-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.example.embedding.EmbeddingApplication</mainClass>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
修改 application.yml
添加 vLLM
服务的链接信息:
spring:
ai:
openai:
base-url: http://127.0.0.1:8070
api-key: token-abc123
embedding:
options:
model: bge-large-zh-v1.5
使用向量能力:
@SpringBootTest
class EmbeddingApplicationTests {
@Resource
private EmbeddingModel embeddingModel;
@Test
void embed() {
EmbeddingResponse embeddingResponse = embeddingModel.call(
new EmbeddingRequest(List.of("你好,小毕超", "你叫什么名字"),
OpenAiEmbeddingOptions.builder().build()));
embeddingResponse.getResults().forEach(e -> {
System.out.println("向量维度:" + e.getOutput().length);
for (float f : e.getOutput()) System.out.print(f);
System.out.println();
});
}
}
运行后可以看到向量模型的维度是 1024
维,已经向量化后的信息。
四、Spring AI 集成 Milvus 向量数据库实现持久化和语义检索能力
Spring AI
关于 Milvus
的文档如下:
在 pom
中增加 Milvus
依赖:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-milvus</artifactId>
</dependency>
application.yml
中添加 Milvus
的链接信息:
spring:
ai:
openai:
base-url: http://127.0.0.1:8070
api-key: token-abc123
embedding:
options:
model: bge-large-zh-v1.5
vectorstore:
milvus:
client:
host: 127.0.0.1
port: 19530
username: "root"
password: "milvus"
databaseName: "default"
collectionName: "vector_test"
embeddingDimension: 1024
indexType: IVF_FLAT
metricType: COSINE
initialize-schema: true
3.1 内容持久化至向量库中
@SpringBootTest
class MilvusTests {
@Resource
VectorStore vectorStore;
@Test
void toMinvus() {
List<Document> documents = List.of(
new Document("我的爱好是打篮球", Map.of("name", "张三", "age", 18)),
new Document("我的爱好的是学习!", Map.of("name", "李四", "age", 30)),
new Document("今天的天气是多云", Map.of("name", "王五", "age", 50)),
new Document("我的心情非常愉悦", Map.of("name", "赵六", "age", 25)),
new Document("我叫小毕超", Map.of("name", "小毕超", "age", 28))
);
vectorStore.add(documents);
}
}
运行后,可在 Milvus
中看到自动创建的 collection
:
3.2 语义内容检索
@SpringBootTest
class MilvusTests {
@Resource
VectorStore vectorStore;
@Test
void similarity() {
List<Document> results = vectorStore.similaritySearch(
SearchRequest.builder()
.query("你叫什么名字")
.topK(3)
.similarityThreshold(0.2)
.build()
);
Optional.ofNullable(results).ifPresent(res->{
res.forEach(d -> System.out.println(d.getText()+" "+d.getMetadata()));
});
}
}
根据 meta
进行过滤检索,过滤表达可以使用文本表达式方式:
@Test
void similarity2() {
List<Document> results = vectorStore.similaritySearch(
SearchRequest.builder()
.query("你叫什么名字")
.topK(5)
.similarityThreshold(0.2)
.filterExpression("age > 15 && age < 30")
.build()
);
Optional.ofNullable(results).ifPresent(res->{
res.forEach(d -> System.out.println(d.getText()+" "+d.getMetadata()));
});
}
其中表达式也可使用 api
的方式:
@Test
void similarity3() {
FilterExpressionBuilder filter = new FilterExpressionBuilder();
List<Document> results = vectorStore.similaritySearch(
SearchRequest.builder()
.query("你叫什么名字")
.topK(5)
.similarityThreshold(0.2)
.filterExpression(
filter.and(filter.gt("age", 15), filter.lt("age", 30)).build()
).build()
);
Optional.ofNullable(results).ifPresent(res->{
res.forEach(d -> System.out.println(d.getText()+" "+d.getMetadata()));
});
}
可以得到同样的结果: