收藏本站,收获最前沿的人工智能与编程资讯!!

Spring AI Azure AI 服务

技术文档 17℃ 0

本节将指导你设置 AzureVectorStore,用于存储文档嵌入向量,并通过 Azure AI 搜索服务执行相似度搜索。

Azure AI 搜索是一款多功能的云托管信息检索系统,隶属于微软大型人工智能平台。其核心特性之一是支持用户基于向量的存储与检索方式查询信息。

前提条件

Azure 订阅:使用任何 Azure 服务都需要具备 Azure 订阅权限。

Azure AI 搜索服务:创建一个 AI 搜索服务。服务创建完成后,在「设置」下的「密钥」部分获取管理员 API 密钥,并在「概述」部分的「URL」字段中获取服务终结点。

(可选)Azure OpenAI 服务:创建一个 Azure OpenAI 服务。注意:你可能需要填写单独的申请表单才能获取 Azure OpenAI 服务的访问权限。服务创建完成后,在「资源管理」下的「密钥和终结点」部分获取终结点地址和 API 密钥。

配置

启动时,如果你通过在构造函数中将相关的 initialize-schema 布尔属性设置为 true 启用了该功能,AzureVectorStore 会尝试在你的 AI 搜索服务实例中创建新索引;若使用 Spring Boot,则在 application.properties 文件中设置 …initialize-schema=true。

这是一项不兼容变更!在早期版本的 Spring AI 中,该索引结构初始化是默认执行的。

你也可以手动创建索引。

配置 AzureVectorStore 时,你需要上述前提条件中获取的配置信息以及索引名称:

  • Azure AI 搜索终结点

  • Azure AI 搜索密钥

  • (可选)Azure OpenAI API 终结点

  • (可选)Azure OpenAI API 密钥

你可以将这些值配置为操作系统环境变量。

export AZURE_AI_SEARCH_API_KEY=<My AI Search API Key>
export AZURE_AI_SEARCH_ENDPOINT=<My AI Search Index>
export OPENAI_API_KEY=<My Azure AI API Key> (Optional)

你可以将 Azure OpenAI 实现替换为任意支持 Embeddings 接口的有效 OpenAI 实现。例如,你可以使用 Spring AI 的 OpenAI 或 TransformersEmbedding 实现替代 Azure 实现来生成嵌入向量。

依赖项

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

将以下依赖项添加到你的项目中:

1. 选择一个 Embeddings 接口实现,可选项包括:

  • OpenAI 嵌入向量

  • Azure AI 嵌入向量

  • 本地句子转换器嵌入向量

<dependency>
   <groupId>org.springframework.ai</groupId>
   <artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>

2. Azure(AI 搜索)向量存储

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-azure-store</artifactId>
</dependency>

参考依赖管理章节,将 Spring AI BOM 添加到你的构建文件中。

配置属性

你可以在 Spring Boot 配置中使用以下属性自定义 Azure 向量存储。

属性默认值
spring.ai.vectorstore.azure.url
spring.ai.vectorstore.azure.api-key
spring.ai.vectorstore.azure.useKeylessAuthfalse
spring.ai.vectorstore.azure.initialize-schemafalse
spring.ai.vectorstore.azure.index-namespring_ai_azure_vector_store
spring.ai.vectorstore.azure.default-top-k4
spring.ai.vectorstore.azure.default-similarity-threshold0.0
spring.ai.vectorstore.azure.content-field-namecontent
spring.ai.vectorstore.azure.embedding-field-nameembedding
spring.ai.vectorstore.azure.metadata-field-namemetadata

示例代码

在应用中配置 SearchIndexClient,可使用以下代码:

@Bean
public SearchIndexClient searchIndexClient() {
  return new SearchIndexClientBuilder().endpoint(System.getenv("AZURE_AI_SEARCH_ENDPOINT"))
    .credential(new AzureKeyCredential(System.getenv("AZURE_AI_SEARCH_API_KEY")))
    .buildClient();
}

创建向量存储,可通过注入上述示例中创建的 SearchIndexClient Bean,以及 Spring AI 库提供的、实现目标 Embeddings 接口的 EmbeddingModel,使用以下代码:

@Bean
public VectorStore vectorStore(SearchIndexClient searchIndexClient, EmbeddingModel embeddingModel) {

  return AzureVectorStore.builder(searchIndexClient, embeddingModel)
    .initializeSchema(true)
    // 定义用于相似度搜索过滤器的元数据字段
    .filterMetadataFields(List.of(MetadataField.text("country"), MetadataField.int64("year"),
            MetadataField.date("activationDate")))
    .defaultTopK(5)
    .defaultSimilarityThreshold(0.7)
    .indexName("spring-ai-document-index")
    .build();
}

你必须显式列出过滤表达式中使用的所有元数据字段名称和类型。上述代码注册了可过滤的元数据字段:文本类型的 country、64 位整型的 year 以及布尔类型的 active。

如果可过滤元数据字段新增了内容,你必须重新上传/更新包含该元数据的文档。

在主代码中,创建若干文档:

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", "BG", "year", 2020)),
	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("country", "NL", "year", 2023)));

将文档添加到向量存储:

vectorStore.add(documents);

最后,检索与查询语句相似的文档:

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

如果一切正常,你将检索到包含文本「Spring AI rocks!!」的文档。

元数据过滤

你也可以在 AzureVectorStore 中使用通用、可移植的元数据过滤器。

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

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

或通过表达式 DSL 编程实现:

FilterExpressionBuilder b = new FilterExpressionBuilder();

vectorStore.similaritySearch(
    SearchRequest.builder()
      .query("The World")
      .topK(TOP_K)
      .similarityThreshold(SIMILARITY_THRESHOLD)
      .filterExpression(b.and(
         b.in("country", "UK", "NL"),
         b.gte("year", 2020)).build()).build());

可移植的过滤表达式会自动转换为 Azure 搜索专用的 OData 过滤器。例如,以下可移植过滤表达式:

country in ['UK', 'NL'] && year >= 2020

会被转换为以下 Azure OData 过滤表达式:

$filter search.in(meta_country, 'UK,NL', ',') and meta_year ge 2020

自定义字段名

默认情况下,Azure 向量存储在 Azure AI 搜索索引中使用以下字段名:

  • content - 存储文档文本

  • embedding - 存储向量嵌入

  • metadata - 存储文档元数据

但是,当使用已存在的、采用不同字段名的 Azure AI 搜索索引时,你可以配置自定义字段名以匹配索引结构。这让你无需修改现有索引,即可将 Spring AI 与之集成。

使用场景

自定义字段名在以下场景中尤为实用:

  • 集成现有索引:你的组织已拥有遵循既定字段命名规范(如 chunk_text、vector、meta_data)的 Azure AI 搜索索引。

  • 遵循命名标准:你的团队使用与默认值不同的特定命名规范。

  • 从其他系统迁移:你正在从其他向量数据库或搜索系统迁移,并希望保持字段名一致。

通过属性配置

你可以通过 Spring Boot 应用属性配置自定义字段名:

spring.ai.vectorstore.azure.url=${AZURE_AI_SEARCH_ENDPOINT}
spring.ai.vectorstore.azure.api-key=${AZURE_AI_SEARCH_API_KEY}
spring.ai.vectorstore.azure.index-name=my-existing-index
spring.ai.vectorstore.azure.initialize-schema=false

# 匹配现有索引结构的自定义字段名
spring.ai.vectorstore.azure.content-field-name=chunk_text
spring.ai.vectorstore.azure.embedding-field-name=vector
spring.ai.vectorstore.azure.metadata-field-name=meta_data

使用带有自定义字段名的现有索引时,设置 initialize-schema=false,避免 Spring AI 尝试使用默认结构创建新索引。

通过构建器 API 配置

此外,你可以通过构建器 API 编程配置自定义字段名:

@Bean
public VectorStore vectorStore(SearchIndexClient searchIndexClient, EmbeddingModel embeddingModel) {

	return AzureVectorStore.builder(searchIndexClient, embeddingModel)
		.indexName("my-existing-index")
		.initializeSchema(false) // 不创建结构 - 使用现有索引
		// 配置自定义字段名以匹配现有索引
		.contentFieldName("chunk_text")
		.embeddingFieldName("vector")
		.metadataFieldName("meta_data")
		.filterMetadataFields(List.of(
			MetadataField.text("category"),
			MetadataField.text("source")))
		.build();
}

完整示例:使用现有索引

以下是一个完整示例,展示如何将 Spring AI 与带有自定义字段名的现有 Azure AI 搜索索引配合使用:

@Configuration
public class VectorStoreConfig {

	@Bean
	public SearchIndexClient searchIndexClient() {
		return new SearchIndexClientBuilder()
			.endpoint(System.getenv("AZURE_AI_SEARCH_ENDPOINT"))
			.credential(new AzureKeyCredential(System.getenv("AZURE_AI_SEARCH_API_KEY")))
			.buildClient();
	}

	@Bean
	public VectorStore vectorStore(SearchIndexClient searchIndexClient,
			EmbeddingModel embeddingModel) {

		return AzureVectorStore.builder(searchIndexClient, embeddingModel)
			.indexName("production-documents-index")
			.initializeSchema(false) // 使用现有索引
			// 映射到现有索引字段名
			.contentFieldName("document_text")
			.embeddingFieldName("text_vector")
			.metadataFieldName("document_metadata")
			// 定义现有结构中的可过滤元数据字段
			.filterMetadataFields(List.of(
				MetadataField.text("department"),
				MetadataField.int64("year"),
				MetadataField.date("created_date")))
			.defaultTopK(10)
			.defaultSimilarityThreshold(0.75)
			.build();
	}
}

之后你可以正常使用向量存储:

// 使用带有自定义字段名的现有索引进行搜索
List<Document> results = vectorStore.similaritySearch(
	SearchRequest.builder()
		.query("artificial intelligence")
		.topK(5)
		.filterExpression("department == 'Engineering' && year >= 2023")
		.build());

// 结果包含来自 document_text 字段的文档文本
results.forEach(doc -> System.out.println(doc.getText()));

使用自定义字段名创建新索引

你也可以通过设置 initialize-schema=true,使用自定义字段名创建新索引:

@Bean
public VectorStore vectorStore(SearchIndexClient searchIndexClient,
		EmbeddingModel embeddingModel) {

	return AzureVectorStore.builder(searchIndexClient, embeddingModel)
		.indexName("new-custom-index")
		.initializeSchema(true) // 使用自定义字段名创建新索引
		.contentFieldName("text_content")
		.embeddingFieldName("content_vector")
		.metadataFieldName("doc_metadata")
		.filterMetadataFields(List.of(
			MetadataField.text("category"),
			MetadataField.text("author")))
		.build();
}

这会创建一个包含你自定义字段名的新 Azure AI 搜索索引,让你从一开始就确立专属的命名规范。

访问原生客户端

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

AzureVectorStore vectorStore = context.getBean(AzureVectorStore.class);
Optional<SearchClient> nativeClient = vectorStore.getNativeClient();

if (nativeClient.isPresent()) {
    SearchClient client = nativeClient.get();
    // 使用原生客户端执行 Azure 搜索专属操作
}

原生客户端允许你访问 VectorStore 接口未暴露的 Azure 搜索专属特性和操作。

相关推荐