Apache Cassandra

本節將引導您設定 CassandraVectorStore,以儲存文件嵌入並執行相似性搜尋。

什麼是 Apache Cassandra ?

Apache Cassandra® 是一個真正的開放原始碼分散式資料庫,以其線性擴展性、經驗證的容錯能力和低延遲而聞名,使其成為任務關鍵型交易資料的完美平台。

其向量相似性搜尋 (VSS) 基於 JVector 函式庫,確保一流的效能和相關性。

在 Apache Cassandra 中執行向量搜尋非常簡單,如下所示:

SELECT content FROM table ORDER BY content_vector ANN OF query_embedding ;

更多相關文件請參閱此處

此 Spring AI 向量儲存區旨在適用於全新的 RAG 應用程式,並且能夠追溯調整到現有的資料和表格之上。

此儲存區也可用於現有資料庫中非 RAG 的使用案例,例如語意搜尋、地理鄰近度搜尋等。

儲存區將根據其配置自動建立或增強架構。如果您不希望進行架構修改,請將儲存區配置為 disallowSchemaChanges

當使用 spring-boot-autoconfigure 時,根據 Spring Boot 標準,disallowSchemaChanges 預設為 true,您必須透過在適當的建構函式中指定 initializeSchema 布林值,或在 application.properties 檔案中設定 …​initialize-schema=true 來選擇加入架構建立/修改。

什麼是 JVector ?

JVector 是一個純 Java 嵌入式向量搜尋引擎。

它在其他 HNSW 向量相似性搜尋實作中脫穎而出,因為它具有:

  • 演算法快速。JVector 使用受 DiskANN 和相關研究啟發的最先進圖形演算法,提供高召回率和低延遲。

  • 實作快速。JVector 使用 Panama SIMD API 加速索引建立和查詢。

  • 記憶體效率高。JVector 使用產品量化壓縮向量,以便它們在搜尋期間可以保留在記憶體中。(作為我們 PQ 實作的一部分,我們的 SIMD 加速 kmeans 類別比 Apache Commons Math 中的快 5 倍。)

  • 磁碟感知。JVector 的磁碟佈局旨在在查詢時執行最少必要的 iops。

  • 並行。索引建立線性擴展到至少 32 個執行緒。執行緒數量加倍,建置時間減半。

  • 增量。在建置索引時查詢您的索引。在新增向量和能夠在搜尋結果中找到它之間沒有延遲。

  • 易於嵌入。API 設計旨在方便嵌入,供在生產環境中使用它的人員使用。

先決條件

  1. 一個 Embedding 實例,用於計算文件嵌入。這通常配置為 Spring Bean。有多種選項可用:

    • Transformers Embedding - 在您的本機環境中計算嵌入。預設是透過 ONNX 和 all-MiniLM-L6-v2 Sentence Transformers。這開箱即用。

    • 如果您想使用 OpenAI 的 Embeddings - 使用 OpenAI 嵌入端點。您需要在 OpenAI 註冊 建立帳戶,並在 API 金鑰 產生 api-key 權杖。

    • 還有更多選擇,請參閱 Embeddings API 文件。

  2. Apache Cassandra 實例,版本 5.0-beta1 或更新版本

    1. DIY 快速入門

    2. 對於託管產品,Astra DB 提供健康的免費方案。

依賴關係

將這些依賴項新增至您的專案

  • 僅適用於 Cassandra 向量儲存區

<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-cassandra-store</artifactId>
</dependency>
  • 或者,對於 RAG 應用程式中所需的一切(使用預設 ONNX 嵌入模型)

<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-cassandra-store-spring-boot-starter</artifactId>
</dependency>
請參閱依賴項管理章節,將 Spring AI BOM 新增至您的建置檔案。

配置屬性

您可以使用 Spring Boot 配置中的以下屬性來自訂 Apache Cassandra 向量儲存區。

屬性 預設值

spring.ai.vectorstore.cassandra.keyspace

springframework

spring.ai.vectorstore.cassandra.table

ai_vector_store

spring.ai.vectorstore.cassandra.initialize-schema

false

spring.ai.vectorstore.cassandra.index-name

spring.ai.vectorstore.cassandra.content-column-name

content

spring.ai.vectorstore.cassandra.embedding-column-name

embedding

spring.ai.vectorstore.cassandra.return-embeddings

false

spring.ai.vectorstore.cassandra.fixed-thread-pool-executor-size

16

用法

建立連線到您的 Apache Cassandra 資料庫的 CassandraVectorStore 實例

@Bean
public VectorStore vectorStore(EmbeddingModel embeddingModel) {

  CassandraVectorStoreConfig config = CassandraVectorStoreConfig.builder().build();

  return new CassandraVectorStore(config, embeddingModel);
}

預設配置連線到 localhost:9042 的 Cassandra,並將在 keyspace springframework、表格 ai_vector_store 中自動建立預設架構。

Cassandra Java Driver 最容易透過類別路徑上的 application.conf 檔案進行配置。更多資訊請參閱此處

然後在您的主要程式碼中,建立一些文件

List<Document> documents = List.of(
   new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("country", "UK", "year", 2020)),
   new Document("The World is Big and Salvation Lurks Around the Corner", Map.of()),
   new Document("You walk forward facing the past and you turn back toward the future.", Map.of("country", "NL", "year", 2023)));

現在將文件新增至您的向量儲存區

vectorStore.add(documents);

最後,檢索與查詢相似的文件

List<Document> results = vectorStore.similaritySearch(
   SearchRequest.query("Spring").withTopK(5));

如果一切順利,您應該會檢索到包含文字 "Spring AI rocks!!" 的文件。

您也可以根據相似性閾值限制結果

List<Document> results = vectorStore.similaritySearch(
   SearchRequest.query("Spring").withTopK(5)
      .withSimilarityThreshold(0.5d));

中繼資料篩選

您也可以將通用的、可移植的 中繼資料篩選器 與 CassandraVectorStore 一起使用。中繼資料欄必須在 CassandraVectorStoreConfig 中配置。

例如,您可以使用文字表達式語言

vectorStore.similaritySearch(
   SearchRequest.query("The World").withTopK(TOP_K)
      .withFilterExpression("country in ['UK', 'NL'] && year >= 2020"));

或以程式設計方式使用表達式 DSL

Filter.Expression f = new FilterExpressionBuilder()
    .and(f.in("country", "UK", "NL"), this.f.gte("year", 2020)).build();

vectorStore.similaritySearch(
   SearchRequest.query("The World").withTopK(TOP_K)
      .withFilterExpression(f));

可移植的篩選表達式會自動轉換為 CQL 查詢

為了使中繼資料欄可搜尋,它們必須是主鍵或 SAI 索引。若要將非主鍵欄編製索引,請使用 SchemaColumnTags.INDEXED 配置中繼資料欄。

進階範例:基於完整 Wikipedia 資料集的向量儲存區

以下範例示範如何在現有架構上使用儲存區。在此範例中,我們使用 github.com/datastax-labs/colbert-wikipedia-data 專案中的架構,該架構隨附已為您向量化的完整 wikipedia 資料集。

用法

首先在 Cassandra 資料庫中建立架構

wget https://s.apache.org/colbert-wikipedia-schema-cql -O colbert-wikipedia-schema.cql

cqlsh -f colbert-wikipedia-schema.cql

然後像這樣配置儲存區

@Bean
public CassandraVectorStore store(EmbeddingModel embeddingModel) {

    List<SchemaColumn> partitionColumns = List.of(new SchemaColumn("wiki", DataTypes.TEXT),
            new SchemaColumn("language", DataTypes.TEXT), new SchemaColumn("title", DataTypes.TEXT));

    List<SchemaColumn> clusteringColumns = List.of(new SchemaColumn("chunk_no", DataTypes.INT),
            new SchemaColumn("bert_embedding_no", DataTypes.INT));

    List<SchemaColumn> extraColumns = List.of(new SchemaColumn("revision", DataTypes.INT),
            new SchemaColumn("id", DataTypes.INT));

    CassandraVectorStoreConfig conf = CassandraVectorStoreConfig.builder()
        .withKeyspaceName("wikidata")
        .withTableName("articles")
        .withPartitionKeys(partitionColumns)
        .withClusteringKeys(clusteringColumns)
        .withContentColumnName("body")
        .withEmbeddingColumndName("all_minilm_l6_v2_embedding")
        .withIndexName("all_minilm_l6_v2_ann")
        .disallowSchemaChanges()
        .addMetadataColumns(extraColumns)
        .withPrimaryKeyTranslator((List<Object> primaryKeys) -> {
            // the deliminator used to join fields together into the document's id is arbitary
            // here "§¶" is used
            if (primaryKeys.isEmpty()) {
                return "test§¶0";
            }
            return format("%s§¶%s", primaryKeys.get(2), primaryKeys.get(3));
        })
        .withDocumentIdTranslator((id) -> {
            String[] parts = id.split("§¶");
            String title = parts[0];
            int chunk_no = 0 < parts.length ? Integer.parseInt(parts[1]) : 0;
            return List.of("simplewiki", "en", title, chunk_no, 0);
        })
        .build();

    return new CassandraVectorStore(conf, embeddingModel());
}

@Bean
public EmbeddingModel embeddingModel() {
    // default is ONNX all-MiniLM-L6-v2 which is what we want
    return new TransformersEmbeddingModel();
}

完整 wikipedia 資料集

而且,如果您想要載入完整的 wikipedia 資料集。首先從此連結下載 simplewiki-sstable.tar s.apache.org/simplewiki-sstable-tar 。這需要一些時間,檔案大小為數十 GB。

tar -xf simplewiki-sstable.tar -C ${CASSANDRA_DATA}/data/wikidata/articles-*/

nodetool import wikidata articles ${CASSANDRA_DATA}/data/wikidata/articles-*/
如果您的表格中已有現有資料,您會想要檢查 tarball 的檔案在執行 tar 時是否會覆蓋現有的 sstable。
nodetool import 的替代方法是重新啟動 Cassandra。
如果索引中有任何失敗,它們將會自動重建。