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

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

在本文中,您将学习如何:

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

本文结束时,可以执行混合查询,将关键字和矢量搜索与可选的语义排名相结合。

先决条件

选择 API、工具和可工作模式

  • Azure 门户中的搜索资源管理器(同时支持稳定且预览版 API 搜索语法)具有 JSON 视图,可用于粘贴混合请求。

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

  • 如果使用的是 maxTextRecallSize 和 countAndFacetMode(预览版)等预览功能,则使用稳定的 REST API 或最近的预览 API 版本。

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

可行的混合模式

如果你不熟悉混合搜索,请选择一种模式,然后按小步骤进行调优。 不要在同一个请求中同时使用最大向量召回、大型文本召回和语义重新排序。

  • 均衡混合(默认):首先对大多数工作负荷使用此模式。 将 k 设置在 30 到 50 的范围内,top 设置在 10 到 20 的范围内,仅当语义排名提高了度量的相关性时才启用语义排名。

  • 召回优先混合:适用于那些旨在实现覆盖范围的复杂查询。 逐渐增加 maxTextRecallSize ,并保持矢量设置适度。 预计合并成本更高。

  • 精度优先的混合模式:在大规模应用中使用以实现低延迟。 保持 ktop 适度,应用选择性筛选器,并避免不增加值的语义功能。

为什么过载的查询会受到限制

混合查询并行运行文本和矢量检索,然后将结果与 RRF 合并。 如果增加词汇贡献(例如,通过更改混合权重以支持 BM25),则必须与矢量候选合并的文本候选数量也会增加。 如果将这与昂贵的矢量设置和语义重新调整相结合,CPU 和内存压力会迅速增加。

在较小的容量配置上,这种额外的合并和重新调整工作可能会导致:

  • 更高的延迟和 p95/p99 峰值
  • 429 调节速率响应
  • 未配置重试行为时客户端观察到的丢弃或超时请求

横向扩展前优化顺序

在添加副本之前优化查询和矢量设置:

  1. 首先减少昂贵的矢量搜索设置。 例如,如果efSearchmaxConnections设置得较为激进,请在横向扩展之前对其进行降低(例如,将efSearch从大约800降低到128至192,并将maxConnections从64降低到32)。
  2. 将语义重新调整范围限制为受益于它的事例。
  3. 在代表性负载下重新测试延迟和 429 速率。
  4. 仅当优化后限制仍然存在时才缩放副本。

使用此序列可以先提高稳定性并控制成本,然后再进行缩放。

设置混合查询

本部分介绍混合查询的基本结构,以及如何在搜索资源管理器中设置一个查询或在 REST 客户端中执行。

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

  1. Azure 门户中,转到你的搜索服务。

  2. “搜索管理>索引”下,选择包含矢量和非矢量内容的索引。 搜索资源管理器 是第一个选项卡。

  3. “视图”下切换到 JSON 视图 ,以便可以粘贴向量查询。

  4. 将默认查询模板替换为混合查询。 基本混合查询具有指定的 search文本查询,以及指定的 vectorQueries.vector向量查询。 文本查询和矢量查询可以等价,也可以不等价,但通常具有相同的意向。

    此示例来自 vector 快速入门,其中包含矢量和非矢量内容,以及多个查询示例。 为简洁起见,本文中截断了矢量。

    {
        "search": "historic hotel walk to restaurants and shopping",
        "vectorQueries": [
            {
                "vector": [0.01944167, 0.0040178085, -0.007816401 ... <remaining values omitted> ], 
                "k": 7,
                "fields": "DescriptionVector",
                "kind": "vector",
                "exhaustive": true
            }
        ]
    }
    
  5. 选择搜索

    提示

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

  6. 下面是另一个版本的查询。 这一项包含一个 count 用于显示找到的匹配项数量,一个 select 参数用于选择特定字段,以及一个 top 参数用于返回前七个结果。

     {
         "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
             }
         ]
     }
    

设置 maxTextRecallSize 和 countAndFacetMode

注意

此功能目前处于预览状态。 此预览版未随附服务级别协议,建议不要用于生产工作负载。 某些功能可能不受支持或者受限。 有关详细信息,请参阅 Azure 预览版的使用条款

可以优化混合查询,以控制每个子查询对合并结果的贡献量。 设置 maxTextRecallSize 指定将多少个 BM25 排名结果传递给混合排名模型。

如果请求包含分面,请使用索引中标记为 facetable 的非向量字段。 矢量字段不可分面。

分面计数取决于查询类型:

  • 在纯文本查询中,分面会统计与该文本查询匹配的文档数量。
  • 在纯向量查询中,分面会统计由向量查询返回的 k 文档数量。
  • 在混合查询中,分面同时涵盖向量和文本结果。 向量端贡献 k 最近的文档。 文本端提供 BM25 排名的文档。 countAndFacetMode 参数决定计数和分面计算是使用所有文本匹配项,还是仅使用为进行排名而检索到的文本匹配项。

如果使用 maxTextRecallSize,可能还需要设置 countAndFacetMode。 此参数用于确定 countfacets 是包含所有与文本查询匹配的文档,还是仅包含在 maxTextRecallSize 窗口内检索到的文档。 默认值为 countAllResults

使用默认的 countAllResults 模式时,计数和分面可以包括文本侧文档,即使这些文档因位于 maxTextRecallSize 窗口之外而不会被检索用于 RRF 排名。 增加 maxTextRecallSize 会增加可用于排序的按 BM25 排序的文档数量,但不会使向量贡献超过 k。 如果您想将计数和分面计算限定在为混合排名检索到的文档范围内,请使用 countRetrievableResults。 建议使用最新的预览版 REST API 来设置这些选项。

提示

混合查询优化的另一种方法是 矢量权重,用于增加请求中矢量查询的重要性。

  1. 使用 搜索 - POST(预览版)搜索 - GET (预览版) 指定预览参数。

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

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

    • countAndFacetMode 返回混合查询的计数和分面范围。 默认值为 countAllResults,它使用完整的混合结果集,包括所有与文本查询匹配的文档,即使其中一些文本匹配项由于位于 maxTextRecallSize 窗口之外而不会被检索用于 RRF 排名。 使用 countRetrievableResults 将计数和分面限定为参与排序时检索到的文档,包括 maxTextRecallSize 按 BM25 排序的文档和 k 向量匹配结果。

  3. 设置 maxTextRecallSize

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

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

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

第一个示例减少 maxTextRecallSize 到 100,将混合查询的文本端限制为仅 100 个文档。 它还将 countAndFacetMode 设置为在计数和分面计算中仅包含可检索文档。

POST https://[service-name].search.azure.cn/indexes/[index-name]/docs/search?api-version=2025-11-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.azure.cn/indexes/[index-name]/docs/search?api-version=2025-11-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" 
      } 
    } 

参考hybridSearch | maxTextRecallSize | countAndFacetMode

混合查询的示例

本部分包含多个演示混合查询模式的查询示例。

示例:使用筛选器进行混合搜索

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

POST https://{{search-service-name}}.search.azure.cn/indexes/{{index-name}}/docs/search?api-version=2026-04-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": "preFilter",
    "filter": "ParkingIncluded",
    "top": "10"
}

要点

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

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

  • 对查询结果进行后置过滤时,结果数量可能小于 top-n。

参考filter | vectorFilterMode

示例:使用筛选器定向矢量子查询的混合搜索(预览版)

使用最新的预览版 REST API,可以通过应用仅针对混合请求中的矢量子查询的辅助筛选器来替代搜索请求中的全局筛选器。

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

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

要应用定向矢量过滤器:

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

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

假设索引定义包括 语义配置,则可以构建包含矢量搜索和关键字搜索的查询,并在合并的结果集中进行语义排名。 (可选)可以添加字幕和答案。

对矢量使用语义排名时,请确保 k 设置为 50。 语义排序器最多使用 50 个匹配项作为输入。 如果指定小于 50 个匹配项,语义排名模型会失去必要的输入。

POST https://{{search-service-name}}.search.azure.cn/indexes/{{index-name}}/docs/search?api-version=2026-04-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”是可选的。 从结果的逐字文本中提取值。 仅当结果包含的内容具有查询答案的特征时,才会返回答案。

参考queryType | semanticConfiguration | captions | answers

示例:使用筛选器进行语义混合搜索

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

POST https://{{search-service-name}}.search.azure.cn/indexes/{{index-name}}/docs/search?api-version=2026-04-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": "ParkingIncluded",
    "vectorFilterMode": "preFilter",
    "top": "50"
}

要点

  • 筛选器模式可能会影响语义重排器可用的结果数。 最佳做法是,向语义排名器提供最大数目的文档 (50)。 如果预筛选器或后筛选器过于挑剔,给语义排名器提供的文档少于50个时,可能无法充分发挥其作用。

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

  • postFilter 在查询执行后应用。 如果 k=50 在向量查询端返回 50 个匹配项,然后向 50 个匹配项应用筛选器,得到的结果数将减少到满足筛选条件的文档数。 最终需要传递给语义排序器的文档少于 50 个。 如果要使用语义排序器,请记住这一点。 语义排序器在输入文档数为 50 个时效果最佳。

  • strictPostFilter(预览版)在查询执行后对未筛选的前 k 个结果应用。 它始终返回小于或等于 k 个文档。 如果未筛选的 k=50 返回 50 个未筛选的结果,并且筛选器与 30 个文档匹配,则即使索引包含超过 30 个与筛选器匹配的文档,结果集中也仅返回 30 个文档。 由于此模式极大地减少了召回率,因此不建议将其用于语义排名器。

配置查询响应

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

响应中的字段

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

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

本文中的示例使用了一个 select 语句来指定响应中的文本(非函数)字段。

注意

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

结果数量

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

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

ktop都是可选的。 如果未指定,响应中的默认结果数为 50。 可以通过设置 topskip 来翻页浏览更多结果或更改默认设置。

注意

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

语义排序器结果

注意

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

如果在 2024-05-01-preview 或更高版本中使用语义排序器,最佳做法是设置 kmaxTextRecallSize 使其总和至少达到 50。 然后,可以使用 top 参数限制返回给用户的结果。

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

  • 对于仅关键字搜索(无向量)设置为 top 50
  • 对于设置为 k 50 的混合搜索,请确保语义排名器至少获得 50 个结果。

排名

为混合查询创建多个集,包含或不包含可选的语义排名。 结果排名由倒数排名融合 (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": 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"
    }
}

解决混合查询故障

使用下表诊断混合查询的常见问题。

問题 可能的原因 解决方案
空结果 矢量字段名称不匹配或缺少索引数据 验证 fieldsvectorQueries 中是否与索引架构中的向量字段匹配。 检查文档是否包含矢量数据。
低 RRF 分数 正常 RRF 行为 RRF 分数本质上低于相似性分数。 分数为 0.03 仍然可以表示很高的匹配度。
矢量结果占主导地位 文本查询性能不佳 增加 maxTextRecallSize 以包括更多的 BM25 结果,或调整矢量权重。
文本结果占主导地位 矢量相似性过低 检查嵌入质量。 确保查询向量使用与文档向量相同的模型。
语义排名器返回的结果更少 输入文档不足 使用语义排名时,需将 k 设置为至少 50。 检查筛选器是否过于严格。
对矢量不适用的筛选器 仅使用全局筛选器 对于矢量特定的筛选,请在矢量查询(预览版)中使用filterOverride
结果中出现了意外字段 select 参数缺失 添加 select 以指定要返回的字段。 排除矢量字段以便可读性。