1. 概述
传统数据库通常依赖精确关键词或基础模式匹配实现搜索功能。这种方案在简单应用中尚可,但无法真正理解自然语言查询背后的语义和上下文。
向量数据库通过将数据存储为捕获其语义的数值向量来解决这一局限。语义相近的词在向量空间中会彼此靠近,从而实现语义搜索——即使结果不包含查询中的精确关键词,也能返回相关内容。
本教程将探讨如何将开源向量数据库 ChromaDB 与 Spring AI 集成。
要将文本数据转换为 ChromaDB 可存储和搜索的向量,我们需要一个嵌入模型。我们将使用 Ollama 在本地运行嵌入模型。
2. 依赖配置
首先在项目的 pom.xml
中添加必要依赖:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-chroma-store-spring-boot-starter</artifactId>
<version>1.0.0-M6</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
<version>1.0.0-M6</version>
</dependency>
✅ ChromaDB 启动依赖用于建立与向量数据库的连接
✅ Ollama 启动依赖用于运行本地嵌入模型
由于当前版本 1.0.0-M6
是里程碑版本,还需在 pom.xml
添加 Spring Milestones 仓库:
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
⚠️ 里程碑版本发布在此仓库而非标准 Maven 中央仓库
为避免版本冲突,添加 Spring AI BOM(物料清单):
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
添加后可移除启动依赖中的 version
标签,BOM 会自动管理版本兼容性。
3. 使用 Testcontainers 搭建本地测试环境
为简化本地开发和测试,我们将使用 Testcontainers 启动 ChromaDB 和 Ollama 服务。
前提:需要运行中的 Docker 实例。
3.1 测试依赖
在 pom.xml
添加测试依赖:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-spring-boot-testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>chromadb</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>ollama</artifactId>
<scope>test</scope>
</dependency>
这些依赖提供启动外部服务临时 Docker 实例所需类。
3.2 定义 Testcontainers Bean
创建 @TestConfiguration
类定义容器 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
注解动态注册连接所需属性。
⚠️ 即使不用 Testcontainers,Spring AI 也会自动连接本地默认端口(ChromaDB 8000,Ollama 11434)
生产环境可通过以下配置覆盖连接信息:
spring:
ai:
vectorstore:
chroma:
client:
host: ${CHROMADB_HOST}
port: ${CHROMADB_PORT}
ollama:
base-url: ${OLLAMA_BASE_URL}
配置正确后,Spring AI 会自动创建 VectorStore
和 EmbeddingModel
Bean。
还需在 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 模式初始化
✅ 指定 nomic-embed-text
为嵌入模型
✅ 配置 Ollama 在缺失模型时自动拉取
3.3 开发阶段使用 Testcontainers
Testcontainers 主要用于集成测试,但也可用于本地开发。在 src/test/java
创建独立启动类:
class TestApplication {
public static void main(String[] args) {
SpringApplication.from(Application::main)
.with(TestcontainersConfiguration.class)
.run(args);
}
}
此配置可便捷启动外部服务,Spring Boot 应用将自动连接到 Testcontainers 管理的服务实例。
4. 应用启动时填充 ChromaDB
现在我们将在应用启动时向 ChromaDB 填充示例数据。
4.1 从 PoetryDB 获取诗歌数据
我们将使用 PoetryDB API 获取诗歌数据。创建工具类:
class PoetryFetcher {
private static final String BASE_URL = "https://poetrydb.org/author/";
private static final String DEFAULT_AUTHOR_NAME = "Shakespeare";
public static List<Poem> fetch() {
return fetch(DEFAULT_AUTHOR_NAME);
}
public static List<Poem> fetch(String authorName) {
return RestClient
.create()
.get()
.uri(URI.create(BASE_URL + authorName))
.retrieve()
.body(new ParameterizedTypeReference<>() {});
}
}
record Poem(String title, List<String> lines) {}
使用 RestClient
调用 PoetryDB API,通过 ParameterizedTypeReference
自动推断泛型类型。
默认获取莎士比亚作品(后续将使用此方法)。
4.2 存储文档到向量数据库
创建 VectorStoreInitializer
在启动时填充数据:
@Component
class VectorStoreInitializer implements ApplicationRunner {
private final VectorStore vectorStore;
// 标准构造器
@Override
public void run(ApplicationArguments args) {
List<Document> documents = PoetryFetcher
.fetch()
.stream()
.map(poem -> {
Map<String, Object> metadata = Map.of("title", poem.title());
String content = String.join("\n", poem.lines());
return new Document(content, metadata);
})
.toList();
vectorStore.add(documents);
}
}
核心处理逻辑:
- 获取诗歌列表
- 转换为
Document
对象(诗句作为内容,标题作为元数据) - 调用
vectorStore.add()
存储
调用 add()
时,Spring AI 会自动将文本内容转换为向量表示,无需手动使用 EmbeddingModel
。
默认集合名为 SpringAiCollection
,可通过 spring.ai.vectorstore.chroma.collection-name
覆盖。
5. 测试语义搜索
验证语义搜索功能:
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<Document> documents = vectorStore.similaritySearch(searchRequest);
assertThat(documents)
.hasSizeLessThanOrEqualTo(MAX_RESULTS)
.allSatisfy(document -> {
String title = String.valueOf(document.getMetadata().get("title"));
assertThat(title)
.isNotBlank();
});
}
测试要点:
- 使用
@ValueSource
注入常见莎士比亚主题 - 构建
SearchRequest
指定查询词和最大结果数 - 调用
similaritySearch()
执行搜索 - 验证返回结果数量及标题非空
返回结果将包含语义相关的诗歌,即使不含精确关键词。
6. 总结
本文探讨了 Spring AI 集成 ChromaDB 向量数据库的完整流程:
- 使用 Testcontainers 启动 ChromaDB 和 Ollama 服务
- 通过 PoetryDB API 获取诗歌数据并存储到向量数据库
- 基于诗歌主题验证语义搜索功能
这种方案特别适合需要理解自然语言语义的搜索场景,相比传统关键词匹配能提供更智能的检索体验。