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 时,disallowSchemaChanges 默认为 true(根据 Spring Boot 标准),您必须通过在相应的构造函数中指定 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,并将在 springframework keyspace、ai_vector_store 表中自动创建一个默认模式。

Cassandra Java 驱动程序最容易通过类路径上的 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配置元数据列。

高级示例:基于完整维基百科数据集的向量存储

以下示例演示如何在现有模式上使用存储。这里我们使用来自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 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();
}

完整维基百科数据集

并且,如果您想加载完整的维基百科数据集。首先从以下链接下载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-*/
如果此表中存在现有数据,则在执行tar操作时,您需要检查压缩包中的文件是否会覆盖现有的 SSTable 文件。
除了nodetool import之外,还可以重新启动 Cassandra。
如果索引中存在任何错误,它们将自动重建。