12. LangChain4j + 向量数据库操作详细说明
@[toc]
LangChain4j 向量化 3 件套:
- Embedding Model模型简介:
docs.langchain4j.dev/tutorials/r…
嵌入(Embedding) 的工作原理是将文本,图像和视频转换为称为向量(Vectors) 的浮点数数组。
- Embedding Store存储简介
docs.langchain4j.dev/tutorials/r…
向量存储(VectorStore) 是一种用于存储和检索高维向量数据的数据库或存储解决方案。
在 VectorStrore 中,查询与传统关系数据库不同。它们执行相似性搜索,而不是精确匹配。
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 当中获取。
<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 。
docker run -p 6333:6333 -p 6334:6334 qdrant/qdrant打开浏览器访问:http://localhost:6333/
打开浏览器访问:http://localhost:6333/dashboard#/collections
可以打开上述两个页面就说明 Qdant 向量数据库安装成功了。
- 设置 applcation.yaml / properties 配置文件,其中指明我们的输出响应的编码格式,因为如果不指定的话,存在返回的中文,就是乱码了。
server.port=9011spring.application.name=langchain4j-11chat-embedding# 设置响应的字符编码,避免流式返回输出乱码server.servlet.encoding.charset=utf-8server.servlet.encoding.enabled=trueserver.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) 三件套的大模型配置类。同时也需要配置,我们的向量数据库,让向量数据库和向量大模型(嵌入式大模型)绑定,进行写入到向量数据库当中
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;/** */@Configurationpublic 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 ,注意:我们先将数据通过向量大模型将文本信息写入到向量数据库,在查询操作向量数据库当中的信息、
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@Slf4jpublic 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); }}- 往向量数据库当中写入向量数据:
注意:我们需要先将(文本,图像,视频)数据通过向量大模型,转换为向量信息,才能写入到向量数据当中。
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@Slf4jpublic 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的数据内容
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@Slf4jpublic 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()); }}最后:
“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”
