12. LangChain4j + 向量数据库操作详细说明
@[toc]





LangChain4j 向量化 3 件套:

- Embedding Model模型简介:
docs.langchain4j.dev/tutorials/r...


嵌入(Embedding) 的工作原理是将文本,图像和视频转换为称为向量(Vectors) 的浮点数数组。
- Embedding Store存储简介
docs.langchain4j.dev/tutorials/r...



向量存储(VectorStore) 是一种用于存储和检索高维向量数据的数据库或存储解决方案。
在 VectorStrore 中,查询与传统关系数据库不同。它们执行相似性搜索,而不是精确匹配。
sql
mysql select * from book where id = 1
- EmbeddingSearchRequest查询
docs.langchain4j.dev/tutorials/r...
小总结:
嵌入模型是一种机器学习模型,旨在在连续的低维向量空间中表示数据(例如文本、图像或其他形式的信息)。
这些嵌入可以捕获数据之间的语义或上下文相似性,使机器能够更有效地执行比较、聚类或分类等任务。
假设你想描述不同的水果。你不用长篇大论,而是用数字来描述甜度、大小和颜色等特征。例如,苹果可能是[8,5,7],而香蕉是[9,7,4]。这些数字使比较或对相似的水果进行分组变得更容易。
向量数据库能做什么:
将文本,图像和视频转换为称为向量(Vectors) 的浮点数数组在 VectroStore 中,查询与传统关系数据库不同。它们执行相似性搜索,而不是精确匹配。当给定一个向量作为查询时,VectorStore 返回与查询向量"相似"的向量。
特点:
- 捕捉复杂的词汇关系(如语义相似性,同义词,同义词)
- 超越传统词袋模型的简单计数方式
- 动态嵌入模式(如 BERT)可根据上下文生成不同的词向量
- 向量嵌入为现代搜索和检索增强生成(RAG) 应用程序提供支持
总结:
将文本映射到高维空间中的点,使语义相似的文本在这个空间中距离较近。
例如:"肯德基" 和 "麦当劳"的向量可能会比"肯德基"和"新疆大盘鸡"的向量更接近
LangChain4j 支持的向量数据库
docs.langchain4j.dev/integration...



docs.langchain4j.info/integration...

LangChain4j + 向量数据库实操------Qdrant
- 创建对应项目的 module 模块内容:
- 导入相关的 pom.xml 的依赖,这里我们采用流式输出的方式,导入 整合 Spring Boot ,langchain4j-open-ai-spring-boot-starter,langchain4j-spring-boot-starter ,同时我们加入我们的 qdrant 向量数据库 jak 依赖。这里我们不指定版本,而是通过继承的 pom.xml 当中获取。
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
</dependency>
<!--qdrant-->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-qdrant</artifactId>
<version>1.2.0-beta8</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
Docker 容器当中安装 Qdrant 向量数据库
关于 Qdarant 学习内容,参考如下官网:
Qdrant是一个高性能的向量数据库,用于存储嵌入并进行快速的向量搜索其它。

这里我们使用 Docker 安装 Qdrant 。
shell
docker run -p 6333:6333 -p 6334:6334 qdrant/qdrant



打开浏览器访问:http://localhost:6333/

打开浏览器访问: http://localhost:6333/dashboard#/collections

可以打开上述两个页面就说明 Qdant 向量数据库安装成功了。
- 设置 applcation.yaml / properties 配置文件,其中指明我们的输出响应的编码格式,因为如果不指定的话,存在返回的中文,就是乱码了。
properties
server.port=9011
spring.application.name=langchain4j-11chat-embedding
# 设置响应的字符编码,避免流式返回输出乱码
server.servlet.encoding.charset=utf-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
# https://docs.langchain4j.dev/tutorials/spring-boot-integration
#langchain4j.open-ai.chat-model.api-key=${aliQwen-api}
#langchain4j.open-ai.chat-model.model-name=qwen-plus
#langchain4j.open-ai.chat-model.base-url=https://dashscope.aliyuncs.com/compatible-mode/v1
# 大模型调用不可以明文配置,你如何解决该问题
# 1 yml: ${aliQwen-api},从环境变量读取
# 2 config配置类: System.getenv("aliQwen-api")从环境变量读取
使用向量数据库实操
- 选取我们合适的向量大模型,注意:向量数据库是一个将文本,图像和视频转换为称为向量(Vectors) 的浮点数数组在 VectroStore 中的数据库,而我们需要将我们的文本,图像,视频等信息转换为向量数据库可以存储是向量数据,就需要借助使用我们的向量大模型(也被称之为嵌入大模型),这里我们选择大阿里云百炼的向量大模型。

- 编写大模型三件套(大模型 key,大模型 name,大模型 url) 三件套的大模型配置类。同时也需要配置,我们的向量数据库,让向量数据库和向量大模型(嵌入式大模型)绑定,进行写入到向量数据库当中
java
package com.rainbowsea.langchain4jchatembedding.config;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiEmbeddingModel;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.qdrant.QdrantEmbeddingStore;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
*/
@Configuration
public class LLMConfig
{
@Bean
public EmbeddingModel embeddingModel()
{
return OpenAiEmbeddingModel.builder()
.apiKey(System.getenv("aliQwen_api"))
.modelName("text-embedding-v3")
.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
.build();
}
/**
* 创建Qdrant客户端
* @return
*/
@Bean
public QdrantClient qdrantClient() {
QdrantGrpcClient.Builder grpcClientBuilder =
QdrantGrpcClient.newBuilder("127.0.0.1", 6334, false);
return new QdrantClient(grpcClientBuilder.build());
}
@Bean
public EmbeddingStore<TextSegment> embeddingStore() {
return QdrantEmbeddingStore.builder()
.host("127.0.0.1")
.port(6334)
.collectionName("test-qdrant")
.build();
}
}
- 编写对外访问的 ctroller ,注意:我们先将数据通过向量大模型将文本信息写入到向量数据库,在查询操作向量数据库当中的信息、

java
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.EmbeddingSearchRequest;
import dev.langchain4j.store.embedding.EmbeddingSearchResult;
import dev.langchain4j.store.embedding.EmbeddingStore;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.grpc.Collections;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import static dev.langchain4j.store.embedding.filter.MetadataFilterBuilder.metadataKey;
/**
* @Description: 知识出处,https://docs.langchain4j.dev/tutorials/rag#embedding-store
*/
@RestController
@Slf4j
public class EmbeddinglController
{
@Resource
private EmbeddingModel embeddingModel; // 文本向量化模型
@Resource
private QdrantClient qdrantClient; // 向量数据库访问的连接客户端
@Resource
private EmbeddingStore<TextSegment> embeddingStore; // 对向量数据库CRUD 的操作类
/**
* 文本向量化测试,看看形成向量后的文本,
* http://localhost:9011/embedding/embed
* @return
*/
@GetMapping(value = "/embedding/embed")
public String embed()
{
String prompt = """
咏鸡
鸡鸣破晓光,
红冠映朝阳。
金羽披霞彩,
昂首步高岗。
""";
// 向量大模型将(文本,图像,视频)信息,转换为向量信息
Response<Embedding> embeddingResponse = embeddingModel.embed(prompt);
System.out.println(embeddingResponse);
return embeddingResponse.content().toString();
}
/**
* 新建向量数据库实例和创建索引:test-qdrant
* 类似mysql create database test-qdrant
* http://localhost:9011/embedding/createCollection
*/
@GetMapping(value = "/embedding/createCollection")
public void createCollection()
{
// 创建向量数据库实例和创建索引:test-qdrant
var vectorParams = Collections.VectorParams.newBuilder()
.setDistance(Collections.Distance.Cosine)
.setSize(1024)
.build();
qdrantClient.createCollectionAsync("test-qdrant", vectorParams);
}
}


- 往向量数据库当中写入向量数据:
注意:我们需要先将(文本,图像,视频)数据通过向量大模型,转换为向量信息,才能写入到向量数据当中。

java
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.EmbeddingSearchRequest;
import dev.langchain4j.store.embedding.EmbeddingSearchResult;
import dev.langchain4j.store.embedding.EmbeddingStore;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.grpc.Collections;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import static dev.langchain4j.store.embedding.filter.MetadataFilterBuilder.metadataKey;
/**
* @Description: 知识出处,https://docs.langchain4j.dev/tutorials/rag#embedding-store
*/
@RestController
@Slf4j
public class EmbeddinglController
{
@Resource
private EmbeddingModel embeddingModel; // 文本向量化模型
@Resource
private QdrantClient qdrantClient; // 向量数据库访问的连接客户端
@Resource
private EmbeddingStore<TextSegment> embeddingStore; // 对向量数据库CRUD 的操作类
/**
* 新建向量数据库实例和创建索引:test-qdrant
* 类似mysql create database test-qdrant
* http://localhost:9011/embedding/createCollection
*/
@GetMapping(value = "/embedding/createCollection")
public void createCollection()
{
var vectorParams = Collections.VectorParams.newBuilder()
.setDistance(Collections.Distance.Cosine)
.setSize(1024)
.build();
qdrantClient.createCollectionAsync("test-qdrant", vectorParams);
}
/*
往向量数据库新增文本记录
*/
@GetMapping(value = "/embedding/add")
public String add()
{
String prompt = """
咏鸡
鸡鸣破晓光,
红冠映朝阳。
金羽披霞彩,
昂首步高岗。
""";
// 为我们的信息添加上 作者
TextSegment segment1 = TextSegment.from(prompt);
// 为我们的信息添加上 作者,便于向量化,相似匹配更接近
segment1.metadata().put("author", "zzyy");
Embedding embedding1 = embeddingModel.embed(segment1).content();
// 向量大模型转换好的信息,写入到向量数据库当中
String result = embeddingStore.add(embedding1, segment1);
System.out.println(result);
return result;
}
}

查询向量数据库的内容(比较相似度,不是精确查找)
注意:查询比较向量数据库当中的内容,也是要将我们查找的"关键词、内容",通过向量大模型向量化,后进行去向量数据库查询比较相似度才行
同时注意:我们向量数据库查询到的数据 还是向量化的, 我们需要通过 :searchResutl.matches().get(0) .embedded().text() 返回回来向量化之前的 样子。注意这里,get(0) 表示获取 第一个,下标为0的数据内容


java
package com.rainbowsea.langchain4jchatembedding.controller;
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.EmbeddingSearchRequest;
import dev.langchain4j.store.embedding.EmbeddingSearchResult;
import dev.langchain4j.store.embedding.EmbeddingStore;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.grpc.Collections;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import static dev.langchain4j.store.embedding.filter.MetadataFilterBuilder.metadataKey;
/**
* @Description: 知识出处,https://docs.langchain4j.dev/tutorials/rag#embedding-store
*/
@RestController
@Slf4j
public class EmbeddinglController
{
@Resource
private EmbeddingModel embeddingModel; // 文本向量化模型
@Resource
private QdrantClient qdrantClient; // 向量数据库访问的连接客户端
@Resource
private EmbeddingStore<TextSegment> embeddingStore; // 对向量数据库CRUD 的操作类
@GetMapping(value = "/embedding/query1")
public void query1(){
// 注意:查询比较向量数据库当中的内容,也是要将我们查找的"关键词、内容",通过向量大模型向量化,后进行去向量数据库查询比较相似度才行
Embedding queryEmbedding = embeddingModel.embed("咏鸡说的是什么").content();
EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
.queryEmbedding(queryEmbedding)
.maxResults(1)
.build();
EmbeddingSearchResult<TextSegment> searchResult = embeddingStore.search(embeddingSearchRequest);
System.out.println(searchResult.matches().get(0).embedded().text());
}
@GetMapping(value = "/embedding/query2")
public void query2(){
// 注意:查询比较向量数据库当中的内容,也是要将我们查找的"关键词、内容",通过向量大模型向量化,后进行去向量数据库查询比较相似度才行
Embedding queryEmbedding = embeddingModel.embed("咏鸡").content();
EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
.queryEmbedding(queryEmbedding)
.filter(metadataKey("author").isEqualTo("zzyy2"))
.maxResults(1)
.build();
EmbeddingSearchResult<TextSegment> searchResult = embeddingStore.search(embeddingSearchRequest);
System.out.println(searchResult.matches().get(0).embedded().text());
}
}

最后:
"在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。"