ブログ

LlamaIndex 経由で Elasticsearch にデータを取り込む方法

LlamaIndex で RAG を使用してデータを取り込み、検索する方法を段階的に説明します。

この記事では、LlamaIndex を使用してデータをインデックスし、FAQ の検索エンジンを実装します。Elasticsearch はベクター データベースとして機能し、ベクター検索を可能にします。一方、RAG (Retrieval-Augmented Generation) はコンテキストを充実させ、より正確な応答を提供します。

LlamaIndex & Elasticsearch: ドキュメントの取り込みとFAQ検索の構築

LlamaIndexとは何ですか?

LlamaIndex は、大規模言語モデル (LLM) を活用したエージェントとワークフローの作成を容易にし、特定のデータやプライベート データと対話できるようにするフレームワークです。さまざまなソース (API、PDF、データベース) からのデータを LLM と統合できるため、調査、情報抽出、コンテキストに応じた応答の生成などのタスクが可能になります。

重要な概念:

  • エージェント: LLM を使用して、単純な応答から複雑なアクションに至るまでのタスクを実行するインテリジェント アシスタント。

  • ワークフロー: 高度なタスク用のエージェント、データ コネクタ、ツールを組み合わせた複数ステップのプロセス。

  • コンテキスト拡張: LLM を外部データで強化し、トレーニングの制限を克服する手法。

LlamaIndexと Elasticsearch の統合:

Elasticsearch は LlamaIndex を使ってさまざまな方法で使用できます。

  • データ ソース: Elasticsearch Reader を使用してドキュメントを抽出します。

  • 埋め込みモデル: セマンティック検索のためにデータをベクトルにエンコードします。

  • ベクター ストレージ: ベクター化されたドキュメントを検索するためのリポジトリとして Elasticsearch を使用します。

  • 高度なストレージ: ドキュメントの概要やナレッジ グラフなどの構造を構成します。

LlamaIndexとElasticsearchを使用してFAQ検索を構築する

データ準備

例として、 Elasticsearch Service FAQ を使用します。各質問はウェブサイトから抽出され、個別のテキスト ファイルに保存されました。データを整理するには任意の方法を使用できます。この例では、ファイルをローカルに保存することを選択しました。

サンプルファイル:

File Name: what-is-elasticsearch-service.txt
Content: Elasticsearch Service is hosted and managed Elasticsearch and Kibana brought to you by the creators of Elasticsearch. Elasticsearch Service is part of Elastic Cloud and ships with features that you can only get from the company behind Elasticsearch, Kibana, Beats, and Logstash. Elasticsearch is a full text search engine that suits a range of uses, from search on websites to big data analytics and more.

すべての質問を保存すると、ディレクトリは次のようになります。

依存関係のインストール

取り込みと検索は Python 言語を使用して実装します。使用したバージョンは 3.9 です。前提条件として、次の依存関係をインストールする必要があります。

llama-index-vector-stores-elasticsearch
llama-index
openai

Elasticsearch と Kibana は Docker を使用して作成され、docker-compose.yml を介してバージョン 8.16.2 を実行するように構成されます。これにより、ローカル環境の作成が容易になります。

version: '3.8'
services:

 elasticsearch:
   image: docker.elastic.co/elasticsearch/elasticsearch:8.16.2
   container_name: elasticsearch-8.16.2
   environment:
     - node.name=elasticsearch
     - xpack.security.enabled=false
     - discovery.type=single-node
     - "ES_JAVA_OPTS=-Xms1024m -Xmx1024m"
   ports:
     - 9200:9200
   networks:
     - shared_network

 kibana:
   image: docker.elastic.co/kibana/kibana:8.16.2
   container_name: kibana-8.16.2
   restart: always
   environment:
     - ELASTICSEARCH_URL=http://elasticsearch:9200
   ports:
     - 5601:5601
   depends_on:
     - elasticsearch
   networks:
     - shared_network

networks:
 shared_network:

LlamaIndexを使用したドキュメントの取り込み

ドキュメントは、LlamaIndex を使用して Elasticsearch にインデックスされます。まず、ローカル ディレクトリからファイルを読み込むことができるSimpleDirectoryReaderを使用してファイルを読み込みます。ドキュメントを読み込んだ後、 VectorStoreIndexを使用してインデックスを作成します。

documents = SimpleDirectoryReader("./faq").load_data()

storage_context = StorageContext.from_defaults(vector_store=es)
index = VectorStoreIndex(documents, storage_context=storage_context, embed_model=embed_model)

LlamaIndex のベクター ストアは、ドキュメントの埋め込みの保存と管理を担当します。LlamaIndex はさまざまなタイプのベクター ストアをサポートしており、この場合は Elasticsearch を使用します。StorageContext で、Elasticsearch インスタンスを構成します。コンテキストはローカルなので、追加のパラメータは必要ありません。他の環境での構成については、ドキュメントを参照して必要なパラメータを確認してください: ElasticsearchStore 構成

デフォルトでは、LlamaIndex は OpenAI text-embedding-ada-002モデルを使用して埋め込みを生成します。ただし、この例では、 text-embedding-3-smallモデルを使用します。モデルを使用するには OpenAI API キーが必要になることに注意してください。

以下はドキュメント取り込みの完全なコードです。

import openai
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, StorageContext
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.vector_stores.elasticsearch import ElasticsearchStore

openai.api_key = os.environ["OPENAI_API_KEY"]

es = ElasticsearchStore(
   index_name="faq",
   es_url="http://localhost:9200"
)

def format_title(filename):
   filename_without_ext = filename.replace('.txt', '')
   text_with_spaces = filename_without_ext.replace('-', ' ')
   formatted_text = text_with_spaces.title()

   return formatted_text


embed_model = OpenAIEmbedding(model="text-embedding-3-small")

documents = SimpleDirectoryReader("./faq").load_data()

for doc in documents:
   doc.metadata['title'] = format_title(doc.metadata['file_name'])

storage_context = StorageContext.from_defaults(vector_store=es)
index = VectorStoreIndex(documents, storage_context=storage_context, embed_model=embed_model)

実行後、ドキュメントは以下に示すようにFAQインデックスにインデックス付けされます。

RAGで検索

検索を実行するには、 ElasticsearchStoreクライアントを構成し、 index_name フィールドes_urlフィールドに Elasticsearch URL を設定します。retrieval_strategyでは、ベクトル検索用のAsyncDenseVectorStrategyを定義しました。AsyncBM25Strategy (キーワード検索) やAsyncSparseVectorStrategy (スパースベクトル) などの他の戦略も利用できます。詳細については、公式ドキュメントをご覧ください。

es = ElasticsearchStore(
   index_name="faq",
   es_url="http://localhost:9200",
   retrieval_strategy=AsyncDenseVectorStrategy(
   )
)

次に、 VectorStoreIndexオブジェクトが作成され、ElasticsearchStore オブジェクトを使用してvector_storeが構成されます。as_retrieverメソッドでは、クエリに最も関連性の高いドキュメントの検索を実行し、 similarity_top_kパラメータを通じて返される結果の数を 5 に設定します。

   index = VectorStoreIndex.from_vector_store(vector_store=es)
   retriever = index.as_retriever(similarity_top_k=5)
   results = retriever.retrieve(query)

次のステップはRAGです。ベクトル検索の結果は LLM のフォーマットされたプロンプトに組み込まれ、取得された情報に基づいてコンテキストに応じた応答が可能になります。

PromptTemplate では、次の内容を含むプロンプト形式を定義します。

  • コンテキスト ( {context_str} ): 取得者によって取得されたドキュメント。

  • クエリ ( {query_str} ): ユーザーの質問。

  • 指示: 外部の知識に依存せずに、コンテキストに基づいてモデルが応答するためのガイドライン。

qa_prompt = PromptTemplate(
   "You are a helpful and knowledgeable assistant."
   "Your task is to answer the user's query based solely on the context provided below."
   "Do not use any prior knowledge or external information.\n"
   "---------------------\n"
   "Context:\n"
   "{context_str}\n"
   "---------------------\n"
   "Query: {query_str}\n"
   "Instructions:\n"
   "1. Carefully read and understand the context provided.\n"
   "2. If the context contains enough information to answer the query, provide a clear and concise answer.\n"
   "3. Do not make up or guess any information.\n"
   "Answer: "
)

最後に、LLM はプロンプトを処理し、正確でコンテキストに応じた応答を返します。

llm = OpenAI(model="gpt-4o")
context_str = "\n\n".join([n.node.get_content() for n in results])
response = llm.complete(
   qa_prompt.format(context_str=context_str, query_str=query)
)

print("Answer:")
print(response)

完全なコードは以下の通りです。

es = ElasticsearchStore(
   index_name="faq",
   es_url="http://localhost:9200",
   retrieval_strategy=AsyncDenseVectorStrategy(
   )
)


def print_results(results):
   for rank, result in enumerate(results, start=1):
       title = result.metadata.get("title")
       score = result.get_score()
       text = result.get_text()
       print(f"{rank}. title={title} \nscore={score} \ncontent={text}")


def search(query: str):
   index = VectorStoreIndex.from_vector_store(vector_store=es)

   retriever = index.as_retriever(similarity_top_k=10)
   results = retriever.retrieve(QueryBundle(query_str=query))
   print_results(results)

   qa_prompt = PromptTemplate(
       "You are a helpful and knowledgeable assistant."
       "Your task is to answer the user's query based solely on the context provided below."
       "Do not use any prior knowledge or external information.\n"
       "---------------------\n"
       "Context:\n"
       "{context_str}\n"
       "---------------------\n"
       "Query: {query_str}\n"
       "Instructions:\n"
       "1. Carefully read and understand the context provided.\n"
       "2. If the context contains enough information to answer the query, provide a clear and concise answer.\n"
       "3. Do not make up or guess any information.\n"
       "Answer: "
   )

   llm = OpenAI(model="gpt-4o")
   context_str = "\n\n".join([n.node.get_content() for n in results])
   response = llm.complete(
       qa_prompt.format(context_str=context_str, query_str=query)
   )

   print("Answer:")
   print(response)


question = "Elastic services are free?"
print(f"Question: {question}")
search(question)

これで、「Elastic サービスは無料ですか?」などの検索を実行し、FAQ データ自体に基づいてコンテキストに応じた応答を取得できるようになりました。

Question: Elastic services are free?
Answer:
Elastic services are not entirely free. However, there is a 14-day free trial available for exploring Elastic solutions. After the trial, access to features and services depends on the subscription level.

この応答を生成するために、次の文書が使用されました。

1. title=Can I Try Elasticsearch Service For Free 
score=1.0 
content=Yes, sign up for a 14-day free trial. The trial starts the moment a cluster is created.
During the free trial period get access to a deployment to explore Elastic solutions for Enterprise Search, Observability, Security, or the latest version of the Elastic Stack.

2. title=Do You Offer Elastic S Commercial Products 
score=0.9941274512218439 
content=Yes, all Elasticsearch Service customers have access to basic authentication, role-based access control, and monitoring.
Elasticsearch Service Gold, Platinum and Enterprise customers get complete access to all the capabilities in X-Pack: Security, Alerting, Monitoring, Reporting, Graph Analysis & Visualization. Contact us to learn more.

3. title=What Is Elasticsearch Service 
score=0.9896776845746571 
content=Elasticsearch Service is hosted and managed Elasticsearch and Kibana brought to you by the creators of Elasticsearch. Elasticsearch Service is part of Elastic Cloud and ships with features that you can only get from the company behind Elasticsearch, Kibana, Beats, and Logstash. Elasticsearch is a full text search engine that suits a range of uses, from search on websites to big data analytics and more.

4. title=Can I Run The Full Elastic Stack In Elasticsearch Service 
score=0.9880631561979476 
content=Many of the products that are part of the Elastic Stack are readily available in Elasticsearch Service, including Elasticsearch, Kibana, plugins, and features such as monitoring and security. Use other Elastic Stack products directly with Elasticsearch Service. For example, both Logstash and Beats can send their data to Elasticsearch Service. What is run is determined by the subscription level.

5. title=What Is The Difference Between Elasticsearch Service And The Amazon Elasticsearch Service 
score=0.9835054890793161 
content=Elasticsearch Service is the only hosted and managed Elasticsearch service built, managed, and supported by the company behind Elasticsearch, Kibana, Beats, and Logstash. With Elasticsearch Service, you always get the latest versions of the software. Our service is built on best practices and years of experience hosting and managing thousands of Elasticsearch clusters in the Cloud and on premise. For more information, check the following Amazon and Elastic Elasticsearch Service comparison page.
Please note that there is no formal partnership between Elastic and Amazon Web Services (AWS), and Elastic does not provide any support on the AWS Elasticsearch Service.

まとめ

LlamaIndex を使用して、Elasticsearch をベクター データベースとしてサポートする効率的な FAQ 検索システムを作成する方法を示しました。ドキュメントは埋め込みを使用して取り込まれ、インデックス化され、ベクトル検索が可能になります。PromptTemplate を通じて、検索結果はコンテキストに組み込まれ、LLM に送信され、取得されたドキュメントに基づいて正確でコンテキストに沿った応答が生成されます。

このワークフローは、情報検索とコンテキストに応じた応答生成を統合して、正確で関連性の高い結果を提供します。

参照資料

https://www.elastic.co/guide/en/cloud/current/ec-faq-getting-started.html

https://docs.llamaindex.ai/en/stable/api_reference/readers/elasticsearch/

https://docs.llamaindex.ai/en/stable/module_guides/indexing/vector_store_index/

https://docs.llamaindex.ai/en/stable/examples/クエリエンジン/カスタムクエリエンジン/

https://www.elastic.co/search-labs/integrations/llama-index

よくあるご質問

LlamaIndexとは何ですか?

LlamaIndex は、大規模言語モデル (LLM) を活用したエージェントとワークフローの作成を容易にし、特定のデータやプライベート データと対話できるようにするフレームワークです。

LlamaIndex は Elasticsearch と統合できますか?

はい、Elasticsearch は LlamaIndex でさまざまな方法で使用できます。たとえば、データ ソース (Elasticsearch Reader を使用してドキュメントを抽出)、埋め込みモデル (セマンティック検索用にデータをベクトルにエンコード)、ベクトル ストレージ (ベクトル化されたドキュメントを検索するためのリポジトリとして Elasticsearch を使用)、高度なストレージ (ドキュメントの概要やナレッジ グラフなどの構造を構成する) などです。

関連記事

Elasticsearchインデックスのフィールドを表示する

Kofi Bartlett

Kafka 経由で Elasticsearch にデータを取り込む方法

Andre Luiz

LogstashでのRubyスクリプト

Dai Sugimori

Elasticsearch のインデックステンプレート: 構成可能なテンプレートの使い方

Kofi Bartlett

Elasticsearchインデックスのフィールドを表示する方法

JD Armada

最先端の検索体験を構築する準備はできましたか?

十分に高度な検索は 1 人の努力だけでは実現できません。Elasticsearch は、データ サイエンティスト、ML オペレーター、エンジニアなど、あなたと同じように検索に情熱を傾ける多くの人々によって支えられています。ぜひつながり、協力して、希望する結果が得られる魔法の検索エクスペリエンスを構築しましょう。

はじめましょう