<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[相关性 - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[相关性 - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/cn/search-labs/blog/category/relevance</link>
    </image>
    <link>https://www.elastic.co/cn/search-labs/blog/category/relevance</link>
    <atom:link href="https://www.elastic.co/cn/search-labs/rss/category/relevance.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[cn]]></language>
    <lastBuildDate>Tue, 22 Sep 2026 08:35:16 GMT</lastBuildDate>
  <item>
    <title><![CDATA[以最低得分阈值确保语义精确性]]></title>
    <description><![CDATA[通过设定最低得分阈值提升语义精确性。本文提供语义搜索与混合搜索的实操案例。 ]]></description>
    <content:encoded><![CDATA[<p>语义搜索为检索相关性开辟了全新可能。以 ELSER、E5、Jina Embedding v4 为代表的高质量稀疏-稠密混合模型，通过解析词义而非简单关键词匹配返回相关结果。然而，这类模型在处理长尾查询或索引缺乏相关内容时，可能返回无关结果，这种特性既可能导致用户困惑，也会造成大型语言模型 (LLM) 的算力资源浪费。</p><p>本文将介绍如何使用最低得分参数来提高语义搜索结果的精确度。如想测试本博客文章中提供的示例，请访问 <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/ensuring-semantic-precision-with-minimum-score/ensuring_semantic_precision_with_minimum_score.ipynb">相关 Jupyter 笔记本</a>。</p><h2>背景：精确率和召回率</h2><p>在搜索相关性中，<em>精确率</em>和<em>召回率</em>是关键概念。强烈建议尚不熟悉这些内容的读者查阅相关资料。以下是摘要。</p><ul><li><p><strong>精确率：</strong>返回的搜索结果中与用户相关的比例。</p></li><li><p><strong>召回率：</strong>搜索结果集中包含的语料库中所有相关文档的百分比。</p></li></ul><p>或者换句话说，精确率<strong>只</strong>返回相关结果，而召回率则返回<strong>所有</strong>相关结果。可以想象，这些需求经常相互冲突。语义搜索往往具有很高的召回率，但在精确率方面可能会不理想。继续阅读，了解如何避免这种情况。</p><h2>推出最低得分参数</h2><p>min_score参数通过设定最低得分阈值提升检索精度，系统将自动过滤得分低于该阈值的匹配结果，从而精简结果集。以下是一个简单的示例：</p>GET search-movies/_search
{
  "retriever": {
    "linear": {
      "min_score": 4,
      "retrievers": [
        ...
      ]
    }
  }
}<h2>得分归一化</h2><p>设置最低得分阈值固然可行，但并非所有语义模型都能返回适用于静态阈值的分值。以 ELSER 为例，它所返回的分值是无界得分。<a href="https://huggingface.co/intfloat/e5-small#faq">有些</a>密集模型得分是密集聚类的，只有在特定查询的背景下才有意义。</p><p>对于大多数语义搜索情况，我们建议在应用“min_score”之前使用归一化方法。归一化确保文档得分在规定区间内。Elasticsearch 检索器提供了两种此类<a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers/linear-retriever#linear-retriever-normalizers">归一化器</a>，即“l2_norm”和“minmax”。最常用的是“minmax”，因为它简单易懂，在很多情况下都很有效。“minmax”的主要属性包括：</p><ul><li><p>文档分数分布在 0 到 1 之间。</p></li><li><p>得分最高的文件总是记为 1 分。</p></li><li><p>得分最低的文件总是记为 0 分。</p><ul><li><p>这可能会使其不太适合关键字搜索。更多讨论请参见“混合搜索”部分。</p></li></ul></li></ul><p>以下是一个包含<code>min_score</code>规范化语义查询的示例。排名窗口参数已增加到 500，使系统能够返回从第 100 条开始的更长结果列表。</p>GET search-movies/_search
{
  "size": 100,
  "_source": [
    "title", "overview"
  ],
  "retriever": {
    "linear": {
      "rank_window_size": 500,
      "min_score": 0.25,
      "retrievers": [
        {
          "normalizer": "minmax",
          "retriever": {
            "standard": {
              "query": {
                "semantic": {
                  "field": "overview_vector",
                  "query": "superhero movie"
                }
              }
            }
          }
        }
      ]
    }
  }
}<p>当前参数已设置为高于生产环境的常规值，以便我们全面检验搜索结果质量并针对性优化输出。</p><h2>使用线性检索器的混合搜索</h2><p>对于混合搜索，最简单的方法是归一化所有分数，分配权重，并应用最低得分。请注意，通过选择总和为 1 的权重，可以将总分控制在 0-1 的范围内。这样使得最终得分易于解读，且便于调整 <code>min_score</code>。以下是一个示例：</p>GET search-movies/_search
{
  "size": 100,
  "_source": ["title", "overview","keywords"],
  "retriever": {
    "linear": {
      "rank_window_size": 500,
      "min_score": 0.25,
      "retrievers": [
        {
          "weight": 0.6,
          "normalizer": "minmax",
          "retriever": {
            "standard": {
              "query": {
                "semantic": {
                  "field": "overview_vector",
                  "query": "superhero movie"
                }
              }
            }
          }
        },
        {
          "weight": 0.4,
          "normalizer": "minmax",
          "retriever": {
            "standard": {
              "query": {
                "multi_match": {
                  "query": "superhero movie",
                  "fields": ["overview","keywords", "title"],
                  "type": "cross_fields",
                  "minimum_should_match": "2"
                }
              }
            }
          }
        }
      ]
    }
  }
}<h2>使用 RRF 的混合搜索</h2><p>使用 BM25 时，我们通常通过其他方式控制精度，例如使用<code>AND</code> 操作符或<code>minimum_should_match</code> 。此外，由单个、精确和罕见术语组成的查询自然会导致搜索结果较少，而且往往都是高度相关的结果。这就可能导致：</p><ul><li><p>在 BM25 检索器中，排名靠后的结果即使绝对 BM25 分值接近头部结果，仍会被赋予较低的归一化分数。</p></li><li><p>将极低的 BM25 分值与语义分值相加，总分即可近似为语义分值。</p></li><li><p>缺少 BM25 分值参考可能导致 <code>min_score threshold</code> 丢弃该文档。</p></li></ul><p>作为解决方案，我们可以改用倒数排序融合 (RRF) 来结合 BM25 和语义结果。该方法通过关注各结果集中的文档位置而非原始分值，巧妙规避了不同检索算法评分体系难以直接比较的技术难题。在这种情况下，<code>min_score</code> 仅应用于语义检索器。</p>GET search-movies/_search
{
  "_source": ["title", "overview","keywords"],
  "retriever": {
    "rrf": {
      "rank_window_size": 500,
      "retrievers": [
        {
          "linear": {
            "rank_window_size": 500,
            "min_score": 0.25,
            "retrievers": [
              {
                "normalizer": "minmax",
                "retriever": {
                  "standard": {
                    "query": {
                      "semantic": {
                        "field": "overview_vector",
                        "query": "superhero movie"
                      }
                    }
                  }
                }
              }
            ]
          }
        },
        {
          "standard": {
            "query": {
              "multi_match": {
                "query": "superhero movie",
                "fields": ["overview", "keywords","title"],
                "type": "cross_fields",
                "minimum_should_match": "2"
              }
            }
          }
        }
      ]
    }
  }
}<h2>结论</h2><p>通过采用 <code>min_score</code>，我们已验证可有效降低语义检索算法因高召回率导致的结果集中误报数量。要了解有关检索器的更多信息，请参阅本<a href="https://www.elastic.co/search-labs/blog/elasticsearch-retrievers">博文</a>和<a href="https://www.elastic.co/docs/solutions/search/retrievers-overview">Elasticsearch 文档</a>。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/semantic-precision-minimum-score</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/semantic-precision-minimum-score</guid>
    <category><![CDATA[相关性]]></category>
    <category><![CDATA[混合搜索]]></category>
    <dc:creator><![CDATA[Mattias Brunnert]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4a3fba607900049/6a170e0fcdacbf8fe17d2a7a/8b3b5910abfe16d48d309341a0027008b16c4340-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 20 Feb 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[使用判断列表评估搜索查询的相关性]]></title>
    <description><![CDATA[针对在 Elasticsearch 中开展可扩展的搜索测试，探究如何构建判定列表以客观评估搜索查询相关性，并提升召回率等性能指标。]]></description>
    <content:encoded><![CDATA[<p>从事搜索引擎开发的工程师们常常会遇到同一个问题：业务团队对某次特定搜索结果并不满意，因为他们期望排在搜索结果首位的文档，实际却出现在结果列表的第三或第四位。</p><p>然而，当你解决这一问题时，却可能因无法手动测试所有情况而不经意间破坏其他查询的功能。但你或你的 QA 团队该如何测试，以确认某一项查询的改动是否会对其他查询产生连锁反应呢？或者更关键的是，你们要如何确保所作的改动确实优化了某项查询呢？</p><h2>转向系统性评估</h2><p>这个时候，判断列表就可以派上用场。与其在每次更改时依赖手动和主观测试，不如定义一组与业务案例相关的固定查询及其相关结果。</p><p>这一组（测试用例或数据）将成为基准参照。每次实施改动时，你都用它来评估搜索效果是否确实得到了提升。</p><p>这种方法的价值在于：</p><ul><li><p><strong>消除不确定性</strong>：无需再费心猜测所做的更改是否会影响其他查询；数据会直接告诉你答案。</p></li><li><p><strong>停止人工测试</strong>：一旦判定集被记录下来，测试便会自动执行。</p></li><li><p><strong>佐证更改</strong>：你可以展示出明确的指标，以佐证某项更改所带来的益处。</p></li></ul><h2>如何开始建立判断列表</h2><p>最简单的开始方式之一是获取具有代表性的查询，并手动选择相关文件。有两种方法可以列出此列表：</p><ul><li><p><strong>二元判断：</strong>与查询关联的每一份文档都会被赋予一个<strong>简单标签</strong>：<em>相关</em>（通常标注分数为“1”）和不相关（标注分数为“0”）。</p></li><li><p><strong>分级判断：</strong>在此情境下，每份文档会依据不同等级获得相应分数。例如：采用 0 至 4 分的评分量表，类似于<a href="https://en.wikipedia.org/wiki/Likert_scale">李克特量表</a>，其中 0 分表示“完全不相关”，4 分表示“完全相关”，中间还设有“相关”“有点相关”等不同程度表述。</p></li></ul><p>当搜索意图具有明确界限时，二元判断（是/否）十分奏效，即判断该文档是否应出现在搜索结果中？</p><p>当存在模糊地带时，分级判断更为实用：某些结果相较于其他结果更优，因此你可以将结果划分为“优秀”“良好”和“毫无价值”等不同等级，并运用能体现结果排序权重及用户反馈的评估指标。然而，分级量表也存在弊端：不同评审者对评分等级的使用方式可能存在差异，这会导致判断结果的一致性降低。并且，由于分级指标对高分赋予了更大的权重，即便是一个微小的改动（比如将某项评分从 4 分改为 3 分），也可能在指标上引发远超评审者预期的巨大波动。这种额外引入的主观性使得分级判断结果更具干扰性，且随时间推移愈发难以把控。</p><h2>我需要自己对文件分类吗？</h2><p>不一定，因为有多种不同方法创建判定列表，且每种方法各有其优缺点：</p><ul><li><p><strong>明确判断：</strong>在这种情况下，领域专家会逐一审阅每个查询/文档，并手动判定其相关性（或相关程度）。尽管此方法能确保质量并实现把控，但其可扩展性较差。</p></li><li><p><strong>隐式判断：</strong>采用这种方法时，你会依据真实用户行为（如点击量、跳出率、购买行为等）来推断相关文档。此方法可实现数据的自动收集，但可能存在偏差。例如，用户往往更倾向于点击排名靠前的结果，即便这些结果并不相关。</p></li><li><p><strong>AI 生成的判断：</strong>最后这种方法是借助模型（如 LLM）自动评估查询和文档，人们通常称之为<a href="https://en.wikipedia.org/wiki/LLM-as-a-Judge">LLM 陪审团</a>。其优势在于速度快且易于扩展，不过数据质量取决于所用模型的性能，以及大语言模型训练数据与您业务<a href="http://interests.as/">需求</a>的契合程度。与人工评分一样，LLM 评审团也可能引入自身偏见或出现前后不一致的情况，因此，必须对照一小部分可信判断结果来验证其输出结果。LLM 模型本质上具有概率性，所以即便将<a href="https://www.ibm.com/think/topics/llm-temperature">温度</a>参数设置为 0，也常见同一结果被 LLM 模型给出不同评分的情况。</p></li></ul><p>以下是一些选择最佳方法来构建判断集的建议：</p><ul><li><p>明确界定哪些仅用户能恰当判断的要素对你而言至关重要（例如价格、品牌、语言、风格以及产品细节等）。如果这些要素至关重要，则至少需针对<em>判断列表</em>中的部分内容获取<strong>明确的判断结果</strong>。</p></li><li><p>当你的搜索引擎已有足够流量时，可运用<strong>隐式判断</strong>，即借助点击量、转化率以及停留时长等指标来洞察使用趋势。不过，你仍需谨慎解读这些数据，将其与显式判断结果进行对比，以规避潜在偏差（例如用户往往更倾向于点击排名靠前的结果，即便排名靠后的结果更具相关性）。</p></li></ul><p>为解决这一问题，位置偏差消除技术会对点击数据进行调整或重新加权，以更准确地反映用户的真实兴趣。以下是一些方法：</p><ul><li><p><strong>结果随机排序：</strong>针对部分用户调整搜索结果的排序，以此估算结果位置对点击量的影响。</p></li><li><p><strong>点击模型</strong>包括<a href="https://wiki.math.uwaterloo.ca/statwiki/index.php?title=a_Dynamic_Bayesian_Network_Click_Model_for_web_search_ranking">动态贝叶斯网络 </a><a href="https://wiki.math.uwaterloo.ca/statwiki/index.php?title=a_Dynamic_Bayesian_Network_Click_Model_for_web_search_ranking"><strong>DBN</strong></a> 和<a href="https://rsrikant.com/papers/kdd10.pdf">用户浏览模型 </a><a href="https://rsrikant.com/papers/kdd10.pdf"><strong>UBM</strong></a>。这些统计模型会借助滚动行为、停留时长、点击顺序以及返回结果页等模式，来估算用户点击行为反映真实兴趣（而非仅受结果位置影响）的概率。</p></li></ul><h2>示例：电影评分应用</h2><h3>准备工作</h3><p>要运行此示例，需要一个正在运行的<a href="https://www.elastic.co/downloads/elasticsearch">本地</a>或部署在 <a href="https://www.elastic.co/cloud/cloud-trial-overview">Elastic Cloud</a> 上（托管或无服务器）的 Elasticsearch 8.x 集群，以及访问 <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis">REST API</a> 或 Kibana 的权限。</p><p>想象有一款应用程序，用户可以在其中上传自己对电影的看法，还可以搜索要观看的电影。由于文本由用户自己撰写，因此可能存在拼写错误和表达方式上的多种差异。因此，搜索引擎必须能够解读这种多样性，并为用户提供有用的结果。</p><p>为能在不影响整体搜索行为的前提下对查询进行迭代优化，贵公司业务团队基于最常执行的搜索查询，创建了以下二元判断集：</p><p>查询</p><p>DocID</p><p>文本</p><p>迪卡普里奥的表演</p><p>doc1</p><p>迪卡普里奥在《荒野猎人》中的表演令人惊叹。</p><p>迪卡普里奥的表演</p><p>doc2</p><p>《盗梦空间》中，莱昂纳多·迪卡普里奥饰演了他最具标志性的角色之一。</p><p>迪卡普里奥的表演</p><p>doc3</p><p>布拉德·皮特在这部犯罪惊悚片中表现出色。</p><p>迪卡普里奥的表演</p><p>doc4</p><p>一部充满惊险动作、视觉效果惊艳的冒险大片。</p><p>让人热泪盈眶的悲伤电影</p><p>doc5</p><p>这是一个令人心碎的关于爱与失去的故事，让我哭了好几个小时。</p><p>让人热泪盈眶的悲伤电影</p><p>doc6</p><p>有史以来最催泪的电影之一，记得带上纸巾！</p><p>让人热泪盈眶的悲伤电影</p><p>doc7</p><p>让你捧腹大笑的轻松喜剧</p><p>让人热泪盈眶的悲伤电影</p><p>doc8</p><p>一部充满动作与激情的科幻史诗巨作。</p><p>正在创建索引：</p>PUT movies
{
  "mappings": {
    "properties": {
      "text": {
        "type": "text"
      }
    }
  }
}<p>批量请求：</p>POST /movies/_bulk
{ "index": { "_id": "doc1" } }
{ "text": "DiCaprio performance in The Revenant was breathtaking." }
{ "index": { "_id": "doc2" } }
{ "text": "Inception shows Leonardo DiCaprio in one of his most iconic roles." }
{ "index": { "_id": "doc3" } }
{ "text": "Brad Pitt delivers a solid performance in this crime thriller." }
{ "index": { "_id": "doc4" } }
{ "text": "An action-packed adventure with stunning visual effects." }
{ "index": { "_id": "doc5" } }
{ "text": "A heartbreaking story of love and loss that made me cry for hours." }
{ "index": { "_id": "doc6" } }
{ "text": "One of the saddest movies ever made -- bring tissues!" }
{ "index": { "_id": "doc7" } }
{ "text": "A lighthearted comedy that will make you laugh." }
{ "index": { "_id": "doc8" } }
{ "text": "A science-fiction epic full of action and excitement." }<p>以下是该应用程序正在使用的 Elasticsearch 查询：</p>GET movies/_search
{
 "query": {
   "match": {
     "text": {
       "query": "DiCaprio performance",
       "minimum_should_match": "100%"
     }
   }
 }
}<h3>从判断到指标</h3><p>就其本身而言，判断列表并不提供太多信息；它们只是我们查询结果的期望。它们真正的优势在于，当我们使用它们来计算客观指标以衡量我们的搜索性能时。</p><p>如今，大多数常用指标包含</p><ul><li><p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#k-precision"><strong>精度</strong></a><strong>：</strong>衡量所有搜索结果中真正相关的结果比例。</p></li><li><p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#k-recall"><strong>召回率</strong></a><strong>：</strong>衡量搜索引擎在检索出的 x 个结果中，找到的相关结果所占的比例。</p></li><li><p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#_discounted_cumulative_gain_dcg"><strong>折损累积增益（DCG）</strong></a><strong>：</strong>用于衡量结果排序的质量，该指标基于最相关的结果应排在前列这一原则进行评估。</p></li><li><p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#_mean_reciprocal_rank"><strong>平均倒数排名（MRR）</strong></a>：用于衡量首个相关结果所处的排名位置情况 。在列表中越靠前，其分数就越高。</p></li></ul><p>以同样的电影评分应用程序为例，我们将计算召回率指标，看看我们的查询是否遗漏了任何信息。</p><p>在 Elasticsearch 中，我们可以通过<a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval">排名评估 API</a>，使用<em>判断列表</em>来计算指标。该 API 将判断列表、查询以及想要评估的指标作为输入，并返回一个数值，该数值是对查询结果与判断列表进行对比后得出的结果。</p><p>让我们针对已提出的这两个查询运行判定列表：</p>POST /movies/_rank_eval
{
 "requests": [
   {
     "id": "dicaprio-performance",
     "request": {
       "query": {
         "match": {
           "text": {
             "query": "DiCaprio performance",
             "minimum_should_match": "100%"
           }
         }
       }
     },
     "ratings": [
       {
         "_index": "movies",
         "_id": "doc1",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc2",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc3",
         "rating": 0
       },
       {
         "_index": "movies",
         "_id": "doc4",
         "rating": 0
       }
     ]
   },
   {
     "id": "sad-movies",
     "request": {
       "query": {
         "match": {
           "text": {
             "query": "sad movies that make you cry",
             "minimum_should_match": "100%"
           }
         }
       }
     },
     "ratings": [
       {
         "_index": "movies",
         "_id": "doc5",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc6",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc7",
         "rating": 0
       },
       {
         "_index": "movies",
         "_id": "doc8",
         "rating": 0
       }
     ]
   }
 ],
 "metric": {
   "recall": {
     "k": 10,
     "relevant_rating_threshold": 1
     }
 }
}<p>我们将向 rank_eval 发送两个请求：一个针对莱昂纳多·迪卡普里奥查询，另一个针对悲伤电影查询每个请求均包含一个查询及其对应的判定列表（评分）。我们无需对所有文档进行评分，因为未纳入评分范围的文档将被视为未作判定。在进行计算时，召回率仅考虑“相关文档集”，即那些在评分中被认定为相关的文档。</p><p>在此情形下，针对莱昂纳多·迪卡普里奥的查询召回率为 1，而悲伤电影查询的召回率为 0。这意味着对于第一个查询，我们能够获取到所有相关结果，而第二个查询则未获取到任何相关结果。因此，平均召回率为 0.5。</p>{
 "metric_score": 0.5,
 "details": {
   "dicaprio-performance": {
     "metric_score": 1,
     "unrated_docs": [],
     "hits": [
       {
         "hit": {
           "_index": "movies",
           "_id": "doc1",
           "_score": 2.4826927
         },
         "rating": 1
       },
       {
         "hit": {
           "_index": "movies",
           "_id": "doc2",
           "_score": 2.0780432
         },
         "rating": 1
       }
     ],
     "metric_details": {
       "recall": {
         "relevant_docs_retrieved": 2,
         "relevant_docs": 2
       }
     }
   },
   "sad-movies": {
     "metric_score": 0,
     "unrated_docs": [],
     "hits": [],
     "metric_details": {
       "recall": {
         "relevant_docs_retrieved": 0,
         "relevant_docs": 2
       }
     }
   }
 },
 "failures": {}
}<p>或许我们对 <strong>minimum_should_match</strong> 参数设置得过于严苛了，因为要求查询中的所有词汇都必须在文档中出现，这很可能会导致我们遗漏掉一些相关结果。不妨去掉 <strong>minimum_should_match</strong> 参数，这样只要文档中包含查询语句里的任意一个词汇，该文档就会被视为相关结果。</p>POST /movies/_rank_eval
{
 "requests": [
   {
     "id": "dicaprio-performance",
     "request": {
       "query": {
         "match": {
           "text": {
             "query": "DiCaprio performance"
           }
         }
       }
     },
     "ratings": [
       {
         "_index": "movies",
         "_id": "doc1",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc2",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc3",
         "rating": 0
       },
       {
         "_index": "movies",
         "_id": "doc4",
         "rating": 0
       }
     ]
   },
   {
     "id": "sad-movies",
     "request": {
       "query": {
         "match": {
           "text": {
             "query": "sad movies that make you cry"
           }
         }
       }
     },
     "ratings": [
       {
         "_index": "movies",
         "_id": "doc5",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc6",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc7",
         "rating": 0
       },
       {
         "_index": "movies",
         "_id": "doc8",
         "rating": 0
       }
     ]
   }
 ],
 "metric": {
   "recall": {
     "k": 10,
     "relevant_rating_threshold": 1
     }
 }
}<p>如你所见，通过在两个查询中的其中一个里移除 <strong>minimum_should_match</strong> 参数，现在两个查询的平均召回率都达到了 1。</p>{
  "metric_score": 1,
  "details": {
    "dicaprio-performance": {
      "metric_score": 1,
      "unrated_docs": [],
      "hits": [
        {
          "hit": {
            "_index": "movies",
            "_id": "doc1",
            "_score": 2.0661702
          },
          "rating": 1
        },
        {
          "hit": {
            "_index": "movies",
            "_id": "doc3",
            "_score": 0.732218
          },
          "rating": 0
        },
        {
          "hit": {
            "_index": "movies",
            "_id": "doc2",
            "_score": 0.6271719
          },
          "rating": 1
        }
      ],
      "metric_details": {
        "recall": {
          "relevant_docs_retrieved": 2,
          "relevant_docs": 2
        }
      }
    },
    "sad-movies": {
      "metric_score": 1,
      "unrated_docs": [],
      "hits": [
        {
          "hit": {
            "_index": "movies",
            "_id": "doc7",
            "_score": 2.1307156
          },
          "rating": 0
        },
        {
          "hit": {
            "_index": "movies",
            "_id": "doc5",
            "_score": 1.3160692
          },
          "rating": 1
        },
        {
          "hit": {
            "_index": "movies",
            "_id": "doc6",
            "_score": 1.190063
          },
          "rating": 1
        }
      ],
      "metric_details": {
        "recall": {
          "relevant_docs_retrieved": 2,
          "relevant_docs": 2
        }
      }
    }
  },
  "failures": {}
}<p>总而言之，移除 minimum_should_match: 100% 这一条件后，我们得以使两个查询均实现完美召回率。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaf4f08a8a2915180/6a170df61949f76cbfe7aaba/24d055da4348c63827ba7046fe8cafb6f47cadd8-546x628.png" alt="" /><p>我们做到了！对不对？</p><p>没那么快！</p><p>通过提升召回率，我们能够获取到更广泛的结果范围。然而，每一次调整都意味着需要权衡取舍。这正是为何要定义完整的测试用例，并运用不同指标来评估各项更改的原因。</p><p>使用判断列表和指标可以防止您在进行更改时盲目行事，因为现在您有数据可以支持这些更改。验证不再是手动和重复的，您可以在多个用例中测试您的更改。此外，A/B 测试允许您实时测试哪种配置最适合您的用户和业务案例，从而实现从技术指标到实际指标的完整循环。</p><h2>使用判断列表的最终建议</h2><p>运用判定列表开展工作，不仅关乎评估测量，更在于构建一个能让你自信迭代优化的框架。为实现这一目标，可遵循以下建议：</p><ol><li><p><strong>从小处着手，但一定要开始行动。</strong>你无需准备 10000 个查询，且每个查询都配有 50 个判断列表。你只需找出 5 到 10 个对业务场景最为关键的查询，并明确你期望在结果顶部看到的文档即可。这已经能为你提供一个基础。通常，你应优先从热门查询以及无结果的查询入手开展工作。你也可以先使用像精确率这样易于配置的指标进行测试，然后再逐步尝试更复杂的指标。</p></li><li><p><strong>与用户核实。</strong>在生产环境中通过 A/B 测试对数据指标进行补充验证。如此一来，你便能知晓那些在指标上表现良好的更改是否也切实产生了实际影响。</p></li><li><p><strong>保持列表有效性。</strong>你的商业案例会不断变化，关键问题也会随之变化。定期更新判断以反映新的需求。</p></li><li><p><strong>使其成为流程的一部分。</strong>将判断列表整合到开发管道之中。确保每次配置更改、同义词添加或文本分析操作，都能自动对照基础列表进行验证。</p></li><li><p><strong>将技术知识与战略相结合。</strong>不要仅仅满足于衡量精确率或召回率等技术指标。要利用评估结果为业务成果提供决策依据。</p></li></ol>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/judgment-lists-search-query-relevance-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/judgment-lists-search-query-relevance-elasticsearch</guid>
    <category><![CDATA[相关性]]></category>
    <category><![CDATA[在 Elastic 内部]]></category>
    <dc:creator><![CDATA[Jhon Guzmán]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcadfd2fb1cc95b4c/6a170df7acf0887798be9bd0/25478d0ffb228afd5d65d82312998ec1c299c565-700x490.png" length="0" type="image/png"/>
    <pubDate>Thu, 11 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[混合搜索不头疼：用检索器简化混合搜索]]></title>
    <description><![CDATA[探索如何利用线性和 RRF 检索器的多字段查询格式简化 Elasticsearch 中的混合搜索，并在不了解 Elasticsearch 索引的情况下创建查询。]]></description>
    <content:encoded><![CDATA[<p><a href="https://www.elastic.co/what-is/hybrid-search">混合搜索</a>被公认为是一种强大的搜索方法，它将<a href="https://www.elastic.co/search-labs/blog/lexical-and-semantic-search-with-elasticsearch#lexical-search---sparse-retrieval">词法搜索</a>的精确性和速度与<a href="https://www.elastic.co/what-is/semantic-search">语义搜索</a>的自然语言能力结合在一起。不过，在实际应用中可能会很棘手，往往需要对索引有深入的了解，并通过非繁琐的配置来构建冗长的查询。在本博客中，我们将探讨<a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers#multi-field-query-format">线性和 RRF 检索器的多字段查询格式</a>如何使混合搜索变得更简单、更易用，从而消除常见的头痛问题，让您更轻松地充分利用其强大功能。我们还将回顾多字段查询格式如何使您在不了解索引的情况下执行混合搜索查询。</p><h2>分数范围问题</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3521a6558cd477ca/6a17f174445de94d3c4d0225/c8b49153c47d2cdc233c0d2e440db04711d48ca5-1600x1600.jpg" alt="" /><p>首先，让我们回顾一下混合搜索困难的主要原因之一：不同的分数范围。我们的老朋友<a href="https://www.elastic.co/elasticon/conf/2016/sf/improved-text-scoring-with-bm25">BM25</a>会产生无限制的分数。换句话说，BM25 可以生成从接近 0 到（理论上）无穷大的分数。与此相反，针对<code>dense_vector</code> 字段的查询会产生介于 0 和 1 之间的分数。由于<code>semantic_text</code> 混淆了用于索引嵌入的字段类型，因此除非您对索引和推理端点配置有详细了解，否则很难说清查询的分数范围。这在试图交错使用词汇和语义搜索结果时会带来问题，因为即使语义结果更相关，词汇结果也可能优先于语义结果。对于这个问题，普遍接受的解决方案是在交织结果之前对分数进行归一化处理。Elasticsearch 为此提供了两种工具：<a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers/linear-retriever">线性</a>检索器和<a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers/rrf-retriever">RRF</a>检索器。
</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt09ed6bf3d25066bd/6a17f1750b0bedbd70dd3686/264481268c8b6ac259e3c257b85431b513f16672-1077x586.png" alt="使用线性/rrf 与不使用线性/rrf 的搜索结果比较" /><p><strong>RRF</strong>检索器采用<a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">RRF 算法</a>，将文档排名作为衡量相关性的标准，并舍弃分数。由于不考虑分数，因此分数范围不匹配不是问题。</p><p><strong>线性</strong>检索器使用线性组合来确定文档的最终得分。这包括获取文档中每个组件查询的得分，对其进行归一化处理，然后求和生成总分。在数学上，这一操作可以表示为</p>Total Score = 𝚺(N(Sx))<p>其中<code>N</code> 是归一化函数，SX 是查询 X 的得分。归一化功能在这里非常关键，因为它将每个查询的得分转换为使用相同的范围。您可以<a href="https://www.elastic.co/search-labs/blog/linear-retriever-hybrid-search">在这里</a>了解有关线性寻回猎犬的更多信息。</p><h2>分解</h2><p>用户可以利用这些工具实现有效的混合搜索，但需要对索引有一定的了解。让我们看一个使用线性检索器的示例，在这个示例中，我们将查询一个包含两个字段的索引：</p>PUT linear_retriever_example
{
  "mappings": {
    "properties": {
      "semantic_text_field": { &lt;1&gt;
        "type": "semantic_text",
        "inference_id": ".multilingual-e5-small-elasticsearch"
      },
      "text_field": { &lt;2&gt;
        "type": "text"
      }
    }
  }
}<p>1.<code>semantic_text_field</code> 是一个<code>semantic_text</code> 字段，使用文本嵌入模型<a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-e5">E5</a></p><p><code>text_field</code> 是一个标准的<code>text</code> 字段</p>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "retrievers": [
        {
          "retriever": {
            "standard": {
              "query": {
                "match": { &lt;1&gt;
                  "semantic_text_field": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        },
        {
          "retriever": {
            "standard": {
              "query": {
                "match": {
                  "text_field": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        }
      ]
    }
  }
}<p>1.我们在 字段上使用 查询，我们<a href="https://www.elastic.co/search-labs/blog/semantic-search-match-knn-sparse-vector#we-made-match-happen-in-semantic-search!"> 在 Elasticsearch 8.18/9.0</a> 中添加了对<code>match</code> 该查询的 支持<code>semantic_text</code></p><p>
在构建查询时，我们需要牢记<code>semantic_text_field</code> 使用的是文本嵌入模型，因此对它的任何查询都会产生 0 到 1 之间的分数。我们还需要知道<code>text_field</code> 是一个标准的<code>text</code> 字段，因此对它的查询将产生一个无限制的分数。为了创建具有适当相关性的结果集，我们需要使用一种检索器，在合并查询得分之前将其归一化。在本例中，我们使用了<code>minmax</code> 归一化的线性检索器，它将每个查询的得分归一化为介于 0 和 1 之间的值。</p><p>本例中的查询结构相当简单，因为只涉及两个字段。然而，随着字段的增加和类型的变化，它很快就会变得复杂。这表明，要编写有效的混合搜索查询，往往需要对所查询的索引有更深入的了解，这样才能在组合之前对组件查询得分进行适当的归一化处理。这对混合搜索的广泛应用构成了障碍。</p><h3>查询分组</h3><p>让我们扩展一下示例：如果我们想查询一个<code>text</code> 字段和两个<code>semantic_text</code> 字段，该怎么办？我们可以构建这样一个查询：</p>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "retrievers": [
        {
          "retriever": {
            "standard": {
              "query": {
                "semantic": {
                  "field": "semantic_text_field_1",
                  "query": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        },
        {
          "retriever": {
            "standard": {
              "query": {
                "semantic": {
                  "field": "semantic_text_field_2",
                  "query": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        },
        {
          "retriever": {
            "standard": {
              "query": {
                "match": {
                  "text_field": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        }
      ]
    }
  }
}<p>这表面上看起来不错，但也有潜在的问题。现在，<code>semantic_text</code> 场比赛占总分的⅔：</p>Total Score = N(semantic_text_field_1 score) + N(semantic_text_field_2 score) + N(text_field score)<p>这可能不是你想要的结果，因为这会造成分数不平衡。在只有 3 个字段的示例中，这种影响可能并不明显，但如果查询的字段较多，就会出现问题。例如，大多数索引包含的词法字段远远多于语义字段（即<code>dense_vector</code>,<code>sparse_vector</code>, 或<code>semantic_text</code>) 。如果我们使用上述模式查询一个包含 9 个词法字段和 1 个语义字段的索引呢？词性匹配将占得分的 90% ，从而削弱语义搜索的有效性。</p><p>解决这一问题的常用方法是将查询分为词汇和语义两个类别，并对两者进行平均加权。这就避免了任一类别在总分中占主导地位。</p><p>让我们付诸实践。在使用线性检索器时，本例中的分组查询方法会是怎样的？</p>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "retrievers": [
        {
          "retriever": {
            "linear": {
              "retrievers": [
                {
                  "retriever": {
                    "standard": {
                      "query": {
                        "semantic": {
                          "field": "semantic_text_field_1",
                          "query": "foo"
                        }
                      }
                    }
                  },
                  "normalizer": "minmax"
                },
                {
                  "retriever": {
                    "standard": {
                      "query": {
                        "semantic": {
                          "field": "semantic_text_field_2",
                          "query": "foo"
                        }
                      }
                    }
                  },
                  "normalizer": "minmax"
                }
              ]
            }
          },
          "normalizer": "minmax"
        },
        {
          "retriever": {
            "standard": {
              "query": {
                "match": {
                  "text_field": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        }
      ]
    }
  }
}<p>哇，真是啰嗦！您甚至可能需要上下滚动多次才能查看整个查询！在这里，我们使用两级标准化来创建查询组。数学上可以表示为</p>Total Score = N(N(semantic_text_field_1 score) + N(semantic_text_field_2 score)) + N(text_field score)<p>这第二级规范化可确保<code>semantic_text</code> 字段和<code>text</code> 字段的查询权重均匀。请注意，在本例中，我们省略了<code>text_field</code> 的二级规范化，因为只有一个词法字段，这样可以避免<em>更多的</em>繁琐。</p><p>这种查询结构已经很笨重了，而且我们只查询三个字段。随着查询字段的增多，即使是经验丰富的搜索从业人员也越来越难以驾驭。</p><h2>多字段查询格式</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5d9007d279f0da29/6a17f17714d90c39cf79b6f5/dd04e1686076a574b717c1460acfe4eb79299208-1600x1600.jpg" alt="" /><p>我们在 Elasticsearch 8.19、9.1 和 无服务器 中为线性和 RRF 检索器添加了<a href="https://www.elastic.co/cloud/serverless"> </a><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers#multi-field-query-format">多字段查询格式</a> ，以简化所有这些操作。现在，您只需使用"...... "即可执行与上述相同的查询：</p>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "fields": [ "semantic_text_field_1", "semantic_text_field_2", "text_field" ],
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}<p>这将查询从 55 行缩减到 9 行！Elasticsearch 自动将索引映射用于</p><ul><li><p>确定每个查询字段的类型</p></li><li><p>将每个字段归入一个词汇或语义类别</p></li><li><p>在最终得分中平均分配每个类别的权重</p></li></ul><p>这样，任何人都可以执行有效的混合搜索查询，而无需了解有关索引或所用推理端点的详细信息。</p><p>使用 RRF 时，可以省略<code>normalizer</code> ，因为排名是相关性的代表：</p>GET rrf_retriever_example/_search
{
  "retriever": {
    "rrf": {
      "fields": [ "semantic_text_field_1", "semantic_text_field_2", "text_field" ],
      "query": "foo"
    }
  }
}<h2>每场增强</h2><p>在使用线性检索器时，您可以应用每个字段增强功能来调整某些字段中匹配的重要性。例如，假设您要查询四个字段：两个<code>semantic_text</code> 字段和两个<code>text</code> 字段：</p>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "fields": [ "semantic_text_field_1", "semantic_text_field_2", "text_field_1", "text_field_2" ],
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}<p>默认情况下，每个字段在其组（词法或语义）中的权重相同。比分细目如下</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdc6e197e5ff7b68d/6a17f179505ac32834ad8c46/ba31c76189e3a1e5b1638437ccf0528aafec2598-1600x549.png" alt="查询组和领域得分的比较" /><p>换句话说，每个字段占总分的 25% 。</p><p>我们可以使用<code>field^boost</code> 语法为任何字段添加每个字段的提升。让我们将<code>semantic_text_field_1</code> 和<code>text_field_1</code> 提升 2：</p>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "fields": [ "semantic_text_field_1^2", "semantic_text_field_2", "text_field_1^2", "text_field_2" ]
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}<p>现在的比分是这样的</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltccc9ed7d6e865a5e/6a17f17abe6086a31d004882/de20e555d52f914bf483a048d056f54f4fece757-1600x549.png" alt="通过 ref 和混合搜索更改字段权重" /><p>每个查询组的权重仍然相同，但组内字段的权重发生了变化：</p><ul><li><p><code>semantic_text_field_1</code> 是语义查询组得分的 66% ，是总分的 33% </p></li><li><p><code>text_field_1</code> 是词法查询组得分的 66% ，是总分的 33% </p></li></ul><p>ℹ️ 请注意，在按字段提升时，总分范围不会改变。这是分数标准化的预期副作用，可确保词法和语义查询分数保持直接可比性。</p><p>ℹ️ 在 Elasticsearch 9.2+ 中，每个字段的提升也可与 RRF 检索器一起使用</p><h3>通配符分辨率</h3><p>您可以在<code>fields</code> 参数中使用<code>*</code> 通配符来匹配多个字段。继续上面的例子，这个查询在功能上等同于明确查询<code>emantic_text_field_1</code>,<code>semantic_text_field_2</code>, 和<code>text_field_1</code> ：</p>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "fields": [ "semantic_text_field_*", "*_field_1" ],
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}<p>值得注意的是，<code>*_field_1</code> 模式同时匹配<code>text_field_1</code> 和<code>semantic_text_field_1</code> 。查询将自动执行，就像明确查询每个字段一样。<code>semantic_text_field_1</code> 同时匹配两种模式也没有问题；在执行查询之前，所有匹配的字段名称都会被去除重复。</p><p>您可以通过多种方式使用通配符：</p><ul><li><p>前缀匹配（例如：<code>*_text_field</code>)</p></li><li><p>内联匹配 (ex:<code>semantic_*_field</code>)</p></li><li><p>后缀匹配（例如：<code>semantic_text_field_*</code>)</p></li></ul><p>您还可以使用多个通配符来应用上述组合，例如<code>*_text_field_*</code> 。</p><h3>默认查询字段</h3><p>多字段查询格式还允许您查询您一无所知的索引。如果省略<code>fields</code> 参数，它将查询由<a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/index-modules">index.query.default_field 索引设置</a>指定的所有字段：</p>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}<p>默认情况下，<code>index.query.default_field</code> 设置为<code>*</code> 。该通配符将解析索引中支持术语查询的所有字段类型，其中大多数字段类型都支持术语查询。例外情况是</p><ul><li><p><code>dense_vector</code> 领域</p></li><li><p><code>rank_vector</code> 领域</p></li><li><p>几何领域：<code>geo_point</code>, <code>shape</code></p></li></ul><p>当你想在第三方提供的索引上执行混合搜索查询时，该功能尤其有用。多字段查询格式可让您以简单的方式执行适当的查询。只需排除<code>fields</code> 参数，就能查询所有适用字段。</p><h2>结论</h2><p>分数范围问题会让有效的混合搜索实施起来很头疼，尤其是在对所查询的索引或所使用的推理端点了解有限的情况下。线性和 RRF 检索器的多字段查询格式将基于查询分组的自动混合搜索方法打包到简单易用的应用程序接口中，从而减轻了这种痛苦。附加功能（如按字段增强、通配符解析和默认查询字段）扩展了功能，涵盖了多种使用情况。</p><h2>立即试用多字段查询格式</h2><p>您可以通过<a href="https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/create-serverless-project"> 免费试用</a> ，在完全托管的 Elasticsearch<a href="https://www.elastic.co/cloud/serverless"> Serverless 项目中使用多字段查询格式检查线性检索器和</a> RRF 检索器。它还提供从 8.19&amp; 9.1 开始的堆栈版本。</p><p>只需一条命令，几分钟即可在本地环境中开始使用：</p>curl -fsSL https://elastic.co/start-local | sh<p></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/hybrid-search-multi-field-query-retrievers-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/hybrid-search-multi-field-query-retrievers-elasticsearch</guid>
    <category><![CDATA[混合搜索]]></category>
    <category><![CDATA[相关性]]></category>
    <dc:creator><![CDATA[Mike Pellegrini]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt89e066372c549595/6a17f17c3e9e457c38ba1583/4494f98ae3958bbdbc6171df9677fc4d65ec5640-1536x1024.png" length="0" type="image/png"/>
    <pubDate>Thu, 27 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[你懂的，语境--第一部分：混合搜索和语境工程的演变]]></title>
    <description><![CDATA[探索混合搜索和上下文工程如何从词汇基础发展到支持下一代代理人工智能工作流程。]]></description>
    <content:encoded><![CDATA[<h2>我们全新的人工智能代理世界</h2><p>和我们许多人一样，我发现自己对人工智能能力的发展速度既目瞪口呆又惊叹不已。我们首先看到大型语言模型（LLMs）和矢量搜索将我们带入语义革命，在这场革命中，我们不再需要用关键字来寻找事物。随后，法学硕士们向我们展示了与数据交互的新方法，他们使用聊天界面将自然语言请求转化为回复，将庞大的知识库提炼为易于使用的摘要。我们现在（已经）以 "代理人工智能"（agentic AI）工作流的形式出现的自动 LLM 驱动逻辑已经初具雏形，它可以从语义上理解接收到的请求，推理出需要采取的步骤，然后从可用的工具中选择迭代执行的行动来实现这些目标。</p><p>人工智能代理的前景正迫使我们从主要使用 "提示工程 "来塑造我们的人工智能生成交互，发展到关注我们如何帮助代理工具获得最相关、最有效的额外信息，以便 LLM 在生成其响应时加以考虑--"情境工程 "是下一个前沿领域。混合搜索是迄今为止最强大、最灵活的浮现相关上下文的手段，Elastic 的搜索人工智能平台开辟了一种全新的方式来利用数据为上下文工程服务。在本文中，我们将从两个角度讨论法律硕士如何改变了信息检索的世界，然后再讨论如何将它们结合起来以取得更大的成果。有相当多的地方需要覆盖...</p><h2>第 I 部分：法律硕士如何改变搜索方式</h2><p>让我们从法律硕士如何改变了我们获取和检索信息的方式这个角度出发。</p><h3>我们的词汇遗产</h3><p>长期以来，我们一直生活在有限的词库搜索世界中（尽我们所能，相当不错）。搜索是我们在研究或开始一个新项目时最先使用的工具，直到最近，我们还需要以词法搜索引擎能够理解的方式来描述我们的查询。词法搜索依赖于将某种形式的查询术语与文档语料库中的关键字进行匹配，无论内容是非结构化的还是结构化的。词法搜索要返回命中的文档，必须与该关键词相匹配（或者有同义词列表或词典等受控词汇来为我们建立概念联系）。</p>POST my-index/_search
{
  "size": 10,
  "query": {
    "semantic": {
      "query": "machine learning applications",
      "field": "semantic-content-field"
    }
  }
}<p><em>词法 </em><a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-multi-match-query"><em>多匹配</em></a><em> 查询</em>示例 </p><p>至少搜索引擎有能力返回相关性得分的点击率。搜索引擎提供丰富的查询语法选项，可有效定位索引数据，并提供内置相关性算法，根据用户查询语法的意图对结果进行评分。几十年来，搜索引擎在相关性排序算法方面取得了长足的进步，这使搜索引擎成为一个高效的数据检索平台，可以根据查询结果的相关性对结果进行评分和排序。使用 SQL 作为主要数据检索方法的数据库和其他系统在这方面处于劣势：数据库查询中没有相关性的概念；它们能做的最好的事情就是按字母或数字对结果进行排序。好消息是，您将获得这些关键词的所有点击率（召回率），但相对于您询问这些关键词的<em>原因</em>（精确度）而言，它们的顺序不一定有帮助。这一点很重要，我们很快就会看到...</p><h3>进入（语义）龙</h3><p>信息矢量表示法作为关键字搜索的替代方法，其潜力已被研究了<a href="https://www.elastic.co/search-labs/blog/introduction-to-vector-search">很长时间</a>。矢量让我们摆脱了只用关键词匹配内容的模式，因此前景十分广阔--由于矢量是术语和权重的数字表示，因此可以根据语言模型对术语在训练领域中相互关系的理解，在数学上接近概念。通用矢量搜索之所以拖延了很长时间，是因为模型大多局限于特定领域，它们根本不足以充分理解一个术语在不同语境中可能代表的许多不同概念。</p><p>直到几年前出现了大型语言模型（LLM），它们能够在更大的数据量上进行训练（使用<a href="https://en.wikipedia.org/wiki/Transformer_(deep_learning_architecture)">转换器</a>和<a href="https://en.wikipedia.org/wiki/Attention_(machine_learning)">注意力</a>），矢量搜索才变得实用起来--LLM 的大小和深度最终使矢量能够存储足够的细微差别，从而真正捕捉语义。理解深度的骤然增加使得 LLM 现在可以实现大量以前无法实现的自然语言处理（NLP）功能，其中影响最大的可能是根据序列中迄今为止的上下文推断序列中最有可能出现的下一个术语。推理过程赋予了生成式人工智能近乎人类的文本生成能力。人工智能生成的文本参考了 LLM 对训练数据中术语相关性的理解，并利用请求的措辞来区分术语可能出现的不同语境。</p><p>尽管生成式人工智能非常神奇，但 LLM 也<em>有</em>其局限性，会导致质量和准确性方面的误差，也就是通常所说的幻觉。当 LLM 无法获得信息（或没有正确的上下文引导）来根据事实回答问题时，就会产生幻觉，因此，为了帮助 LLM，它会产生一个自信满满、听起来似是而非的回答。部分原因在于，虽然 LLM 可以在包含各种信息的大型领域中学习语言的用法，但它们必须在某个时间点停止训练，因此它们的理解存在时效性因素--也就是说，模型只能知道在停止训练之前的准确性。造成幻觉的另一个因素是，模型通常不知道私人持有的数据（不能在公共互联网上获取的数据），当这些数据包含特定术语和名词时，这一点尤为重要。</p><h3>矢量数据库</h3><p>LLM 使用一种称为文本嵌入的技术将内容矢量化到其模型空间中，这种技术是指根据所接受的训练，将内容的语义<a href="https://www.elastic.co/search-labs/blog/hybrid-search-multiple-embeddings">嵌入</a>或映射到模型的世界观中。准备和处理嵌入内容需要几个步骤，包括<a href="https://www.elastic.co/search-labs/blog/chunking-strategies-elasticsearch">分块</a>和标记化（以及<a href="https://www.kaggle.com/code/danishmahdi/subword-tokenization-bpe-wordpiece-and-unigram">子词标记化</a>）。其结果通常是一组密集的向量，代表了模型在其向量空间内对该内容块含义的理解。分块是一个不精确的过程，目的是使内容符合模型生成嵌入的处理限制，同时还尝试使用语义结构（如句子和段落指示符）将相关文本归入一个分块。</p><p>由于单个分块与同一文档中的其他分块并不完全关联，因此分块的需要可能会在嵌入文档中造成一些语义损失。神经网络固有的不透明性会使这种损失变得更加严重--LLM 是一个真正的 "黑盒子"，训练过程中术语和概念之间的联系是非确定的，人类无法解释。这就导致了可解释性、可重复性、无意识偏见等问题，并可能失去信任和准确性。不过，从语义上将想法联系起来的能力，以及在查询时不受特定关键词束缚的能力还是非常强大的：</p>POST my-index/_search 
{
  "size": 10, 
  "query": {
    "semantic": {
      "query": "machine learning applications",
      "field": "semantic-content-field"
    }
  }
} <p><a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-semantic-query"><em>语义</em></a><em> 查询 示例</em></p><p>矢量数据库还有一个问题需要考虑：它们不是搜索引擎，而是数据库！在进行<a href="https://www.elastic.co/search-labs/blog/introduction-to-vector-search">矢量相似性搜索</a>时，会对查询词进行编码，以便在模型的矢量空间中找到一组（嵌入）坐标。然后将这些坐标作为靶心，找出与靶心 "近邻 "的文档--这意味着文档的排名（或在结果中的位置）是由计算出的文档坐标与查询坐标的相似度<em>距离</em>决定的。在可能的上下文中，哪个最接近用户的意图？我将其比喻为电影《<a href="https://www.youtube.com/watch?v=x3h7xz558EY&amp;start=3&amp;end=86">星际之门</a>》中的一个场景，我们有六个相交的坐标点来告诉我们目的地（靶心），但如果不知道 "第七个符号"--代表用户主观意图的起点坐标，我们就无法到达目的地。因此，通过表达式语法和相关性评分来考虑查询的主观意图，我们就能得到类似于主观相关性分级的<em>圆柱体</em>，而不是根据不断扩大和无差别的相似性来对向量进行相对排序。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb49ae330d3de1fc1/6a17053ee8fbce089639fb46/1ddfaae0c1496d08d7d30419e6d2aeaeacfc0ea2-1600x544.png" alt="主观相关性渐变的圆柱体。" /><p>LLM 的推理能力可能有助于确定<em>它</em>对查询所掌握的最有可能的上下文，但问题是，<em>在没有帮助的</em>情况下，输入查询的坐标<em>只能</em>根据模型最初的训练方式来确定。</p><p>在某些方面，你可以说矢量相似性走向了与严格的关键词匹配相反的极端--它的优势在于能够克服术语不匹配的问题，但<a href="https://medium.com/data-science/vector-embeddings-are-lossy-heres-what-to-do-about-it-4f9a8ee58bb7">几乎可以</a>说是无懈可击：LLM 倾向于统一相关概念，而不是区分它们。矢量相似性提高了我们从语义上匹配内容的能力，但并不能保证精确度，因为它可能会忽略精确的关键字和特定的细节，而这些细节在模型中并没有得到足够的消歧。矢量相似性搜索本身就很强大，但我们需要将从矢量数据库中获取的结果与其他检索方法的结果关联起来。</p><h3>重新排名技术</h3><p>现在是提及一种名为 "重排 "的通用技术的好时机。"重排 "是对结果集进行重新评分或归一化，使其达到统一的排名顺序。需要重新排序的原因可能是来自多个来源或检索方法的结果具有不同的排序/评分机制（或者根本没有，SQL！），或者重新排序可能是为了使来自非语义来源的结果与用户的查询在语义上保持一致。重新排序是第二阶段的操作，是指通过某种<em>初始检索</em>方法收集到的一组结果（即SQL、词法搜索、向量搜索），然后用不同的评分方法重新排序。</p><p>有几种可用的方法，包括<a href="https://www.elastic.co/docs/solutions/search/ranking/learning-to-rank-ltr">学习排名（Learning-To-Rank，</a>LTR）和<a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">互惠排名融合（Reciprocal Rank Fusion，RRF</a>）--LTR 适用于捕捉搜索结果特征（喜欢、评分、点击等），并利用这些特征对搜索结果进行评分、提升或倾斜。RRF 非常适合合并不同查询模式返回的结果（如词法搜索和矢量数据库搜索）合并为一个结果列表。Elastic 还提供了使用<a href="https://www.elastic.co/search-labs/blog/linear-retriever-hybrid-search">线性重排</a>方法调整分数的灵活性。</p><p>不过，最有效的重排技术之一是<a href="https://www.elastic.co/docs/solutions/search/ranking/semantic-reranking">语义重排</a>，它利用 LLM 的语义理解能力来分析查询和结果的向量嵌入，然后应用相关性评分/重评分来确定最终顺序。当然，语义重排需要与重排模型建立连接，Elasticsearch提供了<a href="https://www.elastic.co/docs/api/doc/elasticsearch/group/endpoint-inference">推理API</a>，让您可以创建<strong>重排</strong>端点，利用内置模型<a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-rerank">（Elastic Rerank</a>）、<a href="https://www.elastic.co/docs/reference/elasticsearch/clients/eland/machine-learning">导入的</a>第三方模型或外部托管服务（如<a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put-cohere">Cohere</a>或<a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put-googlevertexai">Google Vertex AI</a>）。然后就可以通过<a href="https://www.elastic.co/docs/solutions/search/retrievers-overview">检索器</a>查询抽象语法执行重新排序：</p>POST my-index/_search 
{
  "size": 10,
  "retriever": {
    "text_similarity_reranker": {
      "retriever": {
        "rrf": {
          "retrievers": [
            {
              "standard": {
                "query": {
                  "multi_match": {
                    "query": "machine learning applications",
                    "fields": ["title", "content"]
                  }
                }
              }
            },
            {
              "knn": {
                "field": "semantic-content-field",
                "k": 10,
                "num_candidates": 100,
                "query_vector_builder": {
                  "text_embedding": {
                    "model_id": "my-text-embedding-model",
                    "model_text": "machine learning applications"
                  }
                }
              }
            }
          ],
          "rank_window_size": 50,
          "rank_constant": 20
        }
      }
    },
    "field": "content",
    "inference_id": "my-reranker",
    "inference_text": "machine learning applications",
    "rank_window_size": 20
  }
}<p><em>多级检索器重排操作示例</em></p><p>听起来不错吧？我们可以对来自不同来源的结果进行重新排序，从而接近对所有类型内容的语义理解......语义重新排序的计算成本和处理时间都很高，正因为如此，语义重新排序只能在数量有限的结果上进行，这意味着<em>如何</em>检索这些初始结果非常重要。</p><h3>语境检索方法很重要</h3><p>主观意图是确定结果准确性和评分相关性的一个重要因素。由于无法考虑用户执行查询的意图（如通过灵活的语法或第二阶段重排所表达的意图），我们只能从模型空间中已编码的现有上下文中进行选择。我们通常通过<a href="https://en.wikipedia.org/wiki/Retrieval-augmented_generation">检索增强生成（RAG）</a>等技术来解决这种缺乏上下文的问题。RAG 的工作原理是，它可以有效地转移查询的坐标，包括通过预查询返回的其他相关术语，以获取与上下文相关的数据。这就使得提供额外上下文的引擎及其执行检索<em>的</em>初始方法对上下文的准确性更加重要！</p><p>让我们回顾一下不同的上下文检索方法，以及它们对 RAG 操作的帮助或伤害：</p><ul><li><p><strong>没有搜索引擎的混合搜索检索仍然缺乏主观相关性。</strong>如果提供 RAG 的平台主要基于下面的 SQL（包括大多数 "数据湖 "平台），那么在初始检索阶段就缺乏相关性评分。许多数据湖平台提供自己版本的混合检索（而非搜索），通常在基于 SQL 的检索和矢量数据库结果上结合语义重排和 RRF 等重排技术。简单的排序显然不足以进行主观排序，但即使作为第二阶段语义重排操作的基础，SQL 作为第一阶段检索，在只对 "前 k 个 "点击进行语义重排时也会出现问题--如果不在检索时对结果进行某种评分，我们又如何保证<em>最好的</em>结果确实在最前面的结果中呢？</p></li><li><p><strong>对于 RAG 来说，仅有矢量相似性是不够的</strong>。这实际上是由一系列复杂问题造成的--这是嵌入的损失性，还有天真的分块方法、相似性的计算方法，以及主观意图这一至关重要的缺失部分。RAG 的主要目标之一是将生成式人工智能交互建立在客观事实的基础上，既能防止产生幻觉，又能让 LLM 了解它在训练过程中不知道的私人信息。我们可以利用 RAG 提供的额外语境来约束和引导 LLM，使其考虑我们所知道的对回答当前问题最重要的关联和细节。为此，我们需要<em>同时</em>使用语义和词汇方法。</p></li><li><p><strong>基于文件的 grep/regex RAG。</strong>在人工智能代理领域，有一些<a href="https://www.nicolasbustamante.com/p/the-rag-obituary-killed-by-agents">人</a>指出，应使用大幅放大的上下文窗口，通过 grep 和 regex 访问本地文件，以实现 RAG，而不是使用外部检索平台。我们的想法是，有了更大的上下文窗口，法律硕士就能在自己的思维空间内建立概念联系，而不是依赖分块的碎片和多种检索方法/平台来收集相关信息。虽然从理论上讲，拥有整个文档比拥有文档片段能提供更全面的信息，但这只适用于小数据域（或者，例如，在提供用于<a href="https://en.wikipedia.org/wiki/Vibe_coding">振动编码的</a>文件时），即使在这种情况下，初始检索方法也是扫描所有仅有关键字匹配的文档。</p></li></ul><p><strong>搜索不仅仅是检索</strong></p><p>搜索引擎的设计目的是使查询尽可能快速和灵活。在内部，它们利用专门的数据结构来存储和检索不同类型的数据，以满足这些数据类型的需要。Elasticsearch 可优化所有类型数据的存储和查询，包括非结构化/全文词法搜索（匹配、短语、近似、多重匹配）、快速关键字（精确匹配）匹配和过滤、数字范围、日期、IP 地址，而且存储文档结构的方式也非常灵活（例如，可通过"...嵌套或扁平化文档）。Elasticsearch 还是一个原生矢量数据库，可以存储和查询稀疏和密集矢量类型。我们将继续探索创新方法（例如，<a href="https://www.elastic.co/search-labs/blog/better-binary-quantization-lucene-elasticsearch">更好的二进制量化 (BBQ)</a> &amp; <a href="https://www.elastic.co/search-labs/blog/diskbbq-elasticsearch-introduction">DiskBBQ</a>），以保持搜索保真度，同时提高速度、可扩展性以及与矢量化内容相关的成本。Elasticsearch 平台还提供内置的数据弹性和高可用性，并包含数据生命周期管理功能，<a href="https://www.elastic.co/docs/deploy-manage/tools/snapshot-and-restore/searchable-snapshots">如可搜索快照</a>，让您可以在经济高效的对象存储上保留不常访问或长期保留的数据，但仍可完全搜索。</p><h3>混合搜索是最好的选择</h3><p><a href="https://www.elastic.co/what-is/hybrid-search">混合搜索</a>（不仅仅是混合检索）将传统词汇搜索的优势与 LLM 的语义理解和向量相似性搜索相结合。这种协同作用允许在<em>检索</em>阶段通过搜索引擎提供的任何灵活的查询语法选项：意图驱动语法选项和相关性评分、多模态数据检索、过滤、聚合和偏置来定位高度相关的结果。利用<a href="https://www.elastic.co/docs/reference/query-languages/esql">ES|QL</a>等搜索语法和多级<a href="https://www.elastic.co/docs/solutions/search/retrievers-overview">检索器</a>，我们可以在一个请求中灵活地将传统搜索与语义搜索、过滤器和多种重排技术结合起来。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6ee9512910856ee3/6a17053fcdacbf2ea97d291e/f25180cb430414b99ae553d3b8eb161dbccea4d4-1920x1080.png" alt="混合搜索的工作原理" /><p>混合搜索的最大优势之一是，您的查询可以同时针对多种不同的数据类型使用专门的语法。这些不同的查询语法不仅可用于<em>查找</em>结果，还可用作结果<em>的</em>筛选器或聚合器。例如，最常见的查询类型之一是<a href="https://www.elastic.co/docs/explore-analyze/geospatial-analysis">地理空间分析</a>，它经常与其他语法相结合。您可以查询地理坐标在某一点指定距离内的结果，或要求按地区对结果进行汇总，或进行汇总以跟踪进入/离开某个区域的移动情况并发出警报。使用混合搜索，您可以灵活地组合语法，以最准确的方式定位搜索结果，检索最贴近您的上下文的内容。</p><h2>中场休息</h2><p>第一部分讲述了矢量搜索如何改变了我们检索数据的方式，并为 LLM 给我们用来与数据交互的查询机制带来的变化做了铺垫。我们将假装不得不把这部分内容分成多个部分，以便 LLM 能够在不丢失上下文的情况下理解......;-)让我们在<a href="https://www.elastic.co/search-labs/blog/context-engineering-llm-evolution-agentic-ai">第二部分 "代理人工智能和上下文工程的必要性"中进一步了解 这一点的重要性 ，在第三部分中，我们将继续讨论混合搜索。</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai</guid>
    <category><![CDATA[混合搜索]]></category>
    <category><![CDATA[相关性]]></category>
    <dc:creator><![CDATA[Woody Walton]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc49e39872984c8eb/6a1705410e2e49e42c419fd5/7e59a0671aa9ea32d68188a693936a66ebf48625-1000x628.png" length="0" type="image/png"/>
    <pubDate>Wed, 12 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[混合搜索重温：在 Elasticsearch 中引入线性检索器！]]></title>
    <description><![CDATA[了解线性检索器如何利用加权分数和 MinMax 归一化来增强混合搜索，从而获得更精确、更一致的排名，并学习如何使用它。]]></description>
    <content:encoded><![CDATA[<p>在<a href="https://www.elastic.co/cn/search-labs/blog/elasticsearch-retrievers-ga-8.16.0">上一篇博文</a>中，我们介绍了重新设计的 "从零开始 "检索器框架，它可以创建复杂的排名管道。我们还探讨了互易排名融合（RRF）检索器如何通过合并不同查询的结果来实现混合搜索。虽然 RRF 很容易实现，但它有一个明显的局限性：它只关注相对排名，而忽略了实际得分。这就给微调和优化带来了挑战。</p><h2>直线型寻回犬</h2><p>在本篇文章中，我们将介绍<a href="https://www.elastic.co/cn/docs/solutions/search/retrievers-overview#retrievers-overview-types"><code>linear</code></a> <a href="https://www.elastic.co/cn/docs/solutions/search/retrievers-overview#retrievers-overview-types">retriever</a>，它是我们支持混合搜索的最新成员！与<code>rrf</code> 不同，<code>linear</code> retriever 计算的是与文档匹配的所有查询的加权总和。这种方法既能保留结果集中每个文档的相对重要性，又能精确控制每个查询对最终得分的影响。因此，它为微调混合搜索提供了一种更直观、更灵活的方式。</p><p>定义一个线性检索器，其最终得分的计算公式为</p><p>就这么简单：</p>GET linear_retriever_blog/_search
{
   "retriever": {
       "linear": {
           "retrievers": [
               {
                   "retriever": {
                       "knn": {
                          ...
                        }
                    },
                   "weight": 5
               },
                  {
                   "retriever": {
                       "standard": {
                          ...
                        }
                    },
                   "weight": 1.5
               },


           ]
        }
     }
}<p>注意到它有多简单直观了吗？(与<code>rrf</code> 非常相似！）这种配置允许您精确控制每种查询类型对最终排名的贡献程度，这与<code>rrf</code> 不同，后者仅依赖于相对排名。</p><p>需要注意的是：<code>knn</code> 分数可能有严格的界限，这取决于所使用的相似性指标。例如，使用余弦相似度或单位归一化向量的点积，得分总是在<code>[0, 1]</code> 范围内。相比之下，<code>bm25</code> 分数的可预测性较差，而且没有明确的界限。</p><h2>评分缩放：KNN vs BM25</h2><p>混合搜索面临的一个挑战是，不同的检索器会产生不同的分数。例如，请考虑以下情况：</p><p>查询 A 得分：</p><p></p><p>doc1</p><p>doc2</p><p>doc3</p><p>文档4</p><p>knn</p><p>0.347</p><p>0.35</p><p>0.348</p><p>0.346</p><p>bm25</p><p>100</p><p>1.5</p><p>1</p><p>0.5</p><p>查询 B 得分：</p><p></p><p>doc1</p><p>doc2</p><p>doc3</p><p>文档4</p><p>knn</p><p>0.347</p><p>0.35</p><p>0.348</p><p>0.346</p><p>bm25</p><p>0.63</p><p>0.01</p><p>0.3</p><p>0.4</p><p>您可以从上面看到这种差异：<code>kNN</code> 分数介于 0 和 1 之间，而<code>bm25</code> 分数可能相差悬殊。这种差异使得设置静态最佳权重以合并结果变得非常棘手。</p><h2>归一化拯救：MinMax 归一化器</h2><p>为了解决这个问题，我们引入了一个可选的<code>minmax</code> 归一化器，该归一化器使用以下公式将每个查询的分数独立缩放至<code>[0, 1]</code> 范围：</p><p>这就保留了每个文档在查询结果集中的相对重要性，从而更容易合并来自不同检索器的得分。正常化后，分数变为</p><p>查询 A 得分：</p><p></p><p>doc1</p><p>doc2</p><p>doc3</p><p>文档4</p><p>knn</p><p>0.347</p><p>0.35</p><p>0.348</p><p>0.346</p><p>bm25</p><p>1.00</p><p>0.01</p><p>0.005</p><p>0.000</p><p>查询 B 得分：</p><p></p><p>doc1</p><p>doc2</p><p>doc3</p><p>文档4</p><p>knn</p><p>0.347</p><p>0.35</p><p>0.348</p><p>0.346</p><p>bm25</p><p>1.00</p><p>0.000</p><p>0.465</p><p>0.645</p><p>现在，所有得分都在<code>[0, 1]</code> 范围内，加权总和的优化也更加简单明了，因为我们现在捕捉的是结果的重要性（相对于查询而言），而不是绝对得分，并能在不同查询中保持一致。</p><h2>线性寻回器示例 </h2><p>现在，让我们通过一个例子来展示上述内容，以及<code>linear</code> Retriever 如何解决<code>rrf</code> 的一些不足之处。RRF 仅依靠相对排名，不考虑实际分数差异。例如，给出这些分数：</p><p></p><p>doc1</p><p>doc2</p><p>doc3</p><p>文档4</p><p>knn</p><p>0.347</p><p>0.35</p><p>0.348</p><p>0.346</p><p>bm25</p><p>100</p><p>1.5</p><p>1</p><p>0.5</p><p>rrf 分数</p><p>0.03226</p><p>0.03252</p><p>0.03200</p><p>0.03125</p><p>rrf 会将文件排序为</p><p>但是，doc1 的<code>bm25</code> 得分明显高于其他文件，而<code>rrf</code> 只查看相对排名，因此未能捕捉到这一点。<code>linear</code> Retriever 结合归一化处理，可以正确地考虑分数及其差异，从而得出更有意义的排名：</p><p></p><p>doc1</p><p>doc2</p><p>doc3</p><p>文档4</p><p>knn</p><p>0.347</p><p>0.35</p><p>0.348</p><p>0.346</p><p>bm25</p><p>1</p><p>0.01</p><p>0.005</p><p>0</p><p>如上图所示，doc1 的优秀排名和<code>score</code> 的<code>bm25</code> 都得到了适当的考虑，并反映在最终得分上。此外，所有分数现在都在<code>[0, 1]</code> 范围内，这样我们就能以更直观的方式对它们进行比较和组合（甚至建立离线优化流程）。</p><h2>将所有内容整合在一起</h2><p>要充分利用<code>linear</code> 检索器的正常化功能，搜索请求应如下所示：</p>GET linear_retriever_blog/_search
{
   "retriever": {
       "linear": {
           "retrievers": [
               {
                   "retriever": {
                       "knn": {
                          ...
                        }
                    },
                   "weight": 5
               },
                  {
                   "retriever": {
                       "standard": {
                          ...
                        }
                    },
                   "weight": 1.5,
                   "normalizer": "minmax"
               },


           ]
       }
   }
}<p>这种方法结合了两方面的优点：既保留了<code>linear</code> Retriever 的灵活性和直观评分，又通过 MinMax 归一化确保了一致的评分缩放。</p><p>与我们所有的检索工具一样，<code>linear</code> 检索工具可以集成到分层检索树的任何层级中，并支持可解释性、匹配高亮、字段折叠等功能。</p><h2>何时选择线性寻回犬，为什么会有区别</h2><p><code>linear</code> 猎犬：</p><ul><li><p>通过利用实际得分，而不仅仅是排名，来保留相对重要性。</p></li><li><p>允许利用不同查询的加权贡献进行微调。</p></li><li><p>利用规范化增强一致性，使混合搜索更稳健、更可预测。</p></li></ul><h2>结论</h2><p><code>linear</code> retriever 已经在 Elasticsearch Serverless 以及 8.18 和 9.0 版本中可用！更多示例和配置参数可参阅我们的文档。试用一下，看看它如何改善您的混合搜索体验--我们期待您的反馈。搜索愉快</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/linear-retriever-hybrid-search</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/linear-retriever-hybrid-search</guid>
    <category><![CDATA[相关性]]></category>
    <category><![CDATA[混合搜索]]></category>
    <dc:creator><![CDATA[Panagiotis Bailis]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte58a751cbcc1153d/6a17e3c76317305c4f585a10/7a07e27e3095463ff93b4cb7f8a0cf3b8e44eab0-1777x1000.png" length="0" type="image/png"/>
    <pubDate>Wed, 28 May 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[使用 Quepid 创建判断列表]]></title>
    <description><![CDATA[学习如何通过协作式人工评分流程在 Quepid 中创建判断列表，并利用基准指标优化搜索相关性。]]></description>
    <content:encoded><![CDATA[<p>创建<a href="https://www.elastic.co/search-labs/blog/judgment-lists">判断列表</a>是优化搜索结果质量的关键步骤，但这可能是一项复杂而艰巨的任务。判断列表是一组经过整理的搜索查询，并对其相应结果进行相关性评级，也称为测试集合。使用该列表计算的指标可作为衡量搜索引擎性能的基准。为了帮助简化创建判断列表的过程，<a href="https://opensourceconnections.com/">OpenSource Connections</a>团队开发了<a href="https://quepidapp.com/">Quepid</a>。判断可以是明确的，也可以基于用户的隐性反馈。本博客将指导您在 Quepid 中建立一个协作环境，以便有效地让人类评分员进行明确的判断，这是每个判断列表的基础。</p><p>Quepid 在搜索质量评估过程中为搜索团队提供支持：</p><ul><li><p>建立查询集</p></li><li><p>创建判断列表</p></li><li><p>计算搜索质量指标</p></li><li><p>根据计算得出的搜索质量指标，比较不同的搜索算法/排名器</p></li></ul><p>在我们的博客中，假设我们经营一家电影租赁店，目标是提高搜索结果的质量。</p><h2>准备工作</h2><p>本博客使用<a href="https://github.com/o19s/es-tmdb">es-tmdb 资源库</a>中的数据和映射。数据来自<a href="https://www.themoviedb.org/">电影数据库</a>。接下来，使用映射建立名为 tmdb 的索引，并为数据建立索引。不管是建立本地实例还是使用弹性云部署，都可以正常工作。我们假设本博客使用的是弹性云部署。你可以在<a href="https://github.com/o19s/es-tmdb/blob/master/README.md">es-tmdb 软件仓库的 README</a> 中找到有关如何为数据建立索引的信息。</p><p>对<code>rocky</code> 的标题字段进行简单的匹配查询，以确认有数据可供搜索：</p>GET tmdb/_search
{
 "query": {
   "match": {
     "title": "rocky"
   }
 }
}<p>您将看到 8 项结果。</p>{
 "took": 2,
 "timed_out": false,
 "_shards": {
   "total": 1,
   "successful": 1,
   "skipped": 0,
   "failed": 0
 },
 "hits": {
   "total": {
     "value": 8,
     "relation": "eq"
   }
…
}<h2>登录 Quepid</h2><p><a href="https://github.com/o19s/quepid">Quepid</a>是一款能让用户衡量搜索结果质量并进行离线实验以提高质量的工具。</p><p>您可以通过两种方式使用 Quepid：一种是使用<a href="https://app.quepid.com">https://app.quepid.com</a> 上的免费公开托管版本、或在你可以访问的机器上设置 Quepid。本帖假设您使用的是免费托管版本。如果您想在自己的环境中建立一个 Quepid 实例，请遵循《<a href="https://github.com/o19s/quepid/wiki/Installation-Guide">安装指南》</a>。</p><p>无论您选择哪种设置，如果还没有账户，您都需要创建一个账户。</p><h2>如何设置 Quepid 案例</h2><p>Quepid 的组织结构围绕"案例展开。"案例可存储查询、相关性调整设置以及如何与搜索引擎建立连接。</p><ul><li><p>对于首次使用的用户，请选择<strong>创建第一个相关性案例</strong>。</p></li><li><p>老用户可以从顶层菜单中选择<strong>相关性案例</strong>，然后点击<strong>+ 创建案例</strong>。</p></li></ul><p>请描述性地命名您的案例，例如"电影搜索基线，" ，因为我们希望开始测量和改进我们的基线搜索。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte2913d344c42cd24/6a17e6477b54f906b48b38bd/8f9e480d9aae0d706cfc5371e41f19c706dd452a-594x251.png" alt="设置 Quepid 案例" /><p>选择<strong>继续</strong>，确认名称。</p><p>接下来，我们建立 Quepid 与搜索引擎的连接。Quepid 可以连接各种搜索引擎，包括 Elasticsearch。</p><p>配置会因 Elasticsearch 和 Quepid 设置的不同而有所差异。要将 Quepid 连接到 Elastic Cloud 部署，我们需要为 Elastic Cloud 部署启用和配置 CORS，并准备好 API 密钥。详细说明见<a href="https://quepid-docs.dev.o19s.com/2/quepid/49/how-to-connect-quepid-to-elastic-cloud"> Quepid 文档中的 相应 操作</a> 指南。</p><p>输入 Elasticsearch 端点信息 (<code>https://YOUR_ES_HOST:PORT/tmdb/_search</code>) 和连接所需的其他信息（如果在<strong>高级</strong>配置选项中部署了 Elastic Cloud，则输入 API 密钥），点击<strong>ping</strong>测试连接，然后选择<strong>继续</strong>进入下一步。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcc068309bdc094ae/6a17e6480b0bed14cddd356a/267339dfaecae2740eb2ee2739bdc971608bdb5f-588x1169.png" alt="使用 Quepid 设置 Elasticsearch 终端。" /><p>现在，我们定义要在案例中显示的字段。选择所有有助于我们的人工评判员稍后评估文档与给定查询相关性的内容。</p><p>将<code>title</code> 设置为<em>标题字段</em>，将<code>_id</code> 保留为<em>ID 字段</em>，将<code>overview, tagline, cast, vote_average, thumb:poster_path</code> 添加为<em>附加显示字段</em>。最后一个条目显示了结果中电影的小缩略图，为我们和人类评分员提供视觉指导。</p><p>选择<strong>继续</strong>按钮确认显示设置。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc910bdf5aa7680b3/6a17e64ae8fbce710b3a1906/02c58aae8c2ebb6d31f538b27462b4c65428fdc3-594x493.png" alt="定义希望在该案例中向 Quepid 人工评分员展示的字段。" /><p>最后一步是在案例中添加搜索查询。通过输入框逐一添加 "<em>星球大战</em>"、"<em>哈里森-福特</em>"和 "<em>最佳动作片</em>"三个查询，然后<strong>继续</strong>。</p><p>理想情况下，案例包含的查询能代表真实的用户查询，并能说明不同类型的查询。现在，我们可以把《<em>星球大战》</em>想象成一个查询，代表所有关于电影名称的查询；把<em>哈里森-福特</em>想象成一个查询，代表所有关于演员的查询；把<em>最佳动作片</em>想象成一个查询，代表所有搜索特定类型电影的查询。这通常称为查询集。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt46122bb45e1063e4/6a17e64b505ac36510ad8af8/baccfe96766319aa7255e9bff08913ac87d1517f-595x326.png" alt="向 Quepid 案例添加搜索查询" /><p>在生产场景中，我们将通过应用<a href="https://opensourceconnections.com/blog/2022/10/13/how-to-succeed-with-explicit-relevance-evaluation-using-probability-proportional-to-size-sampling/">概率比例大小采样</a>等统计技术，从事件跟踪数据中抽取查询样本，并将这些采样查询导入 Quepid，以根据查询频率包含头部（频繁查询）和尾部（不频繁查询）的查询，这意味着我们会偏向于更频繁的查询，而不会排除罕见的查询。</p><p>最后，选择 "<strong>完成"</strong>，您将转到案例界面，看到三个已定义的查询。</p><h2>查询和信息需求</h2><p>为了实现我们的总体目标--评判列表，人类评判者需要对给定查询的搜索结果（通常是文档）进行评判。这就是所谓的查询/文档对。</p><p>有时，在查看查询时似乎很容易知道用户想要什么。查询<code>harrison ford</code> 的目的是查找演员哈里森-福特主演的电影。查询<code>action</code> 如何？我知道我很想说用户的意图是寻找动作类型的电影。但是是哪些呢？最新的、最受欢迎的、用户评价最好的？或者，用户是否想找到所有名为 "动作 "的电影？<a href="https://www.themoviedb.org/search/movie?query=Action">在电影数据库中，至少有 12 部（！）电影被称为 "动作片"</a>，它们的名称主要区别在于片名中感叹号的数量。</p><p>如果查询的意图不明确，两名人工评分员对查询的解释可能会有所不同。输入信息需求：<a href="https://en.wikipedia.org/wiki/Information_needs">信息需求</a>是一种有意识或无意识的信息渴望。定义信息需求有助于人类评判员判断查询的文档，因此他们在建立判断列表的过程中发挥着重要作用。专家用户或主题专家是明确信息需求的最佳人选。从用户的角度来定义信息需求是一种很好的做法，因为搜索结果应该满足用户的需求。</p><p>电影搜索基线 "案例查询的信息需求：</p><ol><li><p><strong>星球大战</strong>用户希望查找《星球大战》系列电影或节目。有可能相关的是关于《星球大战》的纪录片。</p></li><li><p><strong>哈里森</strong>-福特用户希望查找演员 Harrison Ford 主演的电影。哈里森-福特扮演不同角色的电影也可能与此相关，比如旁白。</p></li><li><p><strong>最佳动作片</strong>：用户希望找到动作片，最好是用户平均票数高的动作片。</p></li></ol><h2>如何在 Quepid 中定义信息需求</h2><p>要在 Quepid 中定义信息需求，请访问案例界面：</p><p>1.打开一个查询（例如<em>星际大战</em>）并选择<em>切换备注。</em></p><p>2.在第一个字段中输入信息需求，并在第二个字段中输入任何附加说明：</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte57395f64f6e5fab/6a17e64d6864a40fd6b68771/e01d3d5242a350d8797faa665eb3170039f5dfa2-1483x559.png" alt="在 Quepid 中定义信息和查询需求。" /><p>3.单击<strong>保存</strong>。</p><p>对于少数几个查询，这个过程没有问题。但是，当您将案例从 3 个查询扩展到 100 个查询时（Quepid 案例通常在 50 到 100 个查询之间），您可能希望在 Quepid 之外定义信息需求（例如，在电子表格中），然后通过<strong>导入</strong>并选择<strong>信息需求</strong>来上传。</p><h2>在 Quepid 中创建团队并共享案例</h2><p>合作判断可提高相关性评估的质量。组建团队：</p><p>1.在顶层菜单中导航至<strong>团队</strong>。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45c51d1a0e05a80d/6a17e64fe8fbcede793a190f/797706e8d130b474a95d30b6fa22ecaf36f98c03-613x58.png" alt="在 Quepid 中创建团队。" /><p>2.单击<strong>+ 添加新成员</strong>，输入团队名称（例如"Search Relevance Raters" ），然后单击<strong>创建</strong>。</p><p>3.输入成员的电子邮件地址并单击 "<strong>添加用户</strong>"，添加成员。</p><p>4.在个案界面，选择<strong>共享个案</strong>。</p><p>5.选择合适的团队并确认。</p><h2>在 Quepid 中创建评估手册</h2><p>Quepid 中的一本书允许多个评分者对查询/文档对进行系统评估。创建一个</p><p>1.转到案件界面中的<strong> 判决书</strong> ，点击<strong> + 创建一本书</strong> 。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3543c00e0b834b3f/6a17e650e9ea87f57fa9c598/6a077f26225961150b7414463d7db04f090b68d6-896x365.png" alt="在 Quepid 中创建评估手册。" /><p>2.为图书配置一个描述性的名称，将其分配给您的团队，选择一种评分方法（例如 DCG@10），并设置选择策略（单个或多个评分者）。图书使用以下设置：</p><ul><li><p><strong>名称</strong>："电影搜索 0-3 刻度"</p></li><li><p><strong>要与之分享此书的团队</strong>：勾选您创建的团队</p></li><li><p><strong>得分者</strong>DCG@10</p></li></ul><p>3.单击<strong>创建图书。</strong></p><p>名称具有描述性，包含搜索内容（"电影"）和评判标准（"0-3"）的信息。所选的 Scorer DCG@10 定义了搜索指标的计算方式。DCG "是 "<a href="https://en.wikipedia.org/wiki/Discounted_cumulative_gain">贴现累积收益</a>"的缩写，"@10 "是在计算该指标时，从顶部开始考虑的结果数量。</p><p>在这种情况下，我们使用一种衡量信息增益的指标，并将其与位置加权相结合。可能还有其他搜索指标更适合您的使用情况，<a href="https://opensourceconnections.com/blog/2020/02/28/choosing-your-search-relevance-metric"> 选择合适的 指标 本身就是一项挑战</a> 。</p><h2>用查询/文档对填充评估手册</h2><p>要添加查询/文档对进行相关性评估，请按照以下步骤操作：</p><p>1.在案件界面中，导航至"判决。"</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0e98583138535cc3/6a17e6520b0bed4f51dd3571/d717c5b06ae6cb42ed2b9e771486a12f738a9890-1041x218.png" alt="在 Quepid 中用查询/文档对填充评估手册。" /><p>2.选择您创建的图书。</p><p>3.单击"Populate Book" ，然后选择"Refresh Query/Doc Pairs for Book 进行确认。"</p><p>该操作根据每个查询的热门搜索结果生成配对，供团队评估。</p><h2>让您的人工评分团队进行评估 </h2><p>到目前为止，已完成的步骤都是相当技术性和行政性的。现在，这些必要的准备工作已经完成，我们可以让我们的评委团队开展工作了。从本质上讲，法官的工作就是评定特定文档与给定查询的相关性。这一过程的结果就是判断列表，其中包含了被判断的查询文档对的所有相关性标签。接下来，我们将进一步详细解释这一过程及其界面。</p><h3>人工评分界面概览</h3><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt553afb07d8080421/6a17e654505ac30514ad8afc/be3016091b49655dab3354d84e6dc638f3468390-1283x664.png" alt="Quepid 人工评分员如何评估文档与查询中的信息。" /><p>Quepid 的人工评分界面专为高效评估设计：</p><ul><li><p><strong>查询：</strong>显示搜索词。</p></li><li><p><strong>信息需求：</strong>显示用户的意图。</p></li><li><p><strong>评分指南：</strong>提供一致评价的说明。</p></li><li><p><strong>文件元数据：</strong>介绍文件的相关详细信息。</p></li><li><p><strong>评级按钮：</strong>允许评定者使用相应的键盘快捷键指定评定结果。</p></li></ul><h3>使用人工评分界面</h3><p>作为一名人工评审员，我通过图书概览进入界面：</p><p>1.导航至案件界面并单击<strong>判决</strong>。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0e98583138535cc3/6a17e6520b0bed4f51dd3571/d717c5b06ae6cb42ed2b9e771486a12f738a9890-1041x218.png" alt="使用 Quepid 人工评分界面 " /><p>2.点击 "<strong>需要更多判决！</strong>"。</p><p>系统会显示一个尚未评级的查询/文件对，该查询/文件对需要额外的判断。这是由图书的选择策略决定的：</p><ul><li><p><em>单一评判者</em>：每个查询/文档对只有一个评判。</p></li><li><p><em>多个评分者</em>：每个查询/文档对最多可有三个评判。</p></li></ul><h3>评估查询/文档对</h3><p>让我们举几个例子。当您按照本指南进行操作时，很可能会看到不同的电影。不过，评级原则保持不变。</p><p>第一个例子是电影 "英雄 "中的查询 "<em>哈里森-福特</em>"：</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta248e787b5e0a670/6a17e656ec0f89612d5a65ee/c1e14b0d8b04dd579471932dbe4ff72ae5692a02-981x571.png" alt="如何在 Quepid 中使用查询/文档对填充评估手册。" /><p>我们首先查看查询，然后是信息需求，最后根据给出的元数据对电影进行判断。</p><p>这部电影与我们的查询结果相关，因为演员中有哈里森-福特（Harridson Ford）。我们可能会主观地认为近期的电影更具相关性，但这并不是我们信息需求的一部分。因此，我们给这份文件的评分是 "完美"，在我们的评分标准中是 3 分。</p><p>下一个例子是电影 "福特诉法拉利"，查询条件是<em>哈里森-福特</em>：</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf5d86aac918a1e96/6a17e657414c64833e945152/052af7894506d7a765af156ba8e26ceec3559973-981x789.png" alt="以电影“Ford v Ferrari”为例，在 Quepid 中查询 harrison ford。" /><p>按照同样的做法，我们通过查看查询、信息需求以及文档元数据与信息需求的匹配程度来判断该查询/文档。</p><p>这是一个糟糕的结果。我们可能会看到这个结果，因为我们的查询词之一 "福特 "在标题中匹配。但哈里森-福特在这部电影中没有扮演任何角色，也没有扮演任何其他角色。因此，我们将这份文件评为 "差"，在我们的评分标准中是 0 分。</p><p>第三个例子是电影 "动作杰克逊 "的<em>最佳动作片</em>查询：</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2cee335ddd1e6a92/6a17e6596df731d1f40a0ed7/247ab862fbc7435537709f8c96619cb331133d09-985x606.png" alt="以电影“Action Jackson”为例，查询最佳动作片：" /><p>这看起来像是一部动作片，因此至少满足了部分信息需求。不过，投票的平均值为 5.4（满分 10 分）。因此，这部电影可能不是我们收藏的最好的动作片。因此，作为评委，我给这份文件的评分是 "尚可"，在我们的评分标准中是 1 分。</p><p>这些示例特别说明了使用 Quepid 对查询/文档进行评级的过程，既有高层次的，也有一般的。</p><h2>人工评分员最佳实践</h2><p>所展示的示例可能会让人觉得可以直接得出明确的判断。但是，建立一个可靠的人工评级程序并非易事。这是一个充满挑战的过程，很容易影响数据质量：</p><ul><li><p>人类评分员可能会因重复性工作而感到疲劳。</p></li><li><p>个人喜好可能会影响判断。</p></li><li><p>不同法官的领域专业知识水平各不相同。</p></li><li><p>评级员往往身兼数职。</p></li><li><p>文档的感知相关性可能与查询的真实相关性不一致。</p></li></ul><p>这些因素可能导致判决不一致、质量不高。不过不用担心，有一些经过验证的最佳实践可以帮助你最大限度地减少这些问题，并建立一个更强大、更可靠的评估流程：</p><ul><li><p><strong>一致的评估：</strong>依次审查查询、信息需求和文件元数据。</p></li><li><p><strong>参考指南：</strong>使用评分指南以保持一致性。评分指南可以举例说明何时采用哪种等级，从而说明评审过程。事实证明，在第一批评判结束后与人工评判员进行核对是一种很好的做法，可以了解具有挑战性的边缘案例以及在哪些方面需要额外的支持。</p></li><li><p><strong>利用选项：</strong>如果不确定，可使用"I Will Judge Later" 或"I Can't Tell," ，必要时提供解释。</p></li><li><p><strong>休息：</strong>定期休息有助于保持判断质量。每当人工评判员完成一批评判时，Quepid 都会弹出彩纸，帮助用户定期休息。</p></li></ul><p>按照这些步骤，您就可以在 Quepid 中建立一个结构化的协作方法来创建判断列表，从而提高搜索相关性优化工作的效率。</p><h2>后续步骤</h2><p>何去何从？判断列表只是提高搜索结果质量的一个基础步骤。下面是接下来的步骤：</p><h3>计算指标并开始实验</h3><p>一旦有了判断列表，利用判断和计算<a href="https://opensourceconnections.com/blog/2020/02/28/choosing-your-search-relevance-metric/">搜索质量指标</a>就水到渠成了。当有判决书时，Quepid 会自动计算当前案件的配置指标。指标以 "计分器 "的形式实现，如果支持的指标不包括您最喜欢的指标，您可以提供自己的指标！</p><p>进入案例界面，导航至 "<strong>选择评分员</strong>"，选择<em>DCG@10</em>，点击 "<strong>选择评分员</strong>"确认。现在，Quepid 将计算每次查询的 DCG@10，并计算总体查询的平均值，以量化搜索结果的质量。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0d059c959b842139/6a17e65be8fbceb29b3a1913/0ff3b9918342071744d681a43d542102e927abd3-1163x551.png" alt="如何在 Quepid 中量化搜索结果质量。 " /><p>既然已经量化了搜索结果的质量，那么就可以进行第一次实验了。实验从提出假设开始。在对截图中的三个查询进行评级后，可以明显看出这三个查询在搜索质量指标方面的表现截然不同：<em>《星球大战》</em>表现不错，《<em>哈里森-福特》</em>看起来还行，但《<em>最佳动作片》</em>的潜力最大。</p><p>扩大查询范围后，我们就能看到查询结果，并能深入研究细节，探索文档匹配的原因以及影响其得分的因素：</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt35227761f4524a1e/6a17e65cb1e11325c579f25a/c45c6cae085a492198c0f8b7060a1a7204e3724e-1131x691.png" alt="在 Quepid 中尝试不同查询，观察其在各类搜索指标上的表现。" /><p>点击 "Explain Query（解释查询）"并进入 "Parsing（解析）"选项卡，我们可以看到该查询是一个 DisjunctionMaxxQuery，搜索三个字段：<em>演员</em>、<em>概述</em>和<em>标题</em>：</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0966653b9ab3cc9c/6a17e65efaa913171f93c84c/4a1e1bb2a9cd28e9c48e0ba16357d17ed9d3a5cf-894x557.png" alt="解释查询解析" /><p>通常情况下，作为搜索工程师，我们对搜索平台的一些特定领域了如指掌。在这种情况下，我们可能知道我们有一个<em>基因</em>字段。让我们将其添加到查询中，看看搜索质量是否有所提高。</p><p>我们使用在案例界面选择 "<strong>调整相关性"时打开的</strong> " 查询沙盒"。请添加您搜索的<em>流派字段</em>，继续探索：</p>{
  "query": {
    "multi_match": {
      "query": "#$query##",
      "type": "best_fields",
      "fields": [
        "title^10",
        "overview",
        "cast",
        "genres"
      ]
    }
  }
}<p>单击重新运行我的搜索！并查看结果。他们变了吗？遗憾的是没有。我们现在有很多选项可以探索，基本上是 Elasticsearch 提供的所有查询选项：</p><ul><li><p>我们可以增加基因字段的字段权重。</p></li><li><p>我们可以添加一个功能，根据文件的平均得票率来提升文件。</p></li><li><p>我们可以创建一个更复杂的查询，只在有强基因匹配的情况下，才按投票平均值提升文档。</p></li><li><p>…</p></li></ul><p>在 Quepid 中拥有所有这些选项并对其进行探索的最大好处是，我们不仅可以量化我们试图改进的查询的效果，还可以量化我们的所有查询的效果。这就避免了我们通过牺牲其他搜索结果的质量来改善一个表现不佳的查询。我们可以快速、低成本地迭代，并在没有任何风险的情况下验证我们假设的价值，这使得离线实验成为所有搜索团队的基本能力。</p><h3>评估评分员间信度</h3><p>即使有任务说明、信息需求和类似 Quepid 提供的人工评定界面，人工评定者也会出现分歧。</p><p>意见分歧本身并不是坏事，恰恰相反：衡量意见分歧可以让你发现你可能想要解决的问题。相关性可能是主观的，查询可能是模糊的，数据可能是不完整或不正确的。<a href="https://en.wikipedia.org/wiki/Fleiss%27_kappa">弗莱斯卡帕（Fleiss' Kappa</a>）是衡量评分者之间一致性的一种统计方法，Quepid 中有一个示例笔记本可供使用。要找到它，请在顶层导航中选择<strong> 笔记本</strong> ，然后在<strong> 示例</strong> 文件夹中选择笔记本<strong> Fleiss Kappa.ipynb</strong> 。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt92d52b30f1fb19c1/6a17e660abe0f2224bdfe9bb/f0669ae96371368ef4d84bb28669560ef09d755c-624x61.png" alt="如何在 Quepid 中查找用于衡量评分员一致性的 Fleiss' Kappa 统计量笔记本。" /><h2>结论</h2><p>Quepid 使您能够应对最复杂的搜索相关性挑战，并将继续发展：<a href="https://github.com/o19s/quepid/blob/main/CHANGELOG.md#800----2024-02-14">从第 8 版开始，Quepid 支持人工智能生成判断</a>，这对希望扩展判断生成流程的团队特别有用。</p><p>Quepid工作流程使您能够高效地创建可扩展的判断列表，最终产生真正满足用户需求的搜索结果。有了判断列表，您就有了衡量搜索相关性、迭代改进和改善用户体验的坚实基础。</p><p>在前进的过程中，请记住相关性调整是一个持续的过程。判断列表可以让你系统地评估自己的进步，但如果能与实验、指标分析和迭代改进相结合，判断列表的作用会更加强大。</p><h2>延展阅读</h2><ul><li><p>Quepid docs：</p><ul><li><p><a href="https://quepid-docs.dev.o19s.com/2/quepid/32/relevancy-is-a-team-sport">相关性是一项团队运动</a></p></li><li><p><a href="https://quepid-docs.dev.o19s.com/2/quepid/18/quepid-for-human-raters">人类评级员的 Quepid</a></p></li><li><p><a href="https://quepid-docs.dev.o19s.com/2/quepid/49/how-to-connect-quepid-to-elastic-cloud">如何将 Quepid 连接到弹性云</a></p></li></ul></li><li><p><a href="https://github.com/o19s/quepid">Quepid Github 存储库</a></p></li><li><p><a href="https://opensourceconnections.com/blog/2020/07/07/meet-pete-the-e-commerce-search-product-manager/">认识皮特，关于改进电子商务搜索的系列博客</a></p></li><li><p><a href="https://opensourceconnections.com/slack">相关性 Slack</a>：加入 #quepid 频道</p></li></ul><p><strong>与 </strong><a href="https://opensourceconnections.com/"><strong>Open Source Connections</strong></a> 合作 ，改造您的搜索和人工智能能力，并使您的团队能够不断发展这些能力。我们的业绩记录遍布全球，客户在搜索质量、团队能力和业务绩效方面不断取得显著提高。<a href="https://opensourceconnections.com/contact/">现在就联系我们</a>，了解更多信息。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/quepid-judgement-lists</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/quepid-judgement-lists</guid>
    <category><![CDATA[相关性]]></category>
    <dc:creator><![CDATA[Daniel Wrigley]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte6779c72c7a42a5a/6a17e6623e9e45acf0ba146b/307c1774bd31f92bb4aa7b69e1a6796240465100-1600x914.png" length="0" type="image/png"/>
    <pubDate>Mon, 26 May 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[使用 ML 生成过滤器和切面]]></title>
    <description><![CDATA[探索在搜索体验中使用 ML 模型自动创建过滤器和切面与传统硬编码方法的利弊。]]></description>
    <content:encoded><![CDATA[<p>过滤器和切面是用于完善搜索结果的机制，可帮助用户更快地找到相关内容或产品。在传统方法中，规则是人工定义的。例如，在电影目录中，流派等属性是预定义的，可用于筛选器和切面。另一方面，通过人工智能模型，可以自动从电影特征中提取新的属性，使整个过程更加动态和个性化。在本博客中，我们将探讨每种方法的优缺点，重点介绍它们的应用和挑战。</p><h2>筛选器与分面</h2><p>在开始之前，我们先来定义一下什么是过滤器和切面。<strong>过滤器</strong>是用于限制结果集的预定义属性。例如，在市场中，甚至在进行搜索之前就可以使用筛选器。用户可以先选择一个类别，如<strong>"Video games"</strong> ，然后再搜索<strong>"PS5"</strong> ，将搜索范围缩小到更具体的子集，而不是整个数据库。这大大增加了获得更多相关结果的机会。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0a77b38aae238938/6a170b821949f72a52e7aa51/5ed8868fa5017d034e1273e35c884a5430afdf3c-1600x937.png" alt="筛选" /><p><strong>面板的</strong>工作原理与筛选器类似，但只有在执行搜索后才可用。换句话说，搜索会返回结果，并根据这些结果生成新的细化选项列表。例如，在搜索 PS5 游戏机时，可以显示<strong>存储容量</strong>、<strong>运输成本</strong>和<strong>颜色</strong>等信息，帮助用户选择理想的产品。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt166e356b80d423ef/6a170b840e2e494ca341a10f/c5633fcc5b6fbb916110faf32144d8d43572e33a-1600x937.png" alt="面面观 " /><p>既然我们已经定义了过滤器和切面，下面我们就来讨论经典方法和基于机器学习 (ML) 的方法对其实施和使用的影响。每种方法都有影响搜索效率的优势和挑战。</p><h2>过滤器和分面的经典方法</h2><p>在这种方法中，过滤器和切面是根据预定义规则手动定义的。这意味着，考虑到目录结构和用户需求，可用于细化搜索的属性是固定的，并事先进行了规划。</p><p>例如，在市场中，"Electronics" 或"Fashion" 等类别可能有特定的筛选条件，如品牌、格式和价格范围。这些规则是静态创建的，可确保搜索体验的一致性，但每当出现新的产品或类别时，就需要进行手动调整。</p><p>虽然这种方法提供了对所显示过滤器和面的可预测性和控制，但当出现需要动态改进的新趋势时，这种方法就会受到限制。</p><p><strong>优点</strong></p><ul><li><p><strong>可预测性和控制：</strong>由于筛选器和切面是手动定义的，因此管理变得更加容易。</p></li><li><p><strong>低复杂性：</strong>无需训练模型。</p></li><li><p><strong>易于维护：</strong>由于规则是预定义的，因此可以快速进行调整和修正。</p></li></ul><p><strong>缺点</strong></p><ul><li><p><strong>新过滤器需要重新索引：</strong>每当需要使用新属性作为筛选器时，就必须对整个数据集重新索引，以确保文档包含该信息。</p></li><li><p><strong>缺乏动态适应性：</strong>过滤器是静态的，不能根据用户行为的变化自动调整。</p></li></ul><h3>滤波器/滤面的实现 - 经典方法</h3><p>在<strong>开发工具 Kibana</strong> 中，我们将使用<strong>经典方法</strong>创建过滤器/面板演示。</p><p>首先，我们定义映射来构建索引：</p>PUT videogames
{
  "mappings": {
    "properties": {
      "name": { "type": "text" },
      "brand": { "type": "keyword" },
      "storage": { "type": "keyword" },
      "price": { "type": "float" },
      "description": { "type": "text" }
    }
  }
}<p><strong>品牌</strong>和<strong>存储</strong> <strong>字段</strong>被设置为关键字，可直接用于聚合<strong>（面</strong>）。<strong>价格</strong>字段为<strong>浮动</strong>类型，可以创建<strong>价格范围</strong>。</p><p>下一步，将对产品数据编制索引：</p>POST videogames/_bulk
{ "index": { "_id": 1 } }
{ "name": "Play Station 5", "brand": "Sony", "storage": "1TB", "price": 499.99, "description": "Stunning Gaming: Marvel at stunning graphics and experience the features of the new PS5. Breathtaking Immersion: Discover a deeper gaming experience with support for haptic feedback, adaptive triggers, and 3D Audio technology. Slim Design: With the PS5 Digital Edition, gamers get powerful gaming technology in a sleek, compact design. 1TB of Storage: Have your favorite games ready and waiting for you to play with 1TB of built-in SSD storage. Backward Compatibility and Game Boost: The PS5 console can play over 4,000 PS4 games. With Game Boost, you can even enjoy faster, smoother frame rates in some of the best PS4 console games." }
{ "index": { "_id": 2 } }
{ "name": "Xbox Series X", "brand": "Microsoft", "storage": "1TB", "price": 499.99, "description": "Fastest, most powerful Xbox console ever. Play thousands of titles: Every game looks and plays better on Xbox Series X. At the heart of Series X is the Xbox Velocity. Architecture, which combines a custom SSD and built-in software to significantly reduce load times in and out of game. Switch between multiple games in an instant with Quick Resume. Explore new worlds and experience the action like never before with an unparalleled 12 teraflops of graphics processing power. Enjoy 4K gaming at up to 120 frames per second, premium advanced 3D sound, and more. 4K at 120 FPS: requires compatible content and display X version - with disc drive" }
{ "index": { "_id": 3 } }
{ "name": "Nintendo Switch", "brand": "Nintendo", "storage": "512GB", "price": 299.99, "description": "SHARPER, VIBRANT VISUALS. The new 7-inch screen on the Nintendo Switch OLED takes your gaming to the next level: vibrant colors with sharp contrasts for every moment. INTEGRATED GAMEPLAY. Enjoy the console's many multiplayer modes and connect with other players. Online or locally, the fun on the Nintendo Switch is guaranteed. ENJOY IMMERSION FOR LONGER. In addition to delivering an unparalleled experience, thanks to its improved audio, the Nintendo Switch has a rechargeable battery while you play. From 4.5 hours to 9 hours of battery life. INCLUDES SUPER MARIO BROS. WONDER. Transform your world with the phenomenal flowers in this new Mario game, full of amazing adventures, power-ups and new abilities. NINTENDO SWITCH ONLINE SUBSCRIPTION. Access online games, play with friends and enjoy the exclusive benefits of the Nintendo Switch Online subscription." }
{ "index": { "_id": 4 } }
{ "name": "Steam Deck", "brand": "Valve", "storage": "512GB", "price": 399.99, "description": "You can save games, apps, photos and videos without worrying about space. High-Level Performance: The 4-core processor and graphics ensure a dynamic experience and fast responses. High-Definition Images: Smooth transitions and sharp images provide complete immersion in the game. Wireless Connectivity: Wi-Fi technology allows you to play wherever you want, without wires or cables limiting your fun" }
{ "index": { "_id": 5 } }
{ "name": "Nintendo Switch Lite", "brand": "Nintendo", "storage": "512GB", "price": 299.99, "description": "MADE TO BE PORTABLE. Nintendo Switch Lite is designed specifically for portable gaming. The console lets you jump into your favorite games wherever you are. COMPACT AND LIGHTWEIGHT. With its sleek, lightweight design, this console is ready to hit the road wherever you are. COMPATIBLE GAMES. The Nintendo Switch Lite system plays the library of Nintendo Switch games that work in handheld mode. A WORLD OF COLOR TO CHOOSE FROM. Available in a variety of vibrant and unique colors, Nintendo Switch Lite lets you bring even more personality wherever you go." }<p>现在，让我们按照品牌、存储空间和价格范围对结果进行分组，从而检索出经典的面孔。在查询中，定义了 size:0。在这种情况下，目标是只检索聚合结果，而不包括与查询相对应的文档。</p>POST videogames/_search
{
  "size": 0,
  "aggs": {
    "brands": {
      "terms": { "field": "brand" }
    },
    "storage_sizes": {
      "terms": { "field": "storage" }
    },
    "price_ranges": {
      "range": {
        "field": "price",
        "ranges": [
          { "to": 300 },   
          { "from": 300, "to": 500 },  
          { "from": 500 }  
        ]
      }
    }
  }
}<p>回复将包括<strong>品牌</strong>、<strong>存储</strong>和<strong>价格的</strong>计数，有助于创建筛选器和面。</p>"aggregations": {
   "brands": {
     "doc_count_error_upper_bound": 0,
     "sum_other_doc_count": 0,
     "buckets": [
       {
         "key": "Microsoft",
         "doc_count": 1
       },
       {
         "key": "Nintendo",
         "doc_count": 1
       },
       {
         "key": "Sony",
         "doc_count": 1
       },
       {
         "key": "Valve",
         "doc_count": 1
       }
     ]
   },
   "storage_sizes": {
     "doc_count_error_upper_bound": 0,
     "sum_other_doc_count": 0,
     "buckets": [
       {
         "key": "1TB",
         "doc_count": 2
       },
       {
         "key": "512GB",
         "doc_count": 2
       }
     ]
   },
   "price_ranges": {
     "buckets": [
       {
         "key": "*-300.0",
         "to": 300,
         "doc_count": 1
       },
       {
         "key": "300.0-500.0",
         "from": 300,
         "to": 500,
         "doc_count": 3
       },
       {
         "key": "500.0-*",
         "from": 500,
         "doc_count": 0
       }
     ]
   }
 }<h2>基于机器学习/人工智能的筛选器和分面方法</h2><p>在这种方法中，机器学习（ML）模型（包括人工智能（AI）技术）分析数据属性，生成相关的过滤器和面。ML/AI 不依赖预定义规则，而是利用索引数据特征。这样就能动态发现新的切面和过滤器。</p><p><strong>优点</strong></p><ul><li><p><strong>自动更新：</strong>自动生成新的过滤器和切面，无需手动调整。</p></li><li><p><strong>发现新属性：</strong>它可以将<strong>以前未考虑过的 </strong>数据特征识别为过滤器，从而丰富搜索体验。</p></li><li><p><strong>减少人工操作：</strong>当人工智能从可用数据中学习时，团队无需不断定义和更新过滤规则。</p></li></ul><p><strong>缺点</strong></p><ul><li><p><strong>维护复杂性：</strong>使用模型可能需要预先验证，以确保生成的过滤器的一致性。</p></li><li><p><strong>需要 ML 和 AI 专业知识：</strong>该解决方案需要合格的专业人员来微调和监控模型性能。</p></li><li><p><strong>无关过滤器的风险：</strong>如果模型没有得到很好的校准，可能会生成对用户无用的切面。</p></li><li><p><strong>成本：</strong>使用 ML 和 AI 可能需要第三方服务，从而增加运营成本。</p></li></ul><p>值得注意的是，即使有了校准良好的模型和精心制作的提示，生成的切面仍应经过审查步骤。这种验证可以是手动的，也可以基于审核规则，以确保内容的适当性和安全性。虽然这不一定是一个缺点，但这是一个重要的考虑因素，以确保在提供给用户之前，面的质量和适用性。</p><h3>实施过滤器/面板--人工智能方法</h3><p>在本演示中，我们将使用一个人工智能模型来自动分析产品特性并提出相关属性建议。有了结构良好的提示，我们就能从目录中提取信息，并将其转化为过滤器和切面。下面，我们将介绍这一过程的每个步骤。</p><p>最初，我们将使用<strong>推理 API</strong>注册一个端点，以便与 ML 服务集成。以下是与<strong>OpenAI 服务</strong>集成的示例。</p>PUT _inference/completion/generate_filter_ia
{
   "service": "openai",
   "service_settings": {
       "api_key": "your-key",
       "model_id": "gpt-4o-mini"
   }
}<p>现在，我们定义一个管道来执行提示并获取模型生成的新过滤器。</p>PUT /_ingest/pipeline/generate_filter_ai
{
   "processors": [
     {
       "script": {
         "source": """ctx.prompt = "You are an expert in data organization for search and product categorization. Your task is to analyze the following product and identify the best dynamic facets that can be used in an e-commerce search experience. Product: " + ctx.name + "description: " + ctx.description + "Instructions: - Analyze the product name and description. - Extract only the dynamic facets (technological features or product characteristics that can be inferred from the description, try to create max 3 facets by characteristics found). Put the values into an array. Using key and value, e.g. dynamic_facets: [{ \"name\": \"Gaming Experience\", \"value\": \"Haptic Feedback\" },{ \"name\": \"Gaming Experience\", \"value\": \"Adaptive Triggers\" } - Return only a JSON."
         """
       }
     },
     {
       "inference": {
         "model_id": "generate_filter_ia",
         "input_output": {
           "input_field": "prompt",
           "output_field": "result"
         }
       }
     },
     {
       "gsub": {
         "field": "result",
         "pattern": "```json",
         "replacement": ""
       }
     },
     {
       "json" : {
         "field" : "result",
         "strict_json_parsing": false,
         "add_to_root" : true
       }
     },
     {
       "remove": {
         "field": "result"
       }
     },
     {
       "remove": {
         "field": "prompt"
       }
     }
   ]
}<p>为"PlayStation 5" 产品运行该流水线的模拟，说明如下：</p><p><em>令人惊叹的游戏：惊叹于令人惊叹的画面，体验全新 PS5 的功能。</em></p><p><em>令人惊叹的沉浸感：支持触觉反馈、自适应触发器和 3D 音频技术，探索更深层次的游戏体验。</em></p><p><em>超薄设计：通过 PS5 数字版，玩家可以在时尚、紧凑的设计中获得强大的游戏技术。</em></p><p><em>1TB 存储空间：内置 1TB SSD 存储空间，让您随时随地畅玩最喜爱的游戏。</em></p><p><em>向后兼容和游戏提升：PS5 游戏机可播放 4,000 多款 PS4 游戏。有了 Game Boost，您甚至可以在一些最好的 PS4 游戏机游戏中享受更快、更流畅的帧率。</em></p><p>让我们观察一下这次模拟产生的提示输出。</p>{
 "docs": [
   {
     "doc": {
       "_index": "index",
       "_version": "-3",
       "_id": "1",
       "_source": {
         "name": "Play Station 5",
         "result": """```json
{
 "dynamic_facets": [
   { "name": "Storage Capacity", "value": "1TB SSD" },
   { "name": "Graphics Technology", "value": "Stunning Graphics" },
   { "name": "Audio Technology", "value": "3D Audio" }
 ]
}
```""",
         "description": "Stunning Gaming: Marvel at stunning graphics and experience the features of the new PS5. Breathtaking Immersion: Discover a deeper gaming experience with support for haptic feedback, adaptive triggers, and 3D Audio technology. Slim Design: With the PS5 Digital Edition, gamers get powerful gaming technology in a sleek, compact design. 1TB of Storage: Have your favorite games ready and waiting for you to play with 1TB of built-in SSD storage. Backward Compatibility and Game Boost: The PS5 console can play over 4,000 PS4 games. With Game Boost, you can even enjoy faster, smoother frame rates in some of the best PS4 console games.",
         "model_id": "generate_filter_ia",
         "prompt": """You are an expert in data organization for search and product categorization. Your task is to analyze the following product and identify the best dynamic facets that can be used in an e-commerce search experience. Product: Play Station 5description: Stunning Gaming: Marvel at stunning graphics and experience the features of the new PS5. Breathtaking Immersion: Discover a deeper gaming experience with support for haptic feedback, adaptive triggers, and 3D Audio technology. Slim Design: With the PS5 Digital Edition, gamers get powerful gaming technology in a sleek, compact design. 1TB of Storage: Have your favorite games ready and waiting for you to play with 1TB of built-in SSD storage. Backward Compatibility and Game Boost: The PS5 console can play over 4,000 PS4 games. With Game Boost, you can even enjoy faster, smoother frame rates in some of the best PS4 console games.Instructions: - Analyze the product name and description. - Extract only the dynamic facets (technological features or product characteristics that can be inferred from the description, try create max 3 facets by characteristics found). Put the values like arrays. Using key and value, e.g. dynamic_facets: [{ "name": "Gaming Experience", "value": "Haptic Feedback" },{ "name": "Gaming Experience", "value": "Adaptive Triggers" } - Return only a JSON."""
       },
       "_ingest": {
         "timestamp": "2025-03-19T22:14:32.0161803Z"
       }
     }
   }
 ]
}<p>现在，新索引中将添加一个新字段<strong>dynamic_facets</strong>，用于存储人工智能生成的面。</p>PUT videogames_1
{
 "mappings": {
   "properties": {
     "name": { "type": "text" },
     "brand": { "type": "keyword" },
     "storage": { "type": "keyword" },
     "price": { "type": "float" },
     "description": { "type": "text" },
     "dynamic_facets": { "type": "nested",
     "properties": { "name": { "type": "keyword" },
                     "value": { "type": "keyword" } } }
   }
 }
}<p>我们将使用<strong>Reindex API</strong> 将<strong>videogames</strong>索引重新编入<strong>videogames_1</strong>，并在此过程中应用<strong>generate_filter_ai</strong>管道。该管道将在索引编制过程中自动生成动态切面。</p>POST _reindex?wait_for_completion=false
{
 "source": {
   "index": "videogames"
 },
 "dest": {
   "index": "videogames_1",
   "pipeline": "generate_filter_ai"
 }
}<p>现在，我们将运行搜索并获得新的筛选器：</p>GET videogames_1/_search
{
 "size": 0,
 "query": {
   "match": {
     "name": "nintendo"
   }
 },
 "aggs": {
   "dynamic_facets": {
     "nested": {
       "path": "dynamic_facets"
     },
     "aggs": {
       "facets": {
         "terms": {
           "field": "dynamic_facets.name"
         },
         "aggs": {
           "facets": {
             "terms": {
               "field": "dynamic_facets.value"
             }
           }
         }
       }
     }
   }
 }
}<p>结果</p>"aggregations": {
   "dynamic_facets": {
     "doc_count": 3,
     "facets": {
       "doc_count_error_upper_bound": 0,
       "sum_other_doc_count": 0,
       "buckets": [
         {
           "key": "Frame Rate",
           "doc_count": 1,
           "facets": {
             "doc_count_error_upper_bound": 0,
             "sum_other_doc_count": 0,
             "buckets": [
               {
                 "key": "120 FPS",
                 "doc_count": 1
               }
             ]
           }
         },
         {
           "key": "Gaming Resolution",
           "doc_count": 1,
           "facets": {
             "doc_count_error_upper_bound": 0,
             "sum_other_doc_count": 0,
             "buckets": [
               {
                 "key": "4K",
                 "doc_count": 1
               }
             ]
           }
         },
         {
           "key": "Graphics Processing Power",
           "doc_count": 1,
           "facets": {
             "doc_count_error_upper_bound": 0,
             "sum_other_doc_count": 0,
             "buckets": [
               {
                 "key": "12 Teraflops",
                 "doc_count": 1
               }
             ]
           }
         }
       ]
     }
   }
 }<p>下面是一个简单的前端，以表示面的实现：</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb0d6aa40caf7a91a/6a170b86ab7f0839afdb9eb6/12b6d9d4f4d0985848d92841545fd22b7253ae6d-1600x1288.png" alt="执行方面" /><p><a href="https://gist.github.com/andreluiz1987/06d9ec1b381e942e9def0e969bd811a0">这里</a>提供了用户界面代码。</p><h2>结论</h2><p>这两种创建过滤器和切面的方法各有利弊。基于手动规则的传统方法可提供控制并降低成本，但需要不断更新，且无法动态适应新产品或新功能。</p><p>另一方面，基于人工智能和机器学习的方法可以自动提取切面，使搜索更加灵活，并且无需人工干预即可发现新的属性。不过，这种方法的实施和维护可能更为复杂，需要进行校准以确保结果的一致性。</p><p>在传统方法和基于人工智能的方法之间做出选择，取决于企业的需求和复杂程度。对于数据属性稳定且可预测的简单场景，传统方法可以更高效、更易于维护，从而避免基础设施和人工智能模型的不必要成本。另一方面，使用 ML/AI 提取切面可以大大增加价值，改善搜索体验，使过滤更加智能。</p><p>重要的是要评估自动化是否值得投资，或者更传统的解决方案是否已能有效满足业务需求。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/filters-facets-using-ml</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/filters-facets-using-ml</guid>
    <category><![CDATA[相关性]]></category>
    <category><![CDATA[ML 研究]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4084864dcdaa25d3/6a170b880c485781f901aaa9/6f196643d573614fe5124705c7e4db9bfce004b0-1200x628.png" length="0" type="image/png"/>
    <pubDate>Thu, 03 Apr 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[如何使用我们的同义词 API 自动生成和上传同义词]]></title>
    <description><![CDATA[了解如何使用 LLM 自动识别和生成同义词，从而以编程方式将术语加载到 Elasticsearch 同义词 API 中。]]></description>
    <content:encoded><![CDATA[<p>提高搜索结果的质量对于提供高效的用户体验至关重要。优化搜索的方法之一是通过同义词自动扩展查询词。这样就可以更广泛地解释查询，涵盖各种语言，从而改进结果匹配。</p><p>本博客将探讨如何使用大型语言模型 (LLM) 自动识别和生成同义词，并允许以编程方式将这些术语加载到 Elasticsearch 的同义词 API 中。</p><h2>何时使用同义词？</h2><p>与矢量搜索相比，使用同义词是一种更快、更具成本效益的解决方案。它的实现较为简单，因为它不需要深厚的嵌入知识，也不需要复杂的矢量摄取过程。</p><p>此外，由于矢量搜索需要更大的存储容量和内存来嵌入索引和检索，因此资源消耗较低。</p><p>另一个重要方面是搜索区域化。有了同义词，就可以根据当地语言和习俗调整术语。这在嵌入式可能无法匹配区域表达或特定国家术语的情况下非常有用。例如，有些单词或缩略语在不同地区可能有不同的含义，但当地用户自然会将其视为同义词。在巴西，这种情况非常普遍。"Abacaxi" 和"ananás" 是同一种水果（菠萝），但在东北部的一些地区，第二个术语更常用。同样，东南部著名的"pão francês" 在东北部可能被称为"pão careca" 。</p><h2>如何使用 LLM 生成同义词？</h2><p>为了自动获取同义词，我们可以使用 LLM，它可以分析术语的上下文，并建议适当的变体。这种方法可以动态扩展同义词，确保搜索范围更广、更准确，而无需依赖固定词典。</p><p>在本演示中，我们将使用 LLM 生成电子商务产品的同义词。由于查询词的变化，许多搜索结果很少或没有结果。有了同义词，我们就可以解决这个问题。例如，搜索"智能手机" 可以涵盖不同型号的手机，确保用户找到所需的产品。</p><h3>准备工作</h3><p>在开始之前，我们需要设置环境并定义所需的依赖关系。我们将使用 Elastic 提供的解决方案，<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html">在 Docker 中本地运行 Elasticsearch 和 Kibana</a>。代码将使用 Python 3.9.6 版本编写，依赖关系如下：</p>pip install openai==1.59.8 elasticsearch==8.15.1<h3>创建产品索引</h3><p>最初，我们将创建一个不支持同义词的产品索引。这样我们就可以验证查询，然后将其与包含同义词的索引进行比较。</p><p>为了创建索引，我们在 Kibana DevTools 中使用以下命令批量加载产品数据集：</p>POST _bulk
{"index": {"_index": "products", "_id": 10001}}
{"category": "Electronics", "name": "iPhone 14 Pro"}
{"index": {"_index": "products", "_id": 10007}}
{"category": "Electronics", "name": "MacBook Pro 16-inch"}
{"index": {"_index": "products", "_id": 10013}}
{"category": "Electronics", "name": "Samsung Galaxy Tab S8"}
{"index": {"_index": "products", "_id": 10037}}
{"category": "Electronics", "name": "Apple Watch Series 8"}
{"index": {"_index": "products", "_id": 10049}}
{"category": "Electronics", "name": "Kindle Paperwhite"}
{"index": {"_index": "products", "_id": 10067}}
{"category": "Electronics", "name": "Samsung QLED 4K TV"}
{"index": {"_index": "products", "_id": 10073}}
{"category": "Electronics", "name": "HP Spectre x360 Laptop"}
{"index": {"_index": "products", "_id": 10079}}
{"category": "Electronics", "name": "Apple AirPods Pro"}
{"index": {"_index": "products", "_id": 10115}}
{"category": "Electronics", "name": "Amazon Echo Show 10"}
{"index": {"_index": "products", "_id": 10121}}
{"category": "Electronics", "name": "Apple iPad Air"}
{"index": {"_index": "products", "_id": 10127}}
{"category": "Electronics", "name": "Apple AirPods Max"}
{"index": {"_index": "products", "_id": 10151}}
{"category": "Electronics", "name": "Sony WH-1000XM4 Headphones"}
{"index": {"_index": "products", "_id": 10157}}
{"category": "Electronics", "name": "Google Pixel 6 Pro"}
{"index": {"_index": "products", "_id": 10163}}
{"category": "Electronics", "name": "Apple MacBook Air"}
{"index": {"_index": "products", "_id": 10181}}
{"category": "Electronics", "name": "Google Pixelbook Go"}
{"index": {"_index": "products", "_id": 10187}}
{"category": "Electronics", "name": "Sonos Beam Soundbar"}
{"index": {"_index": "products", "_id": 10199}}
{"category": "Electronics", "name": "Apple TV 4K"}
{"index": {"_index": "products", "_id": 10205}}
{"category": "Electronics", "name": "Samsung Galaxy Watch 4"}
{"index": {"_index": "products", "_id": 10211}}
{"category": "Electronics", "name": "Apple MacBook Pro 16-inch"}
{"index": {"_index": "products", "_id": 10223}}
{"category": "Electronics", "name": "Amazon Echo Dot (4th Gen)"}<h3>用 LLM 生成同义词</h3><p>在这一步中，我们将使用 LLM 来动态生成同义词。为此，我们将整合 OpenAI 应用程序接口，定义适当的模型和提示。LLM 将接收产品类别和名称，确保同义词与上下文相关。</p>import json
import logging

from openai import OpenAI

def call_gpt(prompt, model):
    try:
        logging.info("generate synonyms by llm...")
        response = client.chat.completions.create(
            model=model,
            messages=[{"role": "user", "content": prompt}],
            temperature=0.7,
            max_tokens=1000
        )
        content = response.choices[0].message.content.strip()
        return content
    except Exception as e:
        logging.error(f"Failed to use model: {e}")
        return None

def generate_synonyms(category, products):
   synonyms = {}

   for product in products:
       prompt = f"You are an expert in generating synonyms for products. Based on the category and product name provided, generate synonyms or related terms. Follow these rules:\n"
       prompt += "1. **Format**: The first word should be the main item (part of the product name, excluding the brand), followed by up to 3 synonyms separated by commas.\n"
       prompt += "2. **Exclude the brand**: Do not include the brand name in the synonyms.\n"
       prompt += "3. **Maximum synonyms**: Generate a maximum of 3 synonyms per product.\n\n"
       prompt += f"The category is: **{category}**, and the product is: **{product}**. Return only the synonyms in the requested format, without additional explanations."

       response = call_gpt(prompt, "gpt-4o")
       synonyms[product] = response

   return synonyms<p>从创建的产品索引中，我们将检索"Electronics" 类别中的所有项目，并将其名称发送到 LLM。预期输出结果如下</p>{
  "iPhone 14 Pro": ["iPhone", "smartphone", "mobile", "handset"],
  "MacBook Pro 16-inch": ["MacBook", "Laptop", "Notebook", "Ultrabook"],
  "Samsung Galaxy Tab S8": ["Tab", "Tablet", "Slate", "Pad"],
  "Bose QuietComfort 35 Headphones": ["Headphones", "earphones", "earbuds", "headset"]
}<p>有了生成的同义词，我们就可以使用同义词 API 将其注册到 Elasticsearch 中。</p><h3>使用同义词 API 管理同义词</h3><p>同义词 API 提供了在系统内直接管理同义词集的有效方法。每个同义词集都由同义词规则组成，其中一组词在搜索中被视为等同词。</p><p><strong>创建同义词集示例</strong></p>PUT _synonyms/my-synonyms-set
{
  "synonyms_set": [
    {
      "id": "rule-1",
      "synonyms": "hello, hi"
    },
    {
      "synonyms": "bye, goodbye"
    }
  ]
}<p>
这样就创建了一个名为"my-synonyms-set," 的集合，其中"hello" 和"hi" 被视为等同词，"bye" 和"goodbye 也被视为等同词。"</p><h2>为产品目录创建同义词</h2><p>下面是建立同义词集并将其插入 Elasticsearch 的方法。同义词规则是根据 LLM 建议的同义词映射生成的。每条规则都有一个 ID（与 slug 格式的产品名称相对应）和 LLM 计算出的同义词列表。</p>import json
import logging

from elasticsearch import Elasticsearch
from slugify import slugify

es = Elasticsearch(
    "http://localhost:9200",
    api_key="your_api_key"
)

def mount_synonyms(results):
   synonyms_set = [{"id": slugify(product), "synonyms": synonyms} for product, synonyms in
                   results.items()]

   try:
       response = es.synonyms.put_synonym(id="products-synonyms-set",
                                                 synonyms_set=synonyms_set)

       logging.info(json.dumps(response.body, indent=4))
       return response.body
   except Exception as e:
       logging.error(f"Error create synonyms: {str(e)}")
       return None<p>下面是创建同义词集的请求有效载荷：</p>{
   "synonyms_set":[
      {
         "id": "iphone-14-pro",
         "synonyms": "iPhone, smartphone, mobile, handset"
      },
      {
         "id": "macbook-pro-16-inch",
         "synonyms": "MacBook, Laptop, Notebook, Computer"
      },
      {
         "id": "samsung-galaxy-tab-s8",
         "synonyms": "Tablet, Slate, Pad, Device"
      },
      {
         "id": "garmin-forerunner-945",
         "synonyms": "Forerunner, smartwatch, fitness watch, GPS watch"
      },
      {
         "id": "bose-quietcomfort-35-headphones",
         "synonyms": "Headphones, Earphones, Headset, Cans"
      }
   ]
}<p>在集群中创建同义词集后，我们就可以进行下一步，即使用定义的同义词集创建支持同义词的新索引。</p><p>下面是完整的 Python 代码，其中包含 LLM 生成的同义词和同义词 API 定义的同义词集创建：</p>import json
import logging

from elasticsearch import Elasticsearch
from openai import OpenAI
from slugify import slugify

logging.basicConfig(level=logging.INFO)

client = OpenAI(
   api_key="your-key",
)

es = Elasticsearch(
    "http://localhost:9200",
    api_key="your_api_key"
)


def call_gpt(prompt, model):
   try:
       logging.info("generate synonyms by llm...")
       response = client.chat.completions.create(
           model=model,
           messages=[{"role": "user", "content": prompt}],
           temperature=0.7,
           max_tokens=1000
       )
       content = response.choices[0].message.content.strip()
       return content
   except Exception as e:
       logging.error(f"Failed to use model: {e}")
       return None


def generate_synonyms(category, products):
   synonyms = {}

   for product in products:
       prompt = f"You are an expert in generating synonyms for products. Based on the category and product name provided, generate synonyms or related terms. Follow these rules:\n"
       prompt += "1. **Format**: The first word should be the main item (part of the product name, excluding the brand), followed by up to 3 synonyms separated by commas.\n"
       prompt += "2. **Exclude the brand**: Do not include the brand name in the synonyms.\n"
       prompt += "3. **Maximum synonyms**: Generate a maximum of 3 synonyms per product.\n\n"
       prompt += f"The category is: **{category}**, and the product is: **{product}**. Return only the synonyms in the requested format, without additional explanations."

       response = call_gpt(prompt, "gpt-4o")
       synonyms[product] = response

   return synonyms


def get_products(category):
   query = {
       "size": 50,
       "_source": ["name"],
       "query": {
           "bool": {
               "filter": [
                   {
                       "term": {
                           "category.keyword": category
                       }
                   }
               ]
           }
       }
   }
   response = es.search(index="products", body=query)

   if response["hits"]["total"]["value"] &gt; 0:
       product_names = [hit["_source"]["name"] for hit in response["hits"]["hits"]]
       return product_names
   else:
       return []


def mount_synonyms(results):
   synonyms_set = [{"id": slugify(product), "synonyms": synonyms} for product, synonyms in
                   results.items()]

   try:
       es_client = get_client_es()
       response = es_client.synonyms.put_synonym(id="products-synonyms-set",
                                                 synonyms_set=synonyms_set)

       logging.info(json.dumps(response.body, indent=4))
       return response.body
   except Exception as e:
       logging.error(f"Erro update synonyms: {str(e)}")
       return None


if __name__ == '__main__':
   category = "Electronics"
   products = get_products("Electronics")
   llm_synonyms = generate_synonyms(category, products)
   mount_synonyms(llm_synonyms)<h3>创建支持同义词的索引</h3><p>将创建一个新索引，对<code>products</code> 索引中的所有数据进行重新索引。该索引将使用<code>synonyms_filter</code> ，它应用了之前创建的<code>products-synonyms-set</code> 。</p><p>以下是配置为使用同义词的索引映射：</p>PUT products_02
{
  "settings": {
    "analysis": {
      "filter": {
        "synonyms_filter": {
          "type": "synonym",
          "synonyms_set": "products-synonyms-set",
          "updateable": true
        }
      },
      "analyzer": {
        "synonyms_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "synonyms_filter"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "ID": {
        "type": "long"
      },
      "category": {
        "type": "keyword"
      },
      "name": {
        "type": "text",
        "analyzer": "standard",
        "search_analyzer": "synonyms_analyzer"
      }
    }
  }
}<h3>重新索引<code>products</code> 索引</h3><p>现在，我们将使用<strong>Reindex API</strong>将<code>products</code> 索引中的数据迁移到包含同义词支持的新<code>products_02</code> 索引中。在 Kibana DevTools 中执行了以下代码：
</p>POST _reindex
{
  "source": {
    "index": "products"
  },
  "dest": {
    "index": "products_02"
  }
}<p>迁移后，<code>products_02</code> 索引将被填充，并可使用配置的同义词集验证搜索。</p><h3>使用同义词验证搜索</h3><p>让我们比较一下两个索引的搜索结果。我们将在两个索引上执行相同的查询，并验证是否使用同义词来检索结果。</p><h4>在<code>products</code> 索引中搜索（不含同义词）</h4><p>我们将使用 Kibana 执行搜索并分析结果。在分析&gt; 发现菜单中，我们将创建一个数据视图，以可视化我们创建的索引中的数据。</p><p>在 Discovery 中，单击数据视图并定义名称和索引模式。对于"<strong>产品</strong>" 索引，我们将使用"<strong>产品</strong>"模式。然后，我们将重复该过程，使用"<strong>products_02</strong><strong>"</strong>模式为"products_02" 索引创建一个新的数据视图。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte826fd932cfeb9df/6a17fdffec0f8912aa5a6841/3ad4a6891a3905e96532a312932fdf3a8216aec2-1600x599.png" alt="" /><p>配置好数据视图后，我们就可以返回 Analytics&gt; Discovery 并开始验证。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltba3729c60068e8a0/6a17fe01e9ea87ba2aa9c82a/422c4b2b51abae6580cad25085d1b8a365fc6b9e-1294x850.png" alt="" /><p>在这里，选择 DataView 产品并对"tablet" 一词进行搜索后，我们没有得到任何结果，尽管我们知道有"Kindle Paperwhite" 和"Apple iPad Air" 这样的产品。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2c6a383dd4cb0157/6a17fe02577262671d1bce0c/e4ae3a785fdd93f48d7c7d204185ded149126f2c-1600x862.png" alt="" /><h4>在<code>products_02</code> 索引中搜索（支持同义词）</h4><p>在支持同义词的"<strong>products_synonyms</strong>" 数据视图上执行相同查询时，产品被成功检索。这表明配置的同义词集工作正常，确保搜索词的不同变体都能返回预期结果。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt986b4706e2f70014/6a17fe043e9e454edbba16d3/e609749c39e90d5c82fa846af6124679dd62bcb8-1600x526.png" alt="" /><p>我们可以直接在 Kibana DevTools 中运行相同的查询来获得相同的结果。只需使用 Elasticsearch Search API 搜索 products_02 索引即可：</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbe829a3c4d7aac60/6a17fe05e8fbce03d73a1bd7/504d0d1f96dcfbceb309063dc0716bcee64ad2f8-1600x870.png" alt="" /><h2>结论</h2><p>在 Elasticsearch 中使用同义词提高了产品目录搜索的准确性和覆盖范围。与众不同的关键在于使用了<strong>LLM</strong>，它可以根据上下文自动生成同义词，无需预定义清单。该模型分析了产品名称和类别，确保与电子商务相关的同义词。</p><p>此外，<strong>同义词 API</strong>简化了词典管理，允许动态修改同义词集。有了这种方法，搜索变得更加灵活，更能适应不同的用户查询模式。</p><p>这一过程可以通过新数据和模型调整不断改进，确保提供越来越高效的研究体验。</p><h2>参考资料</h2><p><strong>在本地运行 Elasticsearch</strong></p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html</a></p><p><strong>同义词应用程序接口</strong></p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/synonyms-apis.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/synonyms-apis.html</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-synonyms-automate</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-synonyms-automate</guid>
    <category><![CDATA[相关性]]></category>
    <category><![CDATA[基础功能]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0f0247b9bc1d1ccd/6a17fe07ec0f891c745a6845/05a3cfeaa387561d5334ca3f1609035ddfff7481-1200x628.png" length="0" type="image/png"/>
    <pubDate>Thu, 27 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[扩展 Elasticsearch 中的后期交互模型 — 第 2 部分]]></title>
    <description><![CDATA[本文探讨了为大规模生产工作负载优化后期交互向量的技术，例如降低磁盘占用与提升计算效率。]]></description>
    <content:encoded><![CDATA[<p>在我们<a href="https://www.elastic.co/search-labs/blog/elastiacsearch-colpali-document-search">之前关于 ColPali 的博文</a>中，我们探讨了如何使用 Elasticsearch 构建视觉搜索应用。我们主要关注了 ColPali 这类模型带来的应用价值，但相较于使用 E5 等双编码器的向量搜索，它们在性能上存在不足。</p><p>基于<a href="https://www.elastic.co/search-labs/blog/elastiacsearch-colpali-document-search">第 1 部分</a>的示例，本文将探讨如何运用多种技术及 Elasticsearch 强大的向量搜索工具集，使后期交互向量能够胜任大规模生产工作负载。</p><p>如需获取完整代码示例，请访问 <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/colpali">GitHub</a>。</p><h2>后期交互模型的挑战</h2><p>ColPali 会为我们索引中的每个文档页面生成超过 1000 个向量。</p><p>这导致在处理后期交互向量时面临两大挑战：</p><ol><li><p>磁盘空间：将所有向量存储到磁盘会占用大量存储空间，在大规模使用时成本高昂。</p></li><li><p>计算开销：使用 <code>maxSimDotProduct()</code> 比较函数对文档排序时，需要将每个文档的所有向量与查询的 N 个向量逐一对比。</p></li></ol><p>接下来，我们看看解决这些问题的一些技巧。</p><h2>优化后期交互模型的技术</h2><h3>位向量</h3><p>为了减少磁盘占用，我们可以将向量压缩为二值（比特）向量。我们可以使用一个简单的 Python 函数将多向量转换为二值向量：</p>def to_bit_vectors(embeddings: list) -&gt; list:
    return [
        np.packbits(np.where(np.array(embedding) &gt; 0, 1, 0))
        .astype(np.int8)
        .tobytes()
        .hex()
        for embedding in embeddings
    ]<p>函数的核心思想很简单：大于 0 的值量化为 1，小于 0 的值量化为 0。这样会得到一个由 0 和 1 组成的数组，随后我们将其转换为代表二值向量的十六进制字符串。</p><p>对于我们的索引映射，我们将<code>element_type</code>参数设置为<code>bit</code>：</p>mappings = {
    "mappings": {
        "properties": {
            "col_pali_vectors": {
                "type": "rank_vectors",
                "element_type": "bit"
            }
        }
    }
}

es.indices.create(index=INDEX_NAME, body=mappings)<p>将所有新的二值向量写入索引后，我们可以使用以下代码对其进行排序：</p>query = "What do companies use for recruiting?"
query_vector = to_bit_vectors(create_col_pali_query_vectors(query))
es_query = {
    "_source": False,
    "query": {
        "script_score": {
            "query": {
                "match_all": {}
            },
            "script": {
                "source": "maxSimInvHamming(params.query_vector, 'col_pali_vectors')",
                "params": {
                    "query_vector": query_vector
                }
            }
        }
    },
    "size": 5
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcaf0c8f999d68701/6a17f46696142a6f46eb1c56/51b989446e4099745971e1eac27d147a78d13e0a-1600x480.png" alt="" /><p>这以牺牲少量精度为代价，使我们能够利用汉明距离 (<code>maxSimInvHamming(...)</code>) 进行比较，而汉明距离可以利用位掩码、SIMD 等技术进行优化。如需了解更多信息，请<a href="https://www.elastic.co/search-labs/blog/bit-vectors-in-elasticsearch">阅读我们这篇关于二值向量与汉明距离的博文</a>。</p><p>或者，我们可以不将查询向量转换为位向量，而是使用全保真度的后期交互向量进行搜索：</p>query = "What do companies use for recruiting?"
query_vector = create_col_pali_query_vectors(query)
es_query = {
    "_source": False,
    "query": {
        "script_score": {
            "query": {
                "match_all": {}
            },
            "script": {
                "source": "maxSimDotProduct(params.query_vector, 'col_pali_vectors')",
                "params": {
                    "query_vector": query_vector
                }
            }
        }
    },
    "size": 5
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2fa6c89fb451b6e0/6a17f468af47b65da9cde0d9/89f3c795c6dc44b2f7d46801320288316b47e1b2-1600x488.png" alt="使用二值向量优化后期交互模型的效果" /><p>这将使用一种非对称相似度函数来比较我们的向量。</p><p></p><p>让我们来考虑两个比特向量之间的标准汉明距离。假设有一个文档向量 <em>D：</em></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4b6ece1ec4dedac/6a17f4694b055d248143234e/366a70a23e37d403788327b7aefc873fd4482f5e-1235x86.png" alt="" /><p>和一个查询向量 <em>Q：</em></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7721df9c8f0d84f1/6a17f46b3e03d77cf54f2dcb/978ec31e0afbc0eaebf548a015b628c0cad84625-1247x84.png" alt="" /><p></p><p>简单的二值量化会将向量 <em>D</em> 转换为 <code>10101101</code>，将 <em>Q</em> 转换为 <code>11111011</code>。计算汉明距离需要直接的位运算，速度极快。本例中，汉明距离为 <code>01010110</code>，其中 1 的个数为 4。因此，评分即为汉明距离的倒数。请记住，更相似的向量汉明距离更小，取倒数后能让更相似的向量得分更高。具体到这里，得分将是 1/4 = <code>0.25</code>。</p><p>然而，请注意我们丢失了每个维度的幅度信息。一个 <code>1</code> 就只是一个 <code>1</code>。因此，对于 <em>Q</em> 向量，<code>0.01</code> 和 <code>0.79</code> 之间的差异就消失了。由于我们只是根据 <code>&gt;0</code> 进行量化，这里可以用一个小技巧：不量化查询向量 Q。虽然这无法利用极快的位运算，但由于文档向量 D 仍是量化的，因此仍能保持较低的存储成本。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blted50315518e2d599/6a17f46c414c6463709452d3/508e8498ba969534d2a0131d431c75735fc27cb9-1399x611.png" alt="" /><p>简而言之，这保留了 <em>Q</em> 向量中的信息，从而提高了距离估计的质量，同时保持了低存储开销。</p><p>使用二值向量可以为我们显著节省磁盘空间并降低查询时的计算负载。但我们还能做得更多。</p><h3>平均向量</h3><p>要在数十万甚至更多文档中扩展搜索，仅靠二值向量带来的性能提升可能还不够。为了支撑这类规模的工作负载，我们需要利用 Elasticsearch 为向量搜索设计的 HNSW 索引结构。</p><p>ColPali 为每个文档生成约一千个向量，数量过多，无法全部加入 HNSW 图。因此，我们需要减少向量数量。为此，我们可以对 ColPali 为图像生成的所有文档向量取平均值，从而创建代表文档含义的单一向量表示。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc053425fbfcf8de8/6a17f46f148009faf1b488aa/7c3b4dffb70bd95f67deb35f8c01e73c5286c2ab-1476x1102.png" alt="对全部后期交互向量取平均" /><p>目前，这无法在 Elasticsearch 内部直接完成，我们需要在将数据摄入 Elasticsearch 之前对向量进行预处理。 </p><p>这可以通过 Logstash 或 Ingest 管道实现，但这里我们将使用一个简单的 Python 函数：</p>def to_avg_vector(vectors):
    vectors_array = np.array(vectors)
    
    avg_vector = np.mean(vectors_array, axis=0)
    
    norm = np.linalg.norm(avg_vector)
    if norm &gt; 0:
        normalized_avg_vector = avg_vector / norm
    else:
        normalized_avg_vector = avg_vector

    return normalized_avg_vector.tolist()<p>我们同时对向量进行归一化，以便使用点积相似度。</p><p>将所有 ColPali 向量转换为平均向量后，即可将其索引到 dense_vector 字段中：</p>mappings = {
    "mappings": {
        "properties": {
            "avg_vector": {
                "type": "dense_vector",
                "dims": 128,
                "index": True,
                "similarity": "dot_product"
            },
            "col_pali_vectors": {
                "type": "rank_vectors",
                "element_type": "bit"
            }
        }
    }
}

es.indices.create(index=INDEX_NAME, body=mappings)<p>需要注意的是，这可能会增加总磁盘使用量，因为我们在保存后期交互向量之外还存储了额外信息。同时，我们需要额外的内存来存放 HNSW 图，但这使得我们能够在数十亿向量规模上进行搜索。为了降低内存占用，我们可以利用备受欢迎的 <a href="https://www.elastic.co/search-labs/blog/optimized-scalar-quantization-elasticsearch">BBQ 特性</a>。如此一来，我们便能在原本无法处理的海量数据集上获得快速的搜索结果。</p><p>现在，只需使用 kNN 查询即可搜索到最相关的文档。</p>query = "What do companies use for recruiting?"
query_vector = to_avg_vector(create_col_pali_query_vectors(query))
es_query = {
    "_source": False,
    "knn": {
        "field": "avg_vector",
        "query_vector": query_vector,
        "k": 10,
        "num_candidates": 100
    },
    "size": 5
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf4ac6b7fd444f935/6a17f471a29299d76ad02dc0/a500f9907020f032c3b1c7a48ed8c759dc2a1dd4-1600x498.png" alt="" /><p>遗憾的是，原先的最佳匹配项排名跌落至第 3 位。</p><p>为了解决这个问题，我们可以采用多阶段检索策略。在第一阶段，使用 kNN 查询在数百万文档中为查询筛选出最佳候选集。在第二阶段，仅使用精度更高的 ColPali 后期交互向量对前 k 个（例如：10 个）候选进行重排序。</p>query = "What do companies use for recruiting?"
col_pali_vector = create_col_pali_query_vectors(query)
avg_vector = to_avg_vector(col_pali_vector)
es_query = {
  "_source": False,
  "retriever": {
    "rescorer": {
      "retriever": {
        "knn": {
          "field": "avg_vector",
          "query_vector": avg_vector,
          "k": 10,
          "num_candidates": 100
        }
      },
      "rescore": {
        "window_size": 10,
        "query": {
          "rescore_query": {
            "script_score": {
              "query": {
                "match_all": {}
              },
              "script": {
                "source": "maxSimDotProduct(params.query_vector, 'col_pali_vectors')",
                "params": {
                  "query_vector": col_pali_vector
                }
              }
            }
          }
        }
      }
    }
  },
  "size": 5
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt265edd5f37be40b7/6a17f4733e9e45583dba15e6/797f490860af87fcf29f34668a5c4511419c6fd0-1600x501.png" alt="使用平均向量优化后期交互模型的结果" /><p>这里，我们使用 8.18 版本引入的<a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.18/retriever.html#rescorer-retriever">重打分检索器</a>来对结果进行重排序。重打分后，可以看到最佳匹配项又回到了第一位。 </p><p>注意：在实际生产应用中，可以设置比 10 大得多的 k 值，因为 maxSim 函数相对而言仍有不错的性能。</p><h3>令牌池化</h3><p>令牌池化通过聚合冗余信息（例如白色背景的图像块）来减少多向量嵌入的序列长度。该技术在减少嵌入数量的同时，保留了文档页面的绝大部分信息。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3d643e6ac908f640/6a17f4754b055d3783432352/09eae0b768f4b450e555d7e53e57561bd52de2c0-1412x1056.png" alt="令牌池化以优化后期交互模型" /><p>令牌池化的工作原理是，使用聚类算法将文档内相似的令牌嵌入分组到聚类中。然后，计算每个聚类中所有向量的均值，以生成一个聚合的向量表示。这个聚合向量将取代该组中原始的多个令牌向量，从而在不显著损失文档信息的前提下减少向量总数。</p><p>ColPali 论文建议对大多数数据集使用初始池化因子 3，这可以在保持 97.8% 原始性能的同时，将向量总数减少 66.7%。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d78a15c6944a1ea/6a17f477414c64a2929452d9/343cc9eaf54af8c7a125d4115838f3c7d5659de0-1600x1007.png" alt="用于优化后期交互模型的池化因子" /><p>但需要注意：对于包含文本密集、空白极少的“Shift”数据集，随着池化因子增大，其性能会迅速下降。</p><p>要生成池化后的向量，我们可以使用 colpali_engine 库：</p>from colpali_engine.compression.token_pooling import HierarchicalTokenPooler

pooler = HierarchicalTokenPooler(pool_factor=3) # test on your data for a good pool_factor

def pool_vectors(embedding: list) -&gt; list:
    tensor = torch.tensor(embedding).unsqueeze(0)
    pooled = pooler.pool_embeddings(tensor)
    return pooled.squeeze(0).tolist()<p>现在我们得到了一个维度减少约 66.7% 的向量。我们可以照常对其进行索引，并使用 <code>maxSimDotProduct()</code> 函数进行搜索。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc460c14031814ede/6a17f4794202292bf629f72d/2412c5db7d79a590b96d42fe01f140c42f010612-1600x481.png" alt="后期交互模型效果评估" /><p>我们能够获得良好的搜索结果，代价是结果精度有轻微损失。</p><p>提示：使用更高的 pool_factor（如 100-200），可以在平均向量方案和此处讨论的方案之间取得折中。当每个文档仅有 5-10 个向量时，将它们索引到嵌套字段中并利用 HNSW 索引就变得可行。</p><h2>交叉编码器 vs. 后期交互模型 vs. 双编码器</h2><p>根据我们目前的了解，与其他 AI 检索技术相比，如 ColPali 或 ColBERT 这类后期交互模型定位如何？</p><p>虽然 maxSim 函数相比交叉编码器计算成本更低，但仍比使用双编码器的向量搜索需要多得多的比较和计算（后者每个查询-文档对仅比较两个向量）。 </p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbfd97f3bba3e5b17/6a17f47be31791b0052d5943/75e1fc9e601aa7a6e88f137565c1919166c7a71c-1480x458.png" alt="交叉编码器 vs. 后期交互模型 vs. 双编码器" /><p>因此，对于后期交互模型，我们通常建议仅将其用于对前 k 个搜索结果进行重排序。这也体现在其字段类型的名称上：rank_vectors。</p><p>那么交叉编码器呢？后期交互模型是否因为查询时执行成本更低而更优？和往常一样，答案是：视情况而定。交叉编码器通常能产生更高质量的搜索结果，但它们需要大量的计算，因为查询-文档对需要完整地通过 Transformer 模型。它们的优势在于无需对向量进行索引，可以无状态运行。这带来以下特点：</p><ul><li><p>使用更少的磁盘空间</p></li><li><p>更简单的系统</p></li><li><p>搜索结果质量更高</p></li><li><p>延迟更高，因此无法进行深层重排序</p></li></ul><p>另一方面，后期交互模型可以将部分计算转移到索引阶段，从而降低查询成本。我们付出的代价是必须索引向量，这使得索引流水线更复杂，并且需要更多磁盘空间来存储这些向量。</p><p>具体到 ColPali，由于图像包含大量数据，从中分析信息的成本非常高。在这种情况下，权衡的天平倾向于使用 ColPali 这类后期交互模型，因为在查询时评估这些信息将过于消耗资源且速度太慢。 </p><p>对于像 ColBERT 这样处理文本数据（与大多数交叉编码器，如 elastic-rerank-v1，类似）的后期交互模型，决策可能更倾向于使用交叉编码器，以受益于其节省磁盘和架构简单的特点。</p><p>我们鼓励您根据自身的使用场景权衡这些利弊，并尝试 Elasticsearch 提供的各种工具，以构建最佳的搜索应用。</p><h2>结论</h2><p>在本博客中，我们探讨了多种优化后期交互模型（如 ColPali）的技术，以支持在 Elasticsearch 中进行大规模向量搜索。虽然后期交互模型在检索效率和排序质量之间提供了良好的平衡，但也带来了存储和计算方面的挑战。</p><p>为了应对这些挑战，我们研究了以下技术：</p><ul><li><p><strong>二值向量</strong>：可显著减少磁盘空间，同时利用汉明距离或非对称最大相似度等高效相似度计算。</p></li><li><p><strong>平均向量</strong>：将多个嵌入压缩为单一的稠密表示，从而实现利用 HNSW 索引的高效检索。</p></li><li><p><strong>令牌池化</strong>：智能合并冗余的嵌入，同时保持语义完整性，降低查询时的计算开销。</p></li></ul><p>Elasticsearch 提供了一套强大的工具集，让您可以根据需求定制和优化搜索应用。无论您优先考虑检索速度、排序质量还是存储效率，这些工具和技术都能帮助您在实际应用中平衡性能与质量。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/late-interaction-model-colpali-scale</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/late-interaction-model-colpali-scale</guid>
    <category><![CDATA[相关性]]></category>
    <category><![CDATA[向量数据库]]></category>
    <dc:creator><![CDATA[Peter Straßer,Benjamin Trent]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt97a536033e6b0a56/6a17f47dfbc5f88c86491c0d/c780b78a07573f2df1cfef8b29a7109f839b0ab3-1200x628.png" length="0" type="image/png"/>
    <pubDate>Thu, 20 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>