Spring AI 與 ChromaDB 向量存儲
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-M4</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
<version>1.0.0-M4</version>
</dependency>
ChromaDB 啟動器依賴項使我們能夠與 ChromaDB 向量儲存建立連接並與其互動。
此外,我們導入Ollama starter dependency ,我們將使用它來運行我們的嵌入模型。
由於目前版本1.0.0-M4
是一個里程碑版本,我們還需要將 Spring Milestones 儲存庫新增到我們的pom.xml
中:
<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 啟動器,因此我們也將Spring AI 物料清單 (BOM)包含在pom.xml
中:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
透過此添加,我們現在可以從兩個入門依賴項中刪除version
標籤。
BOM 消除了版本衝突的風險,並確保我們的 Spring AI 依賴項相互相容。
3. 使用測試容器設定本機測試環境
為了促進本地開發和測試,我們將使用 Testcontainers 來設定 ChromaDB 向量儲存和 Ollama 服務。
透過 Testcontainers 運行所需服務的先決條件是一個活動的 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.定義測試容器 Bean
接下來,讓我們建立一個@TestConfiguration
類別來定義我們的 Testcontainers beans:
@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 也會在分別在預設連接埠8000
和11434
本地運行時自動連接到 ChromaDB 和 Ollama。
但是,在生產中,我們可以使用相應的 Spring AI 屬性來覆蓋連接詳細資訊:
spring:
ai:
vectorstore:
chroma:
client:
host: ${CHROMADB_HOST}
port: ${CHROMADB_PORT}
ollama:
base-url: ${OLLAMA_BASE_URL}
一旦連接細節配置正確,Spring AI 就會自動為我們建立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 的架構初始化。然後,我們將nomic-embed-text
配置為我們的嵌入模型,並指示 Ollama 提取該模型(如果我們的系統中不存在該模型) 。
或者,我們可以根據要求使用 Ollama 的不同嵌入模型或Hugging Face 模型。
3.3.在開發過程中使用測試容器
雖然 Testcontainers 主要用於整合測試,但我們也可以在本地開發期間使用它。
為了實現這一點,我們將在src/test/java
目錄中建立一個單獨的主類別:
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 取得Poetry
記錄
在我們的演示中,我們將使用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<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
使用指定的authorName
來呼叫PoetryDB API。為了反序列化對Poem
記錄清單的 API 回應,我們使用ParameterizedTypeReference
而不明確指定通用回應類型,Java 將為我們推斷類型。
我們也重載了不含任何參數的fetch()
方法來檢索作者Shakespeare
的詩。我們將在下一節中使用此方法。
4.2.在 ChromaDB 向量儲存中儲存Documents
現在,為了在應用程式啟動期間用詩歌填充 ChromaDB 向量存儲,我們將創建一個實現ApplicationRunner
介面的VectorStoreInitializer
類別:
@Component
class VectorStoreInitializer implements ApplicationRunner {
private final VectorStore vectorStore;
// standard constructor
@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("", poem.lines());
return new Document(content, metadata);
})
.toList();
vectorStore.add(documents);
}
}
在我們的VectorStoreInitializer
中,我們自動組裝VectorStore
的實例。
在run()
方法中,我們使用PoetryFetcher
實用程式類別來檢索Poem
記錄清單。然後,我們將每poem
映射到一個Document
中,其中的lines
作為content
, title
作為metadata
。
最後,我們將所有documents
儲存在向量儲存中。當我們呼叫add()
方法時,Spring AI 會自動將我們的明文content
轉換為向量表示,然後將其儲存在向量儲存中。我們不需要使用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
.query(theme)
.withTopK(MAX_RESULTS);
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
對象,其中theme
作為query
, MAX_RESULTS
作為所需結果的數量
接下來,我們使用searchRequest
呼叫vectorStore
bean 的similaritySearch()
方法。與VectorStore
的add()
方法類似,Spring AI 在查詢向量儲存之前將我們的query
轉換為其向量表示。
傳回的documents
將包含在語義上與給定theme
相關的詩歌,即使它們不包含確切的關鍵字。
六、結論
在本文中,我們探討如何將 ChromaDB 向量儲存與 Spring AI 整合。
使用 Testcontainers,我們為 ChromaDB 和 Ollama 服務啟動了 Docker 容器,並建立了本機測試環境。
我們研究瞭如何在應用程式啟動期間使用 PoetryDB API 中的詩歌填充向量儲存。然後,我們使用常見的詩歌主題來驗證我們的語義搜尋功能。
與往常一樣,本文中使用的所有程式碼範例都可以在 GitHub 上找到。