在 Azure AI 搜索中创建矢量查询

在 Azure AI 搜索中,如果搜索索引中有矢量字段,可以参阅本文来了解如何执行以下操作:

本文使用 REST 进行说明。 有关其他语言的代码示例,请参阅 azure-search-vector-samples GitHub 存储库,了解包含矢量查询的端到端解决方案。

先决条件

提示

若要快速确定索引是否具有矢量,请查找 Collection(Edm.Single) 类型的具有 dimensions 属性和 vectorSearchProfile 赋值的字段。

将查询字符串输入转换为矢量

若要查询矢量字段,查询本身必须是矢量。 将用户的文本查询字符串转换为矢量表示的一种方法是在应用程序代码中调用嵌入库或 API。 最佳做法是始终使用与在源文档中生成嵌入时相同的嵌入模型

可以在 azure-search-vector-samples 存储库中找到显示如何生成嵌入的代码示例。

下面是提交到 Azure OpenAI 嵌入模型部署的查询字符串的 REST API 示例:

POST https://{{openai-service-name}}.openai.azure.com/openai/deployments/{{openai-deployment-name}}/embeddings?api-version={{openai-api-version}}
Content-Type: application/json
api-key: {{admin-api-key}}
{
    "input": "what azure services support generative AI'"
}

如果成功调用了部署的模型,则预期的响应为 202。 响应正文中的“embedding”字段是查询字符串“input”的矢量表示形式。 要进行测试,可以使用接下来几个部分中所示的语法将“embedding”数组的值复制到查询请求中的“vectorQueries.vector”。

对已部署模型的这种 POST 调用的实际响应包含 1536 个嵌入,为了便于阅读,此处修剪了响应内容,仅包含前几个矢量。

{
    "object": "list",
    "data": [
        {
            "object": "embedding",
            "index": 0,
            "embedding": [
                -0.009171937,
                0.018715322,
                ...
                -0.0016804502
            ]
        }
    ],
    "model": "ada",
    "usage": {
        "prompt_tokens": 7,
        "total_tokens": 7
    }
}

在此方法中,应用程序代码负责连接到模型、生成嵌入和处理响应。

提示

尝试使用集成矢量化进行查询(目前处于公共预览阶段),让 Azure AI 搜索处理查询矢量化输入和输出。

矢量查询请求

本部分介绍矢量查询的基本结构。 可以使用 Azure 门户、REST API 或 Azure SDK 来构建矢量查询。 如果要从 2023-07-01-Preview 迁移,则存在中断性变更。 有关详细信息,请参阅升级到最新的 REST API

2023-11-01搜索 POST 的稳定 REST API 版本。 此版本支持:

  • vectorQueries 是矢量搜索的构造。
  • kind 设置为 vector 指定查询是矢量数组。
  • vector 是查询(文本或图像的矢量表示形式)。
  • exhaustive(可选)在查询时调用穷举 KNN,即使字段已根据 HNSW 编制了索引。

在以下示例中,矢量是字符串“what Azure services support full text search”的表示形式。 查询面向 contentVector 字段。 查询返回 k 结果。 实际矢量有 1536 个嵌入,因此在本示例中对其进行了修剪以便于阅读。

POST https://{{search-service-name}}.search.azure.cn/indexes/{{index-name}}/docs/search?api-version=2023-11-01
Content-Type: application/json
api-key: {{admin-api-key}}
{
    "count": true,
    "select": "title, content, category",
    "vectorQueries": [
        {
            "kind": "vector",
            "vector": [
                -0.009154141,
                0.018708462,
                . . . 
                -0.02178128,
                -0.00086512347
            ],
            "exhaustive": true,
            "fields": "contentVector",
            "k": 5
        }
    ]
}

矢量查询响应

在 Azure AI 搜索中,查询响应默认包含所有 retrievable 字段。 但是,通常通过将搜索结果列在 select 语句中,将搜索结果限制为 retrievable 字段的子集。

在矢量查询中,请仔细考虑是否需要在响应中使用矢量字段。 矢量字段不是人类可读的,因此如果要将响应推送到网页,应选择能代表结果的非矢量字段。 例如,如果查询针对 contentVector 执行,则可以改为返回 content

如果确实希望在结果中包含矢量字段,下面是一个响应结构示例。 contentVector 是嵌入的字符串数组,为了简洁起见,此处进行了剪裁。 搜索分数指示相关性。 其他非矢量字段也包括在内,以提供上下文。

{
    "@odata.count": 3,
    "value": [
        {
            "@search.score": 0.80025613,
            "title": "Azure Search",
            "category": "AI + Machine Learning",
            "contentVector": [
                -0.0018343845,
                0.017952163,
                0.0025753193,
                ...
            ]
        },
        {
            "@search.score": 0.78856903,
            "title": "Azure Application Insights",
            "category": "Management + Governance",
            "contentVector": [
                -0.016821077,
                0.0037742127,
                0.016136652,
                ...
            ]
        },
        {
            "@search.score": 0.78650564,
            "title": "Azure Media Services",
            "category": "Media",
            "contentVector": [
                -0.025449317,
                0.0038463024,
                -0.02488436,
                ...
            ]
        }
    ]
}

要点

  • k 决定返回的最近的邻域结果数,在本例中为 3。 假设至少有 k 个文档存在,那么矢量查询总是返回 k 个结果,即使存在相似性较差的文档也是如此,因为该算法会找到查询矢量的任何 k 个最近的邻域。

  • @search.score矢量搜索算法确定。

  • 搜索结果中的字段要么是所有 retrievable 字段,要么是 select 子句中的字段。 在执行矢量查询时,仅根据矢量数据进行匹配。 但是,响应可以包含索引中的任何 retrievable 字段。 由于目前没有用于解码矢量字段结果的机制,所以包含非矢量文本字段对于展示易于人类理解的值非常有帮助。

带筛选器的矢量查询

查询请求可以包含矢量查询和筛选表达式。 筛选器适用于 filterable 文本和数字字段,在根据筛选条件包含或排除搜索文档时非常有用。 尽管矢量字段本身不可筛选,但查询可以在同一索引的其他字段中指定筛选器。

在较新的 API 版本中,可以设置筛选模式,以便在执行矢量查询之前或之后应用筛选器。 有关每种模式的比较以及基于索引大小的预期性能,请参阅矢量查询中的筛选器

提示

如果源字段不包含文本或数字值,请检查文档元数据(例如 LastModified 或 CreatedBy 属性),这在元数据筛选器中可能有用。

2023-11-01 是此 API 的稳定版本。 它具有:

  • 用于前筛选(默认)或后筛选筛选模式vectorFilterMode
  • filter 提供条件。

在以下示例中,矢量是此查询字符串的表示形式:“what Azure services support full text search”。 查询面向 contentVector 字段。 实际矢量有 1536 个嵌入,因此在本示例中对其进行了修剪以便于阅读。

在搜索引擎执行矢量查询之前,筛选条件将应用于可筛选文本字段(在本示例中为 category )。

POST https://{{search-service-name}}.search.azure.cn/indexes/{{index-name}}/docs/search?api-version=2023-11-01
Content-Type: application/json
api-key: {{admin-api-key}}
{
    "count": true,
    "select": "title, content, category",
    "filter": "category eq 'Databases'",
    "vectorFilterMode": "preFilter",
    "vectorQueries": [
        {
            "kind": "vector",
            "vector": [
                -0.009154141,
                0.018708462,
                . . . 
                -0.02178128,
                -0.00086512347
            ],
            "exhaustive": true,
            "fields": "contentVector",
            "k": 5
        }
    ]
}

多个矢量字段

可以将“vectorQueries.fields”属性设置为多个矢量字段。 矢量查询针对你在 fields 列表中提供的每个矢量字段执行。 在查询多个矢量字段时,确保每个矢量字段都包含来自同一嵌入模型的嵌入,并且查询也是由同一嵌入模型生成的。

POST https://{{search-service-name}}.search.azure.cn/indexes/{{index-name}}/docs/search?api-version=2023-11-01
Content-Type: application/json
api-key: {{admin-api-key}}
{
    "count": true,
    "select": "title, content, category",
    "vectorQueries": [
        {
            "kind": "vector",
            "vector": [
                -0.009154141,
                0.018708462,
                . . . 
                -0.02178128,
                -0.00086512347
            ],
            "exhaustive": true,
            "fields": "contentVector, titleVector",
            "k": 5
        }
    ]
}

多个矢量查询

多查询矢量搜索跨搜索索引中的多个矢量字段发送多个查询。 此查询请求的一个常见示例是,将 CLIP 等模型用于多模式矢量搜索,其中同一模型可以向量化图像和文本内容。

以下查询示例在 myImageVectormyTextVector中查找相似性,但分别在两个不同的查询嵌入中发送,每个查询都并行执行。 此查询生成使用倒数排名融合 (RRF) 评分的结果。

  • vectorQueries 提供矢量查询的数组。
  • vector 包含搜索索引中的图像矢量和文本矢量。 每个实例都是单独的查询。
  • fields 指定要针对的矢量字段。
  • k 是要包含在结果中的最近邻域匹配项的数量。
{
    "count": true,
    "select": "title, content, category",
    "vectorQueries": [
        {
            "kind": "vector",
            "vector": [
                -0.009154141,
                0.018708462,
                . . . 
                -0.02178128,
                -0.00086512347
            ],
            "fields": "myimagevector",
            "k": 5
        },
        {
            "kind": "vector"
            "vector": [
                -0.002222222,
                0.018708462,
                -0.013770515,
            . . .
            ],
            "fields": "mytextvector",
            "k": 5
        }
    ]
}

假设搜索索引包含图像文件的字段(搜索索引不存储图像),搜索结果将包括文本和图像的组合。

使用集成矢量化(预览版)进行查询

本部分显示了一个矢量查询,该查询调用新的集成矢量化预览功能,可将文本查询转换为矢量。 使用 2023-10-01-Preview REST API 或更新的 beta 版 Azure SDK 包。

先决条件是提供一个搜索索引,其中配置了矢量化器,并且该矢量化器已分配到矢量字段。 矢量化器为查询时使用的嵌入模型提供连接信息。

查询提供文本字符串而不是矢量:

  • kind 必须设置为 text
  • text 必须有一个文本字符串。 它将被传递给分配到矢量字段的矢量化器。
  • fields 是要搜索的矢量字段。

下面是在查询时矢量化的查询的简单示例。 文本字符串被矢量化,然后用于查询 descriptionVector 字段。

POST https://{{search-service}}.search.azure.cn/indexes/{{index}}/docs/search?api-version=2023-10-01-preview
{
    "select": "title, genre, description",
    "vectorQueries": [
        {
            "kind": "text",
            "text": "mystery novel set in London",
            "fields": "descriptionVector",
            "k": 5
        }
    ]
}

下面是使用文本查询的集成矢量化的混合查询。 此查询包括多个查询向量字段、多个非向量字段和一个筛选器。 同样,区别在于矢量查询的 kind,以及包含 text 字符串而不是 vector

在本例中,搜索引擎对索引中分配给 descriptionVectorsynopsisVectorauthorBioVector 的矢量化程序进行了三次矢量化调用。 生成的矢量用于根据相应字段检索文档。 搜索引擎还对 search 查询“mystery novel set in London”执行了关键词搜索。

POST https://{{search-service}}.search.azure.cn/indexes/{{index}}/docs/search?api-version=2023-10-01-preview
Content-Type: application/json
api-key: {{admin-api-key}}
{
    "search":"mystery novel set in London", 
    "searchFields":"description, synopsis", 
    "select": "title, author, synopsis",
    "filter": "genre eq 'mystery'",
    "vectorFilterMode": "postFilter",
    "vectorQueries": [
        {
            "kind": "text",
            "text": "mystery novel set in London",
            "fields": "descriptionVector, synopsisVector",
            "k": 5
        },
        {
            "kind": "text"
            "text": "living english author",
            "fields": "authorBioVector",
            "k": 5
        }
    ]
}

所有四个查询的评分结果均使用 RRF 排名进行融合。

注意

矢量化器在索引编制和查询期间使用。 如果索引中不需要数据分块和矢量化,则可以跳过创建索引器、技能组和数据源等步骤。 在这种情况下,矢量化器仅在查询时使用,以将文本字符串转换为嵌入。

矢量查询响应中的排名结果数

矢量查询指定 k 参数,该参数确定在结果中返回多少个匹配项。 搜索引擎始终返回 k 个匹配项。 如果 k 大于索引中的文档数,则文档数决定了可以返回的上限。

如果你熟悉全文搜索的话,就会知道,当索引不包含要搜索的字词或短语时,预期会返回零个结果。 但是在矢量搜索中,搜索操作标识最近的邻域,即使最近的邻域不是那么相似,它也始终返回 k 个结果。 因此,对于无意义或偏离主题的查询,尤其是当你未使用提示设置边界时,可以获得结果。 相关性较低的结果的相似性评分较差,但如果没有更接近的矢量,它们仍然是“最接近的”矢量。 因此,不包含有意义结果的响应仍可能返回 k 个结果,但每个结果的相似性评分很低。

包含全文搜索的混合方法可以缓解此问题。 另一种缓解方法是对搜索分数设置最小阈值,但仅限于查询是纯单矢量查询时。 混合查询不适合最小阈值,因为 RRF 范围要小得多且不稳定。

影响结果计数的查询参数包括:

  • 对于仅矢量查询,使用 "k": n 个结果
  • 对于包含“search”参数的混合查询,使用 "top": n 个结果

“k”和“top”都是可选的。 如果未指定,响应中的默认结果数为 50。 可以设置“top”和“skip”以浏览更多结果或更改默认值。

矢量查询中使用的排名算法

结果排名是根据下列其中一项计算的:

  • 相似性指标
  • 如果存在多个搜索结果集,则采用倒数排名融合 (RRF)。

相似性指标

在仅矢量查询的索引 vectorSearch 节中指定的相似性指标。 有效值为:cosineeuclideandotProduct

Azure OpenAI 嵌入模型使用余弦相似性,因此如果你使用 Azure OpenAI 嵌入模型,则 cosine 是建议的指标。 其他支持的排名指标包括 euclideandotProduct

使用 RRF

在查询执行期间,矢量查询只能针对一个内部矢量索引。 因此,对于多个矢量字段多个矢量查询,搜索引擎会生成多个查询,它们针对每个字段的相应矢量索引。 输出是为每个查询返回的一个排名结果集,这些结果使用 RRF 进行融合。 有关详细信息,请参阅使用倒数排序融合 (RRF) 进行相关性评分

后续步骤

接下来,请查看 PythonC#JavaScript 语言的矢量查询代码示例。