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

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

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

也可以在 Azure 门户中使用搜索资源管理器

先决条件

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

若要查询矢量字段,查询本身必须是矢量。

将用户的文本查询字符串转换为矢量表示的一种方法是在应用程序代码中调用嵌入库或 API。 最佳做法是始终使用与在源文档中生成嵌入时相同的嵌入模型。 可以在 azure-search-vector-samples 存储库中找到显示如何生成嵌入的代码示例。

另一种方法是使用集成矢量化(目前已正式发布),让 Azure AI 搜索处理查询矢量化输入和输出。

下面是提交到 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 门户、REST API 或 Azure SDK 来构建矢量查询。 如果要从 2023-07-01-Preview 迁移,则存在中断性变更。 有关详细信息,请参阅升级到最新的 REST API

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

  • vectorQueries 是矢量搜索的构造。
  • 对于矢量数组,vectorQueries.kind 设置为 vector;如果输入是字符串且你有矢量器,则其设置为 text
  • vectorQueries.vector 是查询(文本或图像的矢量表示形式)。
  • vectorQueries.weight(可选)指定搜索操作中包含的每个矢量查询的相对权重(请参阅矢量权重)。
  • 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=2024-07-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",
            "weight": 0.5,
            "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 字段。 由于目前没有用于解码矢量字段结果的机制,所以包含非矢量文本字段对于展示易于人类理解的值非常有帮助。

多个矢量字段

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

POST https://{{search-service-name}}.search.azure.cn/indexes/{{index-name}}/docs/search?api-version=2024-07-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
        }
    ]
}

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

使用集成矢量化进行查询

本部分会介绍调用集成矢量化将文本转换为矢量的矢量查询。 对于此功能,建议使用稳定的 2024-07-01 REST API、搜索资源管理器或较新的 Azure SDK 包。

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

搜索资源管理器支持在查询时进行集成矢量化。 如果索引包含矢量字段且有矢量器,则可以使用内置文本到矢量转换。

  1. 使用 Azure 帐户登录到 Azure 门户,然后转到 Azure AI 搜索服务。

  2. 在左侧菜单中,展开“搜索管理”>“索引”,然后选择索引。 在索引页上,“搜索资源管理器”是第一个选项卡。

  3. 检查“矢量配置文件”以确认你有矢量器

    搜索索引中矢量器设置的屏幕截图。

  4. 在搜索资源管理器中,可以在查询视图的默认搜索栏中输入文本字符串。 内置矢量器将字符串转换为矢量,执行搜索并返回结果。

    或者,你可以选择“视图”>“JSON 视图”来查看或修改查询。 如果存在矢量,搜索资源管理器则会自动设置矢量查询。 可以使用 JSON 视图选择搜索中使用的字段,并在响应中添加筛选器,或构造更高级的查询,例如混合查询。 本部分的 REST API 选项卡中提供了一个 JSON 示例。

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

矢量查询指定 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) 进行相关性评分

矢量权重

添加 weight 查询参数来指定搜索操作中包含的每个矢量查询的相对权重。 当合并由同一请求中的两个或多个矢量查询生成的多个排名列表的结果,或者合并来自混合查询矢量部分的结果时,将使用此值。

默认值为 1.0,该值必须是大于零的正数。

可使用权重来计算每个文档的倒数排序融合分数。 计算结果是 weight 值与文档在其各自结果集中的排名分数的乘积。

以下示例是一个混合查询,其中包含两个矢量查询字符串和一个文本字符串。 权重分配给矢量查询。 第一个查询是 0.5 或一半的权重,其在请求中的重要性有所减少。 第二个矢量查询的重要性达到两倍。

POST https://[service-name].search.azure.cn/indexes/[index-name]/docs/search?api-version=2024-07-01

    { 
      "vectorQueries": [ 
        { 
          "kind": "vector", 
          "vector": [1.0, 2.0, 3.0], 
          "fields": "my_first_vector_field", 
          "k": 10, 
          "weight": 0.5 
        },
        { 
          "kind": "vector", 
          "vector": [4.0, 5.0, 6.0], 
          "fields": "my_second_vector_field", 
          "k": 10, 
          "weight": 2.0
        } 
      ], 
      "search": "hello world" 
    } 

矢量权重仅适用于矢量。 此示例中的文本查询(“hello world”)的隐式权重为 1.0 或中性权重。 但是,在混合查询中,可以通过设置 maxTextRecallSize 来增加或减少文本字段的重要性。

设置阈值以排除低分结果(预览版)

由于最近的邻域搜索始终返回所请求的 k 邻域,因此,为了满足搜索结果的 k 数量要求,可能会显示多个低分匹配项。 若要排除低分搜索结果,可以添加 threshold 查询参数,根据最低分数筛选出结果。 筛选后将从不同的调用集中融合结果

此参数仍以预览版提供。 建议使用预览版 REST API 版本 2024-05-01-preview

在此示例中,即使结果数低于 k,也会从矢量搜索结果中排除分数低于 0.8 的所有匹配项。

POST https://[service-name].search.azure.cn/indexes/[index-name]/docs/search?api-version=2024-05-01-preview 
    Content-Type: application/json 
    api-key: [admin key] 

    { 
      "vectorQueries": [ 
        { 
          "kind": "vector", 
          "vector": [1.0, 2.0, 3.0], 
          "fields": "my-cosine-field", 
          "threshold": { 
            "kind": "vectorSimilarity", 
            "value": 0.8 
          } 
        }
      ]
    }

混合搜索的 MaxTextSizeRecall(预览版)

矢量查询通常用于包含非矢量字段的混合构造中。 如果发现 BM25 排名结果在混合查询结果中表现得过高或过低,则可以设置 maxTextRecallSize来增加或减少为混合排名提供的 BM25 排名结果。

只能在同时包含“search”和“vectorQueries”组成部分的混合请求中设置此属性。

此参数仍以预览版提供。 建议使用预览版 REST API 版本 2024-05-01-preview

有关详细信息,请参阅设置 maxTextRecallSize - 创建混合查询

后续步骤

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