<?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/jp/search-labs/blog/category/hybrid-search</link>
    </image>
    <link>https://www.elastic.co/jp/search-labs/blog/category/hybrid-search</link>
    <atom:link href="https://www.elastic.co/jp/search-labs/rss/category/hybrid-search.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[jp]]></language>
    <lastBuildDate>Wed, 23 Sep 2026 07:38:47 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Elasticsearch Vector Database：数分でシッピング、数百億規模へ手頃なコストでスケール]]></title>
    <description><![CDATA[ハイブリッド検索の難しい部分はすでに完了しています。最適化されたデフォルト設定、サードパーティおよびネイティブのJina AIモデル、マネージドGPU推論をすべてすぐに利用できます。インフラの構築ではなく、高速でスケーラブルなAIアプリの構築に専念しましょう。]]></description>
    <content:encoded><![CDATA[<p>Elasticsearchは、ベクトルワークロード向けに世界で最も広くデプロイされているプラットフォームの1つであり、GitHub、Docusign、Seismicなど多くの企業のセマンティック検索、Retrieval-Augmented Generation（RAG）、レコメンデーションを支えています。本日、ベクトルベースのアプリケーション向けに最適化された新しいサーバーレス製品、Elasticsearch Vector Databaseを発表します。ドキュメントとクエリをご用意いただければ、埋め込み、インデックスチューニング、インフラは当社が対応します。さらに、低価格で拡張性にも優れています。</p><p>新規ユーザーにとって、これは高品質なベクトル検索を最も迅速に開始する方法です。すでにElasticsearchをご利用の場合は、新しいシステムを導入する必要はなく、データが既に存在するプラットフォーム上でベクトル検索を利用できるようになります。Elasticsearch Vector Databaseは、大規模言語モデル（LLM）のグラウンディングから、AIエージェントへの検索機能とメモリの提供、数千億ものベクトルの提供まで、幅広いシナリオをサポートします。わずか数分で<a href="https://cloud.elastic.co/registration?onboarding_token=vector">新しいプロジェクトを立ち上げ</a>、開始できます。</p><h2>1つのエンジン、あらゆるベクトルユースケース</h2><p>Elasticsearch Vector Databaseは、ベクトルを使用してアプリケーションを構築するすべての人向けに設計されています。</p><ul><li><p><strong>RAG：</strong>高密度ベクトル検索と低密度ベクトル検索でLLMに最適なコンテキストを取得するか、ベクトル検索と語彙検索の両方を組み合わせたハイブリッド検索を利用できます。生成結果の質は、検索結果の質に比例して向上します。</p></li><li><p><strong>AIエージェント：</strong>マルチステップのエージェントループで求められる低レイテンシで、ドキュメントや会話記憶に対する高速でフィルタリングされた検索をエージェントに提供します。</p></li><li><p><strong>セマンティック検索：</strong>1つのフィールドタイプとパイプラインコード不要で、キーワードではなく意味に基づいて一致させます。</p></li><li><p><strong>レコメンデーションと類似性：</strong>製品、画像、その他あらゆるコンテンツにわたり、大規模なスケールで最近傍を検索できます。</p></li></ul><h2>ベクトルワークロードに必要なすべてを、すぐに使えるよう最適化</h2><p>ベクトルベースのアプリケーションを構築するには、いくつかの個別の要素を連携させる必要があります。具体的には、埋め込みモデルの設定とホスティング、それらを用いたドキュメントのインデックス作成、ベクトルの効率的な保存、各クエリへの埋め込みモデルの適用、ベクトルストアとの照合、そして最後に、一致したドキュメントの取得です。Elasticsearch Vector Databaseは、追加の設定やセットアップなしで、これらすべてを自動的に処理します。</p><h3>vectordb_documentインデックスモードを使用したベクトルのインデキシング</h3><p>ベクトル優先ワークロード専用に設計された新しいインデックス構成である<a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dense-vector#dense-vector-vectordb-document-mode">vectordb_document</a>インデックスモードはデフォルトで有効になっており、エキスパートが選択するような設定が適用されます。以下が有効化されます。</p><ul><li><p><strong>デフォルトでbfloat16：</strong>ベクトルはfloat32の半分のサイズで格納され、再現率への影響はごくわずかで、量子化を適用する前の段階でもディスク使用量をほぼ半分に削減します。</p></li><li><p><strong>ソースベクトルの除外：</strong>Elasticsearchでは、埋め込みはすでに検索に使用されるインデックス構造内に存在します。_sourceに未加工のコピーを重複して保持すると、ストレージを消費し、結果の取得が遅くなるだけです。重複を除外することで、レスポンスが高速化され、格納するデータ量を削減できます。</p></li><li><p><strong>適切なファイルをキャッシュに事前読み込み：</strong>ベクトルクエリが最初にアクセスするデータ構造があらかじめメモリに読み込まれるされるため、1番目の（そして1,000番目の）クエリも超高速で実行されます。</p></li><li><p><strong>並列マージ：</strong>マージによってセグメントがより整理されたベクトル構造に統合され、再現率とレイテンシーの両方が向上します。さらに、これらのマージをマルチスレッドで実行することで、より高速に完了できます。</p></li></ul><h3>ベクトルストレージ、圧縮、自動チューニング</h3><ul><li><p>ベクトルは自動的に圧縮されます。<a href="https://www.elastic.co/jp/search-labs/blog/better-binary-quantization-lucene-elasticsearch">Better Binary Quantization（BBQ）</a>により、再現率を維持しながらベクトルのメモリ・フットプリントを最大32倍削減できます。さらに、DiskBBQは大規模なワークロードのメモリ要件をさらに削減します。<a href="https://www.elastic.co/jp/search-labs/blog/vector-quantization-auto-calibration-diskbbq"> </a></p></li><li><p><a href="https://www.elastic.co/jp/search-labs/blog/vector-quantization-auto-calibration-diskbbq">自動キャリブレーション</a>を有効にすると、各セグメントの量子化がデータに合わせて調整され、データのずれに応じてマージのたびに再調整されます。18種類のデータセットでテストを行った結果、1秒あたりのクエリ数（QPS）は平均16.7%向上し、そのほとんどで再現率も向上しました。</p></li></ul><h3>マネージドGPU推論での埋め込み</h3><ul><li><p>ネイティブの<a href="https://www.elastic.co/jp/jina-search-models">Jina AIの埋め込みモデルとリランカーモデル</a>で埋め込みを生成することも、サードパーティ製モデルを導入することもできます。いずれも<a href="https://www.elastic.co/docs/explore-analyze/elastic-inference/eis">Elastic Inference Service（EIS）</a>を介したマネージドGPUで実行でき、モデルサーバーを運用する必要はありません。または、ご自身でホストすることも可能です。</p></li><li><p><a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text"><strong>semantic_text</strong></a>フィールドタイプは、チャンキングと埋め込み、そしてクエリを自動的に処理し、市場で最もシンプルなセマンティック検索への道を提供します。</p></li></ul><h3>ハイブリッド検索とフィルタリングされたベクトル検索</h3><ul><li><p><a href="https://www.elastic.co/jp/elasticsearch/hybrid-search">ハイブリッド検索</a>が組み込まれており、単一のクエリで全文検索とベクトル検索を組み合わせることができます。逆順位融合（RRF）やその他の任意のブレンドメカニズムを使用して結果を統合できます。通常、ベクトル検索はハイブリッド検索の中で適切に設定するのが最も難しい部分です。Elasticsearch Vector Databaseを使用すれば、その問題は解決し、ハイブリッドスタック全体の性能が向上します。</p></li><li><p><a href="https://www.elastic.co/jp/search-labs/blog/filtered-hnsw-knn-search">フィルタリングされたベクトル検索</a>では、再現率を損なう後処理としてではなく、ベクトル検索自体の一部としてメタデータフィルタを適用できます。</p></li></ul><h3>初日からエンタープライズ対応</h3><p>また、純粋なベクトルデータベースには一般的に備わっていない、ロールベースのアクセス制御（RBAC）、監査ログ、コンプライアンス認定も利用できます。</p><h2>スケールしても手頃で予測可能</h2><p>Elasticsearch Vector Databaseは、成長に合わせてコスト効率を維持できるように設計されています。ストレージを線形に保ち、メモリ使用量を低く抑えるBBQおよびDiskBBQ圧縮により、数千億のベクトルに拡張してもコストが急増することはありません。支払う料金は、格納するデータ量、作成するインデックス量、必要な検索容量など、すでに把握している数値に基づいて算出されます。ドキュメント数、ベクトルの次元数、クエリ負荷を見積もれば、プロジェクトを作成する前に料金を算出できます。月末には、請求内容を項目ごとに確認することもできます。不透明な計算ユニットはなく、バックグラウンド処理に対する予期せぬ料金も発生しません。</p><h2>Elasticsearch Vector Databaseの利用開始方法</h2><h3>サーバーレスベクトルデータベースプロジェクトを作成する</h3><p><a href="https://cloud.elastic.co/registration?onboarding_token=vector">Elastic Cloudで新しいサーバーレスVector Databaseプロジェクトを作成します</a>。データをエンドポイントに向ければ、インデックスの準備は完了です。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt556cbdfba551f248/6aa10b4332b53038406d321a/image1.png" alt="Elastic Cloud Serverless project types: Elasticsearch, Vector Database, Observability and Security" /><h3>semantic_textを使用したインデックスの作成</h3><p>ベクトルインデックスモードは、ベクトルの構成を処理します。semantic_textを使用すると、マネージドGPU推論上でインデックス設定と同様に埋め込みやチャンキングの設定も自動的に管理されるため、埋め込みパイプラインを構築する必要はありません。</p>PUT my-vectors
{
"mappings": {
"properties": {
"description": { "type": "semantic_text" }
    }
  }
}<h3>ドキュメントを投入する</h3><p>テキストをインデックスすると、埋め込みが自動的に生成されます。</p>POST /my-vectors/_doc
{
  "id": "park_rocky-mountain",
  "title": "Rocky Mountain",
  "description": "Bisected north to south by the Continental Divide, this portion of the Rockies has ecosystems varying from over 150 riparian lakes to montane and subalpine forests to treeless alpine tundra."
}<h3>セマンティック検索クエリを実行する</h3><p>先ほど作成したセマンティックフィールドをクエリします。</p>GET /my-vectors/_search
{
  "query": {
    "semantic": {
      "field": "description",
      "query": "a mountain range in the middle of north america"
    }
  }
}<p>そして、結果が返されます。</p>{
  "took": 80,
  "hits": {
    "max_score": 0.7792325,
    "hits": [
      {
        "_index": "my-vectors",
        "_score": 0.7792325,
        "_source": {
          "id": "park_rocky-mountain",
          "title": "Rocky Mountain",
          "description": "Bisected north to south by the Continental Divide, ..."
        }
      }
    ]
  }
}<p>セマンティック検索は始まりにすぎません。完全なテキストクエリを実行することも、両方を組み合わせてハイブリッドクエリにすることもできます。独自のベクトルクエリを作成して、完全に制御することもできます。詳しい手順については、ドキュメントの<a href="https://www.elastic.co/docs/solutions/vector-database/vector-full-text-search">セマンティック検索クイックスタート</a>をご覧ください。</p><h2>Elasticsearchにおけるベクトル検索の今後</h2><p>私たちは既に次の改善に取り組んでいます。</p><ul><li><p><strong>より優れたマルチテナント処理：</strong>テナントごとにデータを分離する必要がある場合、より少ないコードでより迅速に実現する方法を提供します。</p></li><li><p><strong>インデックスの自動最適化：</strong>できるだけ手をかけずに、「新規作成直後のインデックス」から「完全に最適化済み」へ。</p></li><li><p><strong>継続的なインフラストラクチャーの改善：</strong>Vector Databaseの設定とインフラストラクチャーを継続的に調整することで、常に最高のスループットと最速の対応を得られるようにします。</p></li></ul><h2>Elastic Cloud ServerlessでElasticsearchベクトルデータベースをお試しください</h2><p>本番環境レベルのデフォルト設定がチューニングを行うため、空のプロジェクトから数分でフィルター適用済みのハイブリッドベクトルクエリを作成できます。インフラの構築ではなく、高速でスケーラブルなAIアプリの構築に専念しましょう。</p><p><a href="https://cloud.elastic.co/registration?onboarding_token=vector">Elastic Cloud Serverless</a>を開始するか、<a href="https://www.elastic.co/docs/solutions/vector-database">詳細ドキュメント</a>と<a href="https://www.elastic.co/docs/api/doc/elastic-cloud-serverless/group/endpoint-vectordb-projects">APIリファレンス</a>をご覧ください。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/vector-database-rag-serverless</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/vector-database-rag-serverless</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[Elastic Cloud Serverless]]></category>
    <category><![CDATA[ハイブリッド検索]]></category>
    <dc:creator><![CDATA[Dustin Coates]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4def84aae6aff861/6aa10ab1ee57e53d9b05253c/cover.png" length="0" type="image/png"/>
    <pubDate>Wed, 09 Sep 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearchの検索再現率を測定・改善する方法：ハイブリッド検索で0.43から0.75へ]]></title>
    <description><![CDATA[Elasticsearchにおける検索再現率を測定および改善する方法を学びましょう。BM25の語彙検索とJina AIのベクトル埋め込みを組み合わせ、rank_eval APIを使用して実際の数値で改善効果を検証します。]]></description>
    <content:encoded><![CDATA[<p><a href="https://www.elastic.co/blog/practical-bm25-part-1-how-shards-affect-relevance-scoring-in-elasticsearch">BM25ランキングアルゴリズム</a>を用いた<a href="https://www.elastic.co/docs/solutions/search/full-text">語彙検索</a>は、低コストで高速であり、幅広いクエリに対して非常に効果的です。しかし盲点があります。それは、ドキュメントとトークンを共有しないクエリです。この記事では、BM25が足りないところを正確に測定します。Elasticsearchの<a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval"> ランキング評価API</a>（<code>rank_eval</code>）を使用し、<a href="https://www.elastic.co/docs/explore-analyze/elastic-inference/eis">Elastic Inference Service</a>（EIS）を介して<a href="https://www.elastic.co/search-labs/es/blog/jina-embeddings-v3-elastic-inference-service">Jina AIの埋め込みを追加することでそのギャップを埋めます。</a>再現率スコアが <code>0.43</code> から <code>0.75</code> に上がるのを見て、その理由がわかるでしょう。</p><h2>リコールとは何ですか？</h2><p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#k-recall">再現率</a>は、<code>0</code>から<code>1</code>のスケールで、ユーザーが実際に欲しいドキュメントが検索結果にどれだけ含まれているかを測定します。クエリで3点の製品が表示されるはずなのに、検索結果の上位10位に2つしか表示されない場合は、そのクエリの再現率は<code>recall@10 = 0.67</code>となります。これは集合ベースの指標であり、その<em>k</em>件の結果内の関連ドキュメントの位置は考慮されません。10番目の位置にある関連ドキュメントは、1番目の位置にある関連ドキュメントと同じものとして扱われます。再現率が高いということは、関連性の高い検索結果を見逃さないということです。</p><p>
</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5ffd147b13705680/6a170a6fe8fbce11a539fc22/b13af2a5d0ca055535d8bfe3dfe4b3d1093ee6da-1457x796.png" alt="Recall@10の計算方法を示すベン図です。すべての関連文書とBM25によって検索された上位10件の結果の重複を示し、Recall@10のスコアは0.40となります。" /><p>この図は2つの集合を示しています。1つは関連するすべてのドキュメント（左側）、もう1つはBM25が実際に取得したドキュメント（上位10件、右側）です。再現率に影響する交差部分、<code>prod_1</code>と<code>prod_2</code>が見つかり、<code>prod_3</code>、<code>prod_4</code>、<code>prod_6</code>は完全に見落とされました。結果：<code>Recall@10 = 2/5 = </code><strong><code>0.40</code></strong>。</p><h2>要件</h2><p>再現率がどのように機能するのかをよりよく理解するために、本題に入りましょう。このデモンストレーションではPythonを使用します。付属のノートブック（<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/relevance-tuning-improving-recall-adding-vectors/notebook.ipynb">notebook.ipynb</a>）で一緒に進めることができます。すべてのコードブロックは、実行準備が整ったセルです。</p><p>提供されたコードでは、以下を使用しています。</p><ul><li><p>Elasticsearch 9.3以降</p></li><li><p>Python 3.10+</p></li></ul>pip install elasticsearch pandas plotly python-dotenv<ul><li><p>Elasticsearchの認証情報が含まれる<code>.env</code>ファイル</p></li></ul>ELASTICSEARCH_URL=https://your-cluster-url
ELASTICSEARCH_API_KEY=your-api-key<h2>データセット</h2><p>靴、電子機器、工具など、さまざまなカテゴリーにわたる1,000点の製品を掲載した製品カタログを使用します。</p><p>各ドキュメントには4つのフィールドがあります。</p><p>フィールド</p><p>タイプ</p><p>`タイトル`</p><p>テキスト</p><p>`description`</p><p>テキスト</p><p>`ブランド`</p><p>キーワード</p><p>`category`</p><p>キーワード</p><p>データセットは <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/relevance-tuning-improving-recall-adding-vectors/dataset.csv"><code>dataset.csv</code></a>から読み込まれます。</p><h2>語彙検索の力と限界</h2><p>BM25は、Elasticsearchおよびほとんどの検索エンジンのデフォルトのランキングアルゴリズムです。ドキュメントは、クエリ用語がどれだけ頻繁に出現するかによってスコアリングされます。これは、ドキュメントの長さと、インデックス全体でのそれらの用語の頻度に合わせて調整されます。さらに、小文字への正規化、語幹抽出、ストップワード除去といった<a href="https://www.elastic.co/docs/reference/text-analysis/analyzer-reference">アナライザー</a>も搭載されています。「running shoes」というクエリは、「Running Shoes」と一致し、おそらく「run」とも一致します。</p><p>これは、多くの種類のクエリに対して有効です。</p><ul><li><p>「running shoes」はタイトルにこれらのトークンを含む商品を即座にマッチングします。</p></li><li><p>「Bluetoothスピーカー」は、トークンが逐語的に表示されるため、携帯オーディオ製品として表示されます。</p></li></ul><p>結果は決定論的で説明可能です。つまり、検索クエリに含まれる用語がドキュメント中に含まれているからこそ、そのドキュメントは上位にランク付けされるのです。関連性のデバッグは単純明快です。</p><h3>問題が発生する箇所</h3><p>では、同じカタログに対してこれらのクエリを試してみましょう。</p><ul><li><p><strong>「スキンケアルーティン」：</strong>どの製品名にも「ルーティン」という単語は含まれません。BM25は、「スキンケア」で部分一致することができますが、フェイス美容液、ボディオイル、モイスチャライザーは、「ビタミンC」、「レチノール」、または「ブライトニング」のような用語を使用して説明されており、いずれもクエリと重複しません。完全なスキンケアルーティンを形成する製品は、共通のトークンを持たずにインデックス全体に散らばっています。</p></li></ul>ID: B06XX6DS3P, Score: 9.0552, Title: Replenix Retinol Smooth + Tighten Body Lotion - Collagen-Boosting, Regenerating Anti-Aging Body Cream, Reduces Appearance of Stretch Marks, 6.7 oz.

  ID: B08XMPKJ1L, Score: 5.2699, Title: Bio-Oil Skincare Body Oil (Natural) Serum for Scars and Stretchmarks, Face and Body Moisturizer Hydrates Skin, with Organic Jojoba Oil and Vitamin E, For All Skin Types, 6.7 oz

  ID: B01CY764KQ, Score: 5.0057, Title: Nike Up Or Down Men Deodorant - Pack of 2 | Long-Lasting Fragrance, Body Spray Combo for Men | Deodorant for Active Living | Nike Men's Deo Set | Ultimate Odor Protection | Grooming Essentials | Signature Nike Scent | High-Performance Men's Deodorant<ul><li><p><strong>「ペット用旅行用品」：</strong>これはユースケースのグループ分けであり、製品カテゴリではありません。犬用スリングキャリア、ペット用カーシート、旅行用クレートはどれも関連性のあるアイテムですが、それらの説明文は「旅行用品」というよりは、携帯性、安全性、快適性について述べています。BM25は「ペット」と大まかに一致しますが、旅行固有の商品を他のペットカタログと区別するシグナルはありません。</p></li></ul>ID: B0BVV7BKTW, Score: 7.4371, Title: Large Foldable Travel Duffel Bag with Shoes Compartment

ID: B07TNPHYNV, Score: 6.6455, Title: 40 Pieces Christmas Bronze Jingle Bells Craft Small Bells

ID: B08R8FRW53, Score: 6.6335, Title: CUBY Dog and Cat Sling Carrier
ID: B08QMCQYGM, Score: 6.5259, Title: YTFGGY Whiteboard Pinstripe Tape 6 Rolls 1/8"
ID: B0CP3LQSWM, Score: 6.2994, Title: Portable Dog Water Bottle 32 Oz<p>これは <strong>再現率の問題</strong>です。関連するドキュメントはインデックスに存在しますが、ユーザーの言葉と文書の単語が十分に一致しないため、BM25はそれらを見つけることができません。</p><p>同義語を追加することは、既知の事例に役立ちますが、ユーザーが意図を表現するすべての方法を列挙することはできません。そこでベクトルが役立ちます。</p><h2>リコールを測定すべき理由</h2><p>問題を解決する前に、まずそれを定量化する必要があります。</p><p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#k-recall"><strong>Recall@k</strong></a>は、ユーザーが実際に求めているドキュメントのうち、検索結果に表示されるドキュメントの数を測定します。正式には：</p>Recall@k = (relevant documents found in top k) / (total relevant documents)<p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#k-precision"><strong>Precision@k</strong></a>は、上位k件の結果のうち、実際にどれだけ関連性があるかを測定します。</p>Precision@k = (relevant documents in top k) / k<p>高精度とは、得られる結果が良好であることを意味します。電子商取引においては、関連商品を見落とすこと（再現率の低さ）は、多少不完全な結果を表示すること（精度の低さ）よりも深刻です。なぜなら、商品が表示されないということは、販売機会の損失を意味するからです。</p><p>Elasticsearchの<code>rank_eval</code> APIは、両方を体系的に測定できます。それぞれに評価されたドキュメントを含むクエリのリストを提供すると、Elasticsearchがすべてのクエリのメトリックを計算します。</p><h2>評価の設定</h2><p><code>rank_eval</code> APIには<strong>評価データセット</strong>が必要です。これは、クエリとそれに関連するドキュメントのマッピング、および関連性グレード（0 = 関連性なし、1 = 関連性あり、2 = 非常に関連性あり）で構成されます。</p><p>ノートブックでは、これは<a href="https://www.elastic.co/docs/solutions/search/ranking/learning-to-rank-ltr#learning-to-rank-judgement-list">判断リスト</a>です。</p>judgments = [
    # Query 1: "running shoes" BM25 handles well (tokens appear in product titles) 
    {"query_id": "q1", "doc_id": "B09NQJFRW6", "grade": 2, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B08JMD4LMM", "grade": 2, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B08VRJ6F2Q", "grade": 2, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B07S8NRRWR", "grade": 2, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B01HD620I8", "grade": 2, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B07DX86321", "grade": 2, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B0968YVLQ8", "grade": 1, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B093QJ39ZS", "grade": 1, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B096FGSC39", "grade": 1, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B01GVQWVV2", "grade": 1, "query": "running shoes"},

    # Query 2: "skincare routine" intent-based, "routine" never appears in product titles
    {"query_id": "q2", "doc_id": "B08XMPKJ1L", "grade": 2, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B0BN3WQB92", "grade": 2, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B0BT7B7P5T", "grade": 2, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B00NPA2WEY", "grade": 2, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B06XX6DS3P", "grade": 1, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B07PDRD1KT", "grade": 1, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B074J7869B", "grade": 1, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B08JV31QW4", "grade": 1, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B00K3TVJMQ", "grade": 1, "query": "skincare routine"},

    # Query 3: "study desk setup" intent-based, products are desks/stands/organizers
    {"query_id": "q3", "doc_id": "B08CS35J2T", "grade": 2, "query": "study desk setup"},
    {"query_id": "q3", "doc_id": "B09B3LFDXJ", "grade": 2, "query": "study desk setup"},
    {"query_id": "q3", "doc_id": "B07W58LMND", "grade": 1, "query": "study desk setup"},
    {"query_id": "q3", "doc_id": "B0CHYDX91L", "grade": 1, "query": "study desk setup"},

    # Query 4: "pet travel accessories" use-case grouping, products are carriers/crates/seats
    {"query_id": "q4", "doc_id": "B08R8FRW53", "grade": 2, "query": "pet travel accessories"},
    {"query_id": "q4", "doc_id": "B01MYUYX33", "grade": 2, "query": "pet travel accessories"},
    {"query_id": "q4", "doc_id": "B003C5RKE4", "grade": 2, "query": "pet travel accessories"},
    {"query_id": "q4", "doc_id": "B09GF8GBF6", "grade": 1, "query": "pet travel accessories"},
    {"query_id": "q4", "doc_id": "B0CP3LQSWM", "grade": 1, "query": "pet travel accessories"},
]<p>この組み合わせは意図的なものです。 <code>q1</code>はBM25が適切に処理するクエリ（製品タイトル内の正確なトークン）であり、 <code>q2</code> 、 <code>q3</code> 、 <code>q4</code>はユーザーの意図が特定の製品キーワードではなく概念として表現されるインテントベースのクエリです。</p><h2>BM25ベースライン再現率の測定</h2><p>まず、Elasticsearchクライアントを設定し、生のテキストデータをインデックス化します。</p>import os
import json
import pandas as pd
import plotly.graph_objects as go
from elasticsearch import Elasticsearch, helpers
from dotenv import load_dotenv

load_dotenv()

es = Elasticsearch(
    os.getenv("ELASTICSEARCH_URL"),
    api_key=os.getenv("ELASTICSEARCH_API_KEY")
)

INDEX_NAME = "ecommerce-products"<p>次にBM25の<code>rank_eval</code>リクエストを作成しましょう。リスト内の各リクエストは、クエリとその評価を組み合わせたものです。</p>judgments_df = pd.DataFrame(judgments)

bm25_requests = []
for query_id, query_text in (
    judgments_df[["query_id", "query"]].drop_duplicates().values
):
    relevant_docs = judgments_df[judgments_df["query_id"] == query_id]
    ratings = [
        {"_index": INDEX_NAME, "_id": row["doc_id"], "rating": row["grade"]}
        for _, row in relevant_docs.iterrows()
    ]

    bm25_requests.append({
        "id": query_id,
        "request": {
            "query": {
                "multi_match": {
                    "query": query_text,
                    "fields": ["title", "description"]
                }
            }
        },
        "ratings": ratings,
    })

bm25_eval = {
    "requests": bm25_requests,
    "metric": {"recall": {"k": 10, "relevant_rating_threshold": 1}},
}

bm25_result = es.rank_eval(index=INDEX_NAME, body=bm25_eval)
print("BM25 Recall@10:", bm25_result.body["metric_score"])<p>次の結果が得られます。</p>BM25 Recall@10: 0.43<p><code>0.43</code> は、4つのクエリすべてで、BM25が見つけるべきドキュメントの43%しか見つけられないということです。不足している点は、意図に基づくクエリに集中している。「スキンケアルーティン」という検索では、「ルーティン」が製品タイトルに含まれていないため、フェイス美容液やボディオイルが見つかりません。また、「ペット用旅行用品」という検索では、旅行用品ではなく携帯性や安全性の観点から説明されているキャリーケースやクレートが見つからない一方で、関連性のないペット用品が検索結果に表示されます。</p><p>これがベースラインです。これで、破るべき数値ができました。</p><h2>Jina埋め込みを用いたベクトル検索機能の追加</h2><p><a href="https://www.elastic.co/docs/solutions/search/vector"><code>Vector search</code></a> はドキュメントとクエリを高次元ベクトルとしてエンコードします。高次元ベクトルとは、数百または数千の数値から構成されるベクトルの一種で、各数値はそれが表すデータの特定の特徴をエンコードします。意味が似ているドキュメントは、たとえ共通の単語が一つもなくても、ベクトル空間上では互いに近い位置に配置されます。「ジム器具」と「ダンベルセット」は、概念が関連しているため、近くに配置されるでしょう。私がベクトルデータベースとしてElasticsearchを選んだ理由は、ハイブリッド検索をサポートしており、セマンティックな理解とキーワードの精度をすぐに実現できるからです。</p><p><a href="https://www.elastic.co/docs/explore-analyze/elastic-inference/eis">EIS</a>には、<a href="https://www.elastic.co/docs/api/doc/elasticsearch/group/endpoint-inference">推論API</a>を通じてモデル埋め込みを標準でサポートする機能が含まれています。</p><h3>ステップ1：Jina埋め込みv5を推論エンドポイントとして使用する</h3>INFERENCE_ENDPOINT_ID = ".jina-embeddings-v5-text-small"<p>クラスターにGPUリソースがある場合（Elastic CloudおよびElasticsearch 9.3以降で利用可能）、埋め込みはGPU上で生成されます。これはCPU推論よりも大幅に高速であり、従来、大規模な環境でベクトル処理を高価にしていたパフォーマンスのトレードオフを解消します。</p><p>なぜJina埋め込みが特に重要なのでしょうか。<a href="https://www.elastic.co/search-labs/blog/jina-embeddings-v5-text">JINA-embeddings-v5-text</a>は、32,000トークンのコンテキストウィンドウを持ち、タスク固有の <a href="https://arxiv.org/abs/2106.09685">Low-Rank Adaptation (LoRA) アダプター</a>をサポートする多言語モデル（119言語以上に対応）です。短い商品説明であれば、そのままでも十分に機能します。<code>jina-embeddings-v5-text</code>モデルの詳細については<a href="https://huggingface.co/jinaai/jina-embeddings-v5-text-small">こちらを</a>ご覧ください。</p><h3>ステップ2：セマンティックフィールドを持つインデックスを作成</h3>index_mappings = {
    "mappings": {
        "properties": {
            "title": {"type": "text", "copy_to": "semantic_field"},
            "description": {"type": "text", "copy_to": "semantic_field"},
            "brand": {"type": "keyword"},
            "category": {"type": "keyword"},
            "semantic_field": {
                "type": "semantic_text",
                "inference_id": INFERENCE_ENDPOINT_ID,
            },
        }
    }
}

if not es.indices.exists(index=INDEX_NAME):
    es.indices.create(index=INDEX_NAME, body=index_mappings)
    print(f"Created index: {INDEX_NAME}")<p>ここで重要なのは <a href="https://www.elastic.co/docs/solutions/search/semantic-search/semantic-search-semantic-text"><code>semantic_text</code></a> フィールドタイプです。これは<a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dense-vector"><code>dense_vector</code></a>よりも高レベルの抽象化です。推論エンドポイントを指定すると、Elasticsearchが埋め込みの生成を自動的に行います。</p><p><a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/copy-to"><code>copy_to</code></a>プロパティが<code>title</code>と<code>description</code>にある場合、両方のフィールドのコンテンツが<a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text"><code>semantic_field</code></a>に流れ込んで埋め込まれるため、単一のベクトルで製品の完全な表現を捉えることができます。</p><h3>ステップ3：製品をインデックス化する</h3>def bulk_index(products, index_name):
    actions = []
    for product in products:
        doc_id = product.get("_id")
        source = {k: v for k, v in product.items() if k != "_id"}
        action = {"_index": index_name, "_source": source}
        if doc_id:
            action["_id"] = doc_id
        actions.append(action)

    success, failed = helpers.bulk(es, actions, raise_on_error=False)
    if failed:
        for error in failed:
            print(f"Error: {error}")
    else:
        print(f"Successfully indexed {success} documents")

bulk_index(products, INDEX_NAME)<p>インデックス時には、Elasticsearchは各文書の推論エンドポイントを呼び出し、その結果得られた埋め込みを<code>semantic_field</code>に保存します。余計なコードは必要ありません。</p><h2>ハイブリッド検索：BM25とベクトルおよびRRFの組み合わせ</h2><p>ベクトルを追加すると再現率は向上しますが、ベクトルのみを使用すると、完全一致クエリの精度が低下するリスクがあります。「ランニングシューズ」は、依然として逐語的に一致するものを最優先にランク付けするべきです。ハイブリッド検索は、その精度を維持するために、語彙要素を意図的に保持します。</p><p>ハイブリッド<a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">逆順位融合</a>（RRF）検索は、両方の長所を保持します。</p><ul><li><p>BM25は正確およびほぼ正確なクエリを高精度で処理します。</p></li><li><p>セマンティック検索は、意図に基づく多言語クエリを高い再現率で処理します。</p></li><li><p>RRFは2つのランキングリストを1つのランキングに統合します。</p></li></ul><p>RRF式は、各結果リストのランクに基づいて各文書にスコアを割り当てます。</p>score = sum(1 / (rank_constant + rank))<p>両方のリストで上位に表示されるドキュメントほど、統合スコアは高くなります。<code>rank_constant</code>は、より低いランクの文書に与えられる重みを制御します。</p>hybrid_requests = []

for query_id, query_text in (
    judgments_df[["query_id", "query"]].drop_duplicates().values
):
    relevant_docs = judgments_df[judgments_df["query_id"] == query_id]
    ratings = [
        {"_index": INDEX_NAME, "_id": row["doc_id"], "rating": row["grade"]}
        for _, row in relevant_docs.iterrows()
    ]

    hybrid_requests.append({
        "id": query_id,
        "request": {
            "retriever": {
                "rrf": {
                    "retrievers": [
                        {
                            "standard": {
                                "query": {
                                    "multi_match": {
                                        "query": query_text,
                                        "fields": ["title", "description"],
                                    }
                                }
                            }
                        },
                        {
                            "standard": {
                                "query": {
                                    "match": {
                                        "semantic_field": {"query": query_text}
                                    }
                                }
                            }
                        },
                    ],
                    "rank_window_size": 50,
                    "rank_constant": 5,
                }
            }
        },
        "ratings": ratings,
    })

hybrid_eval = {
    "requests": hybrid_requests,
    "metric": {"recall": {"k": 10, "relevant_rating_threshold": 1}},
}

hybrid_result = es.rank_eval(index=INDEX_NAME, body=hybrid_eval)
print("Hybrid Recall@10:", hybrid_result.body["metric_score"])<p>次の結果が得られます。</p>Hybrid Recall@10: 0.75<p>ハイブリッドはBM25（<code>0.43</code>）よりも大幅に改善されており、「ランニングシューズ」のような完全一致クエリに対しても精度を維持します。</p><h2>結果：ビフォーアフター</h2><p>3つのアプローチの完全な比較は次のとおりです。</p>methods = {
    "BM25 (Lexical)": bm25_requests,
    "Hybrid (BM25 + Vectors)": hybrid_requests,
}

recall_metric = {"recall": {"k": 10, "relevant_rating_threshold": 1}}

comparison_data = []
for method_name, requests in methods.items():
    result = es.rank_eval(
        index=INDEX_NAME,
        body={"requests": requests, "metric": recall_metric}
    )
    comparison_data.append({
        "method": method_name,
        "recall@10": result.body["metric_score"]
    })

comparison_df = pd.DataFrame(comparison_data)
print(comparison_df.to_string(index=False))<p>次の結果が得られます。</p><p>メソッド</p><p>Recall@10</p><p>BM25（語彙）</p><p>0.43</p><p>ハイブリッド（BM25 + ベクター）</p><p>0.75</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5a1d72b57056fe64/6a170a71c1e8a56c58f882ab/e49f6c10516b0a48a0ad75962c6590ee07311407-700x500.png" alt="BM25語彙検索と、BM25とベクトルを組み合わせたハイブリッド検索のRecall@10を比較した棒グラフ。ハイブリッド検索の方がはるかに高いリコール率を達成していることがわかります。" /><p>クエリごとに分解すると次のようになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt871347f754c866d0/6a170a73839dfa40abdcfeb4/40e36dcb7b34cbf4649c512bcb60cef60f1778a6-700x500.png" alt="グループ化された棒グラフ。4つの製品クエリにおけるBM25語彙検索とハイブリッド検索のRecall@10を比較し、各クエリでハイブリッド検索が一貫して語彙検索を上回ることを示しています。" /><h2>まとめ</h2><p>この記事を通して、BM25の語彙検索は、ユーザーが正確なクエリを入力する場合には信頼性が高いものの、キーワードではなく意図に基づいて検索する場合は再現率が低下することがわかりました。<code>rank_eval</code>を用いて、そのギャップを実数で測定するための再現可能な基準を確立しました。そこから、Jina埋め込みを利用した<code>semantic_text</code>フィールドを追加し、評価を再度実行しました。結果：ハイブリッド検索により、<code>0.43</code>から<code>0.75</code>への再現率が向上し、正確な一致クエリでの精度を維持しました。ただし、実際の差はクエリの組み合わせによって異なります。</p><p>このパターンは、この例を超えてスケールします。ユーザーの実際のクエリから判断を収集し、<code>rank_eval</code>をベースラインとして実行し、<code>semantic_text</code>を追加して再度測定します。何がどれだけ改善したかが正確にわかるでしょう。</p><h2>今後の見通し</h2><ul><li><p>再現率とベクトル検索についてさらに詳しく：<a href="https://www.elastic.co/search-labs/blog/recall-vector-search-quantization">再現率とベクトル検索の量子化</a>（Jeff Vestal著）</p></li><li><p>上位結果の精度をさらに向上させるために再ランキング機能を追加</p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html">Elasticsearchハイブリッド検索のドキュメント</a>をご覧ください。</p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-rank-eval.html"><code>rank_eval</code></a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-rank-eval.html"> API</a>について詳しくはこちら</p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-relevance-tuning-improve-recall</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-relevance-tuning-improve-recall</guid>
    <category><![CDATA[ハイブリッド検索]]></category>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt37c9d2971b5a2db3/6a170a75cf4f254223b2d149/492c9b5432a2b9e40cebb3b60f0df019a8c7bf6d-1280x720.png" length="0" type="image/png"/>
    <pubDate>Mon, 04 May 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearchによるエンティティ解決、パート4：究極のチャレンジ]]></title>
    <description><![CDATA[ショートカットを防ぐために設計された、非常に多様な「究極のチャレンジ」データセットにおけるエンティティ解決の課題の解決と評価。]]></description>
    <content:encoded><![CDATA[<p>これまでのインテリジェントなエンティティ解決は2つの方法で実装されてきました。いずれのアプローチも、エンティティの準備と抽出、そしてElasticsearchによる候補の取得という同じ方法で始まります。そこから、プロンプトベースのJSON生成または関数呼び出しのいずれかを通じて、大規模言語モデル（LLM）を使用して候補を評価し、モデルにその判断について透明性のある説明を提供することを要求します。</p><p><a href="https://www.elastic.co/search-labs/blog/elasticsearch-entity-resolution-llm-function-calling">前回の記事</a>で見たように、関数呼び出しによってもたらされる一貫性は、単に便利な最適化ではなく、不可欠なものです。構造的なエラーを評価ループから除去したところ、標準的なシナリオ（ティア4データセットなど）の結果が劇的に向上しました。</p><p>しかし、答えるべき明白な疑問はまだ残っています。</p><p><em>状況が本当に複雑になってきた場合でも、このアプローチは有効でしょうか？</em></p><p>現実世界におけるエンティティ解決が単純なケースで失敗することはめったにありませんが、名前が言語、文化、文字体系、時代、組織の境界を越える場合に失敗します。人が名前ではなく肩書きで言及されている場合、会社名が変更された場合、音訳が一貫していない場合、そして（スペルではなく）文脈だけが言及と現実世界の実体を結びつける唯一の要素である場合、この方法は失敗します。</p><p>そこで、このシリーズの最後の記事として、このシステムにいわば<strong>究極のチャレンジ</strong>を課すこととしました。</p><h2>なぜこれが究極の挑戦なのでしょうか？</h2><p>以前の評価では、ますます複雑になるデータセットを用いてシステムをテストしました。前回の記事で触れた第4段階に到達する頃には、すでにニックネーム、称号、多言語名、意味的な参照などが混在する状況になっていました。これらのテストにより、アーキテクチャ自体は健全であることが示されましたが、信頼性の問題、特に不正な形式のJSONが原因で、リコールが抑制されていることがわかりました。</p><p>関数呼び出しの仕組みが整ったことで、ようやく安定した基盤ができました。そのおかげで、さらに興味深い質問をする機会が得られました。</p><p><em>1つの統一されたパイプラインで </em><em><strong>多くの異なる種類の</strong></em><em>エンティティ解決問題を一度に処理することは可能でしょうか？</em></p><p>究極のチャレンジデータセットは、まさにその側面を徹底的に追求するために設計されました。</p><p>このデータセットは、（ニックネームや音訳といった）単一の困難に焦点を当てるのではなく、 <strong>50種類以上の異なる課題タイプ</strong>を組み合わせています。</p><ul><li><p>文化的な命名規則。</p></li><li><p>タイトルに基づく参照。</p></li><li><p>事業上の関係性と過去の社名変更。</p></li><li><p>多言語および異文字表記での言及。</p></li><li><p>上記のうち複数を組み合わせた複合的な課題。</p></li></ul><p>重要なのは、この試みが特定の狭い用途向けに最適化することではなく、ルールがエンティティごとに変化した場合でも<em>設計パターン</em>が通用するかどうかをテストすることです。</p><h2>データセットの概要</h2><p>究極のチャレンジデータセットは以下で構成されます。</p><ul><li><p>個人、組織、機関などの<strong>50のエンティティ</strong>。</p></li><li><p>構造と言語の複雑さが異なる<strong>約60本の記事</strong>。</p></li><li><p>大きく以下に分類される<strong>51種類の異なるチャレンジカテゴリー</strong>。</p><ul><li><p>文化的な命名規則。</p></li><li><p>肩書きと職務上の背景。</p></li><li><p>事業と組織間の関係。</p></li><li><p>多言語および音訳の課題。</p></li><li><p>複合シナリオとエッジケースのシナリオ。</p></li></ul></li></ul><p>本シリーズの前半で、生成AIを用いてデータセットを作成することは諸刃の剣であることを確認しました。生成AIがなければ十分な規模と多様性を備えたテストデータを収集することは極めて困難になりますが、このモデルは放置すると、物事をあまりにも単純化しすぎる傾向があります。</p><p>例えば、初期世代の検証段階で、モデルに「ロシアの大統領」といったフレーズがウラジーミル・プーチンの明示的な別名として含まれていることが判明しました。それは今日では妥当に思えるかもしれませんが、文脈解決能力をテストするという目的を損なうことになります。記事が1990年代のロシアについて論じている場合はどうなるでしょうか？システムは、ハードコードされたエイリアスに頼るのではなく、文脈から正しいエンティティを推論するべきです。</p><p>そのため、このデータセットは<strong>ショートカットが効かない</strong>ように意図的に設計されています。システムが意味を推測することが想定されている場合、別名は明示的にリスト化されません。記述的なフレーズはエンティティにあらかじめリンクされていません。正確な一致は、単なるローカルテキストだけでなく、記事レベルの文脈によって決まることが多いです。</p><p><strong>重要な注意点：</strong>本システムは多様なシナリオにおける機能を実証していますが、これはあくまで教育用プロトタイプです。実際の制裁対象組織の監視を扱う本番システムでは、追加の検証、コンプライアンスチェック、監査証跡、および機密性の高いユースケースに対する特別な処理が必要となります。</p><h2>これらのシナリオが難しい理由</h2><p>このシリーズの最初の投稿で、単純であいまいな例「新しいSwiftアップデートが登場しました！」を紹介しました。課題は、「Swift」という単語が、文脈によって複数の現実世界の実体として解釈される可能性があることです。この例はより広範な真実、つまり、自然言語は本質的に曖昧であるということを捉えています。</p><p>したがって、エンティティ解決は単なる文字列照合の問題ではありません。人間は日常的に、共通の知識、文化的規範、状況的文脈に頼って参照関係を解決していますが、私たちは自分がそうしていることにほとんど気づきません。</p><p>よくあるケースをいくつか考えてみましょう。</p><ul><li><p>「大統領」という称号は地政学的・時間的な文脈なしには意味がありません。</p></li><li><p>会社名は、記事がいつ書かれたかによって、親会社、子会社、または以前のブランドを指す場合があります。</p></li><li><p>人名は、言語や文化によって、異なる順序、書体、または音訳で表記されることがあります。</p></li><li><p>同じフレーズでも、文脈によって異なる対象を指す場合があり、システムは一致を受け入れるのと同じくらい確信を持って一致を<em>拒否</em>できなければなりません。</p></li></ul><p>これらすべてを適切に処理する単一のルールセットは存在しないため、このプロトタイプは懸念事項を非常に積極的に分離しています。</p><ul><li><p>Elasticsearchは候補の範囲を効率的かつ分かりやすく絞り込みます。</p></li><li><p>LLMは、判断が必要で、それ自体を説明しなければならない場合にのみ使用されます。</p></li><li><p>検索と推論は別個のステップのままです。</p></li></ul><p>課題の種類が多様化するにつれて、この区分けはさらに重要になります。</p><h2>システムが特別なケースなしに多様性を処理する仕組み</h2><p>この評価で最も興味深い結果の一つは、<em>変更しなかった</em>点にあります。</p><ul><li><p>日本語名に関する特別なロジックは追加して<strong>いません</strong>。</p></li><li><p>アラビア語の父称に関するカスタムルールは追加して<strong>いません</strong>。</p></li><li><p>ハードコーディングされたマッピングを過去の会社名に追加して<strong>いません</strong>。</p></li></ul><p>その代わりに、このシステムはシリーズ前半で紹介したものと同じ主要要素に依存していました。</p><ul><li><p>セマンティック検索のためにインデックス化されたコンテキスト強化エンティティ。</p></li><li><p>Elasticsearchでのハイブリッド検索（完全検索、エイリアス、セマンティック）。</p></li><li><p>少数の、明確に定義された一致候補セット。</p></li><li><p>関数呼び出しと最小スキーマによって制約されたLLM判断。</p></li></ul><p>これは、システムの柔軟性が、増え続けるルールのコレクションからではなく、<strong>表現とアーキテクチャ</strong>から生まれることを示唆しています。</p><p>システムが成功するのは、適切な候補が取得され、LLMが参照が特定のエンティティにマッピングされる（またはされない）理由を説明できる十分なコンテキストがある場合です。</p><h2>結果：パフォーマンスの概要</h2><p>究極のチャレンジデータセットにおいて、システムは以下のような全体的な結果を生み出しました。</p><ul><li><p><strong>精度：</strong>約91％</p></li><li><p><strong>再現率：</strong>約86％</p></li><li><p><strong>F1スコア：</strong>約89%</p></li><li><p><strong>LLM合格率：</strong>約72％</p></li></ul><h3>チャレンジの種類ごとのパフォーマンス</h3><p>チャレンジの種類ごとに結果を分解すると、強みと限界が明らかになります。</p><p><strong>最も優れたパフォーマンス（F1スコア100%）</strong>が見られた分野は以下のとおりです。</p><ul><li><p>文字体系間の照合（キリル文字、韓国語、中国語の企業名）。</p></li><li><p>ヘブライ語のシナリオ（父称、専門職称、宗教称号、音写）。</p></li><li><p>事業階層構造（航空宇宙、多角化製造業、多部門企業）。</p></li><li><p>職業上の肩書き（学術、軍事、政治、宗教）。</p></li><li><p>複数の文字体系を含む日本語シナリオの組み合わせ。</p></li></ul><p><strong>優れたパフォーマンス（F1スコア80～99％）</strong>には以下が含まれます。</p><ul><li><p>国際的な政治家（98％）。</p></li><li><p>歴史的な名称変更（90%）。</p></li><li><p>複雑なビジネス階層（89％）。</p></li><li><p>日本の企業名（93％）。</p></li><li><p>異言語間の音訳（86％）。</p></li><li><p>アラビア語の父称（86％）。</p></li></ul><p><strong>より困難な分野</strong>には以下が含まれます。</p><ul><li><p>高度な音訳（中国語、韓国語）：0% F1。</p></li><li><p>特定の日本語シナリオ（敬称、名前の順序、表記体系のバリエーション）：約67% F1。</p></li><li><p>一部のアラビア語のシナリオ（会社名、機関の参考文献）：約40％ F1。</p></li></ul><p>ここで重要なのは、<em>なぜ</em>システムがこれらのケースで機能不全に陥ったのかという点です。失敗の原因は、全体的なアプローチが破綻したことではなく、特定のコンポーネントの限界、特に特定の多言語シナリオにおけるセマンティック検索に使用される高密度ベクトルモデルの限界にありました。</p><p>検索と判断が明確に分離されているため、パフォーマンスを向上させるためにシステムを書き換える必要はありません。より高性能な多言語埋め込みモデルの採用、エンティティコンテキストの強化、または検索戦略の洗練により、コアアーキテクチャを変更することなく、これらのカテゴリー全体で結果が向上します。</p><p>アーキテクチャーの観点から見ると、それが真の成功指標です。</p><h2>この結果が設計について教えてくれること</h2><p>シリーズを振り返ると、いくつかのパターンが際立っています。</p><ul><li><p><strong>準備は巧みなマッチングよりも重要です。 </strong>エンティティに事前にコンテキストを付加することで、後々の曖昧さを劇的に減らすことができます。</p></li><li><p><strong>LLMは、レトリバーではなく、判断者として最も価値があります。</strong>したがって、検索を求めるよりも、<em>なぜ</em>一致が意味をなすかを説明するよう求めることの方がはるかに強力です。</p></li><li><p><strong>信頼性が精度を実現します。</strong>関数呼び出しは、JSONを整理しただけでなく、取得ステップにすでに潜在していた想起を解放しました。</p></li><li><p><strong>一般化は専門化に勝ります。</strong>厳選された少数の抽象化によって、独自のロジックを必要とせずに数十種類の課題に対応できました。</p></li></ul><p>これが、プロトタイプが意図的にElasticsearchネイティブであり、LLMの使用方法が意図的に保守的である理由です。目標は検索を置き換えることではなく、意味が重要な状況において、検索を説明可能なものにすることです。</p><h2>結びに</h2><p>究極のチャレンジとは、完璧な指標を追い求めることではなく、より根本的な問いに答えることでした。</p><p><em>透明性が高く、検索優先で、LLMを活用したアーキテクチャは、ルールやブラックボックスに陥ることなく、現実世界のエンティティの曖昧さを処理できるでしょうか？</em></p><p>その回答は、この教育用プロトタイプに関しては「はい」ですが、本番環境での強化、コンプライアンス、監視、データの品質に関する明確な注意事項があります。エンティティの一致が行われた<em>理由</em>を正当化する必要のあるシステムを構築している場合、このパターンは真剣に検討する価値があります。このシリーズを通して、エンティティ解決は必ずしも難解なものではないということが伝われば幸いです。適切に関心事を分離することで、それは論理的に考え、測定し、改善できるものになります。</p><p>この研究はまた、より広範なアーキテクチャパターンを示唆しています。浮かび上がってくるのは、古典的な検索拡張生成（RAG）の、わずかではあるが重要な進化です。検索結果を直接生成に供給するのではなく、明示的な評価ステップを導入します。LLMはまず、取得された候補を評価し、妥当性を確認するために使用され、承認された結果のみが生成の強化に使用されます。これは、Generation-Augmented Retrieval-Augmented Generation with Evaluation、つまりGARAGEと名付けられるでしょう。うまい頭字語が嫌いな人なんていませんから。</p><p>このパターンは、他にどのような用途で活用できるでしょうか？信頼性、透明性、そして論理的な説明を必要とするシステムは、まさにうってつけの候補と言えます。この分野における今後の研究は、今回得られた成果と同様に説得力のあるものとなるはずであり、コミュニティが今後どのような展開を見せるのか、非常に楽しみです。</p><h2>次のステップ：試してみましょう</h2><p>究極のチャレンジが実際に動作する様子をご覧になりたいですか？実際の実装、詳細な説明、実践的な例を含む完全なウォークスルーについては、<a href="https://github.com/jesslm/entity-resolution-lab-public/tree/main/notebooks#:~:text=5%20minutes%20ago-,05_ultimate_challenge_v3.ipynb,-Initial%20public%20lab"><strong>Ultimate Challenge notebook</strong></a>を参照してください。</p><p>完全なエンティティ解決パイプラインにより、本番での使用に必要なコアコンセプトとアーキテクチャが示されています。これを基盤に、透明性と説明可能性を維持しながら、ニュース記事を監視し、エンティティの言及を追跡し、どのエンティティがどの記事に登場するのかについての質問に回答するシステムを構築できます。
</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/entity-resolution-elasticsearch-llm-challenges</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/entity-resolution-elasticsearch-llm-challenges</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[ハイブリッド検索]]></category>
    <dc:creator><![CDATA[Jessica Moszkowicz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc58be329ffebcd60/6a17043e47d49c0bc62d88ab/70fb0ff949f6db9ac9b8a28ecb4329ab915ebf46-720x420.png" length="0" type="image/png"/>
    <pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ElasticsearchとLLMによるエンティティ解決（第2部）：LLM判定とセマンティック検索によるエンティティのマッチング]]></title>
    <description><![CDATA[Elasticsearch でのエンティティ解決にセマンティック検索と透過的なLLM判断を使用します。]]></description>
    <content:encoded><![CDATA[<p><a href="https://www.elastic.co/search-labs/blog/entity-resolution-llm-elasticsearch">第1部</a>では、ウォッチリストを作成し、エンティティの言及を抽出しました。これで、「言及が実際にどのエンティティを指しているのか」という難しい質問に答える準備ができました。このシリーズの最初のブログの例に戻りましょう。ここでは、エンティティ解決が必要な理由を説明しています。「新しいSwiftアップデートが登場しました！」この見出しにもう少し文脈が添えられていると想像してください。</p><ol><li><p>新しいSwiftアップデートが登場しました！開発者たちは新しい機能を試したがっています。</p></li><li><p>新しいSwiftアップデートが登場しました！新しいアルバムは来月リリースされます。</p></li></ol><p>この追加されたコンテキストにより、「Swift」という名前を正しいエンティティに解決できるはずです。</p><p><a href="https://www.elastic.co/search-labs/blog/entity-resolution-llm-elasticsearch">前回の投稿</a>では、ウォッチリストを設定し、追加のコンテキストでエンティティを充実させました。上記の例を見ると、リストには少なくとも「Taylor Swift」と「Swift Programming Language」の2つのエンティティが必要です。また、テキストからエンティティの言及を抽出する方法も説明しました。これらの例はどちらも「Swift」を抽出します。これらの材料、強化された監視リスト、抽出されたエンティティが揃ったところで、いよいよショーの主役であるエンティティマッチングを紹介する準備が整いました。</p><p><strong>注意：</strong>これは、エンティティマッチングの概念を教えるために設計された教育用プロトタイプです。本番システムは、異なる大規模言語モデル（LLM）、カスタムマッチングルール、特殊な判断パイプライン、または複数のマッチング戦略を組み合わせたアンサンブルアプローチを使用する可能性があります。</p><h2>問題：マッチングが難しい理由</h2><p>人間の言語とは驚くべきものです。その最も興味深い特性の1つは、その無限の創造性です。無限の数の新しい文を生成し、理解することができます。そうであるなら、エンティティ解決において正確な一致が稀なのも不思議ではありません。作家は可能な限り創造的であろうと努めます。エンティティが言及されるたびにフルネームを書いたり読んだりしなければならないとしたら、かなり面倒です。そのため、厳密な一致は簡単ですが、現実には、より洗練されたエンティティ解決アプローチが必要です。それは、人間の作者の無限の創造性に少なくとも部分的には対応できるほどに堅牢なアプローチであるべきです。そのため、私たちは問題を2つのステップに分けます。まずはElasticsearchを使用して大規模な候補を取得し、次にLLMを使用してそれらの候補が実際に同じ現実世界のエンティティを指しているかどうかを判断します。</p><h2>解決策：透明性の高いLLM判断による3段階のマッチング</h2><p>私たちはコンピューターの使い方におけるパラダイムシフトの真っ只中にあります。インターネットの台頭がローカルコンピューティングからグローバルに接続されたネットワークへと私たちを導いたように、生成AIはコンテンツ、コード、情報の作成方法を根本的に変えています。実際、このシリーズに付随する教育プロトタイプは、作者の慎重な指示のもと、LLMを使用してほぼ「バイブコーディング」のみで作成されました。これは、LLMが人間の言語に本来備わっている生産性を実現している、あるいは実現するだろうということと同義ではありませんが、エンティティ解決を支援する強力なリソースが手に入ったことを意味します。</p><p>生成AIでよく使うパターンは、Retrieval-Augmented Generation（RAG）です。ここにおいて、<em>取得（retrieval）</em>とは、エンティティ候補を取得すること（回答を生成することではない）を意味し、LLMは一致の評価と説明にのみ使用されます。エンドツーエンドのエンティティ解決についてLLMに支援を依頼する<em>こともできます</em>が、これは時間と費用の両面でコストのかかるアプローチです。RAGは、より効率的な方法でLLMにコンテキストを提供することでLLMの作業を支援し、それによってLLMがエンティティ解決を効率的に支援できるようにします。</p><p>RAGの取得部分については、再びElasticsearchを利用します。まず、正確な一致、エイリアスとの一致、そしてキーワード検索とセマンティック検索を組み合わせたハイブリッド検索という組み合わせを使用して、潜在的な一致を検索します。一致する可能性のある項目が見つかったら、LLMに送信して判断を仰ぎます。LLMは最終的な一致評価者として機能します。また、LLMにその理由を説明させます。これは他のエンティティ解決システムとの重要な差別化要因です。これらの説明がなければ、エンティティ解決はブラックボックスになります。説明があれば、一致にどんな意味があるのか自分で確認できます。</p><h2>主な概念：3段階マッチング、ハイブリッド検索、透過的なLLM判断</h2><p><strong>3段階マッチングとは？</strong>このプロジェクトの開始時に、セマンティック検索がシステムの重要な一部になるという仮説を立てましたが、すべての一致にこのような高度な検索が必要なわけではありません。効率的にマッチングを見つけるために、私たちは段階的なアプローチを取ります。まず、キーワード検索で正確な一致を確認します。そのような一致が見つかった場合、作業は完了し、先に進むことができます。完全一致が失敗した場合は、エイリアス一致を使用します。このプロトタイプでは、簡素化のために、キーワードとの完全一致によるエイリアスマッチングも行われています。本番環境では、正規化、翻字ルール、あいまい一致、またはキュレートされたエイリアステーブルを使用してこのステップを拡張する場合があります。それでも最初の2つのステップで一致する可能性のあるものが見つからない場合は、Elasticsearchの逆順位融合（RRF）を使用したハイブリッド検索によるセマンティック検索を導入します。</p><p><strong>ハイブリッド検索とは？</strong>Elasticsearchでは、セマンティック検索を使用して、コンテキストを考慮した意味のある一致を見つけることができます。Elasticsearchは、ベクトル検索とハイブリッド検索に広く使用されています。セマンティック類似性は意味を理解する上で強力ですが、構造化されたフィルタリング（例えば、時間範囲、場所、または識別子による）の代替にはならず、正確な一致が利用可能な場合は多くの場合不必要です。Elasticsearchは語彙検索で名声を博しており、これはセマンティック検索が適さないタスクに最適です。両方のアプローチを最大限に活用するために、単一のハイブリッドクエリで語彙検索とセマンティック検索を併用します。次に、結果をマージして、RRFを使用して最も一致する可能性が高いものを見つけます。このプロトタイプでは、上位2つの結果が、LLM判定に送信できる潜在的な一致となります。</p><p><strong>LLM判定を使用する理由とは？</strong>LLMの判断と説明により、システムは曖昧さとコンテキストを透過的に処理できます。これは「the president」のような場合において重要です。コンテキストによって複数のエンティティを指す可能性がありますが、システム内でニックネームや文化的なバリエーションをうまく機能させることもできます。最後に、制裁リストからエンティティを識別するなどのミッションクリティカルなタスクを検討する場合、システムを信頼するために、一致が受け入れられた理由を把握する必要があります。重要なのは、LLMはコーパス全体を検索せず、Elasticsearchによって返された少数の候補のみを評価するということです。</p><h2>実際の結果：LLM推論によるマッチング</h2><p>あらゆる自然言語処理タスクにおける大きな課題は、期待される結果が何であるかを示す「答えの鍵」となるゴールデンドキュメントを作成することです。これがなければ、システムがタスクをどの程度うまく実行するかを判断することはほぼ不可能ですが、そのようなドキュメントを作成するのは面倒なプロセスになる可能性があります。エンティティ解決のプロトタイプでは、テストに使用できるデータの設定に生成AIを再度利用しました。</p><p>まず、ニックネームや翻字などのいくつかのチャレンジタイプを定義し、次にLLMに、システムにとって徐々に大きく、より困難になる階層化されたデータセットコレクションを作成するように依頼しました。データセットの作成は期待していたほど簡単ではありませんでした。LLMでは、正解を得るのがあまりにも簡単すぎるため、「チート」が行われる傾向が強くなりました。例えば、あるチャレンジタイプは意味的なコンテキストに重点を置いています。このタイプには、「ロシアの作家」を「レフ・トルストイ」に解決することなどが含まれます。LLMは誤って「ロシアの作家」を「レフ・トルストイ」の別名として入力したため、一致を見つけるためのハイブリッド検索の必要性がなくなりました。</p><p>このような問題を修正するために何度かリファクタリングを行った結果、5つのデータセット層が使用できるようになりました。第1〜4層は徐々に規模が大きくなり、チャレンジの種類も増えました。第5層は「究極のチャレンジ」データセットで、すべてのチャレンジタイプから最も難しい例で構成されていました。すべてのテストデータは<a href="https://github.com/jesslm/entity-resolution-lab-public/tree/main/comprehensive_evaluation">包括的な評価ディレクトリ</a>で利用可能です。</p><p>プロンプトベースのエンティティ解決アプローチを評価するため、私たちは第4層データセットに注目しました。重要な注意点は、エンティティの一致品質に焦点を当てることができるように、評価が制御された実験として実施されたことです。ウォッチリストデータは事前にコンテキストで強化されており、エンティティは事前に記事から抽出され、評価で抽出精度ではなくマッチングに重点が置かれることが保証されました。これにより、一致品質が分離されます。エンドツーエンドのパフォーマンスは、抽出リコールとエンリッチメント品質にも依存します。</p><h3>評価データセット</h3><p>第4層の評価データセットは、システムの機能の包括的なテストを提供します。[1]</p><ul><li><p><strong>監視リストのエンティティ：</strong>さまざまなタイプ（人、組織、場所）にわたる66個のエンティティ。</p></li><li><p><strong>テスト記事：</strong>実際のエンティティ解決シナリオを網羅した69件の記事。</p></li><li><p><strong>予想される一致数：</strong>すべての記事で206件のエンティティが一致すると予想。</p></li><li><p><strong>チャレンジタイプ：</strong>エンティティ解決のさまざまな側面をテストする15種類のチャレンジタイプ。</p></li></ul><p>データセットに含まれる課題の種類は以下の通りです。</p><ul><li><p><strong>ニックネーム：</strong> 「ボブ・スミス」→「ロバート・スミス」（7つの記事）。</p></li><li><p><strong>称号と敬称：</strong>「Dr. Sarah Williams」→「Sarah Williams」（5つの記事）。</p></li><li><p><strong>意味的文脈：</strong> 「ロシアの作家」→「レフ・トルストイ」（8 つの記事）。</p></li><li><p><strong>多言語名：</strong>異なる文字での名前の取り扱い（6つの記事）。</p></li><li><p><strong>事業体：</strong>会社名のバリエーション（7つの記事）。</p></li><li><p><strong>役員紹介：</strong> 「Microsoft CEO」→「Satya Nadella」（5つの記事）。</p></li><li><p><strong>政治指導者：</strong>タイトルベースの参考文献（5つの記事）。</p></li><li><p><strong>イニシャル：</strong> 「J. Smith」→「John Smith」（3つの記事）。</p></li><li><p><strong>名前の順序のバリエーション：</strong>さまざまな名前の順序付け規則（3つの記事）。</p></li><li><p><strong>切り捨てられた名前：</strong>名前の一部一致（3つの記事）。</p></li><li><p><strong>名前の分割：</strong>名前がテキストに分割（3つの記事）。</p></li><li><p><strong>スペース/ハイフンの欠落：</strong>書式のバリエーション（2つの記事）。</p></li><li><p><strong>翻字：</strong>文字間の名前の一致（2つの記事）。</p></li><li><p><strong>複合チャレンジ：</strong>1つの記事に複数のチャレンジ（6つの記事）。</p></li><li><p><strong>複雑なビジネス：</strong>階層的なビジネス関係（5つの記事）。</p></li></ul><p>プロンプトベースのエンティティ解決がどのように機能したか見てみましょう。</p><h3>全体的なパフォーマンス</h3><p>結果は、LLMを活用したマッチ評価には大きな可能性があることを示していますが、重大な信頼性の問題も明らかにしています。各候補ペアはLLMによって評価される必要があるため、構造化された出力の失敗により、検索が適切に機能している場合でも受け入れと呼び出しが抑制される可能性があります。</p><p>メトリック</p><p>値</p><p>精度</p><p>83.8%</p><p>リコール</p><p>62.6％</p><p>F1スコア</p><p>71.7％</p><p>見つかった一致の合計</p><p>344</p><p>LLM合格率</p><p>44.8％</p><p>エラー率</p><p>30.2%</p><h3>エラー率の問題</h3><p>このプロトタイプで最初に行うステップは、Elasticsearchを使用して潜在的な一致ペアを作成することであることを思い出してください。これらの潜在的な一致はそれぞれ、LLMによって評価される必要があります。これらすべての一致を効率的に処理するために、LLM呼び出しをバッチ処理します。これにより、APIのコストと待ち時間が削減されますが、出力に不正な形式のJSONが表示されるリスクも高まります。バッチサイズが大きくなると、JSONはより長く複雑になり、LLMが無効なJSONを生成する可能性が高くなります。これがエラー率30%となる原因です。評価では、リクエストごとに5つの一致のバッチサイズを使用しました。この保守的なバッチサイズでも、JSON解析エラーが発生し、評価結果が大幅に歪んでいます。</p><h2>次のステップ：LLM統合の最適化</h2><p>セマンティック検索とLLMによる判断を用いてエンティティをマッチングしたことで、完全なエンティティ解決パイプラインが完成しました。ただし、このアプローチでは、モデルの判断は正しいものの、その出力が使用できない場合に、新たな障害モードが発生します。LLM統合を最適化することで、信頼性とコスト効率を向上させることができます。次の投稿では、エラーとコストを削減しながら構造と型の安全性を保証する構造化出力に関数呼び出しを使用する方法について説明します。</p><h2>はじめましょう</h2><p>エンティティマッチングの実際の動作を確認したいですか？実際の実装、詳細な説明、実践的な例を含む完全なウォークスルーについては、<a href="https://github.com/jesslm/entity-resolution-lab-public/tree/main/notebooks#:~:text=5%20minutes%20ago-,03_entity_matching_v3.ipynb,-Initial%20public%20lab">エンティティマッチングノートブック</a>を参照してください。このノートブックでは、3段階の検索、RRFを使用したハイブリッド検索、LLMを利用した推論による判断を使用してエンティティを一致させる方法を正確に示します。</p><p><strong>注意：</strong>これは、概念を教えるために設計された教育用プロトタイプです。本番システムを構築するときは、モデルの選択、コストの最適化、レイテンシ要件、品質検証、エラー処理、監視など、教育に重点を置いたこのプロトタイプではカバーされていない追加の要素を考慮してください。</p><h2>メモ</h2><ol><li><p>これらのデータセットは合成されたもので教育用に設計されており、実際の課題に近似していますが、単一の本番ドメインを代表するものではありません。</p></li></ol>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-entity-resolution-llm-semantic-search</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-entity-resolution-llm-semantic-search</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[ハイブリッド検索]]></category>
    <dc:creator><![CDATA[Jessica Moszkowicz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltefc59243d9990405/6a17056ab339d5778f769ebf/473ca4357c7d60f690edbd2a844acda169aca9c3-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 26 Feb 2026 00:00:00 GMT</pubDate>
  </item>
  <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レトリバーは、「l2_norm」と「minmax」という2つの<a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers/linear-retriever#linear-retriever-normalizers">正規化機能</a>を提供します。最もよく使用されるのは「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>解決策として、BM25とセマンティック結果を組み合わせるために、逆順位融合（RRF）を使用することができます。RRFは、各結果セット内の位置に焦点を当てることで、異なる検索アルゴリズムのスコアを比較するという課題を回避します。この場合、<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[GitHubのイシューをElasticsearchでクエリするChatGPTコネクターの構築]]></title>
    <description><![CDATA[カスタムChatGPTコネクターの構築方法と、ハイブリッド検索して内部のGitHubイシューをクエリするElasticsearch MCPサーバーをデプロイする方法を学びましょう。]]></description>
    <content:encoded><![CDATA[<p>最近、OpenAIはPro/Business/EnterpriseおよびEduプラン向けに<a href="https://help.openai.com/en/articles/11487775-connectors-in-chatgpt">ChatGPT向けのカスタムコネクター</a>機能を発表しました。これは、Gmail、GitHub、Dropboxなどのデータを活用するためのすぐに使えるコネクターへの追加となります。MCPサーバーを使用してカスタムコネクターを作成できます。</p><p>カスタムコネクターを使用すると、既存のChatGPTコネクターをElasticsearchなどの追加のデータソースと組み合わせて、包括的な回答を得ることができます。</p><p>この記事では、内部のGitHubの課題とプルリクエストに関する情報を含むElasticsearchインデックスにChatGPTを接続する<a href="https://modelcontextprotocol.io/docs/getting-started/intro">MCP</a>サーバーを構築します。これにより、Elasticsearchデータを使用して自然言語クエリに回答できるようになります。</p><p>Google Colabの<a href="https://gofastmcp.com/getting-started/welcome">FastMCP</a>とngrokを使ってMCPサーバーをデプロイし、ChatGPTが接続できる公開URLを取得し、複雑なインフラ構築の必要性を排除します。</p><p>MCPとそのエコシステムの包括的な概要については、<a href="https://www.elastic.co/search-labs/blog/mcp-current-state">MCPの現在の状態</a>をご参照ください。</p><h2>要件</h2><p>始める前に必要なものは次のとおりです。</p><ul><li><p>Elasticsearchクラスター（8.X以降）</p></li><li><p>インデックスへの読み取りアクセス権を持つ Elasticsearch APIキー</p></li><li><p>Googleアカウント（Google Colab用）</p></li><li><p>Ngrokアカウント（無料プランでも可）</p></li><li><p>Pro/Enterprise/BusinessまたはEduプランのChatGPTアカウント</p></li></ul><h2>ChatGPT MCPコネクターの要件を理解する</h2><p>ChatGPT MCPコネクターには、<code>search</code>と<code>fetch</code>の2つのツールを実装する必要があります。詳細については、<a href="https://platform.openai.com/docs/mcp#create-an-mcp-server">OpenAIドキュメント</a>をご覧ください。</p><h3><a href="https://platform.openai.com/docs/mcp#search-tool">検索ツール</a></h3><p>ユーザークエリに基づいて、Elasticsearchインデックスから関連する結果のリストを返します。</p><h4>受け取るもの：</h4><ul><li><p>ユーザーの自然言語クエリを含む単一の文字列。</p></li><li><p>例：「Elasticsearch移行に関連するイシューを見つけて」</p></li></ul><h4>返されるもの：</h4><ul><li><p>結果オブジェクトの配列を含む<code>result</code>キーを持つオブジェクト。各結果には以下が含まれます。</p><ul><li><p><code>id</code> - 一意の文書識別子</p></li><li><p><code>title</code> - イシューまたはPRタイトル</p></li><li><p><code>url</code> - イシュー/PRへのリンク</p></li></ul></li></ul><h4>実装内容：</h4>return {
    "results": [
        {
            "id": "PR-612",
            "title": "Fix memory leak in WebSocket notification service",
            "url": "https://internal-git.techcorp.com/pulls/612"
        },
        # ... more results
    ]
}<h3><a href="https://platform.openai.com/docs/mcp#fetch-tool">フェッチ・ツール</a></h3><p>特定の文書の完全な内容を取得します。</p><h4>受け取るもの：</h4><ul><li><p>検索結果からElasticsearch文書IDを入力する単一の文字列</p></li><li><p>例：「PR-578の詳細を教えてください。」</p></li></ul><h4>返されるもの：</h4><ul><li><p>以下を含む完全な文書オブジェクト：</p><ul><li><p><code>id</code> - 一意の文書識別子</p></li><li><p><code>title</code> - イシューまたはPRタイトル</p></li><li><p><code>text</code> - 完全なイシュー・PRの説明と詳細</p></li><li><p><code>url</code> - イシュー/PRへのリンク</p></li><li><p><code>type</code> - 文書の種類（issue, pull_request）</p></li><li><p><code>status</code> - 現在のステータス（open, in_progress, resolved）</p></li><li><p><code>priority</code> - 優先度レベル（low, medium, high, critical）</p></li><li><p><code>assignee</code> - イシュー/PRの担当者</p></li><li><p><code>created_date</code> - 作成された時期</p></li><li><p><code>resolved_date</code> - 解決された時期（該当する場合）</p></li><li><p><code>labels</code> 文書に関連するタグ</p></li><li><p><code>related_pr</code> - 関連するプルリクエストID</p></li></ul></li></ul>return {
    "id": "PR-578",
    "title": "Security hotfix: Patch SQL injection vulnerabilities",
    "text": "Description: CRITICAL SECURITY FIX for ISSUE-1889. Patches SQL...",
    "url": "https://internal-git.techcorp.com/pulls/578",
    "type": "pull_request",
    "status": "closed",
    "priority": "critical",
    "assignee": "sarah_dev",
    "created_date": "2025-09-19",
    "resolved_date": "2025-09-19",
    "labels": "security, hotfix, sql",
    "related_pr": null
}<p><strong>注</strong>：この例では、すべてのフィールドがルートレベルにあるフラット構造を使用しています。OpenAIの要件は柔軟で、ネストされたメタデータオブジェクトもサポートしています。</p><h2>GitHubのデータセットとプルリクエストデータセット</h2><p>このチュートリアルでは、イシューとプルリクエストを含む内部GitHubデータセットを使用します。これは、ChatGPTを通じてプライベートな内部データをクエリするシナリオを表しています。</p><p>データセットは<a href="https://gist.github.com/TomasMurua/4e7bbdf7a7ebbdffaa663c43578d934a">こちら</a>からご覧いただけます。そして、<a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-bulk">Bulk APIを使って</a>データのインデックスを更新します。</p><p>このデータセットには以下が含まれます。</p><ul><li><p>説明、ステータス、優先順位、担当者に関する問題</p></li><li><p>コード変更、レビュー、導入情報を含むプルリクエスト</p></li><li><p>イシューとPRの関係（例：PR-578がISSUE-1889を修正）</p></li><li><p>ラベル、日付、その他のメタデータ</p></li></ul><h3>インデックスマッピング</h3><p>インデックスは、<a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-elser">ELSER</a>とのハイブリッド検索をサポートするために以下の<a href="https://www.elastic.co/docs/manage-data/data-store/mapping">マッピング</a>を使用します。<a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text">text_semantic</a>はセマンティック検索に使用され、他のフィールドはキーワード検索を可能にします。</p>{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "title": {
        "type": "text"
      },
      "text": {
        "type": "text"
      },
      "text_semantic": {
        "type": "semantic_text",
        "inference_id": ".elser-2-elasticsearch"
      },
      "url": {
        "type": "keyword"
      },
      "type": {
        "type": "keyword"
      },
      "status": {
        "type": "keyword"
      },
      "priority": {
        "type": "keyword"
      },
      "assignee": {
        "type": "keyword"
      },
      "created_date": {
        "type": "date",
        "format": "iso8601"
      },
      "resolved_date": {
        "type": "date",
        "format": "iso8601"
      },
      "labels": {
        "type": "keyword"
      },
      "related_pr": {
        "type": "keyword"
      }
    }
  }
}<h2>MCPサーバーを構築する</h2><p>当社のMCPサーバーは、OpenAI仕様に従って2つのツールを実装しています。ハイブリッド検索を使用してセマンティックマッチングとテキストマッチングを組み合わせることで、より良い結果が得られます。</p><h3>検索ツール</h3><p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">RRF</a>（相互ランク融合）を用いたハイブリッド検索を使用し、セマンティック検索とテキストマッチングを組み合わせています。</p>@mcp.tool()
    async def search(query: str) -&gt; Dict[str, List[Dict[str, Any]]]:
        """
        Search for internal issues and PRs using hybrid search (semantic + text with RRF).
        Returns list with id, title, and url per OpenAI spec.
        """
        if not query or not query.strip():
            return {"results": []}

        logger.info(f"Searching for: '{query}'")

        try:
            # Hybrid search with RRF (Reciprocal Rank Fusion)
            response = es_client.search(
                index=ELASTICSEARCH_INDEX,
                size=10,
                source=["id", "title", "url", "type", "priority"],
                retriever={
                    "rrf": {
                        "retrievers": [
                            {
                                # Semantic search with ELSER
                                "standard": {
                                    "query": {
                                        "semantic": {
                                            "field": "text_semantic",
                                            "query": query
                                        }
                                    }
                                }
                            },
                            {
                                # Text search (BM25) for keyword matching
                                "standard": {
                                    "query": {
                                        "multi_match": {
                                            "query": query,
                                            "fields": [
                                                "title^3",
                                                "text^2",
                                                "assignee^2",
                                                "type",
                                                "labels",
                                                "priority"
                                            ],
                                            "type": "best_fields",
                                            "fuzziness": "AUTO"
                                        }
                                    }
                                }
                            }
                        ],
                        "rank_window_size": 50,
                        "rank_constant": 60
                    }
                }
            )

            results = []
            if response and 'hits' in response:
                for hit in response['hits']['hits']:
                    source = hit['_source']
                    results.append({
                        "id": source.get('id', hit['_id']),
                        "title": source.get('title', 'Unknown'),
                        "url": source.get('url', '')
                    })

            logger.info(f"Found {len(results)} results")
            return {"results": results}

        except Exception as e:
            logger.error(f"Search error: {e}")
            raise ValueError(f"Search failed: {str(e)}")<h3>主なポイント：</h3><ul><li><p><strong>RRFを用いたハイブリッド検索：</strong> より良い結果を得るために、セマンティック検索（ELSER）とテキスト検索（BM25）を組み合わせます。</p></li><li><p><strong>複数一致クエリ：</strong>ブースティングを使用して<a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-multi-match-query">複数のフィールドを検索します</a>（title^3, text^2, assignee^2）。キャレット記号（^）は関連性スコアを乗算し、コンテンツよりもタイトルの一致を優先します。</p></li><li><p><strong>あいまい一致：</strong> <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/common-options#fuzziness"><code>fuzziness: AUTO</code></a> は近似一致を許可することでタイプミスやスペルミスを処理します。</p></li><li><p><strong>RRFのパラメーター調整：</strong></p><ul><li><p><code>rank_window_size: 50</code> - マージする前に、各リトリーバー（セマンティックとテキスト）からの上位結果をいくつ考慮するかを指定します。</p></li><li><p><code>rank_constant: 60</code> - この値は、個々の結果セット内の文書が最終的なランク付け結果にどの程度影響を与えるかを決定します。</p></li></ul></li><li><p><strong>必須フィールドのみを返す：</strong> <code>id</code>、<code>title</code>、<code>url</code>はOpenAIの仕様に従い、追加のフィールドを不必要に公開しないようにします。</p></li></ul><h3>フェッチ・ツール</h3><p>文書IDが存在する場合は、そのIDで文書の詳細を取得します。</p>@mcp.tool()
    async def fetch(id: str) -&gt; Dict[str, Any]:
        """
        Retrieve complete issue/PR details by ID.
        Returns id, title, text, url.
        """
        if not id:
            raise ValueError("ID is required")

        logger.info(f"Fetching: {id}")

        try:
            # Search by the 'id' field (not _id) since IDs are stored as a field
            response = es_client.search(
                index=ELASTICSEARCH_INDEX,
                body={
                    "query": {
                        "term": {
                            "id": id  # Search by your custom 'id' field
                        }
                    },
                    "size": 1
                }
            )

            if not response or not response['hits']['hits']:
                raise ValueError(f"Document with id '{id}' not found")

            hit = response['hits']['hits'][0]
            source = hit['_source']

            result = {
                "id": source.get('id', id),
                "title": source.get('title', 'Unknown'),
                "text": source.get('text', ''),
                "url": source.get('url', ''),
                "type": source.get('type', ''),
                "status": source.get('status', ''),
                "priority": source.get('priority', ''),
                "assignee": source.get('assignee', ''),
                "created_date": source.get('created_date', ''),
                "resolved_date": source.get('resolved_date', ''),
                "labels": source.get('labels', ''),
                "related_pr": source.get('related_pr', '')
            }

            logger.info(f"Fetched: {result['title']}")
            return result

        except Exception as e:
            logger.error(f"Fetch error: {e}")
            raise ValueError(f"Failed to fetch '{id}': {str(e)}")<h3>主なポイント：</h3><ul><li><p><strong>文書IDのフィールドで検索：</strong>カスタム <code>id</code>フィールドに用語クエリを使用します</p></li><li><p><strong>完全な文書を返す：</strong> すべてのコンテンツを含む完全な<code>text</code>フィールドが含まれます</p></li><li><p><strong>フラットな構造：</strong>すべてのフィールドがルートレベルにあり、Elasticsearchのドキュメント構造に一致します。</p></li></ul><h2>Google Colabにデプロイする</h2><p>Google Colabを使用してMCPサーバーを実行し、ngrokで公開することで、ChatGPTが接続できるようにします。</p><h3>ステップ1：Google Colabノートブックを開く</h3><p>事前設定されたノートブック<a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/elasticsearch-chatgpt-connector">Elasticsearch MCP for ChatGPT</a>にアクセスします。</p><h3>ステップ2：認証情報を設定する</h3><p>次の3つの情報が必要になります。</p><ul><li><p><strong>Elasticsearch URL：</strong>お客様の<a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-enterprise/connect-elasticsearch">ElasticsearchクラスタリングURL</a>。</p></li><li><p><strong>Elasticsearch API キー：</strong>インデックスへの読み取りアクセス権を持つ<a href="https://www.elastic.co/docs/deploy-manage/api-keys/elasticsearch-api-keys">APIキー</a>。</p></li><li><p><strong>Ngrok認証トークン：</strong><a href="https://ngrok.com/">ngrok</a>からの無料トークン。ngrokを使ってMCPのURLをインターネットに公開し、ChatGPTが接続できるようにします。</p></li></ul><h4>ngrokトークンの取得</h4><ol><li><p><a href="https://ngrok.com/">ngrok</a>で無料アカウントに登録します。</p></li><li><p><a href="https://dashboard.ngrok.com/">ngrok</a>ダッシュボードにアクセスします。</p></li><li><p>認証トークンをコピーします。</p></li></ol><h4>Google Colabにシークレットを追加する</h4><p>Google Colabノートブック内で：</p><ol><li><p>左側のサイドバーにある<strong>キーアイコン</strong>をクリックして、<strong>シークレット</strong>を開きます。</p></li><li><p>次の3つのシークレットを追加します。</p></li></ol>ELASTICSEARCH_URL=https://your-cluster.elastic.com:443
ELASTICSEARCH_API_KEY=your-api-key
NGROK_TOKEN=your-ngrok-token<p>3. 各シークレットのノートブックアクセスを有効にします。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5acae97b386277f8/6a17f08f5ea30f74c964b6c2/d5dd6ac19fe816a562c6351fdb0f11369da0e877-609x321.jpg" alt="Google Colabへのシークレットの追加" /><h3>ステップ3：ノートブックを実行する</h3><ol><li><p><strong>ランタイム</strong>をクリックし、次に<strong>すべて実行</strong>をクリックして、すべてのセルを実行します。</p></li><li><p>サーバーの起動を待ちます（約30秒）。</p></li><li><p>公開ngrok URLを示す出力を探します。</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdd11aacf2deab67c/6a17f091e8fbce81f13a1a41/f185100e8869624bc9e1c7b2b4eb32785e2d89e7-1189x283.png" alt="" /><p>4. 出力は次のようになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8891d917fdbaaf48/6a17f092abe0f208c7dfeaf6/e02e625e91ed9136454e4401b184575fb03a336e-1052x465.jpg" alt="Google Collabでノートブックを実行した結果" /><h2>ChatGPTに接続する</h2><p>次に、MCPサーバーをあなたのChatGPTアカウントに接続します。</p><ol><li><p>ChatGPTを開き、<strong>設定</strong>に移動します。</p></li><li><p><strong>コネクター</strong>に移動します。Proアカウントを使用している場合は、コネクタで<a href="https://platform.openai.com/docs/guides/developer-mode">開発者モード</a>をオンにする必要があります。</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt95efdcb2c39307e7/6a17f094abe0f24d8edfeafa/32c02192912fc0e7e5a52e9399077ba7ae3b4901-739x715.png" alt="ChatGPTアカウントへのMPCサーバーの接続" /><p><em>ChatGPT EnterpriseまたはBusinessを使用している場合は、コネクターを職場に公開する必要があります。</em></p><p>3.  <strong>作成</strong>をクリックします。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4c8fc8dd6033918/6a17f095631730de19585b7b/15c53e5ccc381108a9dc0052cca05bf0fc97679a-755x683.png" alt="ChatGPTへのコネクターの追加" /><p><em><strong>注</strong></em><em>：Business、Enterprise、Eduワークスペースでは、ワークスペースの所有者、管理者、およびそれぞれの設定が有効になっているユーザー（Enterprise/Eduの場合）のみがカスタムコネクターを追加できます。通常のメンバーロールのユーザーには、自分でカスタムコネクターを追加する権限がありません。</em></p><p><em>コネクターが所有者または管理者ユーザーによって追加され有効化されると、ワークスペースのすべてのメンバーが使用できるようになります。</em></p><p>4. 必要な情報と、<code>/sse/</code>で終わるngrokのURLを入力します。「sse」の後の「/」に注意してください。これがない場合、動作しません。</p><ul><li><p><strong>Name:</strong> Elasticsearch MCP</p></li><li><p><strong>Description: </strong>GitHubの内部情報を検索および取得するためのカスタムMCP。</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd716ad0beeeb1d35/6a17f09714d90c11cc79b6d7/162a85705cc8ac48a3f2f665551d513e0719f93d-479x684.png" alt="Elastic MCPコネクターの作成 " /><p>5. <strong>作成</strong>を押してカスタムMCPを保存します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt857794237d7d3b5a/6a17f0983e03d729b74f2d54/97eb5fb0a32b86bfadfb35561f698616f217c049-913x629.png" alt="作成をクリックしてカスタムMCPコネクタを保存する" /><p>サーバーが稼働していれば、接続は瞬時に完了します。追加の認証は不要で、Elasticsearch APIキーはサーバー上で設定されています。</p><h2>MCPサーバーをテストする</h2><p>質問する前に、ChatGPTが使用するコネクターを選択する必要があります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd1602c48878dc7a9/6a17f09a6df731cca90a0fff/77a6fc1eb263a0eb16aac64f2ecaca5f4ac12ec2-966x568.gif" alt="ChatGPTが使用するコネクターを選択する" /><h3>プロンプト1：イシューを検索する</h3><p><strong>「Elasticsearchの移行に関連するイシューを見つけて」</strong>と質問し、アクションツールの呼び出しを確認します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6c204ceacf897f61/6a17f09c9da390fb1de4657d/cfd781acbff8cd7c8095bbe29224f8b26d581f77-650x375.png" alt="ChatGPTに「Elasticsearchの移行に関連する問題を見つける」ように依頼し、アクションツールの呼び出しを確認します。" /><p>ChatGPT はクエリを使用して<code>search</code>ツールを呼び出します。利用可能なツールを検索し、Elasticsearchツールを呼び出す準備をし、ツールに対して何らかのアクションを実行する前にユーザーに確認していることがわかります。</p><h4>ツール呼び出しリクエスト：</h4>{
  "query": "Elasticsearch migration issues"
}<h4>ツールの応答：</h4>{
  "results": [
    {
      "id": "PR-598",
      "title": "Elasticsearch 8.x migration - Application code changes",
      "url": "https://internal-git.techcorp.com/pulls/598"
    },
    {
      "id": "ISSUE-1712",
      "title": "Migrate from Elasticsearch 7.x to 8.x",
      "url": "https://internal-git.techcorp.com/issues/1712"
    },
    {
      "id": "RFC-045",
      "title": "Design Proposal: Microservices Migration Architecture",
      "url": "https://internal-git.techcorp.com/rfcs/045"
    }
    // ... 7 more results
  ]
}<p>ChatGPTは結果を処理し、自然で会話的な形式で提示します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1b4378e7d26b4ad0/6a17f09ddbb4ff4de1fb57bf/9d5b6cff85c7e54ccc2584b8ae96d45495fae8c1-923x1352.png" alt="ChatGPTがツール呼び出しリクエストとツール呼び出しレスポンスの結果を処理する方法" /><h3>仕組み</h3><h4>プロンプト：「Elasticsearch移行に関連するイシューを見つけて」</h4><p>1. ChatGPTの呼び出し <code>search(“Elasticsearch migration”)</code></p><p>2. Elasticsearchがハイブリッド検索を実行する</p><ul><li><p><strong>セマンティック検索は</strong>「アップグレード」や「<em>バージョン互換性」などの概念を理解します。</em></p></li><li><p><strong>テキスト検索</strong>で「<em>Elasticsearch</em>」と「migration」の完全一致を見つけます。</p></li><li><p><strong>RRF</strong>は両方のアプローチの結果を組み合わせてランク付けします。</p></li></ul><p>3. <code>id</code>、<code>title</code>を含むトップ10のマッチングイベントを返します。 <code>url</code></p><p>4. ChatGPTは「<em>ISSUE-1712: migrate from Elasticsearch 7.x to 8.x</em>」を最も関連性の高い結果として特定します。</p><h3>プロンプト2：完全な詳細を取得する</h3><p>質問：<em><strong>「ISSUE-1889の詳細を教えて」</strong></em></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8d1a53db8bfe8326/6a17f09f445de966104d021a/5c0db5245535ce67a36056e61e135bddc97ce496-934x629.png" alt="ChatGPTは、ユーザーが特定のイシューに関する詳細な情報を求めていることを認識し、フェッチツールを呼び出し、ツールに対して何らかのアクションを実行する前にユーザーに確認します。" /><p>ChatGPTは、あなたが特定のイシューに関する詳細な情報を求めていることを認識し、<code>fetch</code>ツールを呼び出し、ツールに対して何らかのアクションを起こす前にユーザーに確認します。</p><h4>ツール呼び出しリクエスト：</h4>{
  "id": "ISSUE-1889"
}<h4>ツールの応答：</h4>{
  "id": "ISSUE-1889",
  "title": "SQL injection vulnerability in search endpoint",
  "text": "Description: Security audit identified SQL injection vulnerability in /api/v1/search endpoint. User input from query parameter is not properly sanitized before being used in raw SQL query. Severity: HIGH - Immediate action required Affected Code: - File: services/search/query_builder.py - Line: 145-152 - Issue: String concatenation used instead of parameterized queries Investigation: - @security_team_alice: Confirmed exploitable with UNION-based injection - @sarah_dev: Checking all other endpoints for similar patterns - @john_backend: Found 3 more instances in legacy codebase Remediation: - Rewrite using SQLAlchemy ORM or parameterized queries - Add input validation and sanitization - Implement WAF rules as additional layer - Security regression tests Comments: - @tech_lead_mike: Stop all other work, this is P0 - @sarah_dev: PR-578 ready with fixes for all 4 vulnerable endpoints - @alex_devops: Deployed hotfix to production 2025-09-19 at 14:30 UTC - @security_team_alice: Verified fix, conducting full pentest next week Resolution: All vulnerable endpoints patched. Added pre-commit hooks to catch raw SQL queries. Security training scheduled for team.",
  "url": "https://internal-git.techcorp.com/issues/1889",
  "type": "issue",
  "status": "closed",
  "priority": "critical",
  "assignee": "sarah_dev",
  "created_date": "2025-09-18",
  "resolved_date": "2025-09-19",
  "labels": "security, vulnerability, bug, sql",
  "related_pr": "PR-578"
}<p>ChatGPTは情報を統合し、明確に提示します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt560958fa3bd212d0/6a17f0a0faa91355ba93c974/410f19f213e94fc4e3c47eeef6e04b69e0c86159-602x462.png" alt="ChatGPTがどのように情報を統合して提示するか " /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcccf35a584e8373b/6a17f0a2505ac3471cad8c2e/54d8ffa117628a1e3afc317c3ab75d4f7731d7ab-767x1600.png" alt="ChatGPTがどのように情報を提示するか" /><h3>仕組み</h3><h4>プロンプト：「ISSUE-1889の詳細を教えて」</h4><ol><li><p>ChatGPT呼び出し <code>fetch(“ISSUE-1889”)</code></p></li><li><p>Elasticsearchが完全な文書を取得する</p></li><li><p>すべてのフィールドがルートレベルにある完全な文書を返す</p></li><li><p>ChatGPTは情報を統合し、適切な引用で回答する</p></li></ol><h2>まとめ</h2><p>この記事では、専用の<strong>検索</strong>および<strong>フェッチ</strong>MCPツールを使用してChatGPTをElasticsearchに接続するカスタムMCPサーバーを構築し、プライベートデータに対する自然言語クエリを可能にしました。</p><p>このMCPパターンは、自然言語を使用してクエリしたい任意のElasticsearchインデックス、ドキュメント、製品、ログ、またはその他のデータで機能します。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/chatgpt-connector-mcp-server-github-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/chatgpt-connector-mcp-server-github-elasticsearch</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[ハイブリッド検索]]></category>
    <dc:creator><![CDATA[Tomás Murúa]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd1602c48878dc7a9/6a17f09a6df731cca90a0fff/77a6fc1eb263a0eb16aac64f2ecaca5f4ac12ec2-966x568.gif" length="0" type="image/gif"/>
    <pubDate>Mon, 01 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>まず最初に、ハイブリッド検索が困難になる主な理由の 1 つである、スコア範囲の多様性について確認しましょう。私たちの古い友人<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>リトリーバーの 2 つがあります。
</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt09ed6bf3d25066bd/6a17f1750b0bedbd70dd3686/264481268c8b6ac259e3c257b85431b513f16672-1077x586.png" alt="線形/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>ユーザーはこれらのツールを使用して効果的なハイブリッド検索を実装できますが、インデックスに関するある程度の知識が必要です。線形リトリーバーを使用して、2 つのフィールドを持つインデックスをクエリする例を見てみましょう。</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>は、テキスト埋め込みモデルである<a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-e5">E5</a>を使用する<code>semantic_text</code>フィールドです。</p><p>2. <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>semantic_text</code>フィールドで<code>match</code>クエリを使用します。</p><p>
クエリを構築するときは、 <code>semantic_text_field</code>テキスト埋め込みモデルを使用するため、このクエリでは 0 から 1 の間のスコアが生成されることに留意する必要があります。また、 <code>text_field</code>は標準の<code>text</code>フィールドであるため、これに対するクエリによって無制限のスコアが生成されることも知っておく必要があります。適切な関連性を持つ結果セットを作成するには、クエリ スコアを結合する前に正規化するリトリーバーを使用する必要があります。この例では、 <code>minmax</code>正規化を備えた線形リトリーバーを使用して、各クエリのスコアを 0 から 1 の間の値に正規化します。</p><p>この例のクエリ構築は、関係するフィールドが 2 つだけなので、非常に簡単です。ただし、さまざまなタイプのフィールドが追加されると、すぐに複雑になる可能性があります。これは、効果的なハイブリッド検索クエリを記述するには、クエリ対象のインデックスに関するより深い知識が必要になることが多く、組み合わせる前にコンポーネント クエリ スコアが適切に正規化される必要があることを示しています。これは、ハイブリッド検索のより広範な導入の障害となります。</p><h3>クエリのグループ化</h3><p>例を拡張してみましょう。1 つの<code>text</code>フィールドと 2 つの<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>フィールドの一致が合計スコアの 2/3 を占めることになります。</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>これに対処する一般的な方法は、クエリを語彙と意味のカテゴリにグループ化し、その 2 つに均等に重み付けすることです。これにより、どちらかのカテゴリーが合計スコアを支配することが防止されます。</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>うわー、これは冗長になってきましたね!クエリ全体を確認するには、上下に何度もスクロールする必要があったかもしれません。ここでは、2 つのレベルの正規化を使用してクエリ グループを作成します。数学的には次のように表現できます。</p>Total Score = N(N(semantic_text_field_1 score) + N(semantic_text_field_2 score)) + N(text_field score)<p>この 2 番目のレベルの正規化により、 <code>semantic_text</code>フィールドと<code>text</code>フィールドに対するクエリが均等に重み付けされるようになります。この例では、語彙フィールドが 1 つしかないため、 <code>text_field</code>の 2 番目のレベルの正規化を省略し、冗長性を<em>さらに</em>軽減していることに注意してください。</p><p>このクエリ構造はすでに扱いにくく、クエリするフィールドは 3 つだけです。より多くのフィールドをクエリするにつれて、熟練した検索実践者にとっても管理がますます困難になります。</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>リニア リトリーバーを使用する場合、フィールドごとにブーストを適用して、特定のフィールドでの一致の重要度を調整できます。たとえば、2 つの<code>semantic_text</code>フィールドと 2 つの<code>text</code>フィールドの 4 つのフィールドをクエリするとします。</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>インラインマッチング（例： <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 リトリーバーのマルチフィールド クエリ形式では、自動化されたクエリ グループ化ベースのハイブリッド検索アプローチをシンプルで使いやすい API にパッケージ化することで、この煩わしさを軽減します。フィールドごとのブースト、ワイルドカード解決、デフォルトのクエリ フィールドなどの追加機能により、機能が拡張され、多くのユース ケースをカバーできます。</p><h2>今すぐマルチフィールドクエリ形式をお試しください</h2><p>無料トライアル では、完全に管理された Elasticsearch<a href="https://www.elastic.co/cloud/serverless"> Serverless</a> プロジェクトで、マルチフィールド<a href="https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/create-serverless-project"> クエリ形式を使用した線形リトリーバーと RRF</a> リトリーバーを試すことができます。8.19 および 9.1 以降のスタック バージョンでも利用できます。</p><p>1 つのコマンドでローカル環境で数分以内に開始できます。</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[コンテキストエンジニアリングにおけるハイブリッド検索の威力 - パート3]]></title>
    <description><![CDATA[コンテキスト エンジニアリングとハイブリッド検索を使用して、集計、RBAC、非コンテンツ シグナルによって AI 出力の精度を向上させる方法を説明します。]]></description>
    <content:encoded><![CDATA[<p>ハイブリッド検索 (<a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">パート I</a> ) とコンテキスト エンジニアリング (<a href="https://www.elastic.co/search-labs/blog/context-engineering-llm-evolution-agentic-ai">パート II</a> ) の両方について説明しました。次に、RAG およびエージェント AI 操作にターゲットを絞ったコンテキストを提供する上で、これらがどのように連携して最大の効果を発揮するかについて詳しく見ていきましょう。</p><h2>検索は死んでいない、ただ移動しただけだ</h2><p>そのため、主にテキスト ボックスでコンテキストを検索し、返された情報 (コンテキスト) を使用して自分で回答を構築するという方法から、自然言語を使用してエージェントに必要なものを伝え、エージェントが自動的に回答を調査してまとめる方法へと移行しました。テクノロジー業界の多くの人々は、この変化を指摘し、「検索は死んだ」と主張しています（まあ、SEO とアドワーズの世界は<a href="https://www.pewresearch.org/short-reads/2025/07/22/google-users-are-less-likely-to-click-on-links-when-an-ai-summary-appears-in-the-results/">確実に変化しています</a>。GEO<a href="https://www.wired.com/story/goodbye-seo-hello-geo-brandlight-openai/">は</a>どうですか？）。しかし、検索は依然として代理店の業務にとって絶対に不可欠です。ただ、現在では主にツールを介して目に見えない形で実行されているだけです。</p><p>以前は、主観的な関連性の主な判断者は人間でした。各ユーザーには検索を実行する独自の理由があり、個人的な経験が結果の相対的な正確性に影響を与えていました。エージェントが私たちと同じ（あるいはそれ以上の）結論に達することができると信頼するには、エージェントがアクセスできるコンテキスト情報が私たちの主観的な意図に可能な限り近いことを保証する必要があります。私たちはその目標に向けて、LLM に提供するコンテキストを設計する必要があります。</p><h2>ハイブリッド検索によるコンテキストの生成</h2><p>パート I でもう一度お伝えしましたが、Elastic のハイブリッド検索は、従来のキーワードベースの検索の強み (構文の柔軟性、キーワードの精度、関連性のスコアリング) とベクトル類似性検索の意味理解を組み合わせ、複数の再ランキング手法を提供します。この相乗効果（この言葉のより正確な使い方はこれまで見つかりませんでした！）クエリによってコンテンツをターゲットする方法がより細かく指定できるため、関連性の高い結果を得ることができます。主観的関連性を検索段階の<em>1 つ</em>として適用できるというだけでなく、実際には、第 1 段階の検索に関連性スコアリングを他のすべてのモードとともに一度に含めることができるのです。</p><h3>優れた精度と効率</h3><p>分散検索、取得、再ランク付け機能を備えたデータ プラットフォームを主要なコンテキスト検索エンジンとして使用することは、非常に理にかなっています。高度なクエリ構文を使用して、主観的な意図の欠落したコンポーネントを追加し、返されるコンテキスト情報の価値を損なったり不明瞭にしたりする可能性のあるコンテンツを除外できます。利用可能な個々の構文オプションから選択することも、モダリティを単一の検索に組み合わせて、各データの種類を最もよく理解できる方法でターゲットにし、それらを再ランク付けして組み合わせたり並べ替えたりすることもできます。不要なデータを除外し、必要なフィールド/値のみが含まれるように応答をフィルタリングできます。エージェントにとって、このターゲティングの柔軟性により、コンテキストを非常に正確に取得できるツールを構築できます。</p><h3>コンテキストの洗練（集約と非コンテンツシグナル）</h3><p>集約は、ツールがコンテキスト ウィンドウに配信するコンテンツを形成する際に特に役立ちます。集計により、返されるコンテキスト データの形状に関する数値ベースの事実が自然に提供されるため、LLM による推論がより容易かつ正確になります。集計は階層的にネストできるため、LLM に複数レベルの詳細を追加して、より微妙な理解を深めることが簡単にできます。集計はコンテキスト ウィンドウのサイズの管理にも役立ちます。10 万件のドキュメントのクエリ結果を、集約された分析情報の数百トークンに簡単に減らすことができます。</p><p>非コンテンツ シグナルは、データに内在する指標であり、見ているものの全体像を示します。つまり、人気、鮮度、地理的位置、カテゴリ、ホストの多様性、価格帯など、結果の追加特性です。これらの情報は、エージェントが受け取ったコンテキストの重要性をどのように評価するかをエージェントに通知するのに役立ちます。これを最もよく説明するために、いくつかの簡単な例を挙げます。</p><ul><li><p><strong>最近公開されたコンテンツや人気コンテンツの強化</strong>- 記事のナレッジ ベースがあると想像してください。ユーザーのクエリに関連する記事を見つけたいが、最近の記事であり、他のユーザーに役立つと判断された記事（「いいね」の数が多いなど）を優先したいとします。このシナリオでは、ハイブリッド検索を使用して関連する記事を見つけ、公開日と人気度の組み合わせに基づいて記事を再ランク付けすることができます。</p></li><li><p><strong>売上と在庫調整を伴う電子商取引の検索</strong>- 電子商取引の設定では、検索語に一致する製品を顧客に表示したいだけでなく、売れ行きがよく在庫がある製品を宣伝したいとも考えます。顧客の不満を避けるために、在庫が少ない商品のランクを下げることもできます。</p></li><li><p><strong>バグ トラッカーで重大度の高い問題を優先する</strong>- ソフトウェア開発チームにとって、問題を検索する際には、重大度が高く、優先度が高く、最近更新された問題を最初に表示することが重要です。「重要度」や「最も議論されている」などの非シグナルを使用して、さまざまな要素を個別に評価し、最も重要で活発に議論されている問題が最上位に表示されるようにすることができます。</p></li></ul><p>これらのサンプルクエリおよびその他の詳細は、付属の Elasticsearch Labs<a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/you-know-for-context/">コンテンツ ページ</a>にあります。</p><h3>セキュリティ強化</h3><p>コンテキストエンジニアリングに Elastic のような検索を活用したスピードレイヤーを活用する重要な利点は、セキュリティ フレームワークが組み込まれていることです。Elastic のプラットフォームは、きめ細かなロールベースのアクセス制御 (RBAC) と属性ベースのアクセス制御 (ABAC) を通じて、エージェントおよび生成 AI オペレーションに提供されるコンテキストが機密性の高い非公開情報を尊重して保護することを保証します。これは、クエリが効率的に処理されるだけでなく、エージェントまたはリクエストを開始したユーザーの特定の権限に応じて結果がフィルタリングされることを意味します。</p><p>エージェントは認証されたユーザーとして実行されるため、プラットフォームに組み込まれたセキュリティ機能を通じてセキュリティが暗黙的に適用されます。</p><ul><li><p><strong>きめ細かな権限:</strong>ドキュメント、フィールド、さらには用語レベルでアクセスを定義し、AI エージェントが表示を許可されているデータのみを受信するようにします。</p></li><li><p><strong>ロールベースのアクセス制御 (RBAC):</strong>エージェントまたはユーザーにロールを割り当て、定義された責任に基づいて特定のデータセットまたは機能へのアクセスを許可します。</p></li><li><p><strong>属性ベースのアクセス制御 (ABAC):</strong>データ、ユーザー、または環境の属性に基づいて動的なアクセス ポリシーを実装し、適応性の高いコンテキスト認識型のセキュリティを実現します。</p></li><li><p><strong>ドキュメント レベルのセキュリティ (DLS) とフィールド レベルのセキュリティ (FLS):</strong>これらの機能により、取得したドキュメント内でも許可された部分のみが表示されるようになり、機密情報の漏洩を防止できます。</p></li><li><p><strong>エンタープライズ セキュリティとの統合:</strong>既存の ID 管理システム (LDAP、SAML、OIDC など) とシームレスに統合し、組織全体で一貫したセキュリティ ポリシーを適用します。</p></li></ul><p>これらのセキュリティ対策をコンテキスト取得メカニズムに直接統合することで、Elastic は安全なゲートキーパーとして機能し、AI エージェントが定義されたデータ境界内で動作し、不正なデータ公開を防ぎ、データプライバシー規制へのコンプライアンスを維持できるようにします。これは、機密情報や独自情報を扱うエージェント AI システムへの信頼を構築する上で非常に重要です。</p><p>追加のボーナスとして、エンタープライズ データ ソース上で統合されたデータ スピード レイヤーを使用することで、エージェント ツールによって作成されるリポジトリでの予期しないアドホック クエリ負荷を軽減できます。ほぼリアルタイムであらゆるものを検索できる単一の場所と、セキュリティとガバナンスの制御を適用できる単一の場所が提供されます。</p><h2>ハイブリッド検索ベースのツール</h2><p>Elastic プラットフォームには、コンテキスト エンジニアリングの追求を加速させるコア機能がいくつかあります (<a href="https://www.elastic.co/blog/whats-new-elastic-9-2-0">今後もさらに増える予定</a>です)。ここで重要なのは、このプラットフォームが、AI エコシステムの進化に合わせて方法を適応、変更、拡張できる柔軟性を備え、さまざまな達成方法を提供していることです。</p><h3>エージェントビルダーの紹介</h3><p>Elastic <a href="https://www.elastic.co/elasticsearch/agent-builder">Agent Builder は</a>、Elastic にすでに保存されているデータと対話するために構築されたエージェント AI ツールの領域への最初の進出です。Agent Builder は、ユーザーが Kibana 内で独自のエージェントとツールを作成および管理できるようにするチャット インターフェースを提供します。組み込みの MCP および A2A サーバー、プログラム API、Elasticsearch インデックスのクエリと探索、および自然言語からの ES|QL クエリの生成用の一連の構築済みシステム ツールが付属しています。Agent Builder を使用すると、表現力豊かな<a href="https://www.elastic.co/docs/reference/query-languages/esql">ES|QL</a>クエリ構文を通じてエージェントに返されるコンテキスト データをターゲットにして整形するカスタム ツールを作成できます。</p><p>ES|QL はハイブリッド検索をどのように実行するのでしょうか?コア機能は、 <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text">semantic_text</a>フィールド タイプと<a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/fork">FORK</a> / <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/fuse">FUSE</a>コマンドの組み合わせによって実現されます (FUSE はデフォルトで<a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">RRF</a>を使用して各フォークの結果をマージします)。架空の製品検索の簡単な例を次に示します。</p>FROM products
| FORK
  (MATCH description "high performance gaming laptop" | EVAL search_type = "bm25"),
  (MATCH description_semantic "high performance gaming laptop" | EVAL search_type = "semantic")
| FUSE 
| LIMIT 20
| KEEP product_name, description, _score, search_type<p>上記の例の各 FORK ブランチに含まれる<a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/eval">EVAL</a>句は厳密には必須ではありません。これは、特定の結果がどの検索モダリティから返されたかを追跡する方法を示すためだけに含まれています。</p><h3>検索テンプレート</h3><p>独自の外部エージェントツールを Elastic デプロイメントにポイントするとします。また、ES|QL の代わりに、マルチステージ リトリーバーを使用したり、開発した既存の DSL 構文を再利用したり、クエリが受け入れる入力、検索を実行するために使用される構文、および出力で返されるフィールドを制御できるようにしたいと考えています。<a href="https://www.elastic.co/docs/solutions/search/search-templates">検索テンプレートを</a>使用すると、ユーザーは一般的な検索パターンの定義済み構造を定義できるため、データ取得の効率と一貫性が向上します。これは、定型コードの標準化と検索ロジックの高速な反復処理を可能にするため、検索 API と対話するエージェント ツールにとって特に有益です。そして、これらの要素のいずれかを調整する必要がある場合は、検索テンプレートを更新するだけで、変更が実装されます。エージェントツールで実際に実行される検索テンプレートの例を探している場合は、Elasticsearch Labs のブログ「 <a href="https://www.elastic.co/search-labs/blog/mcp-intelligent-search">MCP for intelligent search</a> 」をご覧ください。このブログでは、外部 MCP サーバーからのツール呼び出しの背後で検索テンプレートが使用されています。</p><h3>統合ワークフロー (最高!)</h3><p>新しいエージェント AI の世界で最も扱いにくいことの 1 つは、半自律型で自己指向的な「推論」エージェントの非決定論的な性質です。コンテキスト エンジニアリングは、エージェント AI にとって非常に重要な分野です。これは、エージェントが生成できる可能性のある結論を、私たちが知っている事実に絞り込むのに役立つ手法です。非常に正確で関連性の高いコンテキスト ウィンドウがあっても、(数値的事実の領域から外れると) エージェントの応答が完全に再現可能で信頼できるという安心感がまだ少し欠けています。</p><p>エージェントに対して同じリクエストを複数回実行すると、応答に わずかな違いがあるだけ <em>で、回答は 基本的に</em><em> 同じになる可能性があります。</em>これは通常、単純なクエリでは問題なく、ほとんど気づかれない程度で、コンテキスト エンジニアリング手法を使用して出力を調整することができます。しかし、エージェントに要求するタスクが複雑になるにつれて、1 つ以上のサブタスクによって差異が生じ、最終結果がわずかに変わる可能性が高くなります。エージェント間のコミュニケーションにさらに依存するようになると、状況はさらに悪化し、差異が累積していくでしょう。これは、エージェントが対話するツールは、コンテキスト データを正確にターゲットにするために非常に柔軟かつ調整可能である必要があり、予期される出力形式で応答する必要があるという考えを再び示しています。また、多くのユースケースでは、エージェントとツールのやり取りを誘導する必要があることも示しています。ここでワークフローが登場します。</p><p>Elastic ではまもなく、プラットフォームの中核に完全にカスタマイズ可能なワークフローが組み込まれる予定です。これらのワークフローはエージェントやツールと双方向に操作できるため、ワークフローはエージェントやツールを呼び出すことができ、エージェントやツールはワークフローを呼び出すことができます。これらの機能が、すべてのデータが存在する同じ検索 AI プラットフォームに完全に統合されることで、ワークフローの可能性は大きく変化します。もうすぐ、もうすぐ登場です！</p><h3>統合メモリバンクとしてのElastic</h3><p>Elastic は、ほぼリアルタイムの検索向けに作られた分散データ プラットフォームであるため、エージェント AI システムの長期メモリ機能を自然に実行します。組み込みの Agent Builder チャット エクスペリエンスにより、短期記憶とチャット履歴の追跡と管理も行えます。また、プラットフォーム全体が API ファーストであるため、エージェントのコンテキスト ウィンドウを圧倒する可能性のあるツールのコンテキスト出力を永続化するためのプラットフォームとして Elastic を利用する（そして後で参照できるようにする）ことは非常に簡単です。この手法は、コンテキスト エンジニアリングの分野では「<a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents#:~:text=Agents%20can%20assemble%20understanding%20layer%20by%20layer%2C%20maintaining%20only%20what%27s%20necessary%20in%20working%20memory%20and%20leveraging%20note%2Dtaking%20strategies%20for%20additional%20persistence">メモを取る</a>」と呼ばれることもあります。</p><p>同じ検索プラットフォームに短期記憶と長期記憶の両方を持つことで、多くの本質的なメリットが生まれます。チャット履歴と永続的なコンテキスト応答を、将来のチャットのやり取りに対する意味的影響要因の一部として使用したり、脅威分析を実行したり、頻繁に繰り返されるツール呼び出しから自動的に生成される永続的なデータ製品を作成したりできるようになることを想像してみてください。可能性は無限です。</p><h2>まとめ</h2><p>大規模言語モデルの出現により、コンテンツを一致させる方法や、データを調査するために使用する手法が変化しました。私たちは、人間が自らの疑問に答えるために調査、状況の考慮、論理的推論を行う現在の世界から、それらのステップがエージェント AI によって大部分が自動化される世界へと急速に移行しつつあります。生成された回答を信頼するには、エージェントが応答を生成する際に<em>最も関連性の高い</em> 情報（主観的関連性の要素を含む） をすべて 考慮したという保証が必要です。エージェント AI を信頼できるものにするための主な方法は、RAG とコンテキスト エンジニアリング技術を通じて追加のコンテキストを取得するツールを基盤化することですが、それらのツールが<em>最初の取得を</em>どのように実行するかが応答の精度に非常に重要になる場合があります。</p><p>Elastic Search AI プラットフォームは、ハイブリッド検索の柔軟性と利点に加えて、エージェント AI の精度、パフォーマンス、スケーラビリティを向上させるいくつかの組み込み機能を提供します。つまり、Elastic はコンテキスト エンジニアリングのさまざまな側面に対応する素晴らしいプラットフォームを実現します。検索プラットフォームを介したコンテキスト検索の標準化により、エージェントツールの操作がいくつかの面で簡素化されます。「速度を落としてスピードを上げる」という矛盾した表現と同様に、コンテキスト生成レイヤーの簡素化は、より高速で信頼性の高いエージェントAIを意味します。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-agentic-ai-accuracy</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-agentic-ai-accuracy</guid>
    <category><![CDATA[ハイブリッド検索]]></category>
    <category><![CDATA[エージェント型AI]]></category>
    <dc:creator><![CDATA[Woody Walton]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt42a203a316f0e22e/6a170932b339d58ebc769f5f/b82ff25242e4229cc20b218d9cc91c60cfd680bc-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 20 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[コンテキストのためのYou Know - パート1：ハイブリッド検索とコンテキストエンジニアリングの進化]]></title>
    <description><![CDATA[ハイブリッド検索とコンテキスト エンジニアリングが語彙の基礎からどのように進化し、次世代のエージェント AI ワークフローを可能にしたかを探ります。]]></description>
    <content:encoded><![CDATA[<h2>私たちの新しいエージェントAIの世界</h2><p>私たちの多くと同じように、私も AI の能力が進化している速さに興奮すると同時に驚いています。大規模言語モデル (LLM) とベクトル検索によって、キーワードで検索する必要がなくなったセマンティック革命が初めて実現しました。その後、LLM は、チャット インターフェースを使用して自然言語によるリクエストを応答に変換し、膨大な知識ベースから簡単に利用できる要約を抽出するなど、データと対話する新しい方法を教えてくれました。私たちは今（すでに！）「エージェント AI」ワークフローの形で自動化された LLM 駆動型ロジックが始まります。このワークフローは、受信したリクエストを意味的に理解し、実行する手順を推論し、利用可能なツールから選択してアクションを反復的に実行し、目標を達成します。</p><p>エージェント AI の可能性により、私たちは、主に「プロンプト エンジニアリング」を使用して生成 AI のインタラクションを形成することから、エージェント ツールが LLM が応答を生成する際に考慮する必要がある最も関連性の高い効率的な追加情報を取得できるようにする方法に重点を置くように進化することを余儀なくされています。つまり、「コンテキスト エンジニアリング」が次のフロンティアです。ハイブリッド検索は、関連するコンテキストを明らかにするための最も強力で柔軟な手段であり、Elastic の Search AI プラットフォームは、コンテキストエンジニアリングに役立つデータを活用するまったく新しい方法を実現します。この記事では、LLM が情報検索の世界をどのように変えたかを 2 つの角度から説明し、さらに、LLM がどのように連携してより優れた成果を上げることができるかについて説明します。カバーすべき領域はかなり広いです…</p><h2>パート1: LLMが検索に与えた影響</h2><p>まず、LLM が情報にアクセスし取得する方法をどのように変えたかという観点から始めましょう。</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) 機能を提供できるようになり、おそらく最も影響力があるのは、これまでのシーケンスの内容に基づいて、シーケンス内で最も可能性の高い次の用語を推測する機能です。推論は、生成 AI に人間に近いテキスト生成能力を与えるプロセスです。AI によって生成されたテキストは、LLM がトレーニング データ内で用語がどのように関連しているかを理解した上で生成され、また、リクエストのフレーズを使用して、用語が出現する可能性のあるさまざまなコンテキスト間の曖昧さを解消します。</p><p>生成 AI は魔法のようですが、LLM には品質と精度のエラー (一般に幻覚と呼ばれる) を引き起こす制限<em>があります</em>。幻覚は、LLM が真実に基づいた回答をするための情報にアクセスできない (または正しいコンテキストに誘導されない) 場合に発生します。そのため、LLM は役に立とうとして、代わりに自信に満ちたもっともらしい応答をでっち上げで生成します。原因の一部は、LLM が多様な情報の大規模な領域内で言語の使用法を学習する一方で、ある時点でトレーニングを停止する必要があるため、理解に適時性の要素があることです。つまり、モデルはトレーニングを停止した時点までの正確さしか認識できないということです。幻覚を引き起こすもう 1 つの要因は、モデルが非公開データ (パブリック インターネットで利用できないデータ) を認識しないことです。これは、データに特定の用語や命名法が含まれている場合に特に重要です。</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>ベクター データベースに関して考慮すべきもう 1 つの問題があります。ベクター データベースは検索エンジンではなく、データベースなのです。<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>」のワンシーンになります。そのシーンでは、交差する 6 つの座標点が目的地 (的) を示しますが、ユーザーの主観的な意図を表す出発点の座標である「7 番目のシンボル」を知らないとそこに到達できません。したがって、ベクトルの相対的なランキングが常に拡大し区別のない類似性の領域に基づくのではなく、表現構文と関連性スコアリングを通じてクエリの主観的な意図を考慮することによって、段階的な主観的関連性の<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 の場合はまったくメカニズムがない) 検索方法による場合です。また、非セマンティック ソースからの結果をユーザーのクエリに意味的に合わせるために再ランク付けを使用する場合もあります。再ランキングは第2段階の操作であり、何らかの<em>初期検索</em>方法（つまり、その後、検索クエリ (SQL、語彙検索、ベクトル検索) は、異なるスコアリング方法で並べ替えられます。</p><p>利用可能なアプローチはいくつかありますが、その中には<a href="https://www.elastic.co/docs/solutions/search/ranking/learning-to-rank-ltr">Learning-To-Rank (LTR)</a>や<a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">Reciprocal Rank Fusion (RRF)</a>などがあります。LTR は、検索結果の特徴 (いいね、評価、クリックなど) をキャプチャし、それらを使用して結果にスコアを付けたり、結果をブーストしたり、バイアスをかけたりするのに役立ちます。RRFは、異なるクエリモダリティから返された結果をマージするのに最適です（例：語彙データベース検索とベクトルデータベース検索を 1 つの結果リストにまとめます。Elastic は、<a href="https://www.elastic.co/search-labs/blog/linear-retriever-hybrid-search">線形再ランキング</a>方式を使用してスコアを調整する柔軟性も提供します。</p><p>ただし、最も効果的な再ランキング手法の 1 つは、<a href="https://www.elastic.co/docs/solutions/search/ranking/semantic-reranking">セマンティック再ランキング</a>です。これは、LLM のセマンティック理解を使用して、クエリと結果の両方のベクトル埋め込みを分析し、関連性スコアリング/再スコアリングを適用して最終的な順序を決定します。もちろん、セマンティック再ランク付けには再ランク付けモデルへの接続が必要です 。Elasticsearch は、組み込みモデル (<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><strong> などの外部でホストされるサービスを活用する 再ランク付け</strong> エンドポイントを作成できる推論<a href="https://www.elastic.co/docs/api/doc/elasticsearch/group/endpoint-inference"> API</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>主観的な意図は、結果の正確性を判断し、関連性を評価する上で重要な要素です。クエリを実行するユーザーの意図（柔軟な構文または第 2 段階の再ランク付けによって表現される）を考慮する機能がなければ、モデル空間内にすでにエンコードされている既存のコンテキストから選択することしかできません。このコンテキストの欠如に対処する一般的な方法は、<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 などの再ランキング手法を組み合わせています。単純なソートは主観的なランキング付けには明らかに不十分ですが、第 2 段階のセマンティック リランキング操作の基礎として使用した場合でも、第 1 段階の検索としての SQL では、セマンティック リランキングが「上位 k」のヒットに対してのみ実行される場合に問題が生じます。検索時に結果にスコアを付ける方法がなければ、<em>最善の</em>結果が実際に上位の結果にあるという保証はありません。</p></li><li><p><strong>ベクトルの類似性だけでは RAG には不十分です</strong>。これは実際には、一連の問題が複合的に絡み合った結果です。つまり、埋め込みの損失、単純なチャンク化方法、類似性の計算方法、そして主観的な意図という重要な要素が欠落しているという問題です。RAG の主な目標の 1 つは、生成 AI のインタラクションを客観的な真実に基づいて確立することです。これにより、幻覚を防ぐと同時に、トレーニング中に認識されなかったプライベート情報を LLM に通知します。RAG を通じて提供される追加のコンテキストを使用して、手近の質問に答えるために最も重要であることがわかっている接続と詳細を考慮するように LLM を制限および指示できます。そのためには、意味論的アプローチと語彙的アプローチの<em>両方</em>を使用する必要があります。</p></li><li><p><strong>ファイルベースの grep/regex RAG。</strong>エージェント AI の世界では、外部の検索プラットフォームではなく、RAG の grep と regex を介してローカル ファイルにアクセスする、大幅に拡大されたコンテキスト ウィンドウの使用を指摘する<a href="https://www.nicolasbustamante.com/p/the-rag-obituary-killed-by-agents">声</a>も上がっています。その考え方は、はるかに大きなコンテキスト ウィンドウを利用できることで、LLM が、関連情報を収集するために断片的な情報や複数の検索方法/プラットフォームに頼るのではなく、独自の思考空間内で概念的なつながりを構築できるようになるというものです。理論上は、文書全体があれば文書セグメントよりも完全な画像が得られるというのは本当ですが、これは小さなデータドメインでのみ機能します (または、たとえば、 <a href="https://en.wikipedia.org/wiki/Vibe_coding">vibecoding</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">Better Binary Quantization (BBQ)</a>や<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>などの検索構文を使用すると、従来の検索とセマンティック検索、フィルター、複数の再ランキング手法をすべて 1 つのリクエストで柔軟に組み合わせることができます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6ee9512910856ee3/6a17053fcdacbf2ea97d291e/f25180cb430414b99ae553d3b8eb161dbccea4d4-1920x1080.png" alt="ハイブリッド検索の仕組み" /><p>ハイブリッド検索の最大の利点の 1 つは、クエリで複数の異なるデータ タイプに同時に特殊な構文を使用できることです。これらのさまざまなクエリ構文は、結果の<em>検索</em>だけでなく、結果<em>の</em>フィルターや集計としても使用できます。たとえば、他の構文と頻繁に組み合わせられる最も一般的なクエリ タイプの 1 つは、<a href="https://www.elastic.co/docs/explore-analyze/geospatial-analysis">地理空間分析</a>です。特定のポイントから指定された距離内の地理座標を持つ結果をクエリしたり、地域別に結果の集計を要求したり、ゾーンへの出入りの動きを追跡してアラートを発する集計を実行したりできます。ハイブリッド検索を使用すると、構文を柔軟に組み合わせて、最も正確な方法で結果をターゲットにし、コンテキストに最も近いコンテンツを取得できます。</p><h2>休憩</h2><p>この最初の部分では、ベクトル検索によってデータの取得方法がどのように変化したかを説明し、LLM がデータの操作に使用するクエリ メカニズムにもたらした変化の基礎を説明します。LLM がコンテキストを失うことなく理解できるように、これを複数の部分に分割する必要があったと仮定します... ;-)<em>これがなぜ重要なのか</em>については<a href="https://www.elastic.co/search-labs/blog/context-engineering-llm-evolution-agentic-ai">、パート II: エージェント AI とコンテキスト エンジニアリングの必要性</a>で詳しく説明し、パート III ではハイブリッド検索について再び説明します。</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 と SigLIP-2 による山頂のマルチモーダル探索 ]]></title>
    <description><![CDATA[SigLIP-2 埋め込みと Elasticsearch kNN ベクトル検索を使用して、テキストから画像、画像から画像へのマルチモーダル検索を実装する方法を学びます。プロジェクトの焦点: エベレスト トレッキングでアマ ダブラム山の山頂の写真を探す。]]></description>
    <content:encoded><![CDATA[<p>写真アルバムを意味で検索したいと思ったことはありませんか?「青いジャケットを着てベンチに座っている写真を見せてください」「エベレストの写真を見せてください」「日本酒と寿司」などのクエリを試してみてください。コーヒー（またはお好みの飲み物）を飲みながら、読み続けてください。このブログでは、マルチモーダル ハイブリッド検索アプリケーションの構築方法を紹介します。マルチモーダルとは、アプリが単語だけでなく、テキスト、画像、音声などさまざまな種類の入力を理解して検索できることを意味します。ハイブリッドとは、キーワード マッチング、kNN ベクトル検索、ジオフェンシングなどの技術を組み合わせて、より鮮明な結果を提供することを意味します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdfa1ec1ccd450e94/6a17da751d1b8308ee93e344/0ec6bbb45013846b59ee00d2bf73ee2182ee7392-1920x1080.gif" alt="エベレスト登山のさまざまな山頂の写真のライブラリ。" /><p>これを実現するために、Google の SigLIP-2 を使用して画像とテキストの両方のベクトル埋め込みを生成し、Elasticsearch ベクトル データベースに保存します。クエリ時に、検索入力、テキストまたは画像を埋め込みに変換し、高速 kNN ベクトル検索を実行して結果を取得します。この設定により、効率的なテキストから画像への検索、画像から画像への検索が可能になります。Streamlit UI は、テキストベースの検索を行ってアルバムから一致する写真を検索して表示するだけでなく、アップロードされた画像から山頂を識別し、フォトアルバムでその山の他の写真を表示できるフロントエンドを提供することで、このプロジェクトを実現します。また、検索精度を向上させるために実行した手順や、実用的なヒントやコツについても説明します。さらに詳しく調べるために、 <a href="https://github.com/navneet83/multimodal-mountain-peak-search">GitHub リポジトリ</a>と<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb">Colab ノートブック</a>を提供しています。</p><h2>始まり</h2><p>このブログ投稿は、エベレストベースキャンプトレッキングで撮ったアマダブラム山の写真を全部見せてほしいと私に頼んだ10歳の子供からインスピレーションを受けたものです。写真アルバムを精査しながら、私は他のいくつかの山頂を特定するよう求められましたが、そのうちのいくつかは名前がわかりませんでした。</p><p>それで、これは楽しいコンピューター ビジョン プロジェクトになるかもしれないというアイデアが浮かびました。私たちが達成したかったこと:</p><ul><li><p>山頂の写真を名前で検索する</p></li><li><p>画像から山頂の名前を推測し、写真アルバムで似たような山頂を見つける</p></li><li><p>概念クエリを機能させる（<em>人</em>、<em>川</em>、<em>祈りの旗</em><em>など）</em></p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf82df9d7005fc3fe/6a17da78abe0f2e77bdfe8b9/e9d0d720a9b565d5b749bdc915068852d4f157ad-1200x1600.png" alt="アマ・ダブラム山 " /><h2>ドリームチームを結成: SigLIP-2、Elasticsearch、Streamlit</h2><p>これを機能させるには、テキスト (「Ama Dablam」) と画像 (私のアルバムの写真) の両方を、意味のある比較が可能なベクトル、つまり同じベクトル空間に変換する必要があることがすぐに明らかになりました。これを実行すると、検索は単に「最も近いものを見つける」だけになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f80b69a9d5bd28a/6a17da7a4b055ddd1243209e/20e6f8b7d4fa48414f407ec200adbe00ee28d517-1536x1024.png" alt="SigLIP-2、Elasticsearch、Streamlit のドリームチーム。" /><p>画像の埋め込みを生成するために、多言語<a href="https://huggingface.co/blog/vlms-2025">ビジョン言語エンコーダー</a>を使用します。これにより、山の写真と「Ama Dablam」などのフレーズが同じベクトル空間に配置されます。</p><p>最近 Google がリリースした<a href="https://huggingface.co/blog/siglip2"><strong>SigLIP-2</strong></a>はここによく当てはまります。タスク固有のトレーニング (<strong>ゼロショット</strong>設定) なしで埋め込みを生成でき、ラベルのない写真や異なる名前と言語を持つピークなど、私たちのユースケースに適しています。テキストと画像のマッチングがトレーニングされているため、クエリ言語やスペルが異なっていても、トレッキング中の山の写真と短いテキストプロンプトは埋め込みとして近いものになります。</p><p>SigLIP-2 は、品質と速度のバランスが優れており、複数の入力解像度をサポートし、CPU と GPU の両方で実行されます。SigLIP-2 は、オリジナルの CLIP などの以前のモデルと比較して、屋外での写真撮影に対してより堅牢になるように設計されています。私たちのテストでは、SigLIP-2 は一貫して信頼できる結果を生成しました。また、サポートも非常に充実しており、このプロジェクトに最適な選択肢となっています。</p><p>次に、埋め込みとパワー検索を保存するためのベクトル データベースが必要です。画像埋め込みに対するコサイン kNN 検索をサポートするだけでなく、単一のクエリでジオフェンスとテキスト フィルターを適用することもサポートする必要があります。Elasticsearch はここで最適です。ベクトル (dense_vector フィールドの HNSW kNN) を非常に適切に処理し、テキスト、ベクトル、地理クエリを組み合わせたハイブリッド検索をサポートし、フィルタリングと並べ替えをすぐに使用できます。また、水平方向にも拡張できるため、数枚の写真から数千枚の写真まで簡単に拡張できます。公式の<a href="https://www.elastic.co/docs/reference/elasticsearch/clients/python">Elasticsearch Python クライアントは</a>、配管をシンプルに保ち、プロジェクトときれいに統合します。最後に、検索クエリを入力して結果を表示できる軽量のフロントエンドが必要です。簡単な Python ベースのデモには、Streamlit が最適です。ファイルのアップロード、レスポンシブな画像グリッド、並べ替えとジオフェンシングのためのドロップダウン メニューなど、必要な基本的な機能を提供します。簡単にクローンを作成してローカルで実行でき、Colab ノートブックでも動作します。</p><h2>実装</h2><h3>Elasticsearchのインデックス設計とインデックス戦略</h3><p>このプロジェクトでは、 <code>peaks_catalog</code>と<code>photos</code>の 2 つのインデックスを使用します。</p><h4>Peaks_catalogインデックス</h4><p>この索引は、エベレストベースキャンプトレッキング中に見える主要な山頂のコンパクトなカタログとして機能します。このインデックス内の各ドキュメントは、エベレスト山などの単一の山頂に対応しています。各山頂ドキュメントには、名前/エイリアス、オプションの緯度経度座標、および SigLIP-2 テキストプロンプト (+ オプションの参照画像) を組み合わせて構築された単一のプロトタイプ ベクトルが保存されます。</p><p><strong>インデックスマッピング:</strong></p><p>分野</p><p>タイプ</p><p>例</p><p>目的/注意事項</p><p>ベクトル/インデックス</p><p>id</p><p>キーワード</p><p>アマ・ダブラム</p><p>安定したスラッグ/ID</p><p>—</p><p>名前</p><p>テキスト + キーワードサブフィールド</p><p>["アマ・ダブラム"、"アマダブラム"]</p><p>エイリアス/多言語名; 正確なフィルターのためのnames.raw</p><p>—</p><p>ラトロン</p><p>ジオポイント</p><p>{"lat":27.8617,"lon":86.8614}</p><p>緯度/経度の組み合わせによるピーク GPS 座標 (オプション)</p><p>—</p><p>高度m</p><p>整数</p><p>6812</p><p>標高（オプション）</p><p>—</p><p>テキスト埋め込み</p><p>dense_vector</p><p>768</p><p>このピークのブレンドプロトタイプ（プロンプトとオプションで1～3枚の参照画像）</p><p>index:true、類似度:"cosine"、index_options: {type:"hnsw", m:16, ef_construction:128}</p><p>このインデックスは主に、画像から山頂を識別するなど、画像間の検索に使用されます。このインデックスは、テキストから画像への検索結果を強化するためにも使用されます。</p><p>要約すると、 <code>peaks_catalog</code>は「これは何の山ですか？」という質問を焦点を絞った最近傍問題に変換し、概念的理解を画像データの複雑さから効果的に分離します。</p><p><strong>peaks_catalog インデックスのインデックス戦略:</strong> EBC トレッキング中に見える最も目立つ山頂のリストを作成することから始めます。各山頂の地理的位置、名前、同義語、標高を<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/data/peaks.yaml">yaml ファイル</a>に保存します。次のステップは、各ピークの<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L351">埋め込みを生成し</a>、それを<code>text_embed</code>フィールドに保存することです。堅牢な埋め込みを生成するために、次の手法を使用します。</p><ul><li><p>以下を使用してテキスト プロトタイプを作成します。</p><ul><li><p>山の名前</p></li><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L301">プロンプト アンサンブル</a>(複数の異なるプロンプトを使用して同じ質問に答える)、例:</p><ul><li><p>「ネパール、ヒマラヤ山脈の山頂{name}の自然写真」</p></li><li><p>「クンブ地域の{name}マーク的な山頂、高山の風景」</p></li><li><p>「 {name}山頂、雪、岩だらけの尾根」</p></li></ul></li><li><p>オプションの<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L333">反概念</a>（SigLIP-2 に一致しないものを指示する）: 「絵画、イラスト、ポスター、地図、ロゴ」の小さなベクトルを減算して、実際の写真に偏向させます。</p></li></ul></li><li><p>ピークの参照画像が提供されている場合は、オプションで<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L388C13-L388C29">画像プロトタイプを作成します</a>。</p></li></ul><p>次に、<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L392">テキストと画像のプロトタイプをブレンドして</a>、最終的な埋め込みを生成します。最後に、ドキュメントはすべての必須フィールドで<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L396">インデックス化され</a>ます。</p>def l2norm(v: np.ndarray) -&gt; np.ndarray:
    return v / (np.linalg.norm(v) + 1e-12)
def compute_blended_peak_vec(
        emb: Siglip2,
        names: List[str],
        peak_id: str,
        peaks_images_root: str,
        alpha_text: float = 0.5,
        max_images: int = 3,
) -&gt; Tuple[np.ndarray, int, int, List[str]]:
    """
    Build blended vector for a single peak.

    Returns:
      vec           : np.ndarray (L2-normalized)
      found_count   : number of reference images discovered
      used_count    : number of references used (&lt;= max_images)
      used_filenames: list of filenames used (for logging)
    """
    # 1) TEXT vector
    tv = embed_text_blend(emb, names)

    # 2) IMAGE refs: prefer folder by id; fallback to slug of the primary name
    root = Path(peaks_images_root)
    candidates = [root / peak_id]
    if names:
        candidates.append(root / slugify(names[0]))

    all_refs: List[Path] = []
    for c in candidates:
        if c.exists() and c.is_dir():
            all_refs = list_ref_images(c)
            if all_refs:
                break

    found = len(all_refs)
    used_list = all_refs[:max_images] if (max_images and found &gt; max_images) else all_refs
    used = len(used_list)

    img_v = embed_image_mean(emb, used_list) if used_list else None

    # 3) Blend TEXT and IMAGE vectors, clamp alpha to [0,1]
    a = max(0.0, min(1.0, float(alpha_text)))
    vec = l2norm(tv if img_v is None else (a * tv + (1.0 - a) * img_v)).astype("float32")
    return vec, found, used, [p.name for p in used_list]<p><code>peaks_catalog</code>インデックスからのサンプル ドキュメント:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1219f5d0e39b512c/6a17da7c57726263161bcace/bc05fbd0c4f8d721d5170c28a3884a9eda80bb7d-1210x1132.png" alt="Elasticsearch の peaks_catalog インデックスからのサンプル ドキュメント。" /><h4>写真インデックス</h4><p>このプライマリ インデックスには、アルバム内のすべての写真に関する詳細情報が保存されます。各ドキュメントは 1 枚の写真を表し、次の情報が含まれています。</p><ul><li><p>フォトアルバム内の写真への相対パス。これを使用して、一致する画像を表示したり、検索 UI に画像を読み込んだりできます。</p></li><li><p>写真のGPSと時間情報。</p></li><li><p>SigLIP-2 によって生成された画像エンコーディング用の密なベクトル。</p></li><li><p><code>predicted_peaks</code> ピーク名でフィルタリングできます。<strong>インデックスマッピング</strong></p></li></ul><p>分野</p><p>タイプ</p><p>例</p><p>目的/注意事項</p><p>ベクター / インデックス</p><p>パス</p><p>キーワード</p><p>データ/画像/IMG_1234.HEIC</p><p>UI でサムネイル/フル画像を開く方法</p><p>—</p><p>クリップ画像</p><p>dense_vector</p><p>768</p><p>SigLIP-2画像埋め込み</p><p>index:true、類似度:"cosine"、index_options: {type:"hnsw", m:16, ef_construction:128}</p><p>予測ピーク</p><p>キーワード</p><p>["ama-dablam","pumori"]</p><p>インデックス時の上位Kの推測（安価なUXフィルター/ファセット）</p><p>—</p><p>GPS</p><p>ジオポイント</p><p>{"lat":27.96,"lon":86.83}</p><p>地理フィルターを有効にする</p><p>—</p><p>ショット時間</p><p>date</p><p>2023年10月18日09:41:00Z</p><p>撮影時間: 並べ替え/フィルター</p><p>—</p><p><strong>写真インデックスのインデックス戦略:</strong>アルバム内の写真ごとに、次の操作を実行します。
<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L526">画像メタデータから</a>画像<code>shot_time</code>と<code>gps</code>情報を抽出します。</p><ul><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L511">SigLIP-2 画像埋め込み</a>: 画像をモデルに渡し、ベクトルを L2 正規化します。埋め込みを<code>clip_image</code>フィールドに保存します。</p></li><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L519">ピークを予測し</a>、 <code>predicted_peaks</code>フィールドに保存します。これを行うには、まず前の手順で生成された写真の画像ベクトルを取得し、次に<code>peaks_catalog</code>インデックスの text_embed フィールドに対して簡単な kNN 検索を実行します。上位 3 ～ 4 つのピークを保持し、残りは無視します。</p></li><li><p>画像名とパスの<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L509">ハッシュ</a>を実行して<code>_id</code>フィールドを計算します。これにより、複数回実行した後に重複が発生しなくなります。</p></li></ul><p>写真のすべてのフィールドを決定したら、<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L530">一括</a>インデックスを使用して写真ドキュメントを一括でインデックスします。</p>def bulk_index_photos(
        es: Elasticsearch,
        images_root: str,
        photos_index: str = "photos",
        peaks_index: str = "peaks_catalog",
        topk_predicted: int = 5,
        batch_size: int = 200,
        refresh: str = "false",
) -&gt; None:
    """Walk a folder of images, embed + enrich, and bulk index to Elasticsearch."""
    root = Path(images_root)
    if not root.exists():
        raise SystemExit(f"Images root not found: {images_root}")

    emb = Siglip2()
    batch: List[Dict[str, Any]] = []
    n_indexed = 0

    for p in iter_images(root):
        rel = relpath_within(root, p)
        _id = id_for_path(rel)

        # 1) Image embedding (and reuse it for predicted_peaks)
        try:
            with Image.open(p) as im:
                ivec = emb.image_vec(im.convert("RGB")).astype("float32")
        except (UnidentifiedImageError, OSError) as e:
            print(f"[skip] {rel} — cannot embed: {e}")
            continue

        # 2) Predict top-k peak names
        try:
            top_names = predict_peaks(es, ivec.tolist(), peaks_index=peaks_index, k=topk_predicted)
        except Exception as e:
            print(f"[warn] predict_peaks failed for {rel}: {e}")
            top_names = []

        # 3) EXIF enrichment (safe)
        gps = get_gps_decimal(str(p))
        shot = get_shot_time(str(p))

        # 4) Build doc and stage for bulk
        doc = {"path": rel, "clip_image": ivec.tolist(), "predicted_peaks": top_names}
        if gps:
            doc["gps"] = gps
        if shot:
            doc["shot_time"] = shot

        batch.append(
            {"_op_type": "index", "_index": photos_index, "_id": _id, "_source": doc}
        )

        # 5) Periodic flush
        if len(batch) &gt;= batch_size:
            helpers.bulk(es, batch, refresh=refresh)
            n_indexed += len(batch)
            print(f"[photos] indexed {n_indexed} (last: {rel})")
            batch.clear()

    # Final flush
    if batch:
        helpers.bulk(es, batch, refresh=refresh)
        n_indexed += len(batch)
        print(f"[photos] indexed {n_indexed} total.")

    print("[done] photos indexing")<p>写真インデックスからのサンプルドキュメント:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt744b7e6326937cfc/6a17da7e6df731d3040a0da8/1dc1406ac2a97440b6804838795b3c2205c4c6b2-1080x1234.png" alt="Elasticsearch の写真インデックスからのサンプル ドキュメント。" /><p>要約すると、写真のインデックスは、アルバム内のすべての写真を格納する、高速でフィルタリング可能な kNN 対応のストアです。マッピングは意図的に最小限に抑えられており、すばやく取得し、きれいに表示し、結果を空間と時間で分割するのに十分な構造になっています。このインデックスは、両方の検索ユースケースに対応します。両方のインデックスを作成するための Python スクリプトは<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/create_indices.py">ここに</a>あります。</p><p>以下の Kibana マップの視覚化では、写真アルバムのドキュメントが緑のドットで表示され、 <code>peaks_catalog</code>インデックスの山頂が赤い三角形で表示されています。緑のドットはエベレスト ベース キャンプのトレッキング トレイルとぴったり一致しています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb5bf016e8d9c3e84/6a17da80be608681f10045e6/1c75d0ed0ce53d28a94bf2f47a354e25581d2baf-1600x1402.png" alt="Kibana マップの視覚化では、写真アルバムのドキュメントが緑のドットで表示され、peaks_catalog インデックスの山頂が赤い三角形で表示されています。緑のドットは、エベレスト ベース キャンプのトレッキング トレイルとよく揃っています。" /><h2>検索ユースケース</h2><p><strong>名前による検索（テキストから画像へ）：</strong>この機能により、ユーザーはテキスト クエリを使用して山頂の写真（さらには「祈りの旗」のような抽象的な概念）を見つけることができます。これを実現するために、テキスト入力は SigLIP-2 を使用して<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L87C5-L87C20">テキスト ベクトルに変換されます</a>。堅牢なテキストベクトル生成のために、 インデックスでテキスト埋め込みを作成するために使用したのと同じ戦略を採用しています。つまり、テキスト入力を小さな<code>peaks_catalog</code><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L100"> プロンプトアンサンブル</a> と<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L104"> 組み合わせ</a><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L103"> 、小さな 反概念ベクトル</a> を減算し、<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L104"> L2正規化</a> を適用して最終的なクエリベクトルを生成します。次に、 <code>photos.clip_image</code>フィールドで kNN<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L140">クエリを</a>実行し、コサイン類似度に基づいて一致する上位のピークを取得して、最も近い画像を見つけます。オプションとして、クエリの一部として地理および日付<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L152">フィルター</a>、および/または<code>photos.predicted_peaks</code>用語フィルターを適用することで、検索結果の関連性を高めることができます (以下のクエリ例を参照)。これにより、トレッキング中に実際には見えていない、似たような山頂を除外することができます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9bb9abf5ce64fcbb/6a17da81e8fbce20db3a17da/b5fac28ffdbedb820505365ca07df125cd01b939-946x370.png" alt="Elasticsearch でのマルチモーダルな名前による検索 (テキストから画像への検索) の仕組み。" /><p><strong>ジオフィルターを使用した Elasticsearch クエリ:</strong></p>POST photos/_search
{
  "knn": {
    "field": "clip_image",
    "query_vector": [ ... ],
    "k": 60,
    "num_candidates": 2000
  },
  "query": {
    "bool": {
      "filter": [
        { "geo_bounding_box": { "gps": { "top_left": "...", "bottom_right": "..." } } }
      ]
    }
  },
  "_source": ["path","predicted_peaks","gps","shot_time"]
}

Response (first two documents):
{
 "hits": {
   "total": {
     "value": 56,
     "relation": "eq"
   },
   "max_score": 0.5779596,
   "hits": [
     {
       "_index": "photos",
       "_id": "d01da3a1141981486c3493f6053c79e92a788463",
       "_score": 0.5779596,
       "_source": {
         "path": "IMG_2738.HEIC",
         "predicted_peaks": [
           "Pumori",
           "Kyajo Ri",
           "Khumbila",
           "Nangkartshang",
           "Kongde Ri"
         ],
         "gps": {
           "lat": 27.97116388888889,
           "lon": 86.82331111111111
         },
         "shot_time": "2023-11-03T08:07:13"
       }
     },
     {
       "_index": "photos",
       "_id": "c79d251f07adc5efaedc53561110a7fd78e23914",
       "_score": 0.5766071,
       "_source": {
         "path": "IMG_2761.HEIC",
         "predicted_peaks": [
           "Kyajo Ri",
           "Makalu",
           "Baruntse",
           "Cho Oyu",
           "Khumbila"
         ],
         "gps": {
           "lat": 27.975558333333332,
           "lon": 86.82515
         },
         "shot_time": "2023-11-03T08:51:08"
       }
     }
}<p><strong>画像による検索 (画像間):</strong>この機能を使用すると、写真内の山を識別し、写真アルバム内で同じ山の他の画像を見つけることができます。画像がアップロードされると、SigLIP-2 画像エンコーダーによって処理され、<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L228">画像ベクトル</a>が生成されます。次に、 <code>peaks_catalog.text_embed</code>フィールドで<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L234">kNN 検索を</a>実行し、最も一致するピーク名を特定します。次に、これらの一致するピーク名から<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L257">テキスト ベクトルが生成され</a>、写真インデックスに対して別の<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L263">kNN 検索が</a>実行され、対応する写真が検索されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltab9d16333e2a9e69/6a17da827f6f155448c099cc/3a3d5635bee7a222b95529dd7f9fbee016381610-1226x550.png" alt="Elasticsearch で画像によるマルチモーダル検索 (画像間検索) がどのように機能するかを説明します。" /><p><strong>Elasticsearchクエリ:</strong></p><p>ステップ1: 一致するピーク名を見つける</p>GET peaks_catalog/_search
{
 "knn": {
   "field": "text_embed",
   "query_vector": [...image-vector... ],
   "k": 3,
   "num_candidates": 500
 },
 "_source": [
   "id",
   "names",
   "latlon",
   "text_embed"
 ]
}


Response (first two documents):
{
 "took": 2,
 "timed_out": false,
 "_shards": {
   "total": 1,
   "successful": 1,
   "skipped": 0,
   "failed": 0
 },
 "hits": {
   "total": {
     "value": 3,
     "relation": "eq"
   },
   "max_score": 0.58039916,
   "hits": [
     {
       "_index": "peaks_catalog",
       "_id": "pumori",
       "_score": 0.58039916,
       "_source": {
         "id": "pumori",
         "names": [
           "Pumori",
           "Pumo Ri"
         ],
         "latlon": {
           "lat": 28.01472,
           "lon": 86.82806
         },
         "text_embed": [
                  ... embeddings...
         ]
       }
     },
     {
       "_index": "peaks_catalog",
       "_id": "kyajo-ri",
       "_score": 0.57942784,
       "_source": {
         "id": "kyajo-ri",
         "names": [
           "Kyajo Ri",
           "Kyazo Ri"
         ],
         "latlon": {
           "lat": 27.909167,
           "lon": 86.673611
         },
         "text_embed": [
           ... embeddings...
         ]
       }
     }
   ]
 }
}<p>ステップ 2: <code>photos</code>インデックスで検索を実行し、一致する画像を見つけます (テキストから画像への検索ユース ケースに示されているのと同じクエリ)。</p>POST photos/_search
{
 "knn": {
   "field": "clip_image",
   "query_vector": [ ...image-vector... ],
   "k": 30,
   "num_candidates": 2000
 },
 "_source": [
   "path",
   "gps",
   "shot_time",
   "predicted_peaks",
   "clip_image"
 ],
 "query": {
   "bool": {
     "filter": [
       {
         "term": {
           "predicted_peaks": "Pumori"
         }
       }
     ]
   }
 }
}


Response (first two documents):
{
 "hits": {
   "total": {
     "value": 56,
     "relation": "eq"
   },
   "max_score": 0.5779596,
   "hits": [
     {
       "_index": "photos",
       "_id": "d01da3a1141981486c3493f6053c79e92a788463",
       "_score": 0.5779596,
       "_source": {
         "path": "IMG_2738.HEIC",
         "predicted_peaks": [
           "Pumori",
           "Kyajo Ri",
           "Khumbila",
           "Nangkartshang",
           "Kongde Ri"
         ],
         "gps": {
           "lat": 27.97116388888889,
           "lon": 86.82331111111111
         },
         "shot_time": "2023-11-03T08:07:13"
       }
     },
     {
       "_index": "photos",
       "_id": "c79d251f07adc5efaedc53561110a7fd78e23914",
       "_score": 0.5766071,
       "_source": {
         "path": "IMG_2761.HEIC",
         "predicted_peaks": [
           "Kyajo Ri",
           "Makalu",
           "Baruntse",
           "Cho Oyu",
           "Khumbila"
         ],
         "gps": {
           "lat": 27.975558333333332,
           "lon": 86.82515
         },
         "shot_time": "2023-11-03T08:51:08"
       }
     }
}<h2>流線型のUI</h2><p>すべてをまとめるために、両方の検索ユースケースを実行できるシンプルな Streamlit UI を作成しました。左側のレールには、チェックボックスとミニマップ/ジオフィルターが付いた、スクロール可能なピークのリスト（ <code>photos.predicted_peaks</code>から集約）が表示されます。上部には、<strong>名前による検索</strong>ボックスと<strong>写真アップロードからの識別</strong>ボタンがあります。中央のペインには、kNN スコア、予測ピーク バッジ、キャプチャ時間を表示するレスポンシブなサムネイル グリッドがあります。各画像には、フル解像度のプレビューを表示するための<strong>画像表示</strong>ボタンが含まれています。</p><p><strong>画像をアップロードして検索:</strong>ピークを予測し、写真アルバムから一致するピークを見つけます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd1fb2b0304a310d2/6a17da8425daab7cda08a0fa/dca540cbf5279e6d6102c5a0c0351ddd4ac91cda-1600x1112.png" alt="アマダブラム山の山頂をテキストから画像、画像から画像の両方でマルチモーダル検索できる、シンプルで合理化された UI。" /><p><strong>テキスト検索</strong>: アルバム内の一致するピークをテキストから検索します</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt496c1ae8f7886320/6a17da86abe0f2da48dfe8bd/b1e8618db746cd49ea4962d3dc73031387b975dd-1600x1166.png" alt="山頂ライブラリでテキスト検索を使用してエベレスト山の山頂を検索する方法。" /><h2>まとめ</h2><p><em><strong>アマ・ダブラムの</strong></em> <em>写真</em><em> だけ見せてもらえませんか ？ というところから始まりました。</em>小規模で実用的な<strong>マルチモーダル検索</strong>システムになりました。私たちは生のトレッキング写真を撮影し、それを<strong>SigLIP-2 埋め込み</strong>に変換し、 <strong>Elasticsearch</strong>を使用してベクトルに対して高速<strong>kNN を</strong>実行し、さらに単純な地理/時間フィルターを使用して<em>意味</em>によって適切な画像を浮かび上がらせました。途中で、私たちは 2 つのインデックス、つまり混合プロトタイプの小さな<code>peaks_catalog</code> (識別用) と、画像ベクトルと EXIF のスケーラブルな<code>photos</code>インデックス (検索用) で関心を分離しました。実用的かつ再現性があり、拡張も簡単です。</p><p>調整したい場合は、いくつかの設定を試すことができます。</p><ul><li><p><strong>クエリ時間の設定:</strong> <code>k</code> (返す近隣の数) と<code>num_candidates</code> (最終スコアリングの前に検索する範囲)。これらの設定については、<a href="https://www.elastic.co/search-labs/blog/elasticsearch-knn-and-num-candidates-strategies">こちらの</a>ブログで説明されています。</p></li><li><p><strong>インデックス時間の設定:</strong> <code>m</code> (グラフの接続性) および<code>ef_construction</code> (ビルド時間の精度とメモリ)。クエリの場合は、 <code>ef_search</code>も試してください。値が大きいほど、通常は、レイテンシを多少トレードオフして、リコール率が向上します。これらの設定の詳細については、<a href="https://www.elastic.co/search-labs/blog/hnsw-graph">このブログ</a>を参照してください。</p></li></ul><p>今後、<strong>マルチモーダル</strong>および<strong>多言語</strong>検索用のネイティブモデル/リランカーがElasticエコシステムにまもなく導入される予定です。これにより、画像/テキスト検索とハイブリッドランキングがさらに強化されるはずです<a href="https://ir.elastic.co/news/news-details/2025/Elastic-Completes-Acquisition-of-Jina-AI-a-Leader-in-Frontier-Models-for-Multimodal-and-Multilingual-Search/default.aspx?utm_source=chatgpt.com">。ir.elastic.co+1</a></p><p>これを自分で試してみたい場合は:</p><ul><li><p><strong>GitHub リポジトリ:</strong> <a href="https://github.com/navneet83/multimodal-mountain-peak-search"><em>https://github.com/navneet83/multimodal-mountain-peak-search</em></a></p></li><li><p><strong>Colab クイックスタート:</strong> <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb">https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb</a></p></li></ul><p>これで私たちの旅は終わり、帰る時間になりました。これが役に立つことを願っています。これを壊した場合（または改善した場合）、何を変更したかをお聞かせください。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdce2fff1569d2a8b/6a17da894b055dd1f24320a2/d324d1e1472f1bfbd8f25747f57bdeeb9c7f16b2-1600x1200.png" alt="" />]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/multimodal-search-siglip-2-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/multimodal-search-siglip-2-elasticsearch</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[ハイブリッド検索]]></category>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Python]]></category>
    <dc:creator><![CDATA[Navneet Kumar]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltccb66279debb05f9/6a17da8b63baffe228741b15/ffcf93358a7c5dadcea82faf3de460bf060d003c-1600x1200.png" length="0" type="image/png"/>
    <pubDate>Tue, 04 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch向けAgentic AIツールの改善実験]]></title>
    <description><![CDATA[スケーラブルな RAG 最適化のために線形リトリーバー、ハイブリッド検索、および semantic_text を組み合わせることで、反復的な実験を通じて Elasticsearch の AI エージェント ワークフローをどのように改善したかを学びます。]]></description>
    <content:encoded><![CDATA[<p>最近の他社と同様に、Elastic ではチャット、エージェント、RAG に全力を注いでいます。検索部門では最近、エージェント ビルダーとツール レジストリに取り組んでおり、その目的は、Elasticsearch 内のデータとの「チャット」を簡単に行えるようにすることです。</p><p>この取り組みの「全体像」について詳しくは、<a href="https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder">ブログ「Elasticsearch を使用した AI エージェントワークフローの構築」</a>をお読みください。より実践的な入門書として<a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">、「初めての Elastic エージェント: 単一のクエリから AI を活用したチャットまで」もご覧ください</a>。</p><p>ただし、このブログでは、チャットを開始したときに最初に起こることの 1 つに焦点を絞り、最近行った改善点のいくつかについて説明します。</p><h2>ここで何が起こっているのですか?</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1331b1043612efe3/6a17f115505ac3dc41ad8c3c/25a24055a166d7d6ba81d80aa35cb97163662e23-1600x443.png" alt="" /><p>Elasticsearch データとチャットする場合、デフォルトの AI エージェントが次の標準フローを実行します。</p><ol><li><p>プロンプトを検査します。</p></li><li><p>どのインデックスにそのプロンプトの回答が含まれている可能性があるかを特定します。</p></li><li><p>プロンプトに基づいて、そのインデックスのクエリを生成します。</p></li><li><p>そのクエリでそのインデックスを検索します。</p></li><li><p>結果を統合します。</p></li><li><p>結果はプロンプトに対応できますか?はいの場合は応答してください。そうでない場合は、別の方法を試しながら繰り返します。</p></li></ol><p>これはあまり目新しいものではないはずです。これは単に Retrieval Augmented Generation (RAG) です。そして当然のことですが、応答の質は最初の検索結果の関連性に大きく左右されます。そのため、応答品質の向上に取り組む中で、ステップ 3 で生成してステップ 4 で実行するクエリに細心の注意を払ってきました。そして、私たちは興味深いパターンに気づきました。</p><p>多くの場合、最初の応答が「悪い」場合、それは実行したクエリが悪かったからではありません。クエリを実行するために<em>間違ったインデックスを選択した</em>ためです。通常、ステップ 3 と 4 は問題ではありません。問題はステップ 2 です。</p><h2>私たちは何をしていたのでしょうか?</h2><p>当初の実装はシンプルでした。私たちは、 <code>_cat/indices</code>を効果的に実行して利用可能なすべてのインデックスをリストし、これらのインデックスのうちどれがユーザーのメッセージ/質問/プロンプトに最も一致するかを LLM に識別させるツール (index_explorer と呼ばれる) を構築しました。この<a href="https://github.com/elastic/kibana/blob/0cc78184957fcd12110dabae50353392ea937508/x-pack/platform/packages/shared/onechat/onechat-genai-utils/tools/index_explorer.ts#L98-L113">オリジナルの実装はここで</a>見ることができます。</p>You are an AI assistant for the Elasticsearch company.
based on a natural language query from the user, your task is to select up to ${limit} most relevant indices from a list of indices.

*The natural language query is:* ${nlQuery}

*List of indices:*
${indices.map((index) =&gt; `- ${index.index}`).join('\n')}

Based on those information, please return most relevant indices with your reasoning.
Remember, you should select at maximum ${limit} indices.<p>これはどれくらいうまく機能しましたか?よく分かりませんでした！うまく機能して<em>いない</em>明確な例はありましたが、私たちにとっての本当の最初の課題は、現状を定量化することでした。</p><h2>ベースラインの確立</h2><h3>それはデータから始まる</h3><p>私たちが必要としていたのは、ユーザーのプロンプトと既存のインデックス セットに基づいて適切なインデックスを選択するツールの有効性を測定するためのゴールデン データ セットでした。そして、手元にそのようなデータセットがなかったので、それを生成しました。</p><p>謝辞: これは「ベスト プラクティス」ではないことは承知しています。しかし、時には、自転車を捨てるよりも前進する方が良いこともあります。<a href="https://www.elastic.co/about/our-source-code#progress-perfection">進歩、シンプルな完璧さ</a>。</p><p><a href="https://gist.github.com/seanstory/a08db2e149897da656db3a1ca72e17ac">このプロンプト</a>を使用して、いくつかの異なるドメインのシードのインデックスを生成しました。次に、生成されたドメインごとに、<a href="https://gist.github.com/seanstory/a280a85d067e61bfeb5911bf2654e6e2">このプロンプト</a>を使用してさらにいくつかのインデックスを生成しました (ここでの目標は、ハードネガティブと分類が難しい例を使用して LLM に混乱を引き起こすことです)。次に、生成された各インデックスとその説明を手動で編集しました。最後に、<a href="https://gist.github.com/seanstory/44291b666c05a383136f6e36bb9106fa">このプロンプト</a>を使用してテストクエリを生成しました。次のようなサンプルデータが得られました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1bd9cd78154195e3/6a17f117dbb4fff7b5fb57d2/9d96d87e286eddbc012402b1ecccd57419a99253-1600x782.png" alt="" /><p>そして次のようなテストケース:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltadf30a0aeafd56ef/6a17f1192f4a5c160ffa89eb/4c2e9ad941d98d7e66033bbc08c9b8060ec19097-1600x797.png" alt="" /><h3>テストハーネスの作成</h3><p>ここからのプロセスは非常に簡単でした。次の機能を備えたツールをスクリプト化します。</p><ol><li><p>ターゲット Elasticsearch クラスターを使用してクリーンな状態を確立します。</p></li><li><p>ターゲット データセットで定義されているすべてのインデックスを作成します。</p></li><li><p>各テスト シナリオに対して、 i <code>ndex_explorer</code>ツールを実行します (便利なことに、<a href="https://www.elastic.co/docs/api/doc/kibana/operation/operation-post-agent-builder-tools-execute">実行ツール API が</a>あります)。</p></li><li><p>結果のインデックスを予想インデックスと比較し、結果を取得します。</p></li><li><p>すべてのテストシナリオを終了したら、結果を表にまとめます。</p></li></ol><h3>調査によると…</h3><p>当初の結果は予想通り平凡なものでした。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt73367741359e258d/6a17f11a505ac39749ad8c40/9c10679bcd6291edfa2a9ba42e7dd922aa483f0b-1216x806.png" alt="" /><p>全体として、正しいインデックスを識別する精度は 77.14% です。これは、すべてのインデックスに意味的に意味のある適切な名前が付けられている「最良のケース」のシナリオでした。`PUT test2/_doc/foo {...} ` を実行したことがある人なら、インデックスの名前が必ずしも意味のあるものではないことはご存じでしょう。</p><p>つまり、ベースラインがあり、改善の余地が十分にあることがわかります。さあ、科学の時間です！🧪</p><h2>実験</h2><h3>仮説1: マッピングは役立つ</h3><p>ここでの目標は、元のプロンプトに関連するデータが含まれるインデックスを識別することです。インデックスに含まれるデータを最もよく表す部分は、インデックスの<em>マッピング</em>です。インデックスの内容のサンプルを取得しなくても、インデックスに double 型の価格フィールドがあることがわかれば、そのデータは販売されるものを表していることがわかります。テキストタイプの著者フィールドは、何らかの非構造化言語データを意味します。これら 2 つを組み合わせると、データが書籍、物語、詩であることを意味する可能性があります。インデックスのプロパティを知るだけで、意味上の手がかりを数多く得ることができます。そこでローカルブランチで`.index_explorer`を調整しましたインデックスの完全なマッピング (およびその名前) を LLM に送信して決定を下すツール。 </p><p>結果（Kibana ログより）:</p>[2025-09-05T11:01:21.552-05:00][ERROR][plugins.onechat] Error: Error calling connector: event: error
data: {"error":{"code":"request_entity_too_large","message":"Received a content too large status code for request from inference entity id [.rainbow-sprinkles-elastic] status [413]","type":"error"}}


    at createInferenceProviderError (errors.ts:90:10)
    at convertUpstreamError (convert_upstream_error.ts:39:38)
    at handle_connector_response.ts:26:33
    at Observable.init [as _subscribe] (/Users/seanstory/Desktop/Dev/kibana/node_modules/rxjs/src/internal/observable/throwError.ts:123:68)...<p>ツールの最初の作成者はこれを予期していました。インデックスのマッピングは情報の宝庫ですが、非常に冗長な JSON ブロックでもあります。そして、多数のインデックス (評価データセットでは 20 個が定義されています) を比較する現実的なシナリオでは、これらの JSON BLOB が加算されます。したがって、LLM に、すべてのオプションのインデックス名だけでなく、それぞれの完全なマッピングほどではなく、決定のためのより多くのコンテキストを提供したいと考えています。</p><h3>仮説2: 妥協案としての「フラット化された」マッピング（フィールドリスト）</h3><p>私たちは、インデックス作成者が意味的に意味のあるインデックス名を使用するという前提から始めました。その仮定をフィールド名にも拡張するとどうなるでしょうか?前回の実験は、JSON のマッピングに大量の煩わしいメタデータと定型句が含まれているため失敗しました。</p>     "description_text": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword"
            }
          },
          "copy_to": [
            "description_semantic"
          ]
        },<p>たとえば、上記のブロックは 236 文字で、Elasticsearch マッピング内の 1 つのフィールドのみを定義します。一方、文字列「description_text」は 16 文字だけです。これは文字数が約 15 倍に増加していることを意味しますが、そのフィールドが利用可能なデータについて何を意味するかを説明する意味的な改善は見られません。すべてのインデックスのマッピングをフェッチしたが、それを LLM に送信する前に、フィールド名のリストだけに「フラット化」するとどうなるでしょうか?</p><p>試してみました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta5eda7a79493ee81/6a17f11c9da390327fe46590/112c2f447c11f154b5082725cd49b51d0a3c8a65-1214x804.png" alt="" /><p>これは素晴らしいですね！全面的に改善されました。しかし、もっと良い方法はないでしょうか?</p><h3>仮説3: マッピング_meta内の説明</h3><p>追加のコンテキストのないフィールド名だけでこれほど大きな変化が生じたのであれば、実質的なコンテキストを追加すればさらに良くなると思われます。すべてのインデックスに説明を添付することが必ずしも慣例ではありませんが、マッピングの _meta オブジェクトにあらゆる種類のインデックス レベルのメタデータを追加することは可能です。生成されたインデックスに戻り、データセット内のすべてのインデックスに説明を追加しました。説明が極端に長くない限り、完全なマッピングよりも少ないトークンが使用され、インデックスに含まれるデータに関するはるかに優れた洞察が提供されるはずです。私たちの実験はこの仮説を検証しました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt61b85cf40e0e6357/6a17f11dfbc5f82809491bbe/32d2692ad4479d0e52d8ee723dcc5710a6ec90f3-1208x806.png" alt="" /><p>若干の改善があり、現在では全体的に 90% を超える精度を実現しています。</p><h3>仮説4：全体は部分の合計よりも大きい</h3><p>フィールド名により結果が向上しました。説明により結果が向上しました。したがって、説明とフィールド名の<em>両方</em>を利用すると、さらに良い結果が得られるはずです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte6297c6aaf7db802/6a17f11e14d90c1bd779b6e6/114cbb408ff16b136251d2265416bd5270380fe5-1208x794.png" alt="" /><p>データは「いいえ」（前回の実験から変化なし）を示しました。ここでの主な理論は、説明はそもそもインデックス フィールド/マッピングから生成されたため、これら 2 つのコンテキストの間には、組み合わせたときに何か「新しい」ものを追加するのに十分な情報がないというものでした。さらに、20 個のテスト インデックスに送信するペイロードもかなり大きくなっています。これまで私たちが辿ってきた考え方はスケーラブルではありません。実際、これまでの私たちの実験は、数百または数千のインデックスから選択できる Elasticsearch クラスターでは機能しないと考えられる十分な理由があります。インデックスの合計数が増加するにつれて、LLM に送信されるメッセージ サイズが直線的に増加するアプローチは、おそらく一般化可能な戦略にはなりません。</p><p>私たちに本当に必要なのは、多数の候補から最も関連性の高い選択肢だけを絞り込むのに役立つアプローチです...</p><p>ここで問題となるのは検索の問題です。</p><h3>仮説5：意味検索による選択</h3><p>インデックスの名前に意味がある場合は、ベクトルとして保存し、意味的に検索することができます。</p><p>インデックスのフィールド名に意味がある場合は、それらをベクトルとして保存し、意味的に検索することができます。</p><p>インデックスに意味を持つ記述がある場合は、それもベクトルとして保存し、意味的に検索することができます。</p><p>現在、Elasticsearch インデックスではこの情報を検索可能にしていません (検索可能にすべきかもしれませんが) が、そのギャップを回避できる<a href="https://github.com/elastic/connectors/pull/3638">ものをハックする</a>のは非常に簡単でした。Elastic のコネクタ フレームワークを使用して、クラスター内のすべてのインデックスのドキュメントを出力するコネクタを構築しました。出力ドキュメントは次のようになります。</p> doc = {
                "_id": index_name,
                "index_name": index_name,
			"meta_description”: description,
"field_descriptions" = field_descriptions,
                "mapping": json.dumps(mapping),  
                "source_cluster": self.es_client.configured_host,
            }<p>これらのドキュメントを、次のように手動でマッピングを定義した新しいインデックスに送信しました。</p>{
   "mappings": {
       "properties": {
           "semantic_content": {
               "type": "semantic_text"
           },
           "index_name": {
               "type": "text",
               "copy_to": "semantic_content"
           },
           "mapping": {
               "type": "keyword",
               "copy_to": "semantic_content"
           },
           "source_cluster": {
               "type": "keyword"
           },
           "meta_description": {
               "type": "text",
               "copy_to": "semantic_content"
           },
           "field_descriptions": {
               "type": "text",
               "copy_to": "semantic_content"
           }
       }
   }
}<p>これにより、単一の semantic_content フィールドが作成され、セマンティックな意味を持つ他のすべてのフィールドがチャンク化され、インデックスが作成されます。このインデックスの検索は、次のようにするだけで簡単になります。</p>GET indexed-indices/_search
{
 "query": {
   "semantic": {
     "field": "semantic_content",
     "query": "$query"
   }
 }
}<p>修正された<code>index_explorer</code>ツールは、LLM へのリクエストを行う必要がなくなり、代わりに指定されたクエリに対して単一の埋め込みをリクエストして効率的なベクトル検索操作を実行できるため、<em>大幅に</em>高速化されました。トップヒットを選択したインデックスとして取得すると、次の結果が得られました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc27c302e6bef0b23/6a17f120577262d2f21bccdc/06ef5d78040d064d3444793f636d527d9e19a869-1214x800.png" alt="" /><p>このアプローチはスケーラブルです。このアプローチは効率的です。しかし、このアプローチはベースラインよりわずかに優れているだけです。しかし、これは驚くことではありません。ここでの検索アプローチは信じられないほど単純です。ニュアンスがない。インデックスの名前と説明は、インデックスに含まれる任意のフィールド名よりも重視されるべきであるという認識がありません。正確な語彙の一致を同義語の一致よりも重視するアフォーダンスはありません。ただし、非常に微妙なニュアンスのあるクエリを構築するには、手元のデータについて多くのことを想定する必要があります。これまで、インデックス名とフィールド名には意味があるという大きな仮定をすでに立ててきましたが、さらに一歩進んで、インデックス名とフィールド名が<em>どの程度の</em>意味を持ち、互いにどのように関連しているかを仮定する必要があります。そうしないと、最上位の結果として最適な一致を確実に特定することはできないかもしれませんが、最上位 N 個の結果のどこかに最上位の一致があると言える可能性が高くなります。意味情報をそれが存在するコンテキスト内で消費し、意味的に異なる方法で自身を表現する別のエンティティと比較し、それらを判断できるものが必要です。LLM のようなものです。</p><h3>仮説6: 候補セットの削減</h3><p>他にも簡単に触れる実験はいくつかありましたが、重要な突破口となったのは、純粋にセマンティック検索から最適な一致を選択したいという欲求を捨て、代わりにセマンティック検索をフィルターとして活用して、LLM の検討対象から無関係なインデックスを除外したことです。<a href="https://gist.github.com/seanstory/d704443120e20f6c844db10e30066860">検索</a>では、リニア リトリーバー、RRF を使用したハイブリッド検索、 <code>semantic_text</code>を組み合わせて、一致する上位 5 つのインデックスに結果を制限しました。</p><p>次に、一致ごとに、インデックスの名前、説明、フィールド名を LLM のメッセージに追加しました。結果は素晴らしかったです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7ac4cb8f7153fdf9/6a17f121af47b66d1dcde082/8fcabd78f591f90d6bc7c0e087d31317e4eef791-1206x804.png" alt="" /><p>これまでのどの実験よりも最高の精度です!また、このアプローチではインデックスの合計数に比例してメッセージ サイズが増加しないため、このアプローチははるかにスケーラブルです。</p><h2>成果</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8d66130d9fae6bea/6a17f123ddf97d7527910c19/04d630797213dbb8bf567da41d1cdd5c7b4586c9-1600x521.png" alt="" /><p>最初の明らかな結果は、ベースラインを改善<em>できる</em>ということでした。振り返ってみるとこれは明らかなようですが、実験が始まる前に、 <code>index_explorer</code>ツールを完全に放棄して、ユーザーからの明示的な構成に依存して検索空間を制限すべきかどうかについて真剣な議論がありました。これはまだ実行可能かつ有効なオプションですが、この調査では、そのようなユーザー入力が利用できない場合にインデックス選択を自動化するための有望な道筋があることが示されています。</p><p>次の明らかな結果は、問題に対して説明文字をさらに追加するだけでは、効果は減少するということです。この調査を行う前、Elasticsearch の<a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-field-meta">フィールドレベルのメタデータ</a>保存機能を拡張することに投資すべきかどうかについて議論していました。現在、これらの<code>meta</code>値は 50 文字に制限されており、フィールドの意味を理解できるようにするにはこの値を増やす必要があると想定されていました。これは明らかに事実ではなく、LLM はフィールド名だけでかなりうまく機能しているようです。これについては後でさらに調査するかもしれませんが、もはや緊急の問題ではないように思われます。</p><p>逆に言えば、これは「検索可能な」インデックス メタデータを持つことの重要性を明確に示しています。これらの実験のために、インデックスのインデックスをハッキングしました。しかし、これを Elasticsearch に直接組み込むか、管理するための API を構築するか、少なくとも規則を確立することを調査することはできます。私たちは選択肢を検討し、社内で議論する予定ですので、お楽しみに。</p><p>最後に、この取り組みにより、時間をかけて実験し、データに基づいた意思決定を行うことの価値が確認されました。実際、これにより、Agent Builder 製品には強力な製品内評価機能が必要になることが再確認されました。インデックスを選択するツール専用のテスト ハーネス全体を構築する必要がある場合、お客様は反復的な調整を行う際にカスタム ツールを定性的に評価する方法が絶対に必要になります。</p><p>私たちが何を構築するのか楽しみにしています。皆さんも楽しみにしていただければ幸いです。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-agent-builder-experiments-performance</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agent-builder-experiments-performance</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[Elastic内部の実情]]></category>
    <category><![CDATA[ハイブリッド検索]]></category>
    <dc:creator><![CDATA[Sean Story]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt68d11a4c7fd11d4c/6a17f1257b54f9b6598b39d4/42903c869e034674b30bb36013345aaa97f6608b-1184x864.png" length="0" type="image/png"/>
    <pubDate>Mon, 06 Oct 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/jp/search-labs/blog/elasticsearch-retrievers-ga-8.16.0">前回のブログ</a>投稿では、複雑なランキング パイプラインの作成を可能にする、ゼロから再設計されたリトリーバー フレームワークを紹介しました。また、Reciprocal Rank Fusion (RRF) リトリーバーが、異なるクエリの結果を結合してハイブリッド検索を可能にする方法についても調査しました。RRF は簡単に実装できますが、実際のスコアを無視して相対的なランクのみに焦点を当てるという、顕著な制限があります。これにより、微調整と最適化が困難になります。</p><h2>リニアレトリーバーに会いましょう！</h2><p>この投稿では、ハイブリッド検索をサポートするために最近追加された<a href="https://www.elastic.co/jp/docs/solutions/search/retrievers-overview#retrievers-overview-types"><code>linear</code></a><a href="https://www.elastic.co/jp/docs/solutions/search/retrievers-overview#retrievers-overview-types">リトリーバー</a>を紹介します。<code>rrf</code>とは異なり、 <code>linear</code>リトリーバーはドキュメントに一致したすべてのクエリの加重合計を計算します。このアプローチにより、結果セット内の各ドキュメントの相対的な重要度が維持され、各クエリが最終スコアに与える影響を正確に制御できるようになります。その結果、ハイブリッド検索を微調整するためのより直感的で柔軟な方法が提供されます。</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>注意点が 1 つあります: 使用される類似度メトリックに応じて、 <code>knn</code>スコアは厳密に制限される場合があります。たとえば、コサイン類似度または単位正規化ベクトルのドット積では、スコアは常に<code>[0, 1]</code>範囲内になります。対照的に、 <code>bm25</code>スコアは予測しにくく、明確に定義された境界がありません。</p><h2>スコアのスケーリング：kNN vs BM25</h2><p>ハイブリッド検索の課題の 1 つは、異なるリトリーバーが異なるスケールでスコアを生成することです。たとえば、次のシナリオを考えてみましょう。</p><p>クエリAのスコア:</p><p></p><p>ドキュメント1</p><p>ドキュメント2</p><p>ドキュメント3</p><p>ドキュメント4</p><p>わかる</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>ドキュメント1</p><p>ドキュメント2</p><p>ドキュメント3</p><p>ドキュメント4</p><p>わかる</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>[0, 1]</code>範囲にスケーリングするオプションの<code>minmax</code>ノーマライザーを導入しました。</p><p>これにより、クエリの結果セット内の各ドキュメントの相対的な重要度が保持され、異なるリトリーバーからのスコアを簡単に組み合わせることができます。正規化すると、スコアは次のようになります。</p><p>クエリAのスコア:</p><p></p><p>ドキュメント1</p><p>ドキュメント2</p><p>ドキュメント3</p><p>ドキュメント4</p><p>わかる</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>ドキュメント1</p><p>ドキュメント2</p><p>ドキュメント3</p><p>ドキュメント4</p><p>わかる</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>リトリーバーが<code>rrf</code>の欠点のいくつかをどのように解決しているかを見てみましょう。RRF は相対的なランクのみに依存し、実際のスコアの違いは考慮しません。たとえば、次のスコアがあるとします。</p><p></p><p>ドキュメント1</p><p>ドキュメント2</p><p>ドキュメント3</p><p>ドキュメント4</p><p>わかる</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>リトリーバーは正規化と組み合わせることで、スコアとその差異の両方を正しく考慮し、より意味のあるランキングを生成します。</p><p></p><p>ドキュメント1</p><p>ドキュメント2</p><p>ドキュメント3</p><p>ドキュメント4</p><p>わかる</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>bm25</code>の<code>score</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>リトリーバーの柔軟性と直感的なスコアリングを維持しながら、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>リトリーバーは、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>
  </channel>
</rss>