Elastic CloudとLangChainでQiitaの記事をベクトル/セマンティック検索する方法の紹介

# はじめに
生成AIの出現によりRAGパターンに使われるベクトル検索も注目を浴びてますが、従来のキーワード検索や全文検索とともに、ベクトル検索もElasticsearchの得意とするところです。

今回はElasticsearchでの日本語文書に対するベクトル検索を気軽に試せるJupyter Notebookを作りました。Qiita記事を検索対象に行っているので、割と専門的な文書に対する検索が試せるのではないでしょうか。
なお、今回の検索対象と検索ワードは割と適当に決めたものなので、何かの精度を結論づけるものではありませんのでご了承ください。

# 比較した検索手法
* **Elasticsearchの日本語全文検索** ... BM25という検索ランキングを使ったElasticsearchでの全文検索。日本語の形態素解析にkuromojiを使っています。
* **OpenAI Embeddingを使ったベクトル検索** ... ベクトル値の算出にOpenAI Embedding API (text-embedding-ada-002モデル)を利用し、ベクトル検索はElasticsearchで行いました。
* **intfloat/multilingual-e5-smallモデルを使ったベクトル検索** ... ベクトル値の算出に、多言語に対応したMLモデルintfloat/multilingual-e5-smallモデルを利用し、ベクトル検索はElasticsearchで行いました。モデルについては[こちら](https://huggingface.co/intfloat/multilingual-e5-small)を参照ください。
* **Elasticsearchの全文検索とベクトル検索のハイブリッド** ... Elasticsearchで全文検索結果のランキングとベクトル検索のランキングをRRFというランキングロジックで組み合わせた結果

上記の各MLモデルが生成したベクトル値同士のベクトル検索にはElasticsearchのKNNサーチ（cosine類似度評価）を使っています。
今回はLangchainを使って、この4つの検索ロジックを同じような形式のコードに共通化することができています。検索方法を切り替えて試すのに便利です。

# 検索対象
Qiita APIを使い、[Elasticsearch JapanのOrgの計69記事](https://qiita.com/organizations/elasticsearch_japan/items)をダウンロードしてElasticsearchに入れました。

# テスト1: 記事の検索
質問形式の文章を検索ワードにしてテストしました：「Frozen tierの使い方について教えてください」。先に結果を見てみます。
どの記事が検索ヒットしてほしかったかと言うと、以下の上位２つプラス3つ目も関係なくはない、といった感じです。
1. [ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (1)](https://qiita.com/nobuhikosekiya/items/dd2ce836b184730f8d70)
1. [ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (2)](https://qiita.com/nobuhikosekiya/items/8ab82e70953b6b5d1736)
1. [Elastic CloudでHotに溜まっているデータを別のティアに移動するやり方](https://qiita.com/nobuhikosekiya/items/3c03932c3efec0a9f04d)

上記以外の記事は検索ワードと関係ない記事といっていいです。
よって、OpenAIとe5-smallは的確に上位３つを当てていました。全文検索の1位の記事は、記事の中に「Frozen」や「使い方」という言葉が入っていたためか、1位に検索されたものと考えられます。

**検索ワード**： Frozen tierの使い方について教えてください
|順位|全文検索|ベクトル検索OpenAI |ベクトル検索e5-small|全文検索とベクトルのハイブリッド|
|:-|:-|:-|:-|:-|
|1 |[Elastic Cloud について　〜実際にデプロイメントを作ってみよう〜](https://qiita.com/tomo_s_el/items/3584d0b1fabb0bafa4fa)| [ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (1)](https://qiita.com/nobuhikosekiya/items/dd2ce836b184730f8d70)  |[ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (1)](https://qiita.com/nobuhikosekiya/items/dd2ce836b184730f8d70)    |[ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (1)](https://qiita.com/nobuhikosekiya/items/dd2ce836b184730f8d70)    |
|2 |[Elastic CloudでHotに溜まっているデータを別のティアに移動するやり方](https://qiita.com/nobuhikosekiya/items/3c03932c3efec0a9f04d)   |[ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (2)](https://qiita.com/nobuhikosekiya/items/8ab82e70953b6b5d1736)   |[ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (2)](https://qiita.com/nobuhikosekiya/items/8ab82e70953b6b5d1736)   |[Elastic CloudでHotに溜まっているデータを別のティアに移動するやり方](https://qiita.com/nobuhikosekiya/items/3c03932c3efec0a9f04d)   |
|3 |[ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (1)](https://qiita.com/nobuhikosekiya/items/dd2ce836b184730f8d70)    |[Elastic CloudでHotに溜まっているデータを別のティアに移動するやり方](https://qiita.com/nobuhikosekiya/items/3c03932c3efec0a9f04d)   |[Elastic CloudでHotに溜まっているデータを別のティアに移動するやり方](https://qiita.com/nobuhikosekiya/items/3c03932c3efec0a9f04d)   |[ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (2)](https://qiita.com/nobuhikosekiya/items/8ab82e70953b6b5d1736)   |
|4 |[Elasticsearch v8.9 で実装した日本語NLP、ベクトル検索（セマンティック検索）を使ってみる](https://qiita.com/daixque/items/931b8be343075b835097)   |[オンプレミスから Elastic on AWS への Elastic ワークロード移行を自動化するための10のステップ](https://qiita.com/shosuz/items/a2714b98adfd35149c57)   |[Elastic Stack 8.0 の NLP で日本語センチメント分析を試してみた - 後編](https://qiita.com/ijokarumawak@github/items/6cc714060090160cf2d5)   |[Elastic Cloud について　〜実際にデプロイメントを作ってみよう〜](https://qiita.com/tomo_s_el/items/3584d0b1fabb0bafa4fa)   |
|5 |[ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (2)](https://qiita.com/nobuhikosekiya/items/8ab82e70953b6b5d1736)   |[AzureのログをさくっとElastic Cloudに送る方法](https://qiita.com/nobuhikosekiya/items/9751fda90ba59dde1561)   |[Elastic における OpenTelemetry による独立性](https://qiita.com/shosuz/items/1eee784c5dd8f009e204)   |[Elastic Stack 8.0 の NLP で日本語センチメント分析を試してみた - 後編](https://qiita.com/ijokarumawak@github/items/6cc714060090160cf2d5)   |


# テスト2: 類似質問の検索
ベクトル検索は、あくまで入力（文章、画像など）に対して類似するコンテンツ（文章、画像）を見つけるものなので、質問->文書ではなく、質問->質問の検索の方が正確な類似性を見られると思います。
検証のための大量の質問を用意するのが苦労するところですが、今回はOpenAI のChat Completions APIを使い、各記事から10個ずつ質問を生成してもらいました。生成された質問の精度は今回は気にせず、あくまで類似質問を検索するという部分を見たいと思います。

**検索ワード**： Frozen tierの使い方について教えてください
**太字**が検索ヒットした質問文。[]は質問が生成された元の記事。
|順位|全文検索|ベクトル検索OpenAI |ベクトル検索e5-small|全文検索とベクトルのハイブリッド|
|:-|:-|:-|:-|:-|
|1   |**Hot TierとFrozen Tierのそれぞれの選択肢は何ですか？**[ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (2)]  |**Hot TierとFrozen Tierのそれぞれの選択肢は何ですか？** [ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (2)]   |**Hot TierとFrozen Tierのそれぞれの選択肢は何ですか？** [ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (2)]   |**Hot TierとFrozen Tierのそれぞれの選択肢は何ですか？** [ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (2)]   |
|2   |**Warm/Cold/FrozenのTierを選択的に設ける場合、何を使ってデータを移動させる必要がありますか？**[Elastic CloudでHotに溜まっているデータを別のティアに移動するやり方]|**Elastic CloudでFrozenデータティアを使用する際の手間を減らすための方法はありますか？** [ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (1)]   |**Warm/Cold/FrozenのTierを選択的に設ける場合、何を使ってデータを移動させる必要がありますか？** [Elastic CloudでHotに溜まっているデータを別のティアに移動するやり方]   |**サンプルコードの実行方法について教えてください。** [APMでElasticsearchのクエリーをどこまで分析できるかやってみた]   |
|3   |**Dev Toolsの使い方はどのようになりますか？**[Lookup Runtime Field　〜Elasticsearch 8.2 新機能〜]  |**Warm/Cold/FrozenのTierを選択的に設ける場合、何を使ってデータを移動させる必要がありますか？** [Elastic CloudでHotに溜まっているデータを別のティアに移動するやり方]   | **Frozenに対する検索を行った結果はどうなりましたか？** [ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (2)]   |**異常検知ジョブのクローンを使った変更方法について詳しく教えてください。** [Elasticsearchのマシン・ラーニング異常検知の動きを理解する(3) [変更設定編]]   |
|4   |**Frozenに対する検索を行った結果はどうなりましたか？**[ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (2)]   |**Frozenに対する検索を行った結果はどうなりましたか？** [ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (2)]   |**Elastic CloudでFrozenデータティアを使用する際の手間を減らすための方法はありますか？** [ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (1)]   |**Warm/Cold/FrozenのTierを選択的に設ける場合、何を使ってデータを移動させる必要がありますか？** [Elastic CloudでHotに溜まっているデータを別のティアに移動するやり方]   |
|5   |**Frozenデータティアを使用するには、どのようなライセンスが必要ですか？**[ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (1)] |**Elasticsearchバージョン7.12からFrozenデータティアが使えるようになりました。Frozenデータティアを使用するには、どのようなライセンスが必要ですか？** [ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (1)]   |**Elasticsearchバージョン7.12からFrozenデータティアが使えるようになりました。Frozenデータティアを使用するには、どのようなライセンスが必要ですか？** [ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (1)]   |**Frozenに対する検索を行った結果はどうなりましたか？** [ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (2)]   |

ユーザーの入力した質問に対して、類似質問/関連質問を返すような検索システムに使えそうですね。

# 公開スクリプト
以下のGitHubに今回のテストを一回のRunボタンでランスルーできるJupyter Notebookを公開しています。Google Colabから開き、Elastic CloudやOpenAIのアクセスキーを入力すればすぐに今回のテストが実行されるようになっています。
https://github.com/nobuhikosekiya/qiitablog_vectorsearch_elasticsearch/blob/main/qiitablog_vectorsearch_elasticsearch.ipynb


**スクリプト実行にあたって必要なもの**
* 自分のElastic Cloud環境（[無料トライアルを申し込みできます](https://cloud.elastic.co/registration)）。Machine Learningノードを有効にしておいてください。(１つのMLモデルを動かす場合は最低1GB　RAMのHOTノードと4GB RAMのMLノードをプロビジョンしてください。)　有償環境の場合はPlatinumサブスクリプションが必要で、時間課金で$0.3くらいからで実行できます。
* 自分のOpenAIかAzure OpenAIのアクセストークン ... 今回のスクリプトではテスト実行にあたっては100円もかからないです。

なお、e5のMLモデルはこのJupyter Notebook内でHugging Faceからダウンロードし、Elastic CloudのMLインスタンスにアップする流れになっているので、自分で何か用意する必要はありません。