Spring AI / Vector Databases / Elasticsearch

Spring AI

参考文档

向量数据库

Elasticsearch

Elasticsearch

本节将引导您设置Elasticsearch VectorStore,用于存储文档嵌入并执行相似性搜索。

Elasticsearch是一个基于Apache Lucene库的开源搜索和分析引擎。

前置条件

一个正在运行的Elasticsearch实例。有以下选项可供选择:

  • Docker
  • 自管理Elasticsearch
  • Elastic Cloud

自动配置

Spring AI的自动配置、启动模块的构件名称发生了重大变化。请参阅升级说明以获取更多信息。

Spring AI为Elasticsearch向量存储提供了Spring Boot自动配置。要启用它,请将以下依赖项添加到项目的Maven pom.xml或Gradle build.gradle构建文件中:

Maven

xml 复制代码
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-vector-store-elasticsearch</artifactId>
</dependency>

Gradle

gradle 复制代码
dependencies {
    implementation 'org.springframework.ai:spring-ai-starter-vector-store-elasticsearch'
}

对于3.3.0之前的spring-boot版本,有必要显式添加版本大于8.13.3的elasticsearch-java依赖,否则使用的旧版本将与执行的查询不兼容:

Maven

xml 复制代码
<dependency>
    <groupId>co.elastic.clients</groupId>
    <artifactId>elasticsearch-java</artifactId>
    <version>8.13.3</version>
</dependency>

Gradle

gradle 复制代码
dependencies {
    implementation 'co.elastic.clients:elasticsearch-java:8.13.3'
}

请参阅依赖管理部分,将Spring AI BOM添加到您的构建文件中。

请参阅构件仓库部分,将Maven Central和/或快照仓库添加到您的构建文件中。

向量存储实现可以为您初始化所需的模式,但您必须通过指定适当的构造函数中的initializeSchema布尔值,或在application.properties文件中设置...​initialize-schema=true来选择启用。或者,您也可以选择不进行初始化,而是使用Elasticsearch客户端手动创建索引,如果索引需要高级映射或额外配置,这可能会很有用。

这是一个破坏性变更! 在早期版本的Spring AI中,这种模式初始化是默认发生的。

请查看向量存储的配置参数列表,以了解默认值和配置选项。这些属性也可以通过配置ElasticsearchVectorStoreOptions bean来设置。

此外,您还需要一个配置好的EmbeddingModel bean。请参阅嵌入模型部分以获取更多信息。

现在您可以在应用程序中将ElasticsearchVectorStore作为向量存储进行自动装配。

java 复制代码
@Autowired VectorStore vectorStore;

// ...

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

// 将文档添加到Elasticsearch
vectorStore.add(documents);

// 检索与查询相似的文档
List<Document> results = this.vectorStore.similaritySearch(SearchRequest.builder().query("Spring").topK(5).build());

配置属性

要连接Elasticsearch并使用ElasticsearchVectorStore,您需要提供实例的访问详细信息。一个简单的配置可以通过Spring Boot的application.yml提供:

yaml 复制代码
spring:
  elasticsearch:
    uris: <elasticsearch实例URIs>
    username: <elasticsearch用户名>
    password: <elasticsearch密码>
  ai:
    vectorstore:
      elasticsearch:
        initialize-schema: true
        index-name: custom-index
        dimensions: 1536
        similarity: cosine

spring.elasticsearch.*开头的Spring Boot属性用于配置Elasticsearch客户端:

属性 描述 默认值
spring.elasticsearch.connection-timeout 与Elasticsearch通信时使用的连接超时时间 1s
spring.elasticsearch.password Elasticsearch认证密码 -
spring.elasticsearch.username Elasticsearch认证用户名 -
spring.elasticsearch.uris 要使用的Elasticsearch实例的逗号分隔列表 http://localhost:9200
spring.elasticsearch.path-prefix 添加到发送给Elasticsearch的每个请求路径的前缀 -
spring.elasticsearch.restclient.sniffer.delay-after-failure 失败后调度的嗅探执行延迟 1m
spring.elasticsearch.restclient.sniffer.interval 连续普通嗅探执行之间的间隔 5m
spring.elasticsearch.restclient.ssl.bundle SSL捆绑包名称 -
spring.elasticsearch.socket-keep-alive 是否启用客户端与Elasticsearch之间的套接字保持活动 false
spring.elasticsearch.socket-timeout 与Elasticsearch通信时使用的套接字超时时间 30s

spring.ai.vectorstore.elasticsearch.*开头的属性用于配置ElasticsearchVectorStore:

属性 描述 默认值
spring.ai.vectorstore.elasticsearch.initialize-schema 是否初始化所需的模式 false
spring.ai.vectorstore.elasticsearch.index-name 存储向量的索引名称 spring-ai-document-index
spring.ai.vectorstore.elasticsearch.dimensions 向量的维度数 1536
spring.ai.vectorstore.elasticsearch.similarity 要使用的相似度函数 cosine
spring.ai.vectorstore.elasticsearch.embedding-field-name 要搜索的向量字段名称 embedding

以下相似度函数可用:

  • cosine - 默认值,适用于大多数用例。衡量向量之间的余弦相似度。
  • l2_norm - 向量之间的欧几里得距离。值越低表示相似度越高。
  • dot_product - 归一化向量的最佳性能(例如,OpenAI嵌入)。

有关每种相似度函数的更多详细信息,请参阅Elasticsearch密集向量文档。

元数据过滤

您可以像使用Elasticsearch一样,利用通用的、可移植的元数据过滤器。

例如,您可以使用文本表达式语言:

java 复制代码
vectorStore.similaritySearch(SearchRequest.builder()
        .query("The World")
        .topK(TOP_K)
        .similarityThreshold(SIMILARITY_THRESHOLD)
        .filterExpression("author in ['john', 'jill'] && 'article_type' == 'blog'").build());

或者使用Filter.Expression DSL以编程方式:

java 复制代码
FilterExpressionBuilder b = new FilterExpressionBuilder();

vectorStore.similaritySearch(SearchRequest.builder()
        .query("The World")
        .topK(TOP_K)
        .similarityThreshold(SIMILARITY_THRESHOLD)
        .filterExpression(b.and(
                b.in("author", "john", "jill"),
                b.eq("article_type", "blog")).build()).build());

这些(可移植的)过滤器表达式会自动转换为专有的Elasticsearch查询字符串查询。

例如,这个可移植的过滤器表达式:

复制代码
author in ['john', 'jill'] && 'article_type' == 'blog'

会被转换为专有的Elasticsearch过滤器格式:

复制代码
(metadata.author:john OR jill) AND metadata.article_type:blog

手动配置

您可以选择不使用Spring Boot自动配置,而是手动配置Elasticsearch向量存储。为此,您需要将spring-ai-elasticsearch-store添加到项目中:

Maven

xml 复制代码
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-elasticsearch-store</artifactId>
</dependency>

Gradle

gradle 复制代码
dependencies {
    implementation 'org.springframework.ai:spring-ai-elasticsearch-store'
}

创建一个Elasticsearch RestClient bean。请阅读Elasticsearch文档,了解有关自定义RestClient配置的更深入信息。

java 复制代码
@Bean
public RestClient restClient() {
    return RestClient.builder(new HttpHost("<host>", 9200, "http"))
        .setDefaultHeaders(new Header[]{
            new BasicHeader("Authorization", "Basic <编码后的用户名和密码>")
        })
        .build();
}

然后使用构建器模式创建ElasticsearchVectorStore bean:

java 复制代码
@Bean
public VectorStore vectorStore(RestClient restClient, EmbeddingModel embeddingModel) {
    ElasticsearchVectorStoreOptions options = new ElasticsearchVectorStoreOptions();
    options.setIndexName("custom-index");    // 可选:默认为 "spring-ai-document-index"
    options.setSimilarity(COSINE);           // 可选:默认为 COSINE
    options.setDimensions(1536);             // 可选:默认为模型维度或 1536

    return ElasticsearchVectorStore.builder(restClient, embeddingModel)
        .options(options)                     // 可选:使用自定义选项
        .initializeSchema(true)               // 可选:默认为 false
        .batchingStrategy(new TokenCountBatchingStrategy()) // 可选:默认为 TokenCountBatchingStrategy
        .build();
}

// 这可以是任何 EmbeddingModel 实现
@Bean
public EmbeddingModel embeddingModel() {
    return new OpenAiEmbeddingModel(OpenAiEmbeddingOptions.builder().apiKey(System.getenv("OPENAI_API_KEY")).build());
}

访问原生客户端

Elasticsearch向量存储实现通过getNativeClient()方法提供对底层原生Elasticsearch客户端(ElasticsearchClient)的访问:

java 复制代码
ElasticsearchVectorStore vectorStore = context.getBean(ElasticsearchVectorStore.class);
Optional<ElasticsearchClient> nativeClient = vectorStore.getNativeClient();

if (nativeClient.isPresent()) {
    ElasticsearchClient client = nativeClient.get();
    // 使用原生客户端执行Elasticsearch特定的操作
}

原生客户端让您可以访问Elasticsearch特定的功能和操作,这些可能未通过VectorStore接口暴露。