Apache Cassandra ベクトルストア

このセクションでは、ドキュメントの埋め込みを保存し、類似性検索を実行するための CassandraVectorStore のセットアップについて説明します。

Apache Cassandra とは何ですか?

Apache Cassandra ® (英語) は、線形スケーラビリティ、実証済みのフォールトトレランス、低レイテンシで定評のある真のオープンソース分散データベースであり、ミッションクリティカルなトランザクションデータに最適なプラットフォームです。

Vector Similarity Search (VSS) は、クラス最高のパフォーマンスと関連性を保証する JVector ライブラリに基づいています。

Apache Cassandra でのベクトル検索は次のように簡単に実行されます。

SELECT content FROM table ORDER BY content_vector ANN OF query_embedding;

これに関する詳細なドキュメントは、ここ [Apache] (英語) で読むことができます。

この Spring AI ベクトルストアは、最新の RAG アプリケーションで動作し、既存のデータやテーブルの上に後付けできるように設計されています。

ストアは、セマンティック検索、地理的近接検索など、既存のデータベース内の RAG 以外のユースケースにも使用できます。

ストアは、構成に応じて必要に応じてスキーマを自動的に作成または拡張します。スキーマの変更を望まない場合は、disallowSchemaChanges を使用してストアを構成します。

spring-boot-autoconfigure を使用する場合、disallowSchemaChanges は Spring Boot 標準に従ってデフォルトで true に設定され、application.properties ファイルで …​initialize-schema=true を設定してスキーマの作成 / 変更をオプトインする必要があります。

JVector とは何ですか ?

JVector [GitHub] (英語) は、純粋な Java 組み込みベクトル検索エンジンです。

これは、他の HNSW ベクトル類似性検索実装と比べて、次の点で優れています。

  • アルゴリズムが高速。JVector は、DiskANN や関連研究にヒントを得た最先端のグラフアルゴリズムを使用して、高い再現性と低いレイテンシを実現します。

  • 実装が高速。JVector は Panama SIMD API を使用してインデックスの構築とクエリを高速化します。

  • メモリ効率に優れています。JVector は積量子化を使用してベクトルを圧縮し、検索中にベクトルをメモリ内に保持できるようにします。

  • ディスク対応。JVector のディスクレイアウトは、クエリ時に必要な最小限の IOPS を実行するように設計されています。

  • 同時実行。インデックスビルドは少なくとも 32 スレッドまで直線的に拡張されます。スレッドが 2 倍になると、ビルド時間は半分になります。

  • 増分。インデックスを構築しながらクエリを実行します。ベクトルを追加してから検索結果で見つけられるようになるまでの間に遅延はありません。

  • 簡単に埋め込めます。本番環境で使用するユーザーが簡単に埋め込めるように設計された API です。

前提条件

  1. ドキュメントの埋め込みを計算するための EmbeddingModel インスタンス。これは通常、Spring Bean として構成されます。いくつかのオプションが利用可能です:

    • Transformers Embedding - ローカル環境での埋め込みを計算します。デフォルトは ONNX と all-MiniLM-L6-v2 Sentence Transformers 経由です。これで問題なく動作します。

    • OpenAI の埋め込みを使用する場合は、OpenAI 埋め込みエンドポイントを使用します。OpenAI サインアップ (英語) でアカウントを作成し、API キー (英語) で API キートークンを生成する必要があります。

    • 他にも多くの選択肢があります。Embeddings API のドキュメントを参照してください。

  2. バージョン 5.0-beta1 の Apache Cassandra インスタンス

    1. DIY クイックスタート [Apache] (英語)

    2. マネージドサービスとして、アストラ DB (英語) は充実したフリーレベルのサービスを提供します。

依存関係

Spring AI 自動構成、スターターモジュールのアーティファクト名に大きな変更がありました。詳細については、アップグレードノートを参照してください。

依存関係の管理には、依存関係管理セクションに従って、Spring AI BOM を使用することをお勧めします。

これらの依存関係をプロジェクトに追加します。

  • 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-starter-vector-store-cassandra</artifactId>
</dependency>

プロパティの構成

Spring Boot 構成で次のプロパティを使用して、Apache Cassandra ベクトルストアをカスタマイズできます。

プロパティ デフォルト値

spring.ai.vectorstore.cassandra.keyspace

Spring フレームワーク

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

コンテンツ

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

埋め込み

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

16

使用方法

基本的な使い方

CassandraVectorStore インスタンスを Spring Bean として作成します。

@Bean
public VectorStore vectorStore(CqlSession session, EmbeddingModel embeddingModel) {
    return CassandraVectorStore.builder(embeddingModel)
        .session(session)
        .keyspace("my_keyspace")
        .table("my_vectors")
        .build();
}

ベクトルストアインスタンスを取得したら、ドキュメントを追加して検索を実行できます。

// Add documents
vectorStore.add(List.of(
    new Document("1", "content1", Map.of("key1", "value1")),
    new Document("2", "content2", Map.of("key2", "value2"))
));

// Search with filters
List<Document> results = vectorStore.similaritySearch(
    SearchRequest.query("search text")
        .withTopK(5)
        .withSimilarityThreshold(0.7f)
        .withFilterExpression("metadata.key1 == 'value1'")
);

高度な構成

より複雑な使用ケースの場合は、Spring Bean で追加の設定を構成できます。

@Bean
public VectorStore vectorStore(CqlSession session, EmbeddingModel embeddingModel) {
    return CassandraVectorStore.builder(embeddingModel)
        .session(session)
        .keyspace("my_keyspace")
        .table("my_vectors")
        // Configure primary keys
        .partitionKeys(List.of(
            new SchemaColumn("id", DataTypes.TEXT),
            new SchemaColumn("category", DataTypes.TEXT)
        ))
        .clusteringKeys(List.of(
            new SchemaColumn("timestamp", DataTypes.TIMESTAMP)
        ))
        // Add metadata columns with optional indexing
        .addMetadataColumns(
            new SchemaColumn("category", DataTypes.TEXT, SchemaColumnTags.INDEXED),
            new SchemaColumn("score", DataTypes.DOUBLE)
        )
        // Customize column names
        .contentColumnName("text")
        .embeddingColumnName("vector")
        // Performance tuning
        .fixedThreadPoolExecutorSize(32)
        // Schema management
        .disallowSchemaChanges(false)
        // Custom batching strategy
        .batchingStrategy(new TokenCountBatchingStrategy())
        .build();
}

接続構成

Cassandra への接続を構成するには、次の 2 つの方法があります。

  • 注入された CqlSession を使用する (推奨):

@Bean
public VectorStore vectorStore(CqlSession session, EmbeddingModel embeddingModel) {
    return CassandraVectorStore.builder(embeddingModel)
        .session(session)
        .keyspace("my_keyspace")
        .table("my_vectors")
        .build();
}
  • ビルダーで接続の詳細を直接使用する:

@Bean
public VectorStore vectorStore(EmbeddingModel embeddingModel) {
    return CassandraVectorStore.builder(embeddingModel)
        .contactPoint(new InetSocketAddress("localhost", 9042))
        .localDatacenter("datacenter1")
        .keyspace("my_keyspace")
        .build();
}

メタデータフィルタリング

CassandraVectorStore を使用すると、汎用的でポータブルなメタデータフィルターを活用できます。メタデータ列を検索可能にするには、主キーまたは SAI インデックスのいずれかにする必要があります。主キー以外の列をインデックス化するには、メタデータ列を SchemaColumnTags.INDEXED で構成します。

例: 次のいずれかのテキスト式言語を使用できます。

vectorStore.similaritySearch(
    SearchRequest.builder().query("The World")
        .topK(5)
        .filterExpression("country in ['UK', 'NL'] && year >= 2020").build());

または、プログラムで DSL という式を使用します。

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

vectorStore.similaritySearch(
    SearchRequest.builder().query("The World")
        .topK(5)
        .filterExpression(f).build());

ポータブルフィルター式は自動的に CQL クエリ [Apache] (英語) に変換されます。

高度な例: Wikipedia データセット上のベクトルストア

次の例は、既存のスキーマでストアを使用する方法を示しています。ここでは、完全なウィキペディアデータセットがベクトル化された状態で付属する github.com/datastax-labs/colbert-wikipedia-data (英語) プロジェクトのスキーマを使用します。

まず、Cassandra データベースにスキーマを作成します。

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

次に、ビルダーパターンを使用してストアを構成します。

@Bean
public VectorStore vectorStore(CqlSession session, 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)
    );

    return CassandraVectorStore.builder()
        .session(session)
        .embeddingModel(embeddingModel)
        .keyspace("wikidata")
        .table("articles")
        .partitionKeys(partitionColumns)
        .clusteringKeys(clusteringColumns)
        .contentColumnName("body")
        .embeddingColumnName("all_minilm_l6_v2_embedding")
        .indexName("all_minilm_l6_v2_ann")
        .disallowSchemaChanges(true)
        .addMetadataColumns(extraColumns)
        .primaryKeyTranslator((List<Object> primaryKeys) -> {
            if (primaryKeys.isEmpty()) {
                return "test§¶0";
            }
            return String.format("%s§¶%s", primaryKeys.get(2), primaryKeys.get(3));
        })
        .documentIdTranslator((id) -> {
            String[] parts = id.split("§¶");
            String title = parts[0];
            int chunk_no = parts.length > 1 ? Integer.parseInt(parts[1]) : 0;
            return List.of("simplewiki", "en", title, chunk_no, 0);
        })
        .build();
}

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

完全な Wikipedia データセットの読み込み

完全な Wikipedia データセットを読み込むには:

  1. s.apache.org/simplewiki-sstable-tar (英語) から simplewiki-sstable.tar をダウンロード (これには時間がかかります。ファイルは数十 GB あります)

  2. データをロードします:

tar -xf simplewiki-sstable.tar -C ${CASSANDRA_DATA}/data/wikidata/articles-*/
nodetool import wikidata articles ${CASSANDRA_DATA}/data/wikidata/articles-*/
  • このテーブルに既存のデータがある場合は、tar を実行するときに tarball のファイルが既存の sstables を上書きしないことを確認してください。

  • nodetool import の代替案としては、Cassandra を再起動するだけです。

  • インデックスに障害がある場合は、自動的に再構築されます。

ネイティブクライアントへのアクセス

Cassandra ベクトルストアの実装は、getNativeClient() メソッドを通じて、基盤となるネイティブ Cassandra クライアント (CqlSession) へのアクセスを提供します。

CassandraVectorStore vectorStore = context.getBean(CassandraVectorStore.class);
Optional<CqlSession> nativeClient = vectorStore.getNativeClient();

if (nativeClient.isPresent()) {
    CqlSession session = nativeClient.get();
    // Use the native client for Cassandra-specific operations
}

ネイティブクライアントを使用すると、VectorStore インターフェースでは公開されない可能性のある Cassandra 固有の機能や操作にアクセスできます。