你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

在 Azure AI 搜索中创建混合查询

混合搜索将文本(关键字)和矢量查询结合在单个搜索请求中。 请求中的所有子查询都并行执行。 结果按新的搜索分数进行合并和重新排序,使用倒数排名融合 (RRF) 返回一个统一的结果集。 在许多情况下,根据基准测试,语义排名的混合查询返回最相关的结果。

在本文中,将学习以下内容:

  • 设置基本请求
  • 使用更多参数和筛选器构建混合查询
  • 使用语义排名或矢量权重提高相关性
  • 通过控制文本和矢量输入来优化查询行为

注意

2024-09-01-preview 中的新功能是可以将筛选器仅定位到混合请求中的矢量子查询。 这使你可以更加精确地控制筛选器的应用方式。 有关详细信息,请参阅本文中的将筛选器定位到矢量子查询

先决条件

选择 API 或工具

  • Azure 门户中的搜索资源管理器(支持稳定版和预览版 API 搜索语法)有一个 JSON 视图,可让你将混合请求粘贴进去。

  • 如果使用的是 maxTextRecallSize 和 countAndFacetMode(preview) 之类的预览功能,则使用 2024-07-01 稳定版本或最新的预览 API 版本。

    为了方便阅读,我们使用 REST 示例来解释 API 的工作原理。 可以使用带 REST 扩展的 REST 客户端(例如 Visual Studio Code)来构建混合查询。 有关详细信息,请参阅快速入门:使用 REST API 进行矢量搜索

  • Azure SDK 的较新的稳定版或 beta 版软件包(请参阅 SDK 功能支持的更改日志)。

在搜索资源管理器中设置混合查询

  1. 搜索资源管理器中,确保 API 版本为 2024-07-01 或更高版本的预览 API 版本。

  2. 在“视图”下选择“JSON 视图”,以便将矢量查询粘贴进去。

  3. 将默认查询模板替换为混合查询,例如矢量快速入门中从第 539 行开始的“运行混合查询”示例。 为简洁起见,本文中截断了矢量。

    混合查询在 search 中指定了文本查询,在 vectorQueries.vector 下指定了矢量查询。

    文本查询和矢量查询可以等价,也可以不等价,但通常具有相同的意向。

    {
        "count": true,
        "search": "historic hotel walk to restaurants and shopping",
        "select": "HotelId, HotelName, Category, Tags, Description",
        "top": 7,
        "vectorQueries": [
            {
                "vector": [0.01944167, 0.0040178085, -0.007816401 ... <remaining values omitted> ], 
                "k": 7,
                "fields": "DescriptionVector",
                "kind": "vector",
                "exhaustive": true
            }
        ]
    }
    
  4. 选择搜索

提示

如果隐藏矢量,搜索结果将更易读。 在“查询选项”中,启用“在搜索结果中隐藏矢量值”

混合查询请求 (REST API)

混合查询结合了搜索和矢量搜索,其中 search 参数采用查询字符串,vectorQueries.vector 采用矢量查询。 搜索引擎并行运行全文查询和矢量查询。 使用倒数排名融合 (RRF) 评估所有匹配项的并集的相关性,并在响应中返回单个结果集。

结果均以纯文本形式返回,包括标记为 retrievable 的字段中的矢量。 由于数值矢量在搜索结果中不起作用,因此请选择索引中的其他字段作为矢量匹配的代理。 例如,如果索引具有“descriptionVector”和“descriptionText”字段,则查询可以匹配“descriptionVector”,但搜索结果可以显示“descriptionText”。 使用 select 参数仅指定结果中可人工读取的字段。

以下示例演示了混合查询配置。

POST https://{{search-service-name}}.search.windows.net/indexes/{{index-name}}/docs/search?api-version=2024-07-01
Content-Type: application/json
api-key: {{admin-api-key}}
{
    "vectorQueries": [
        {
            "vector": [
                -0.009154141,
                0.018708462,
                . . . 
                -0.02178128,
                -0.00086512347
            ],
            "fields": "DescriptionVector",
            "kind": "vector",
            "exhaustive": true,
            "k": 10
        },
        {
            "vector": [
                -0.009154141,
                0.018708462,
                . . . 
                -0.02178128,
                -0.00086512347
            ],
            "fields": "DescriptionVector",
            "kind": "vector",
            "exhaustive": true,
            "k": 10
        }
    ],
    "search": "historic hotel walk to restaurants and shopping",
    "select": "HotelName, Description, Address/City",
    "top": 10
}

要点

  • 矢量查询字符串通过 vectorQueries.vector 属性指定。 查询针对“DescriptionVector”字段执行。 将 kind 设置为“vector”以指示查询类型。 (可选)将 exhaustive 设置为“true”以查询矢量字段的完整内容。

  • 关键字搜索通过 search 属性指定。 它与矢量查询并行执行。

  • k 确定从向量查询返回的最近邻匹配项数,并将其提供给 RRF 排名器。

  • top 确定响应中返回的匹配项数。 在此示例中,假设合并的结果中至少有 10 个匹配项,响应会包含 10 个结果。

使用筛选器的混合搜索

此示例添加了一个筛选器,该筛选器应用于搜索索引的 filterable 非矢量字段。

POST https://{{search-service-name}}.search.windows.net/indexes/{{index-name}}/docs/search?api-version=2024-07-01
Content-Type: application/json
api-key: {{admin-api-key}}
{
    "vectorQueries": [
        {
            "vector": [
                -0.009154141,
                0.018708462,
                . . . 
                -0.02178128,
                -0.00086512347
            ],
            "fields": "DescriptionVector",
            "kind": "vector",
            "k": 10
        }
    ],
    "search": "historic hotel walk to restaurants and shopping",
    "vectorFilterMode": "postFilter",
    "filter": "ParkingIncluded",
    "top": "10"
}

要点

  • 筛选器应用于可筛选字段的内容。 在此示例中,ParkingIncluded 字段是一个布尔值,标记为索引架构中的 filterable

  • 在混合查询中,可以在查询执行之前应用筛选器以减少查询图面,也可以在查询执行之后应用筛选器来剪裁结果。 "preFilter" 是默认值。 若要使用 postFilter,需设置筛选器处理模式,如以下示例所示。

  • 在筛选后查询结果时,结果数可能小于 top-n。

使用针对矢量子查询的筛选器进行混合搜索(预览版)

使用 2024-09-01-preview,可以通过应用仅针对混合请求中的矢量子查询的辅助筛选器来替代搜索请求中的全局筛选器。

此功能通过确保筛选器仅影响矢量搜索结果而使基于关键字的搜索结果不受影响,提供精细的控制。

目标筛选器会完全替代全局筛选器(包括用于安全修整或地理空间搜索的任何筛选器)。 在需要全局筛选器的情况下(例如进行安全修整时),必须在顶级筛选器和每个矢量级筛选器中显式包含这些筛选器,以确保一致地强制执行安全性和其他约束。

应用目标矢量筛选器:

下面是添加筛选器替代的混合查询的示例。 全局筛选器“Rating gt 3”在运行时被 filterOvrride 替换。

POST https://{{search-service-name}}.search.windows.net/indexes/{{index-name}}/docs/search?api-version=2024-09-01=preview

{
    "vectorQueries": [
        {
            "vector": [
                -0.009154141,
                0.018708462,
                . . . 
                -0.02178128,
                -0.00086512347
            ],
            "fields": "DescriptionVector",
            "kind": "vector",
            "exhaustive": true,
            "filterOverride": "Address/City eq 'Seattle'",
            "k": 10
        }
    ],
    "search": "historic hotel walk to restaurants and shopping",
    "select": "HotelName, Description, Address/City, Rating",
    "filter": "Rating gt 3"
    "debug": "vector",
    "top": 10
}

假设你启用了语义排序器,并且你的索引定义包含一个语义配置,那么你可构建这样一个查询,它包含矢量搜索和关键字搜索,并对合并的结果集进行语义排名。 (可选)可以添加字幕和答案。

POST https://{{search-service-name}}.search.windows.net/indexes/{{index-name}}/docs/search?api-version=2024-07-01
Content-Type: application/json
api-key: {{admin-api-key}}
{
    "vectorQueries": [
        {
            "vector": [
                -0.009154141,
                0.018708462,
                . . . 
                -0.02178128,
                -0.00086512347
            ],
            "fields": "DescriptionVector",
            "kind": "vector",
            "k": 50
        }
    ],
    "search": "historic hotel walk to restaurants and shopping",
    "select": "HotelName, Description, Tags",
    "queryType": "semantic",
    "semanticConfiguration": "my-semantic-config",
    "captions": "extractive",
    "answers": "extractive",
    "top": "50"
}

要点

  • 语义排序器接受来自合并响应的最多 50 个结果。

  • “queryType”和“semanticConfiguration”是必需的。

  • “captions”和“answers”是可选的。 从结果的逐字文本中提取值。 仅当结果包含的内容具有查询答案的特征时,才会返回答案。

使用筛选器的语义混合搜索

下面是集合中的最后一个查询。 它是与上一示例相同的语义混合查询,但带有筛选器。

POST https://{{search-service-name}}.search.windows.net/indexes/{{index-name}}/docs/search?api-version=2024-07-01
Content-Type: application/json
api-key: {{admin-api-key}}
{
    "vectorQueries": [
        {
            "vector": [
                -0.009154141,
                0.018708462,
                . . . 
                -0.02178128,
                -0.00086512347
            ],
            "fields": "DescriptionVector",
            "kind": "vector",
            "k": 50
        }
    ],
    "search": "historic hotel walk to restaurants and shopping",
    "select": "HotelName, Description, Tags",
    "queryType": "semantic",
    "semanticConfiguration": "my-semantic-config",
    "captions": "extractive",
    "answers": "extractive",
    "filter": "ParkingIsIncluded'",
    "vectorFilterMode": "postFilter",
    "top": "50"
}

要点

  • 筛选器模式可能会影响语义重排器可用的结果数。 最佳做法是,向语义排名器提供最大数目的文档 (50)。 如果预筛选器或后筛选器过于严格,向语义排名器提供少于 50 个文档时,可能就没有为其提供足够的支持。

  • 在查询执行之前应用预筛选。 如果预筛选器将搜索区域减少到 100 个文档,则矢量查询将针对这 100 个文档的“DescriptionVector”字段执行,返回 k=50 个最佳匹配项。 然后,这 50 个匹配的文档将传递给 RRF 以获取合并结果,然后再传递给语义排名器。

  • 在查询执行后应用后筛选器。 如果 k=50 在矢量查询端返回 50 个匹配项,则后筛选器将应用于这 50 个匹配项,从而减少满足筛选条件的结果,使传递给语义排名器的文档少于 50 个。

设置 maxTextRecallSize 和 countAndFacetMode(预览版)

本节介绍如何通过控制流向混合排名模型的 BM25 排名结果的数量来调整混合查询的输入。 通过控制 BM25 排名输入,可以在混合方案中提供更多的相关性优化选项。

建议使用预览版 REST API 版本 2024-05-01-preview

提示

可以考虑的另一种选项是补充或替代技术,即矢量加权,它增加了矢量查询在请求中的重要性。

  1. 在 2024-05-01-preview 中使用搜索 - POST搜索 - GET 来指定这些参数。

  2. 添加 hybridSearch 查询参数对象,设置通过混合查询的 BM25 排名结果重新调用的最大文档数。 它具有两个属性:

    • maxTextRecallSize 指定向混合查询中使用的倒数排序融合 (RRF) 排名程序提供的 BM25 排序结果的数量。 默认值为 1,000。 最大值为 10,000。

    • countAndFacetMode 报告 BM25 排名结果的计数(如果使用的是分面)。 默认值为与查询匹配的所有文档。 (可选)可以将“count”限定为 maxTextRecallSize

  3. 如果矢量相似性搜索通常优于混合查询的文本端,请减少 maxTextRecallSize

  4. 如果索引较大,并且默认值未捕获足够多数量的结果,则提高 maxTextRecallSize。 使用更大的 BM25 排名结果集,还可以设置 topskipnext 来检索这些结果的部分。

以下 REST 示例显示了设置 maxTextRecallSize 的两个用例。

第一个示例将 maxTextRecallSize 减少到 100,将混合查询的文本端限制为仅 100 个文档。 它还将 countAndFacetMode 设置为仅包含来自 maxTextRecallSize 的结果。

POST https://[service-name].search.windows.net/indexes/[index-name]/docs/search?api-version=2024-05-01-Preview 

    { 
      "vectorQueries": [ 
        { 
          "kind": "vector", 
          "vector": [1.0, 2.0, 3.0], 
          "fields": "my_vector_field", 
          "k": 10 
        } 
      ], 
      "search": "hello world", 
      "hybridSearch": { 
        "maxTextRecallSize": 100, 
        "countAndFacetMode": "countRetrievableResults" 
      } 
    } 

第二个示例将 maxTextRecallSize 提高到 5,000。 它还使用 top、skip 和 next 从大型结果集中拉取结果。 在这种情况下,请求将从位置 1,500 到 2,000 开始的 BM25 排名结果作为文本查询对 RRF 复合结果集的贡献。

POST https://[service-name].search.windows.net/indexes/[index-name]/docs/search?api-version=2024-05-01-Preview 

    { 
      "vectorQueries": [ 
        { 
          "kind": "vector", 
          "vector": [1.0, 2.0, 3.0], 
          "fields": "my_vector_field", 
          "k": 10 
        } 
      ], 
      "search": "hello world",
      "top": 500,
      "skip": 1500,
      "next": 500,
      "hybridSearch": { 
        "maxTextRecallSize": 5000, 
        "countAndFacetMode": "countRetrievableResults" 
      } 
    } 

配置查询响应

设置混合查询时,请考虑响应结构。 响应是平展行集。 查询中的参数确定每行中有哪些字段以及响应中有多少行。 搜索引擎对匹配的文档进行排名,并返回相关度最高的结果。

响应中的字段

搜索结果由搜索索引中的retrievable 字段组成。 结果可以是:

  • 所有 retrievable 字段(REST API 默认值)。
  • 在查询的“select”参数中显式列出的字段。

本文中的示例使用了“select”语句来指定响应中的文本(非矢量)字段。

注意

不会对矢量执行反向工程使其成为人类可读的文本,因此要避免在响应中将其返回。 相反,选择代表搜索文档的非矢量字段。 例如,如果查询以“DescriptionVector”字段为目标,则在响应中有一个(“Description”)时返回等效的文本字段。

结果数量

查询可能与任意数量的文档匹配,如果搜索条件较弱(例如“search=*”用于 null 查询),则会尽可能匹配所有文档。 由于很少有实际返回无限制的结果的情况,因此应为整体响应指定最大值

  • 对于仅关键字查询(无向量),为 "top": n 个结果
  • 对于仅矢量查询,使用 "k": n 个结果
  • 对于包含“search”参数的混合查询,为 "top": n 个结果(具有或不具有语义)

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

注意

如果在 2024-05-01-preview API 中使用混合搜索,可以使用 maxTextRecallSize 控制关键字查询的结果数。 将其与一个“k”设置组合使用,可控制每个搜索子系统中的表示形式(关键字和矢量)。

语义排序器结果

注意

语义排名器最多可接受 50 个结果。

如果在 2024-05-01-preview API 中使用语义排序器,最佳做法是让“k”和“maxTextRecallSize”的总和至少为 50。 然后,可以使用“top”参数限制返回给用户的结果数。

如果在以前的 API 中使用语义排序器,请按以下操作:

  • 如果进行仅关键字搜索(无矢量),将“top”设置为 50
  • 如果进行混合搜索,将“k”设置为 50,以确保语义排序器获取至少 50 个结果。

Ranking

为混合查询创建多个集,包含或不包含可选的语义排名。 结果排名由倒数排名融合 (RRF) 计算得出。

在本部分中,比较单矢量搜索和简单混合搜索之间的响应,来获得排名靠前的结果。 不同的排名算法、HNSW 的相似性指标和 RRF 就是这种情况,会产生具有不同量级的分数。 此行为是设计使然。 即使存在很高的相似性匹配,RRF 分数也可能看起来很低。 较低的分数是 RRF 算法的一个特征。 在使用 RRF 的混合搜索中,考虑到已通过 RRF 排名的文档的分数相对较小(这与纯矢量搜索相反),结果中会包含已排名文档的更多倒数。

单矢量搜索:按余弦相似性(默认矢量相似性距离函数)排序的结果的 @search.score。

{
    "@search.score": 0.8399121,
    "HotelId": "49",
    "HotelName": "Swirling Currents Hotel",
    "Description": "Spacious rooms, glamorous suites and residences, rooftop pool, walking access to shopping, dining, entertainment and the city center.",
    "Category": "Luxury",
    "Address": {
    "City": "Arlington"
    }
}

混合搜索:使用倒数排名融合进行排名的混合结果的 @search.score。

{
    "@search.score": 0.032786883413791656,
    "HotelId": "49",
    "HotelName": "Swirling Currents Hotel",
    "Description": "Spacious rooms, glamorous suites and residences, rooftop pool, walking access to shopping, dining, entertainment and the city center.",
    "Category": "Luxury",
    "Address": {
    "City": "Arlington"
    }
}

后续步骤

接下来,建议查看 PythonC#JavaScript 的演示代码。