在数学中,向量(也称为欧几里得向量、几何向量),指具有大小(magnitude)和方向的量。二维平面中,一个向量表示xy坐标轴的坐标点 `A(1,2)`,在编程领域,一个二维向量对应的就是一个大小为二的float类型的数组。 ![什么是向量.png][1] ## 文本向量化 文本向量化是指,利用大模型可以把一个字、一个词或一段话映射为一个多维向量。在LangChain4j中向量模型OpenAiEmbeddingModel对话展示文本向量: public static void main(String[] args) { OpenAiEmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder() .baseUrl("http://langchain4j.dev/demo/openai/v1") .apiKey("demo") .build(); Response embed = embeddingModel.embed("你好,我叫风"); System.out.println(embed.content().toString()); System.out.println(embed.content().vector().length); // 输出结果 Embedding { vector = [-0.010026788, -0.009126372, -0.0085925525, ......-0.045200944] } 从结果可以知道"你好,我叫风"这句话经过OpenAiEmbeddingModel向量化之后得到的一个长度为1536的float数组。 > 基于向量来判断两句话之间的相似度。 > 1536是固定的,不会随着句子长度而变化 ## 向量相似度 向量模型也是经过大量机器学习训练之后产生,向量模型效果与其对自然语言理解的程度成正相关。如果两句话对应的向量相似,那么就表示大模型对这两句话语义理解比较相似,在假设大模型的理解比较理性的情况下,就表示这两句话的意思越相近。 比如可以通过计算两个坐标的余弦相似度(代码是ChatGPT生成的): public class CosineSimilarity { // 计算两个向量的点积 public static double dotProduct(double[] vectorA, double[] vectorB) { double dotProduct = 0; for (int i = 0; i < vectorA.length; i++) { dotProduct += vectorA[i] * vectorB[i]; } return dotProduct; } // 计算向量的模 public static double vectorMagnitude(double[] vector) { double magnitude = 0; for (double component : vector) { magnitude += Math.pow(component, 2); } return Math.sqrt(magnitude); } // 计算余弦相似度 public static double cosineSimilarity(double[] vectorA, double[] vectorB) { double dotProduct = dotProduct(vectorA, vectorB); double magnitudeA = vectorMagnitude(vectorA); double magnitudeB = vectorMagnitude(vectorB); if (magnitudeA == 0 || magnitudeB == 0) { return 0; // 避免除以零 } else { return dotProduct / (magnitudeA * magnitudeB); } } // 通过向量的相近度近似计算语义的相似度 public static void main(String[] args) { // 示例向量 double[] vectorA = {1, 2, 3}; double[] vectorB = {3, 2, 1}; // 计算余弦相似度 double similarity = cosineSimilarity(vectorA, vectorB); System.out.println("Cosine Similarity: " + similarity); } } ## 向量数据库 向量数据库可以存储向量模型生成的向量,就可以利用向量数据库来计算两个向量之间的相似度,或者根据一个向量查找与之相似的向量。 在LangChain4j中,EmbeddingStore表示向量数据库,它有20个实现类: AstraDbEmbeddingStore AzureAiSearchEmbeddingStore CassandraEmbeddingStore ChromaEmbeddingStore ElasticsearchEmbeddingStore InMemoryEmbeddingStore InfinispanEmbeddingStore MemoryIdEmbeddingStore MilvusEmbeddingStore MinimalEmbeddingStore MongoDbEmbeddingStore Neo4jEmbeddingStore OpenSearchEmbeddingStore PgVectorEmbeddingStore PineconeEmbeddingStore QdrantEmbeddingStore RedisEmbeddingStore VearchEmbeddingStore VespaEmbeddingStore WeaviateEmbeddingStore ## 使用Redis操作向量 首先添加依赖: dev.langchain4j langchain4j-redis ${langchain4j.version} 注意普通的Redis是不支持向量存储和查询的,需要额外的redisearch模块, 使用docker来运行一个带有redisearch模块的redis容器`docker run -p 6379:6379 redis/redis-stack-server:latest` 存储向量到redis RedisEmbeddingStore embeddingStore = RedisEmbeddingStore.builder() .host("127.0.0.1") .port(6379) .dimension(1536) .build(); // 生成向量 Response embed = embeddingModel.embed("我是风"); // 存储向量 embeddingStore.add(embed.content()); // 清空向量 redis-cli FT.DROPINDEX embedding-index DD ## 匹配向量 使用“我的名字叫风”来查找到“我是风” public static void main(String[] args) { OpenAiEmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder() .baseUrl("http://langchain4j.dev/demo/openai/v1") .apiKey("demo") .build(); RedisEmbeddingStore embeddingStore = RedisEmbeddingStore.builder() .host("127.0.0.1") .port(6379) .dimension(1536) .build(); // 生成向量 Response embed = embeddingModel.embed("我的名字叫周瑜"); List> result = embeddingStore.findRelevant(embed.content(), 4); for (EmbeddingMatch embeddingMatch : result) { System.out.println(embeddingMatch.score()); } } // 执行结果 0.9765566289425 // 提示词换成:今天天气很好,执行结果 0.8937962949275 看上去似乎分数差别不大,应该是小数位很多,分数精度比较高,两个分数还是有一定距离。 ## 向量搜索 基于向量快速的得到和文本相似的文本 // 生成向量 TextSegment textSegment1 = TextSegment.textSegment("客服电话是400-8888888"); TextSegment textSegment2 = TextSegment.textSegment("客服工作时间是周一到周五"); TextSegment textSegment3 = TextSegment.textSegment("客服投诉电话是400-9999999"); Response embed1 = embeddingModel.embed(textSegment1); Response embed2 = embeddingModel.embed(textSegment2); Response embed3 = embeddingModel.embed(textSegment3); // 存储向量 embeddingStore.add(embed1.content(), textSegment1); embeddingStore.add(embed2.content(), textSegment2); embeddingStore.add(embed3.content(), textSegment3); 向量数据库中添加三条客户相关的知识,并且通过TextSegment把原始文本也存入了Redis,相当于Redis中现在存储了三条原始文本以及对应的向量,然后查询: // 生成向量 Response embed = embeddingModel.embed("客服电话多少"); // 查询 List> result = embeddingStore.findRelevant(embed.content(), 5); for (EmbeddingMatch embeddingMatch : result) { System.out.println(embeddingMatch.embedded().text() + ",分数为:" + embeddingMatch.score()); } 客服电话是400-8558558,分数为:0.9529553055763 客服投诉电话是400-8668668,分数为:0.9520588517189 客服工作时间是周一到周五,分数为:0.9305278658864999 [1]: https://oss.zxse.cn/2025/12/226841178.png Loading... 在数学中,向量(也称为欧几里得向量、几何向量),指具有大小(magnitude)和方向的量。二维平面中,一个向量表示xy坐标轴的坐标点 `A(1,2)`,在编程领域,一个二维向量对应的就是一个大小为二的float类型的数组。 <!--more--> ![什么是向量.png][1] ## 文本向量化 文本向量化是指,利用大模型可以把一个字、一个词或一段话映射为一个多维向量。在LangChain4j中向量模型OpenAiEmbeddingModel对话展示文本向量: public static void main(String[] args) { OpenAiEmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder() .baseUrl("http://langchain4j.dev/demo/openai/v1") .apiKey("demo") .build(); Response<Embedding> embed = embeddingModel.embed("你好,我叫风"); System.out.println(embed.content().toString()); System.out.println(embed.content().vector().length); // 输出结果 Embedding { vector = [-0.010026788, -0.009126372, -0.0085925525, ......-0.045200944] } 从结果可以知道"你好,我叫风"这句话经过OpenAiEmbeddingModel向量化之后得到的一个长度为1536的float数组。 > 基于向量来判断两句话之间的相似度。 > 1536是固定的,不会随着句子长度而变化 ## 向量相似度 向量模型也是经过大量机器学习训练之后产生,向量模型效果与其对自然语言理解的程度成正相关。如果两句话对应的向量相似,那么就表示大模型对这两句话语义理解比较相似,在假设大模型的理解比较理性的情况下,就表示这两句话的意思越相近。 比如可以通过计算两个坐标的余弦相似度(代码是ChatGPT生成的): public class CosineSimilarity { // 计算两个向量的点积 public static double dotProduct(double[] vectorA, double[] vectorB) { double dotProduct = 0; for (int i = 0; i < vectorA.length; i++) { dotProduct += vectorA[i] * vectorB[i]; } return dotProduct; } // 计算向量的模 public static double vectorMagnitude(double[] vector) { double magnitude = 0; for (double component : vector) { magnitude += Math.pow(component, 2); } return Math.sqrt(magnitude); } // 计算余弦相似度 public static double cosineSimilarity(double[] vectorA, double[] vectorB) { double dotProduct = dotProduct(vectorA, vectorB); double magnitudeA = vectorMagnitude(vectorA); double magnitudeB = vectorMagnitude(vectorB); if (magnitudeA == 0 || magnitudeB == 0) { return 0; // 避免除以零 } else { return dotProduct / (magnitudeA * magnitudeB); } } // 通过向量的相近度近似计算语义的相似度 public static void main(String[] args) { // 示例向量 double[] vectorA = {1, 2, 3}; double[] vectorB = {3, 2, 1}; // 计算余弦相似度 double similarity = cosineSimilarity(vectorA, vectorB); System.out.println("Cosine Similarity: " + similarity); } } ## 向量数据库 向量数据库可以存储向量模型生成的向量,就可以利用向量数据库来计算两个向量之间的相似度,或者根据一个向量查找与之相似的向量。 在LangChain4j中,EmbeddingStore表示向量数据库,它有20个实现类: AstraDbEmbeddingStore AzureAiSearchEmbeddingStore CassandraEmbeddingStore ChromaEmbeddingStore ElasticsearchEmbeddingStore InMemoryEmbeddingStore InfinispanEmbeddingStore MemoryIdEmbeddingStore MilvusEmbeddingStore MinimalEmbeddingStore MongoDbEmbeddingStore Neo4jEmbeddingStore OpenSearchEmbeddingStore PgVectorEmbeddingStore PineconeEmbeddingStore QdrantEmbeddingStore RedisEmbeddingStore VearchEmbeddingStore VespaEmbeddingStore WeaviateEmbeddingStore ## 使用Redis操作向量 首先添加依赖: <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-redis</artifactId> <version>${langchain4j.version}</version> </dependency> 注意普通的Redis是不支持向量存储和查询的,需要额外的redisearch模块, 使用docker来运行一个带有redisearch模块的redis容器`docker run -p 6379:6379 redis/redis-stack-server:latest` 存储向量到redis RedisEmbeddingStore embeddingStore = RedisEmbeddingStore.builder() .host("127.0.0.1") .port(6379) .dimension(1536) .build(); // 生成向量 Response<Embedding> embed = embeddingModel.embed("我是风"); // 存储向量 embeddingStore.add(embed.content()); // 清空向量 redis-cli FT.DROPINDEX embedding-index DD ## 匹配向量 使用“我的名字叫风”来查找到“我是风” public static void main(String[] args) { OpenAiEmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder() .baseUrl("http://langchain4j.dev/demo/openai/v1") .apiKey("demo") .build(); RedisEmbeddingStore embeddingStore = RedisEmbeddingStore.builder() .host("127.0.0.1") .port(6379) .dimension(1536) .build(); // 生成向量 Response<Embedding> embed = embeddingModel.embed("我的名字叫周瑜"); List<EmbeddingMatch<TextSegment>> result = embeddingStore.findRelevant(embed.content(), 4); for (EmbeddingMatch<TextSegment> embeddingMatch : result) { System.out.println(embeddingMatch.score()); } } // 执行结果 0.9765566289425 // 提示词换成:今天天气很好,执行结果 0.8937962949275 看上去似乎分数差别不大,应该是小数位很多,分数精度比较高,两个分数还是有一定距离。 ## 向量搜索 基于向量快速的得到和文本相似的文本 // 生成向量 TextSegment textSegment1 = TextSegment.textSegment("客服电话是400-8888888"); TextSegment textSegment2 = TextSegment.textSegment("客服工作时间是周一到周五"); TextSegment textSegment3 = TextSegment.textSegment("客服投诉电话是400-9999999"); Response<Embedding> embed1 = embeddingModel.embed(textSegment1); Response<Embedding> embed2 = embeddingModel.embed(textSegment2); Response<Embedding> embed3 = embeddingModel.embed(textSegment3); // 存储向量 embeddingStore.add(embed1.content(), textSegment1); embeddingStore.add(embed2.content(), textSegment2); embeddingStore.add(embed3.content(), textSegment3); 向量数据库中添加三条客户相关的知识,并且通过TextSegment把原始文本也存入了Redis,相当于Redis中现在存储了三条原始文本以及对应的向量,然后查询: // 生成向量 Response<Embedding> embed = embeddingModel.embed("客服电话多少"); // 查询 List<EmbeddingMatch<TextSegment>> result = embeddingStore.findRelevant(embed.content(), 5); for (EmbeddingMatch<TextSegment> embeddingMatch : result) { System.out.println(embeddingMatch.embedded().text() + ",分数为:" + embeddingMatch.score()); } 客服电话是400-8558558,分数为:0.9529553055763 客服投诉电话是400-8668668,分数为:0.9520588517189 客服工作时间是周一到周五,分数为:0.9305278658864999 [1]: https://oss.zxse.cn/2025/12/226841178.png 最后修改:2025 年 12 月 08 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏