目录
[2.1 什么是向量数据库](#2.1 什么是向量数据库)
[2.2 向量数据库特点](#2.2 向量数据库特点)
[2.3 向量数据库使用场景](#2.3 向量数据库使用场景)
[3.1 Milvus](#3.1 Milvus)
[3.1.1 Milvus是什么](#3.1.1 Milvus是什么)
[3.1.2 Milvus主要特点](#3.1.2 Milvus主要特点)
[3.2 Faiss](#3.2 Faiss)
[3.2.1 Faiss是什么](#3.2.1 Faiss是什么)
[3.2.2 Faiss主要特点](#3.2.2 Faiss主要特点)
[3.3 Pinecone](#3.3 Pinecone)
[3.3.1 Pinecone是什么](#3.3.1 Pinecone是什么)
[3.3.2 Pinecone主要特点](#3.3.2 Pinecone主要特点)
[3.4 Weaviate](#3.4 Weaviate)
[3.4.1 Weaviate介绍](#3.4.1 Weaviate介绍)
[3.4.2 Weaviate主要特点](#3.4.2 Weaviate主要特点)
[3.5 Redis Stack](#3.5 Redis Stack)
[3.5.1 Redis Stack介绍](#3.5.1 Redis Stack介绍)
[3.5.2 Redis Stack核心模块](#3.5.2 Redis Stack核心模块)
[3.5.3 Redis Stack主要特点](#3.5.3 Redis Stack主要特点)
[3.5.4 Redis Stack应用场景](#3.5.4 Redis Stack应用场景)
[4.1 redis向量数据库是什么](#4.1 redis向量数据库是什么)
[4.2 向量检索核心原理](#4.2 向量检索核心原理)
[4.2.1 匹配原理](#4.2.1 匹配原理)
[4.2.2 具体实现过程](#4.2.2 具体实现过程)
[五、整合springboot 实现向量数据库相似性搜索](#五、整合springboot 实现向量数据库相似性搜索)
[5.1 前置准备](#5.1 前置准备)
[5.1.1 docker搭建Redis-Stack](#5.1.1 docker搭建Redis-Stack)
[5.1.2 导入maven依赖](#5.1.2 导入maven依赖)
[5.1.3 获取apikey](#5.1.3 获取apikey)
[5.1.4 添加配置文件](#5.1.4 添加配置文件)
[5.2 文本嵌入代码实现过程](#5.2 文本嵌入代码实现过程)
[5.2.1 添加一个测试接口](#5.2.1 添加一个测试接口)
[5.3 ETL Pipeline](#5.3 ETL Pipeline)
[5.3.1 Spring AI ETL 介绍](#5.3.1 Spring AI ETL 介绍)
[5.3.2 Spring AI 处理ETL完整过程](#5.3.2 Spring AI 处理ETL完整过程)
[5.3.3 Spring AI ETL 技术框架](#5.3.3 Spring AI ETL 技术框架)
[5.3.3.1 读取器 DocumentReader](#5.3.3.1 读取器 DocumentReader)
[5.3.3.2 文档转换器 DocumentTransformer](#5.3.3.2 文档转换器 DocumentTransformer)
[5.3.3.3 文档写入器 DocumentWriter](#5.3.3.3 文档写入器 DocumentWriter)
[5.4 Spring AI 文档读取操作演示](#5.4 Spring AI 文档读取操作演示)
[5.4.1 文件读取代码演示](#5.4.1 文件读取代码演示)
[5.5 Spring AI 文档转换操作演示](#5.5 Spring AI 文档转换操作演示)
[5.5.1 添加测试接口](#5.5.1 添加测试接口)
[5.6 文档存储](#5.6 文档存储)
[5.6.1 导入依赖文件](#5.6.1 导入依赖文件)
[5.6.2 配置redis信息](#5.6.2 配置redis信息)
[5.6.3 增加一个redis的向量数据库配置类](#5.6.3 增加一个redis的向量数据库配置类)
[5.6.4 增加嵌入文档接口](#5.6.4 增加嵌入文档接口)
[5.6.5 增加文档检索接口](#5.6.5 增加文档检索接口)
一、前言
AI工具的使用让数据检索的需求场景变得越来越多,也越来越复杂,传统的关系型数据库尽管可以满足日常各类关系数据的存储,但是遇到一些文档类型,非结构化的数据时,其作用和效果就会打折扣,而且数据不仅仅是存储的需要,也需要能够满足数据分析的场景,这种情况下,就可以考虑使用向量数据库了。
二、向量数据库介绍
2.1 什么是向量数据库
向量数据库是一种优化后的数据库系统,专门设计用于高效地存储和检索高维向量数据。这些向量数据通常是从原始数据(如文本、图像、音频等)中提取的特征表示。向量数据库的核心优势在于其能够快速地进行相似性搜索,即找到与给定向量最相似的向量。
2.2 向量数据库特点
向量数据库主要具备如下特征:
-
高效相似性搜索
-
支持多种相似性度量,如欧氏距离(L2)、余弦相似度、Jaccard 相似度等。
-
使用特殊的索引结构(如局部敏感哈希LSH、近似最近邻ANN等)来加速相似性搜索。
-
-
高维数据处理:
-
优化了高维向量数据的存储和检索,能够在大规模数据集中高效地进行相似性搜索。
-
支持分布式存储和计算,能够轻松扩展到多个节点。
-
-
灵活的数据模型:
-
通常采用无模式或半结构化数据模型,允许灵活地存储和处理不同类型的向量数据。
-
支持元数据存储,可以将向量数据与相关的元数据(如标签、时间戳等)一起存储。
-
-
实时和批量处理:
-
支持实时插入和查询,适用于在线推荐系统等实时应用场景。
-
也支持批量数据插入和查询,适用于离线数据分析和处理。
-
2.3 向量数据库使用场景
在下面的一些场景中可以考虑使用向量数据库
-
推荐系统:
-
通过用户行为数据生成用户和物品的向量表示,用于推荐相似的物品或用户。
-
例如,电商网站可以根据用户的浏览和购买历史生成用户向量,推荐相似的商品。
-
-
图像搜索:
-
将图像转换为向量表示,用于相似图像的搜索和检索。
-
例如,搜索引擎可以根据上传的图片找到相似的图片。
-
-
生物信息学:
-
将DNA序列或其他生物数据转换为向量表示,用于基因相似性搜索和分析。
-
例如,研究人员可以使用向量数据库来寻找与已知基因序列相似的新基因。
-
-
自然语言处理:
-
将文本转换为向量表示,用于文本相似性搜索、情感分析、机器翻译等任务。
-
例如,聊天机器人可以根据用户输入的文本生成向量,找到最合适的回复。
-
-
广告推荐:
-
根据用户的兴趣和行为生成用户向量,推荐相关的广告。
-
例如,社交媒体平台可以根据用户的兴趣生成用户向量,推荐相关的广告内容。
-
三、常用的向量数据库解决方案
向量数据库的选择也比较多,下面列举几种常用的向量数据库解决方案。
3.1 Milvus
3.1.1 Milvus是什么
Milvus 是一个高性能的向量数据库,由 Zilliz 开发并维护。它支持多种相似性度量(如欧氏距离、余弦相似度等),并使用高效的索引结构(如 IVF、HNSW 等)来加速相似性搜索。Milvus 可以处理大规模的向量数据集,并支持分布式部署,适用于多种应用场景。Milvus 在推荐系统、图像搜索、自然语言处理等领域表现出色,能够快速地进行相似性搜索,找到与给定向量最相似的向量。
3.1.2 Milvus主要特点
Milvus具有如下特点:
-
高效的相似性搜索
-
多种相似性度量:支持欧氏距离(L2)、余弦相似度、内积等相似性度量。
-
高效的索引结构:支持多种索引结构,如 IVF(Inverted File)、HNSW(Hierarchical Navigable Small World)等,能够显著加速相似性搜索。
-
-
使用简单
-
丰富的 API 和 SDK:提供多种编程语言的 SDK,包括 Python、Java、C++、Go 等,方便开发者集成。
-
详细的文档和社区支持:提供详细的文档和活跃的社区支持,帮助开发者快速上手。
-
-
多样化的数据源支持
- 多种数据源:支持多种数据源,可以与 TensorFlow、PyTorch 等深度学习框架集成,方便数据预处理和特征提取。
-
扩展性和可伸缩性好
-
分布式部署:支持分布式部署,能够处理大规模数据集,支持水平扩展。
-
高可用性:支持自动故障恢复和数据备份,保证系统的高可用性。
-
3.2 Faiss
3.2.1 Faiss是什么
Faiss 是一个开源库,专注于大规模向量相似性搜索和聚类。它支持多种相似性度量(如欧氏距离、余弦相似度等),并使用高效的索引结构(如 IVF(Inverted File)、HNSW(Hierarchical Navigable Small World)等)来加速搜索过程。Faiss 可以在 CPU 和 GPU 上运行,适用于多种应用场景。
3.2.2 Faiss主要特点
Faiss具有如下特点:
-
高效的相似性搜索
-
多种相似性度量:支持欧氏距离(L2)、余弦相似度、内积等相似性度量。
-
高效的索引结构:支持多种索引结构,如 IVF(Inverted File)、HNSW(Hierarchical Navigable Small World)、PQ(Product Quantization)等,能够显著加速相似性搜索。
-
-
高性能
-
CPU 和 GPU 支持:可以在 CPU 和 GPU 上运行,利用硬件加速提高搜索性能。
-
内存优化:对内存使用进行了优化,能够在有限的内存中处理大规模数据集。
-
-
灵活性好
-
多种索引方法:提供了多种索引方法,可以根据具体需求选择合适的索引结构。
-
自定义索引:支持自定义索引结构和搜索算法,满足特定应用的需求。
-
-
易于使用
-
丰富的 API:提供了 C++ 和 Python API,方便开发者集成。
-
详细的文档和社区支持:提供详细的文档和活跃的社区支持,帮助开发者快速上手。
-
3.3 Pinecone
3.3.1 Pinecone是什么
Pinecone 是一个云原生的向量数据库,专为高效地存储和检索大规模向量数据而设计。它旨在简化向量数据的存储和检索过程,提供高性能的相似性搜索功能。Pinecone 提供了高性能的相似性搜索功能,支持实时和批量处理,适用于推荐系统、图像搜索、自然语言处理等多种应用场景。
官方地址:The vector database to build knowledgeable AI | Pinecone
3.3.2 Pinecone主要特点
Pinecone具备如下特点:
-
高性能
-
高效的相似性搜索:支持多种相似性度量,如余弦相似度、欧氏距离等,使用高效的索引结构(如 HNSW、IVF 等)来加速搜索过程。
-
低延迟:优化了搜索性能,能够在毫秒级别内返回结果,适用于实时应用场景。
-
-
使用简单
-
简单的 API 和 SDK:提供丰富的 API 和 SDK,支持多种编程语言(如 Python、JavaScript 等),方便开发者集成。
-
详细的文档和社区支持:提供详细的文档和活跃的社区支持,帮助开发者快速上手。
-
-
扩展性和可伸缩性好
-
云原生:提供托管服务,支持自动扩展和故障恢复,保证系统的高可用性。
-
水平扩展:支持水平扩展,能够处理大规模数据集,适用于企业级应用。
-
-
多样化的数据源支持
- 多种数据源:支持多种数据源,可以与 TensorFlow、PyTorch 等深度学习框架集成,方便数据预处理和特征提取。
3.4 Weaviate
3.4.1 Weaviate介绍
Weaviate 是一个开源的向量搜索引擎,由 Semitechnologies 开发并维护。它不仅支持高效的向量数据存储和检索,还结合了图数据模型,使其在处理复杂数据关系方面表现出色。Weaviate 适用于推荐系统、图像搜索、自然语言处理等多种应用场景。Weaviate 支持多种相似性度量(如余弦相似度、欧氏距离等),并使用高效的索引结构来加速搜索过程。Weaviate 提供了丰富的 API 和 SDK,支持多种编程语言,方便开发者集成。
3.4.2 Weaviate主要特点
Weaviate具备如下特点
-
图数据模型
-
图数据模型:支持图数据模型,可以表示和查询复杂的数据关系。
-
灵活的数据模型:支持多种数据类型和格式,可以轻松适应各种用例。
-
-
高效的相似性搜索
-
多种相似性度量:支持余弦相似度、欧氏距离等相似性度量。
-
高效的索引结构:使用高效的索引结构(如 HNSW)来加速相似性搜索。
-
-
使用简单
-
丰富的 API 和 SDK:提供丰富的 API 和 SDK,支持多种编程语言(如 Python、JavaScript、Go 等),方便开发者集成。
-
详细的文档和社区支持:提供详细的文档和活跃的社区支持,帮助开发者快速上手。
-
-
扩展性和可伸缩性好
-
云原生支持:提供托管服务,支持自动扩展和故障恢复,保证系统的高可用性。
-
水平扩展:支持水平扩展,能够处理大规模数据集,适用于企业级应用。
-
-
查询语言
- 支持 GraphQL:提供强大的 GraphQL 查询功能,支持复杂的查询和过滤条件。
3.5 Redis Stack
3.5.1 Redis Stack介绍
Redis Stack 是 Redis 的一个扩展版本,它不仅包含了 Redis 的核心功能,还增加了多个模块,如 RediSearch(全文搜索)、RedisGraph(图数据库)、RedisTimeSeries(时间序列数据库)和 RedisAI(AI推理)。这些模块使得 Redis Stack 成为一个功能更加强大的数据库系统,适用于多种应用场景。
3.5.2 Redis Stack核心模块
Redis Stack核心模块主要包括下面几块
-
RediSearch
-
全文搜索:提供全文搜索功能,支持复杂的查询和过滤条件。
-
向量搜索:支持向量数据的存储和相似性搜索,适用于推荐系统、图像搜索等场景。
-
性能优化:使用倒排索引和 BM25 算法优化搜索性能。
-
-
RedisGraph
-
图数据库:提供图数据模型,支持节点和边的存储和查询。
-
Cypher 查询语言:支持 Cypher 查询语言,方便进行复杂的图数据查询。
-
高性能:优化了图数据的存储和查询性能。
-
-
RedisTimeSeries
-
时间序列数据:提供时间序列数据的存储和查询功能,适用于监控、日志分析等场景。
-
压缩和聚合:支持数据压缩和聚合,节省存储空间和提高查询性能。
-
-
RedisAI
-
AI 推理:提供 AI 推理功能,支持 TensorFlow、PyTorch 等深度学习框架。
-
模型管理和执行:支持模型的加载、管理和执行,适用于实时推理和预测。
-
3.5.3 Redis Stack主要特点
Redis Stack主要具备如下特点:
-
高性能
-
内存优化:Redis Stack 优化了内存使用,能够在有限的内存中处理大规模数据集。
-
低延迟:Redis Stack 提供低延迟的数据访问,适用于实时应用场景。
-
-
使用简单
-
丰富的 API 和 SDK:提供丰富的 API 和 SDK,支持多种编程语言(如 Python、Java、C++、Go 等),方便开发者集成。
-
详细的文档和社区支持:提供详细的文档和活跃的社区支持,帮助开发者快速上手。
-
-
多样化的数据源支持
- 多种数据源:支持多种数据源,可以与 TensorFlow、PyTorch 等深度学习框架集成,方便数据预处理和特征提取。
-
扩展性和可伸缩性
-
分布式部署:支持分布式部署,能够处理大规模数据集,支持水平扩展。
-
高可用性:支持自动故障恢复和数据备份,保证系统的高可用性。
-
3.5.4 Redis Stack应用场景
Redis Stack主要有下面的应用场景
-
全文搜索
-
电子商务:支持商品搜索和推荐。
-
内容管理系统:支持文章和文档的搜索和管理。
-
-
图数据库
-
社交网络:支持用户关系和社交图谱的存储和查询。
-
推荐系统:支持基于图数据的推荐算法。
-
-
时间序列数据
-
监控系统:支持系统监控和日志分析。
-
物联网:支持 IoT 设备的数据收集和分析。
-
-
AI 推理
-
实时推荐:支持基于 AI 的实时推荐系统。
-
图像识别:支持图像识别和分类。
-
四、Redis向量数据库介绍
4.1 redis向量数据库是什么
Redis 是一个开源(BSD 许可)的内存数据结构存储,用作数据库、缓存、消息代理和流式处理引擎。Redis 提供数据结构,例如字符串、哈希、列表、集合、带范围查询的有序集合、位图、超对数日志、地理空间索引和流。
Redis 搜索和查询, 扩展了 Redis OSS 的核心功能,因此可以将 Redis 用作向量数据库
-
在哈希或 JSON 文档中存储向量和关联的元数据
-
检索向量
-
执行向量搜索
4.2 向量检索核心原理
向量检索(Vector Search)的核心原理是通过将文本或数据表示为高维向量,并在查询时根据向量的相似度进行搜索。具体来说,向量检索过程涉及以下核心几点。
4.2.1 匹配原理
-
检索的核心是将文本或数据转换成向量,在高维向量空间中查找与查询最相似的向量。
-
在存储数据时将指定的字段通过嵌入模型生成了向量。
-
在检索时,查询文本被向量化,然后与 Redis 中存储的向量进行相似度比较,找到相似度最高的向量(即相关的文档)。
技术关键点:
-
嵌入模型
- 将文本转换成向量。
-
相似度计算
- 通过余弦相似度或欧几里得距离来度量相似性。
-
Top K
- 返回相似度最高的 K 个文档。
4.2.2 具体实现过程
在代码中操作时,主要包括下面几步:
-
向量化数据:
- 当你将 JSON 中的字段存入 Redis 时,向量化工具(例如 vectorStore)会将指定的字段转换为高维向量。每个字段的内容会通过某种嵌入模型(如 Word2Vec、BERT、OpenAI Embeddings 等)转换成向量表示。每个向量表示的是该字段内容的语义特征。
-
搜索时向量生成:
- 当执行 SearchRequest.query(message) 时,系统会将输入的 message 转换为一个查询向量。这一步是通过同样的嵌入模型,将查询文本转换为与存储在 Redis 中相同维度的向量。
-
相似度匹配:
- vectorStore.similaritySearch(request) 函数使用了一个向量相似度计算方法来查找最相似的向量。这通常是通过 余弦相似度 或 欧几里得距离 来度量查询向量和存储向量之间的距离。然后返回与查询最相似的前 K 个文档,即 withTopK(topK) 所指定的 K 个最相关的结果。
-
返回匹配的文档:
- 匹配的结果是根据相似度得分排序的 List<Document>。这些文档是你最初存储在 Redis 中的记录,包含了 JSON 中指定的字段。
五、整合springboot 实现向量数据库相似性搜索
接下来使用Redis Stack作为向量数据库,并整合springboot 实现向量数据库相似性搜索的过程。
5.1 前置准备
5.1.1 docker搭建Redis-Stack
使用下面的docker命令搭建Redis-Stack
bash
docker run -d --name redis-stack -v /redis-data:/data -p 6379:6379 -p 8001:8001 redis/redis-stack:latest
也可以使用下面的docker compose命令启动容器
bash
version: '3'
services:
redis-stack:
image: redis/redis-stack
ports:
- 6379:6379
redis-insight:
image: redislabs/redisinsight:latest
ports:
- 5540:5540
可以通过 IP:8001 访问 Redis-Stack 的web控制台,这里在后面将会用于存储程序中的文本嵌入数据
5.1.2 导入maven依赖
在你的maven工程中导入如下依赖
java
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.4</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter</artifactId>
<version>1.0.0-M2.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.35</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/dashscope-sdk-java -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dashscope-sdk-java</artifactId>
<version>2.16.9</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.milvus/milvus-sdk-java -->
<dependency>
<groupId>io.milvus</groupId>
<artifactId>milvus-sdk-java</artifactId>
<version>2.4.6</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
5.1.3 获取apikey
下文中将会先演示使用云厂商提供的向量数据库,所以这里使用百炼平台的,需要先获取apikey
获取apikey,可以去阿里云的百炼平台获取,然后在模型广场中向量模型那里选择一种文本向量使用
5.1.4 添加配置文件
在工程的配置文件中增加下面的配置内容
java
server:
port: 8081
spring:
ai:
dashscope:
api-key: 你的apikey
embedding:
options:
model: text-embedding-v2
5.2 文本嵌入代码实现过程
如何用一段话去数据库查找数据?答案是向量求相似度。相似度越高,返回的数据就越靠前,比如大家熟知的搜索引擎es,你输入一段文本去查询,es会根据算分将得分最高的文档放在前面返回给你,在大模型中,嵌入模型就可以帮我们做到这件事,所以第一件事情需要将文本中的句子向量化,然后和向量数据库中的向量进行比较,找到最相似的向量。下面使用代码演示下这个过程。
5.2.1 添加一个测试接口
添加一个测试接口,参考下面的代码
java
package com.congge.web;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.embedding.EmbeddingResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RestController
@Slf4j
public class DocumentController {
private final EmbeddingModel embeddingModel;
@Autowired
public DocumentController(EmbeddingModel embeddingModel) {
this.embeddingModel = embeddingModel;
}
@GetMapping("/ai/embedding")
public Map embed(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
EmbeddingResponse embeddingResponse = this.embeddingModel.embedForResponse(List.of(message));
return Map.of("embedding", embeddingResponse);
}
//localhost:8081/embedding
@GetMapping("/embedding")
public Object embeddingTest() {
// 文本嵌入
float[] embed = embeddingModel.embed("你好,我是小码农叔叔");
log.info("文本转换得到的向量: {}", embed);
return embed;
}
}
两个接口均可用于测试,选择第二个测试,请求之后,返回了很多向量的参数
5.3 ETL Pipeline
ETL是提取、转换、加载的缩写,从原始的文档到数据库需要经历提取(.doc、.ppt、.xlsx等)、转换(数据结构化、清理数据、数据分块)、写入向量数据库。这个过程可以进行多种处理,确保最后的数据适合AI问答。
SpringAI提供了ETL框架。它是搭建知识库框架的基石。
5.3.1 Spring AI ETL 介绍
在AI应用程序中,数据处理是至关重要的一部分。Spring AI提供了一个强大的ETL框架,帮助开发者高效地进行数据提取、转换和加载操作。通过这个框架,你可以轻松地将数据准备好用于AI模型的训练和推理。
5.3.2 Spring AI 处理ETL完整过程
与传统的etl过程稍有不同,springai 中etl 的完整过程如下:
-
数据提取
- 数据提取是ETL流程的第一步。在这个阶段,你需要从各种数据源中提取原始数据。Spring AI支持多种数据源,包括数据库、文件系统、API等。
-
数据转换
- 数据转换是ETL流程的第二步。在这个阶段,你需要将原始数据转换为适合AI模型使用的格式。Spring AI提供了多种数据转换工具,帮助你高效地处理数据。
-
向量化数据
- 对于AI模型来说,向量化数据是必不可少的步骤。只有经过向量化之后的数据才能在后续的检索中正常检索出结果。
-
数据加载
- 数据加载是ETL流程的最后一步。在这个阶段,你需要将转换后的数据加载到目标存储系统中,以便AI模型使用。Spring AI支持将数据加载到多种目标存储系统中。
5.3.3 Spring AI ETL 技术框架
5.3.3.1 读取器 DocumentReader
DocumentReader ,文档读取器:
-
用于读取文档,比如PDF、Word、Excel等
-
如:JsonReader(读取JSON),TextReader(读取文本),PagePdfDocumentReader(读取PDF),TikaDocumentReader(读取各种文件,大部分都可以支持.pdf,.xlsx,.docx,.pptx,.md,.json等)。
-
这些reader都是DocumentReader的实现类。
-
5.3.3.2 文档转换器 DocumentTransformer
文档转换器,处理文档:
- TextSplitter(文档切割成小块),ContentFormatTransformer(将文档变成键值对),SummaryMetadataEnricher(使用大模型总结文档),KeywordMetadataEnricher(使用大模型提取文档关键词)
5.3.3.3 文档写入器 DocumentWriter
文档写入器:
-
将文档写入向量数据库或者本地文件。
-
VectorStore(向量数据库写入器),FileDocumentWriter(文件写入器)。
使用这几个组件,完整的执行流程如下:
5.4 Spring AI 文档读取操作演示
5.4.1 文件读取代码演示
TikaDocumentReader 比较全能,大部分文件都可读取,支持的文件格式可以参考官方文档。如果是比较个性化文档的场景,最好自己实现一个Reader,便于业务扩展。pom文件中导入下面的依赖。
java
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-tika-document-reader</artifactId>
</dependency>
从输入流读取文件,参考下面的接口代码
java
@SneakyThrows
@PostMapping("read/multipart-file")
public void readMultipartFile(@RequestParam MultipartFile file) {
// 从IO流中读取文件
Resource resource = new InputStreamResource(file.getInputStream());
List<Document> documents = new TikaDocumentReader(resource)
.read();
}
上传一个本地的文档,接口测试效果,可以看到被分割成很多片段了
从本地文件读取,参考下面的接口代码
java
/**
* 从本地文件读取
* @return
*/
@GetMapping("/read/from-local-file")
public Object readLocalFile() {
FileSystemResource resource = new FileSystemResource("E:\\工作空间\\wzlocal\\Java编码规范V1.0.pdf");
List<Document> documents = new TikaDocumentReader(resource)
.read();
return documents;
}
调用一下接口,使用相同的文件,得到类似的效果
5.5 Spring AI 文档转换操作演示
在上一步的操作中,读取返回的是Document对象,而是DocumentETL Pipeline的核心对象,它包含了文档的元数据和内容。接下来就需要对文档进行转换。此时就要用到内容转换器。包括内容转换器和元数据转换器。
内容转换器
-
TokenTextSplitter:可以把内容切割成更小的块方便RAG的时候提升响应速度节省Token。
-
ContentFormatTransformer:可以把元数据的内容变成键值对字符串。
元数据转换器
-
SummaryMetadataEnricher:使用大模型总结文档。会在元数据里面增加一个summary字段。
-
KeywordMetadataEnricher:使用大模型提取文档关键词。可以在元数据里面增加一个keywords字段。
5.5.1 添加测试接口
参考下面的代码
java
/**
* 分割后的文档列表 localhost:8081/doc/split
* @param file
* @return
* @throws Exception
*/
@PostMapping("/doc/split")
public Object docSplit(@RequestParam MultipartFile file) throws Exception{
InputStreamResource inputStreamResource = new InputStreamResource(file.getInputStream());
List<Document> documentList = new TikaDocumentReader(inputStreamResource).read();
// 分割后的文档列表
List<Document> splitDocList = new TokenTextSplitter().split(documentList);
List<String> contentList = splitDocList.stream().map(Document::getContent).collect(Collectors.toList());
return contentList;
}
测试一下接口,可以看到被转换后的文档分割成多个片段
5.6 文档存储
经过前面的步骤,我们得到了一个文档列表,为了方便后续的文档搜索,需要将文档进行存储,文档存储到哪里去呢?即向量数据库。上面准备阶段搭建的Redis-Stack即是用于做向量数据库使用的。下面看代码中的操作过程。
5.6.1 导入依赖文件
导入下面的依赖坐标
java
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-tika-document-reader</artifactId>
<version>1.0.0-M3</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-redis-store</artifactId>
<version>1.0.0-M3</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
5.6.2 配置redis信息
在配置文件中添加redis的信息
java
spring:
ai:
dashscope:
api-key: 你的apikey
embedding:
options:
model: text-embedding-v2
vectorstore:
redis:
uri: redis://IP:6379
data:
redis:
database: 0
timeout: 10s
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
repositories:
enabled: false
host: redis地址
5.6.3 增加一个redis的向量数据库配置类
如果在你的项目里面有用到redis,需要先禁用RedisVectorStoreAutoConfiguration。这是SpringAI自动配置RedisStack的向量数据库连接,会导致Redis的连接配置冲突。参考下面的代码。
java
package com.congge.config;
import lombok.AllArgsConstructor;
import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreAutoConfiguration;
import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.RedisVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPooled;
@Configuration
// 禁用SpringAI提供的RedisStack向量数据库的自动配置,会和Redis的配置冲突。
@EnableAutoConfiguration(exclude = {RedisVectorStoreAutoConfiguration.class})
// 读取RedisStack的配置信息
@EnableConfigurationProperties({RedisVectorStoreProperties.class})
@AllArgsConstructor
public class RedisVectorConfig {
/**
* 创建RedisStack向量数据库
*
* @param embeddingModel 嵌入模型
* @param properties redis-stack的配置信息
* @return vectorStore 向量数据库
*/
@Bean
public VectorStore vectorStore(EmbeddingModel embeddingModel,
RedisVectorStoreProperties properties,
RedisConnectionDetails redisConnectionDetails) {
RedisVectorStore.RedisVectorStoreConfig config = RedisVectorStore.RedisVectorStoreConfig.builder().withIndexName(properties.getIndex()).withPrefix(properties.getPrefix()).build();
return new RedisVectorStore(config, embeddingModel,
new JedisPooled(redisConnectionDetails.getStandalone().getHost(),
redisConnectionDetails.getStandalone().getPort()
, redisConnectionDetails.getUsername(),
redisConnectionDetails.getPassword()),
properties.isInitializeSchema());
}
}
5.6.4 增加嵌入文档接口
在上面的VectorStore配置中我们提供了EmbeddingModel,调用vectorStore.add(splitDocuments)底层会把文档给EmbeddingModel把文本变成向量然后再存入向量数据库。参考下面的代码。
java
/**
* 文档保存到向量数据库 localhost:8081/doc/embedding
* @param file
* @return
* @throws Exception
*/
@PostMapping("/doc/embedding")
public Object docEmbedding(@RequestParam MultipartFile file) throws Exception{
TikaDocumentReader tikaDocumentReader = new TikaDocumentReader(new InputStreamResource(file.getInputStream()));
List<Document> splitDocuments = new TokenTextSplitter().apply(tikaDocumentReader.read());
// 存入向量数据库,这个过程会自动调用embeddingModel,将文本变成向量存入
vectorStore.add(splitDocuments);
return true;
}
调用一下接口
接口执行成功后,在Redis-Stack的控制台界面可以看到数据存入了redis中
5.6.5 增加文档检索接口
数据存入进去之后接下来就可以进行搜索了,添加下面文档检索的接口
java
/**
* 关键字检索文档 localhost:8081/doc/query?keyword=面向对象
* @param keyword
* @return
*/
@GetMapping("/doc/query")
public List<Document> query(@RequestParam String keyword) {
List<Document> documentList = vectorStore.similaritySearch(keyword);
return documentList;
}
六、写在文末
本文通过较大的篇幅详细介绍了常用的向量数据库解决方案,并通过搭建redis stack环境,结合代码演示了如何基于redis stack作为向量数据库进行使用,希望对看到的同学有用,本篇到此介绍,感谢观看。