> 技术文档 > Spring AI系列之Spring AI 集成 ChromaDB 向量数据库 _spring ai chroma

Spring AI系列之Spring AI 集成 ChromaDB 向量数据库 _spring ai chroma


1. 概述

在传统数据库中,我们通常依赖精确的关键词或基本的模式匹配来实现搜索功能。虽然这种方法对于简单的应用程序已经足够,但它无法真正理解自然语言查询背后的含义和上下文。

向量存储解决了这一限制,它通过将数据以数值向量的形式存储,从而捕捉数据的语义。相似的词会在向量空间中靠得很近,这使得语义搜索成为可能——即使查询中没有包含确切的关键词,也能返回相关结果。

在本教程中,我们将探讨如何将 ChromaDB(一款开源的向量存储库)与 Spring AI 集成使用。

为了将文本数据转换成 ChromaDB 能够存储和搜索的向量,我们需要使用一个嵌入模型。我们将使用 Ollama 在本地运行嵌入模型。


2. 依赖项

首先,我们需要在项目的 pom.xml 文件中添加必要的依赖项:

 org.springframework.ai spring-ai-chroma-store-spring-boot-starter 1.0.0-M6 org.springframework.ai spring-ai-ollama-spring-boot-starter 1.0.0-M6

ChromaDB starter依赖项使我们能够建立与 ChromaDB 向量存储的连接并与之交互。

此外,我们还引入了 Ollama 启动器依赖项,用于运行我们的嵌入模型。

由于当前版本(1.0.0-M6)是一个里程碑版本,我们还需要在 pom.xml 文件中添加 Spring Milestones 仓库:

  spring-milestones Spring Milestones https://repo.spring.io/milestone  false  

这个仓库用于发布里程碑版本,不同于标准的 Maven Central 仓库。

由于我们在项目中使用了多个SpringAI启动器(starter),我们还需要在pom.xml中引入SpringAI的BOM(依赖管理清单,Bill of Materials):

   org.springframework.ai spring-ai-bom 1.0.0-M6 pom import  

通过引入 BOM,我们现在可以从两个启动器依赖中移除版本号标签。

BOM 的作用是消除版本冲突的风险,并确保我们使用的 Spring AI 相关依赖彼此兼容。


3. 使用 Testcontainers 搭建本地测试环境

为了便于本地开发和测试,我们将使用Testcontainers来搭建 ChromaDB 向量存储和 Ollama 服务。

通过Testcontainers运行这些服务的前提是本机已安装并正在运行 Docker。


3.1 测试依赖项

首先,我们需要在 pom.xml 中添加必要的测试依赖项:

 org.springframework.ai spring-ai-spring-boot-testcontainers test org.testcontainers chromadb test org.testcontainers ollama test

这些依赖项为我们提供了必要的类,可以快速创建用于两个外部服务的临时 Docker 实例。


3.2 定义 Testcontainers Bean

接下来,我们创建一个带有 

@TestConfiguration 注解的类,用于定义我们的 Testcontainers Bean:

@TestConfiguration(proxyBeanMethods = false)class TestcontainersConfiguration { @Bean @ServiceConnection public ChromaDBContainer chromaDB() { return new ChromaDBContainer(\"chromadb/chroma:0.5.20\"); } @Bean @ServiceConnection public OllamaContainer ollama() { return new OllamaContainer(\"ollama/ollama:0.4.5\"); }}

我们为容器指定了最新的稳定版本。

同时,我们使用 @ServiceConnection 注解标注了 Bean 方法。该注解会自动注册所有连接外部服务所需的属性,从而建立连接。

即使不使用 Testcontainers 支持,Spring AI 在本地运行时也会自动连接到ChromaDB和Ollama,前提是它们分别运行在默认端口 8000 和 11434 上。

然而,在生产环境中,我们可以通过相应的 Spring AI 配置属性来覆盖这些连接信息:

spring: ai: vectorstore: chroma: client: host: ${CHROMADB_HOST} port: ${CHROMADB_PORT} ollama: base-url: ${OLLAMA_BASE_URL}

一旦连接信息配置正确,SpringAI会自动为我们创建 VectorStore 

和 EmbeddingModel 类型的 Bean,

使我们可以分别与向量存储和嵌入模型进行交互。我们将在本教程后续部分了解如何使用这些 Bean。

尽管@ServiceConnection会自动定义所需的连接信息,但我们仍然需要在 application.yml 文件中配置一些额外的属性:

spring: ai: vectorstore: chroma: initialize-schema: true ollama: embedding: options: model: nomic-embed-text init: chat: include: false pull-model-strategy: WHEN_MISSING

在这里,我们启用了ChromaDB的schema初始化。接着,我们将nomic-embed-text配置为嵌入模型,并指示Ollama在本地未安装该模型时自动拉取。

当然,我们也可以根据需求使用Ollama中的其他嵌入模型,或是 Hugging Face 的模型。


3.3 开发过程中使用 Testcontainers

虽然 Testcontainers 主要用于集成测试,但我们也可以在本地开发时使用它。

为此,我们将在src/test/java目录中创建一个单独的主类(main class):

class TestApplication { public static void main(String[] args) { SpringApplication.from(Application::main) .with(TestcontainersConfiguration.class) .run(args); }}

我们创建了一个TestApplication类,

并在其main方法中,通过 

TestcontainersConfiguration 启动我们的主应用类 Application

这个配置有助于我们在本地搭建和管理外部服务。我们可以运行Spring Boot 应用程序,并让它连接通过 Testcontainers 启动的外部服务。


4. 在应用启动时填充 ChromaDB

现在我们已经搭建好了本地环境,接下来在应用启动时向 ChromaDB 向量存储中填充一些示例数据。


4.1 从 PoetryDB 获取诗歌数据

为了演示,我们将使用 PoetryDB API 获取一些诗歌。

我们先创建一个名为 PoetryFetcher 的工具类来实现这个功能:

class PoetryFetcher { private static final String BASE_URL = \"https://poetrydb.org/author/\"; private static final String DEFAULT_AUTHOR_NAME = \"Shakespeare\"; public static List fetch() { return fetch(DEFAULT_AUTHOR_NAME); } public static List fetch(String authorName) { return RestClient .create() .get() .uri(URI.create(BASE_URL + authorName)) .retrieve() .body(new ParameterizedTypeReference() {}); }}
record Poem(String title, List lines) {}

我们使用 RestClient 调用 PoetryDB API,并传入指定的 authorName。为了将API响应反序列化为Poem记录的列表,我们使用了 ParameterizedTypeReference,无需显式指定泛型类型,Java 会为我们自动推断类型。我们还重载了fetch()方法(不带任何参数),以便默认获取莎士比亚(Shakespeare)的诗歌。


4.2 将文档存入 ChromaDB 向量存储

现在,为了在应用启动时将诗歌填充到 ChromaDB 向量存储中,我们将创建一个 VectorStoreInitializer 类,并实现 ApplicationRunner 接口:

@Componentclass VectorStoreInitializer implements ApplicationRunner { private final VectorStore vectorStore; // standard constructor @Override public void run(ApplicationArguments args) { List documents = PoetryFetcher .fetch() .stream() .map(poem -> {  Map metadata = Map.of(\"title\", poem.title());  String content = String.join(\"\\n\", poem.lines());  return new Document(content, metadata); }) .toList(); vectorStore.add(documents); }}

在我们的VectorStoreInitializer中,我们通过自动注入(@Autowired)获取了一个 VectorStore 实例。

run()方法中,我们使用 

PoetryFetcher 工具类获取诗歌列表。

然后,将每首诗映射成一Document,将诗的内容(lines)作为文本内容,标题(title)作为元数据。

最后,我们将所有文档存入向量存储。当调用 add() 方法时,Spring AI 会自动将纯文本内容转换成向量表示,然后存入向量存储中,无需我们显式调用 EmbeddingModel Bean 进行转换。

默认情况下,Spring AI使用

SpringAiCollection作为向量存储中

的集合名称,但我们可以通过spring.ai.vectorstore.chroma.collection-name 

属性来覆盖该名称。


5. 测试语义搜索

在 ChromaDB 向量存储被填充数据后,让我们来验证语义搜索功能:

private static final int MAX_RESULTS = 3;@ParameterizedTest@ValueSource(strings = {\"Love and Romance\", \"Time and Mortality\", \"Jealousy and Betrayal\"})void whenSearchingShakespeareTheme_thenRelevantPoemsReturned(String theme) { SearchRequest searchRequest = SearchRequest .builder() .query(theme) .topK(MAX_RESULTS) .build(); List documents = vectorStore.similaritySearch(searchRequest); assertThat(documents) .hasSizeLessThanOrEqualTo(MAX_RESULTS) .allSatisfy(document -> { String title = String.valueOf(document.getMetadata().get(\"title\")); assertThat(title) .isNotBlank(); });}

这里,我们使用 @ValueSource 向测试方法传入一些常见的莎士比亚主题。然后,创建一个SearchRequest对象,将主题作为查询内容,将 MAX_RESULTS 设为期望的返回结果数量。

接着,我们调用 vectorStore Bean 的 similaritySearch() 方法,传入我们的 searchRequest。与 add() 方法类似,Spring AI 会先将查询内容转换为向量表示,再去向量存储中执行搜索。

返回的文档中包含的诗歌都是与给定主题语义相关的,即使它们没有包含确切的关键词。


6. 总结

本文介绍了如何将 ChromaDB 向量存储与 Spring AI 集成。

我们通过Testcontainers启动了ChromaDB 和 Ollama 的 Docker 容器,搭建了本地测试环境。演示了如何在应用启动时通过 PoetryDB API 向向量存储中填充诗歌数据。最后,我们用常见的诗歌主题验证了语义搜索功能。

私信1v1直连大厂总监·「免。米」