Spring AI 如何集成向量数据库入门:以ChromaDB为例详细解析
在人工智能领域,向量数据库是一个关键技术,它能高效存储向量嵌入,并实现相似性搜索。本文以ChromaDB为例,深入介绍Spring AI中向量数据库的使用,帮助读者快速掌握相关技术,将其应用到实际项目中。
一、什么是向量数据库
向量(也叫嵌入向量)是一个由数字组成的长数组,它能用来表示单词、句子、文件、音视频数据等各种对象。数组中的每个数字,都代表了数据的某个独特特征或属性,比如情感的积极程度、强度、上下文等。通过计算两个向量之间的距离,我们就能判断不同对象之间的相关性,这个过程就是语义搜索。
向量数据库是一种特殊的数据库,它能大规模、低延迟且安全地存储高维向量数据。下面简单介绍下向量数据库的工作原理:
- 数据存储:先利用AI模型把文本、图像、视频这些原始数据转化为向量,然后存储到向量数据库中。
- 数据检索:进行搜索时,先把搜索的内容(文本、音频或视频)用生成原始数据向量的同一模型转化为向量,接着用这个查询向量在数据库里找到最相似的向量。向量数据库通过语义搜索,能有效解决大语言模型(LLMs)中的一些问题,比如幻觉问题。
- 计算相似性:计算向量相似性的方法有很多,具体选择哪种方法,取决于实际应用场景和数据特点。常见的方法包括:
- 欧氏距离:计算两个向量在欧氏空间中的直线距离。
- 余弦相似度:通过计算两个向量夹角的余弦值来衡量相似度。
- 曼哈顿距离(L1距离):把向量各分量的绝对差值相加得到的结果。
- 杰卡德相似度:用两个向量交集的大小除以并集的大小来计算。
目前市面上的向量数据库有很多,像Pinecone、Elasticsearch这些来自初创公司,Chroma、Weaviate、Quadrant则属于开源数据库。
二、Spring向量存储接口
在Spring AI里,VectorStore
接口是与向量数据库交互的主要接口。它提供了在向量数据库中存储Document
对象的方法,以及对存储的向量进行相似性搜索的方法。代码如下:
public interface VectorStore { // 将文档列表添加到向量数据库 void add(List<Document> documents); // 根据文档ID列表删除文档,返回删除操作是否成功的结果 Optional<Boolean> delete(List<String> idList); // 根据查询字符串进行相似性搜索,返回最相似的文档列表 List<Document> similaritySearch(String query); // 根据搜索请求进行相似性搜索,返回最相似的文档列表 List<Document> similaritySearch(SearchRequest request); }
这里的Document
对象封装了原始内容、向量嵌入,以及文件名等相关元数据,具体定义如下:
public class Document implements Content { // 存储文档的元数据,如文件名、文件类型等 private Map<String, Object> metadata; // 文档的原始内容,比如文本文件的文本内容 private String content; // 文档对应的向量嵌入,是一个由双精度浮点数组成的列表 private List<Double> embedding = new ArrayList<>(); // 其他方法和构造函数省略 }
三、文档的存储与查询
当Spring Boot检测到项目中添加了支持向量数据库的启动器模块时,会自动初始化VectorStore
类型的bean。比如,添加spring-ai-chroma-store-spring-boot-starter
依赖后,Spring Boot会自动配置ChromaDB,并创建ChromaVectorStore
类型的bean。我们可以通过通用的VectorStore
接口来访问这个bean,这样后续如果要切换到其他向量数据库,也无需修改代码。
// 通过依赖注入获取VectorStore类型的bean @Autowired VectorStore vectorStore;
获取到VectorStore
bean后,就可以用它在数据库中存储文档:
// 创建包含多个Document对象的列表,每个Document对象包含文档内容 List<Document> documents = List.of( new Document("...content..."), new Document("...content..."), new Document("...content...")); // 将文档列表添加到向量数据库 vectorStore.add(documents);
也可以进行语义搜索:
// 根据搜索条件构建搜索请求,查询与“...search-terms...”相关的文档,并返回最相似的5个 List<Document> results = vectorStore.similaritySearch( SearchRequest.query("...search-terms...").withTopK(5) );
Spring AI支持多种向量数据库,并且未来还会增加更多支持。如需查看完整的支持数据库列表及配置,请访问官方文档。
四、SimpleVectorStore介绍
在做演示时,为了避免配置复杂的向量存储数据库,我们可以使用SimpleVectorStore
。它是VectorStore
接口的一个简单实现,提供了将向量当前状态保存到文件,以及从文件加载向量的方法。这有点像在开发环境中,用H2内存数据库代替常规关系型数据库。不过要注意,它不会从数据库保存或加载嵌入文件,只是提供文件操作的方法。
public class SimpleVectorStore implements VectorStore { // 添加文档到向量存储 public void add(List<Document> documents) {...} // 根据搜索请求进行相似性搜索 public List<Document> similaritySearch(SearchRequest request) {...} // 将向量存储的当前状态保存到指定文件 public void save(File file) {...} // 从指定文件加载向量 public void load(File file) {...} // 从指定资源加载向量 public void load(Resource resource) {...} // 其他方法省略 }
SimpleVectorStore
bean只需要EmbeddingModel
bean的引用,用于从原始数据生成向量嵌入。可以通过以下方式创建SimpleVectorStore
bean:
// 定义一个Spring Bean,创建SimpleVectorStore实例,并传入EmbeddingModel实例 @Bean SimpleVectorStore vectorStore(EmbeddingModel embeddingModel) { return new SimpleVectorStore(embeddingModel); }
五、使用ChromaDB进行向量存储演示
下面通过配置Chroma数据库,并利用它存储和查询嵌入向量,来展示VectorStore
bean的实际应用。
(一)设置ChromaDB
这里借助Spring Boot的Docker Compose支持来设置Chroma数据库。在项目根目录下创建docker-compose.yml
文件,内容如下:
version: '3.9' networks: net: driver: bridge services: server: image: ghcr.io/chroma-core/chroma:latest environment: - IS_PERSISTENT=TRUE volumes: - chroma-data:/chroma/chroma/ ports: - 8000:8000 networks: - net volumes: chroma-data: driver: local
这样,docker-compose
模块就能启动Chroma数据库。
(二)配置Maven依赖
接着,在项目的pom.xml
文件中添加相关的Maven依赖,让Spring Boot配置必要的bean:
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-chroma-store-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-docker-compose</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency>
其中,OpenAiEmbeddingModel
用于从原始数据创建嵌入向量,ChromaVectorStore
用于存储和查询生成的嵌入向量。spring-boot-docker-compose
会扫描前面定义的docker-compose.yml
文件,启动数据库实例,并自动读取数据库连接信息,创建VectorStore
bean。
此外,还可以添加其他库来辅助读取、解析和分词文档,比如:
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-tika-document-reader</artifactId> </dependency>
(三)将数据加载到向量存储
读取原始数据并创建嵌入向量的方式因项目而异。在这个演示中,创建了VectorStoreLoader
类,借助Apache Tika库读取文本文件、Markdown文件和PDF文件。代码如下:
import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import org.springframework.ai.document.Document; import org.springframework.ai.reader.TextReader; import org.springframework.ai.reader.tika.TikaDocumentReader; import org.springframework.ai.transformer.splitter.TokenTextSplitter; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.core.io.Resource; import org.springframework.stereotype.Component; @Component public class VectorStoreLoader implements ApplicationRunner { // 注入PDF文件资源 @Value("classpath:/CallingRates.pdf") Resource pdfResource; // 注入文本文件资源 @Value("classpath:/story.txt") Resource txtResource; // 注入Markdown文件资源 @Value("classpath:/story.md") Resource mdResource; // 注入VectorStore实例 @Autowired VectorStore vectorStore; @Override public void run(ApplicationArguments args) throws Exception { List<Document> documents = new ArrayList<>(); // 使用TikaDocumentReader读取PDF文件内容,并添加到documents列表 TikaDocumentReader reader = new TikaDocumentReader(pdfResource); documents.addAll(reader.get()); // 将documents列表添加到向量存储 vectorStore.add(documents); // 创建TextReader读取文本文件,设置字符集后读取内容并添加到documents列表 var textReader1 = new TextReader(txtResource); textReader1.setCharset(Charset.defaultCharset()); documents.addAll(textReader1.get()); // 创建TextReader读取Markdown文件,设置字符集后读取内容并添加到documents列表 var textReader2 = new TextReader(mdResource); textReader2.setCharset(Charset.defaultCharset()); documents.addAll(textReader2.get()); // 使用TokenTextSplitter对documents进行分词处理后,再添加到向量存储 vectorStore.add(new TokenTextSplitter(300, 300, 5, 1000, true).split(documents)); System.out.println("Added documents to vector store"); } }
这个类会把读取的数据添加到Document
对象,最后存入向量存储。向量存储内部会调用嵌入模型,将生成的向量存储到数据库中。
(四)相似性搜索
需要时,调用vectorStore.similaritySearch()
方法,就能找到与查询词匹配的相似文档。示例代码如下:
// 根据“investigation”进行相似性搜索,获取最相似的文档列表 List<Document> documents = vectorStore.similaritySearch("investigation"); // 遍历并打印搜索结果 documents.stream().forEach(System.out::println);
程序输出结果类似:
Document{id='7cec17aa-...', metadata={source=story.md , distance=0.7674138}, content='...', media=[]} Document{id='42726cdb-...', metadata={source=story.text, distance=0.8732333}, content='...', media=[]} Document{id='9aad7daa-...', metadata={source=story.pdf , distance=0.8799484}, content='...', media=[]}
六、总结
通过这个Spring AI向量数据库的示例,我们了解了向量和向量数据库的概念,掌握了向量在数据库中的存储方式,以及如何利用相似性搜索查找相关数据。还探讨了Spring AI的VectorStore
接口,以及如何借助Spring Boot自动配置创建和使用具体的实现类。最后,通过使用Docker Compose设置Chroma向量数据库,并结合OpenAI嵌入模型存储向量嵌入的示例,对整个流程有了更直观的认识。希望读者能通过本文的学习,在实际项目中灵活运用向量数据库技术。