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

混合搜索将文本(关键字)和矢量查询结合在单个搜索请求中。 请求中的所有子查询都并行执行。 结果按新的搜索分数进行合并和重新排序,使用倒数排名融合 (RRF) 返回一个统一的结果集。

在许多情况下,根据基准测试,语义排名的混合查询返回最相关的结果。

先决条件

选择 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.azure.cn/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.azure.cn/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。

配置查询响应

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

响应中的字段

搜索结果由搜索索引中的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 的演示代码。