本篇文章仅介绍一下 LangChain4j 支持的向量数据库,并演示如何安装Milvus向量数据库,并使用LangChain4j 框架实现对接 Milvus向量数据库完成向量存储以及相似性搜索功能的演示。
LangChain4j支持的向量数据库
向量数据库 | 支持元数据存储 | 支持元数据过滤 | 删除嵌入数据 | 用途 |
---|---|---|---|---|
In-memory | ✅ | ✅ | ✅ | 一般用于测试 |
Elasticsearch | ✅ | ✅ | ✅ | |
Milvus | ✅ | ✅ | ✅ | |
PGVector | ✅ | ✅ | ✅ | |
Azure AI Search | ✅ | ✅ | ||
Weaviate | ✅ | ✅ | ||
Astra DB | ✅ | |||
Cassandra | ✅ | |||
Chroma | ✅ | |||
Infinispan | ✅ | |||
MongoDB Atlas | ✅ | |||
OpenSearch | ✅ | |||
Qdrant | ✅ | |||
Redis | ✅ | |||
Vearch | ✅ | |||
Azure CosmosDB Mongo vCore | ✅ | |||
Azure CosmosDB NoSQL | ✅ | |||
Vespa | ||||
Neo4j | ||||
Pinecone |
LangChain4j 框架支持15+向量数据库的集成,从表格看,仅对向量数据库是否支持元数据,是否支持元数据过滤以及是否支持删除能力三个方面对比。
安装 Milvus 数据库
Milvus 创建于 2019 年,其目标只有一个:存储、索引和管理由深度神经网络和其他机器学习 (ML) 模型生成的海量嵌入向量。
Docker 上安装
wget https://github.com/milvus-io/milvus/releases/download/v2.4.5/milvus-standalone-docker-compose.yml -O docker-compose.yml
sudo docker compose up -d
当出现如下,就说明安装Ok。
具体其它安装方式请参考官方网站。支持多种方式安装!!!
安装 attu 「 The GUI for Milvus 」
docker run -p 8000:3000 -e MILVUS_URL=127.0.0.1:19530 zilliz/attu:v2.4
访问:http://127.0.0.1:8000 但是当连接到数据库时,会报如下错误;
原因在于:Milvus使用docker-compose安装,指定了网络,而且attu单独启动,导致网络不通。我们将attu安装加入到docker-compose.yml文件中,该文件可以在示例项目中找到。
安装客户端:github.com/zilliztech/…
代码实战
依赖包
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-ollama-spring-boot-starter</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-milvus-spring-boot-starter</artifactId>
<version>${langchain4j.version}</version>
</dependency>
yml 配置
spring:
application:
name: vector-store
langchain4j:
ollama:
embedding-model:
base-url: http://localhost:11434
model-name: qwen:7b
milvus:
host: 127.0.0.1
port: 19530
database-name: milvus_embedding
server:
port: 8808
Service 实现
在Service中实现两种方法:1.仅存向量,2.向量 + 存储元数据和文本数据
推荐我们使用第二种方式,在相似性查询时需要,同时元数据也有一定的用处!!!
package org.ivy.vectorstore.store;
import dev.langchain4j.data.document.Metadata;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.store.embedding.milvus.MilvusEmbeddingStore;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class VectorStoreService {
private final EmbeddingModel embeddingModel;
private final MilvusEmbeddingStore milvusEmbeddingStore;
/**
* 仅存储了向量,没有原文本和元数据信息
*
* @param text 要嵌入的文本
* @return 生成ID
*/
public String embedding(String text) {
Response<Embedding> embed = embeddingModel.embed(text);
return milvusEmbeddingStore.add(embed.content());
}
/**
* 带有元数据的方式
*
* @param text 要嵌入的文本
* @return 生成ID
*/
public String embeddingWithMeta(String text) {
TextSegment textSegment = TextSegment.from(text, Metadata.from("userId", "1"));
Response<Embedding> embed = embeddingModel.embed(textSegment);
return milvusEmbeddingStore.add(embed.content(), textSegment);
}
}
Controller
package org.ivy.vectorstore.controller;
import lombok.RequiredArgsConstructor;
import org.ivy.vectorstore.store.VectorStoreService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class EmbeddingController {
private final VectorStoreService vectorStoreService;
@PostMapping("/embed")
public String embed(@RequestBody String text) {
return vectorStoreService.embedding(text);
}
@PostMapping("/embed-meta")
public String embedMeta(@RequestBody String text) {
return vectorStoreService.embeddingWithMeta(text);
}
}
相似性查询使用
查询方法
public interface EmbeddingStore<Embedded> {
default EmbeddingSearchResult<Embedded> search(EmbeddingSearchRequest request) {
List<EmbeddingMatch<Embedded>> matches =
findRelevant(request.queryEmbedding(), request.maxResults(), request.minScore());
return new EmbeddingSearchResult<>(matches);
}
}
@Deprecated
default List<EmbeddingMatch<Embedded>> findRelevant(Embedding referenceEmbedding, int maxResults, double minScore) {
EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
.queryEmbedding(referenceEmbedding)
.maxResults(maxResults)
.minScore(minScore)
.build();
EmbeddingSearchResult<Embedded> embeddingSearchResult = search(embeddingSearchRequest);
return embeddingSearchResult.matches();
}
仅使用这一个方法即可,像 findRelevant
方法都废弃,直接忘掉吧!一般在 xxxEmbeddingStore 都会重写 search
方法的。
元数据过滤
统一过滤接口,提供一个test
方法。默认提供 and、or、not 的Filter。
public interface Filter {
// 判断是否满足条件,true:满足/false:不满足
boolean test(Object object);
default Filter and(Filter filter) {
return and(this, filter);
}
static Filter and(Filter left, Filter right) {
return new And(left, right);
}
default Filter or(Filter filter) {
return or(this, filter);
}
static Filter or(Filter left, Filter right) {
return new Or(left, right);
}
static Filter not(Filter expression) {
return new Not(expression);
}
}
Filter实现接口
名称 | 功能 | 使用示例 |
---|---|---|
And | 同时满足 | 姓名为张三并且年龄大于20: Filter.and(new IsEqualTo("username","张三"), new IsGreaterThan("age",20)) |
Or | 满足其中之一 | 姓名为张三或者年龄大于20: Filter.or(new IsEqualTo("username","张三"), new IsGreaterThan("age",20)) |
Not | 不满足 | 姓名不为张三:Filter.not(new IsEqualTo("username","张三")) |
IsEqualTo | 等于 | 用户ID为1:new IsEqualTo("userId", "1") |
IsNotEqualTo | 不等于 | 用户ID不为1:new IsNotEqualTo("userId", "1") |
IsGreaterThan | 大于 | 年龄大于18:new IsGreaterThan("age", 18) |
IsGreaterThanOrEqualTo | 大于等于 | 年龄大于等于18:new IsGreaterThanOrEqualTo("age", 18) |
IsLessThan | 小于 | 年龄小于18:new IsLessThan("age", 18) |
IsLessThanOrEqualTo | 小于等于 | 年龄小于等于18:new IsLessThanOrEqualTo("age", 18) |
IsIn | 在..内 | 用户ID在1,2,3 之间的:new IsIn("userId", List.of("1", "2","3")) |
IsNotIn | 不在...内 | 用户ID不在1,2,3 之间的:new IsNotIn("userId", List.of("1","2","3")) |
创建Filter方式:
- 使用
new
的方式。如上表中的方式 - 使用
MetadataFilterBuilder
。MetadataFilterBuilder.metadataKey("userId").isEqualTo("1")
相似度评分设置
在 EmbeddingSearchRequest
请求类中设置 minScore的值,其取值范围在 0~1 之间。如果想更加精确,就将值设置的大一点。
示例代码与总结
介绍了LangChain4j框架支持的向量数据库,并使用LangChain4j + Omalla:qwen:7b 作为嵌入模型 + Milvus向量数据库,完成向量的存储,相似性搜索功能,特别对相似性搜索的元数据过滤进行说明。