T-CREATOR

LlamaIndex のコア概念を図解で理解:Data Loader/Index/Retriever/Query Engine

LlamaIndex のコア概念を図解で理解:Data Loader/Index/Retriever/Query Engine

LLM(大規模言語モデル)を活用したアプリケーション開発において、自社データや外部データと LLM を効率的に連携させることは非常に重要です。LlamaIndex は、この課題を解決するための強力なフレームワークとして注目を集めています。

本記事では、LlamaIndex の中核を成す 4 つのコンポーネント——Data Loader、Index、Retriever、Query Engine——について、図解を交えながら初心者の方にもわかりやすく解説いたします。これらの概念を理解することで、効率的な RAG(Retrieval-Augmented Generation)システムを構築できるようになるでしょう。

背景

LLM とデータ連携の重要性

LLM は膨大な知識を持っていますが、最新情報や企業固有のデータにはアクセスできません。ChatGPT や Claude などの LLM に自社のドキュメントや最新情報を活用させるためには、外部データを効果的に取り込む仕組みが必要です。

RAG アーキテクチャの登場

RAG(Retrieval-Augmented Generation)は、LLM の回答生成時に関連する外部データを検索・取得し、それを基に回答を生成する手法です。この仕組みにより、LLM は常に最新かつ正確な情報を基にした回答が可能になります。

LlamaIndex の役割

LlamaIndex は、RAG システムを簡単に構築するための Python ライブラリです。データの読み込みからインデックス作成、検索、回答生成まで、一連のプロセスを統一されたインターフェースで提供してくれます。

以下の図は、LlamaIndex における基本的なデータフローを示しています。

mermaidflowchart LR
    raw["生データ<br/>(PDF/Web/DB)"] -->|読み込み| loader["Data Loader"]
    loader -->|ドキュメント化| docs["Documents"]
    docs -->|構造化・保存| index["Index"]
    index -->|検索| retriever["Retriever"]
    retriever -->|関連情報取得| engine["Query Engine"]
    user["ユーザー"] -->|質問| engine
    engine -->|LLM 活用| llm["LLM"]
    llm -->|回答生成| engine
    engine -->|回答| user

この図から、データが段階的に処理され、最終的にユーザーの質問に対する回答が生成される流れがわかりますね。

図で理解できる要点:

  • データは Data Loader から始まり、段階的に変換される
  • Index がデータを構造化し、効率的な検索を可能にする
  • Retriever と Query Engine が連携してユーザーの質問に回答する

課題

データソースの多様性

現代のアプリケーションでは、PDF、Web ページ、データベース、API など、さまざまな形式のデータを扱う必要があります。それぞれのデータソースには異なるアクセス方法や形式があり、統一的に処理することが困難です。

大量データの効率的な検索

数千、数万のドキュメントから関連情報を素早く見つけ出すには、適切なインデックス構造と検索アルゴリズムが必要になります。単純な全文検索では、応答時間やコストの面で課題が生じるでしょう。

LLM との効果的な連携

LLM には入力トークン数の制限があり、すべてのデータを一度に渡すことはできません。必要な情報だけを的確に選択し、LLM に渡す仕組みが求められます。

以下の図は、これらの課題がどのように関連しているかを示しています。

mermaidflowchart TD
    challenge["RAG システムの課題"]
    challenge --> c1["データソースの多様性"]
    challenge --> c2["大量データの効率的な検索"]
    challenge --> c3["LLM との効果的な連携"]

    c1 --> s1["異なる形式の統一処理"]
    c2 --> s2["高速な検索アルゴリズム"]
    c3 --> s3["トークン制限への対応"]

    s1 --> solution["LlamaIndex の<br/>4 つのコア概念"]
    s2 --> solution
    s3 --> solution

図で理解できる要点:

  • 3 つの主要課題が相互に関連している
  • それぞれの課題に対する個別の解決策が必要
  • LlamaIndex のコア概念がこれらを統合的に解決する

解決策

LlamaIndex は、4 つのコア概念を通じて上記の課題を解決します。それぞれのコンポーネントが明確な役割を持ち、連携することで強力な RAG システムを実現できるのです。

Data Loader(データローダー)

Data Loader は、多様なデータソースから情報を読み込み、LlamaIndex が扱える統一形式(Document オブジェクト)に変換する役割を担います。

主な特徴:

  • 100 種類以上のデータソースに対応
  • PDF、Word、Markdown、Web ページ、データベースなど
  • LlamaHub からさまざまなローダーを利用可能

提供される情報:

  • テキストコンテンツ
  • メタデータ(作成日時、著者、URL など)
  • ドキュメント ID

Index(インデックス)

Index は、読み込んだドキュメントを効率的に検索可能な構造に変換し、保存します。ベクトル埋め込みを活用することで、意味的な類似性に基づく検索を実現できるのです。

主なインデックスタイプ:

  • VectorStoreIndex:ベクトル埋め込みを使った類似度検索
  • ListIndex:シンプルなリスト形式
  • TreeIndex:階層的な木構造
  • KeywordTableIndex:キーワードベースの検索

Retriever(リトリーバー)

Retriever は、ユーザーの質問に関連する情報をインデックスから検索・取得します。検索戦略をカスタマイズすることで、より精度の高い情報取得が可能になります。

主な検索戦略:

  • Top-K 検索:類似度が高い上位 K 件を取得
  • 類似度閾値フィルタリング:一定以上の類似度を持つもののみ取得
  • ハイブリッド検索:ベクトル検索とキーワード検索の組み合わせ

Query Engine(クエリエンジン)

Query Engine は、Retriever が取得した情報と LLM を組み合わせて、ユーザーの質問に対する回答を生成します。プロンプトテンプレートや回答生成ロジックをカスタマイズできるため、用途に応じた柔軟な対応が可能です。

以下の図は、4 つのコンポーネントがどのように連携するかを詳しく示しています。

mermaidsequenceDiagram
    participant User as ユーザー
    participant QE as Query Engine
    participant Ret as Retriever
    participant Idx as Index
    participant LLM as LLM

    User->>QE: 質問を送信
    QE->>Ret: 関連情報を要求
    Ret->>Idx: クエリ実行
    Idx-->>Ret: 関連ドキュメント返却
    Ret-->>QE: コンテキスト情報返却
    QE->>LLM: プロンプト送信<br/>(質問+コンテキスト)
    LLM-->>QE: 回答生成
    QE-->>User: 最終回答返却

図で理解できる要点:

  • ユーザーの質問が複数のコンポーネントを経由して処理される
  • Retriever と Index の連携により効率的な情報取得が実現される
  • Query Engine が全体のオーケストレーションを担当する

具体例

実際のコードを通じて、4 つのコア概念がどのように使われるかを見ていきましょう。段階的に説明していきます。

ステップ 1:必要なライブラリのインポート

まず、LlamaIndex の必要なモジュールをインポートします。

typescript// 必要なモジュールのインポート
import {
  VectorStoreIndex,
  SimpleDirectoryReader,
  Settings,
} from 'llamaindex';

TypeScript でも LlamaIndex を使用できますが、Python の方が機能が豊富です。以降は Python での実装例を示します。

python# 必要なモジュールのインポート
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.core import Settings
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding

ステップ 2:LLM と埋め込みモデルの設定

LlamaIndex で使用する LLM と埋め込みモデルを設定します。

python# LLM の設定(OpenAI の GPT-4 を使用)
Settings.llm = OpenAI(
    model="gpt-4",
    temperature=0.1  # 回答の一貫性を高めるため低めに設定
)

# 埋め込みモデルの設定
Settings.embed_model = OpenAIEmbedding(
    model="text-embedding-3-small"
)

この設定により、ベクトル化と回答生成に使用するモデルが決まります。

ステップ 3:Data Loader でデータを読み込む

Data Loader の実装例:

SimpleDirectoryReader を使用して、ディレクトリ内のドキュメントを読み込みます。

python# ドキュメントの読み込み
# ./data ディレクトリ内のすべてのファイルを対象とする
documents = SimpleDirectoryReader(
    input_dir="./data",
    recursive=True  # サブディレクトリも再帰的に読み込む
).load_data()

# 読み込んだドキュメント数を確認
print(f"読み込んだドキュメント数: {len(documents)}")

各ドキュメントには、テキスト内容とメタデータが含まれています。

ステップ 4:Index でドキュメントをインデックス化

Index の実装例:

読み込んだドキュメントから VectorStoreIndex を作成します。

python# VectorStoreIndex の作成
# ドキュメントが自動的にベクトル化され、インデックスに保存される
index = VectorStoreIndex.from_documents(
    documents,
    show_progress=True  # 進捗バーを表示
)

# インデックスを永続化(次回読み込みを高速化)
index.storage_context.persist(persist_dir="./storage")

インデックスの作成には時間がかかる場合があるため、永続化しておくと便利です。

ステップ 5:既存のインデックスを読み込む(オプション)

既に作成したインデックスを再利用する場合は、以下のように読み込めます。

pythonfrom llama_index.core import StorageContext, load_index_from_storage

# 保存済みのインデックスを読み込む
storage_context = StorageContext.from_defaults(
    persist_dir="./storage"
)
index = load_index_from_storage(storage_context)

ステップ 6:Retriever でカスタム検索を実装

Retriever の実装例:

インデックスから Retriever を取得し、検索パラメータをカスタマイズします。

python# Retriever の取得
retriever = index.as_retriever(
    similarity_top_k=3,  # 上位 3 件を取得
    similarity_cutoff=0.7  # 類似度 0.7 以上のみ取得
)

# 検索の実行
query = "LlamaIndex の主な特徴は何ですか?"
retrieved_nodes = retriever.retrieve(query)

# 取得したノードの確認
for i, node in enumerate(retrieved_nodes):
    print(f"\n--- 検索結果 {i+1} ---")
    print(f"スコア: {node.score:.3f}")
    print(f"テキスト: {node.text[:200]}...")

この例では、類似度が高い上位 3 件のノードを取得しています。

ステップ 7:Query Engine で質問に回答

Query Engine の実装例:

インデックスから Query Engine を作成し、質問に対する回答を生成します。

python# Query Engine の作成
query_engine = index.as_query_engine(
    similarity_top_k=3,  # Retriever の設定
    response_mode="compact"  # 回答生成モード
)

# 質問の実行
response = query_engine.query(
    "LlamaIndex を使うメリットを 3 つ教えてください"
)

# 回答の表示
print(f"回答: {response.response}")

response_mode には以下のオプションがあります:

#モード説明
1compact複数のチャンクをまとめて LLM に送信
2refine各チャンクを順次処理して回答を洗練
3tree_summarize階層的に要約を作成
4simple_summarizeシンプルな要約生成

ステップ 8:カスタムプロンプトの使用

Query Engine のプロンプトをカスタマイズすることで、回答の形式や内容を制御できます。

pythonfrom llama_index.core import PromptTemplate

# カスタムプロンプトテンプレートの定義
template = (
    "以下のコンテキスト情報を参考にしてください。\n"
    "---------------------\n"
    "{context_str}\n"
    "---------------------\n"
    "上記の情報を基に、次の質問に日本語で簡潔に答えてください。\n"
    "質問: {query_str}\n"
    "回答: "
)

qa_template = PromptTemplate(template)

プロンプトテンプレートを Query Engine に適用します。

python# カスタムプロンプトを使用した Query Engine の作成
query_engine = index.as_query_engine(
    text_qa_template=qa_template,
    similarity_top_k=3
)

# 質問の実行
response = query_engine.query(
    "RAG システムの利点を説明してください"
)
print(response.response)

ステップ 9:ストリーミングレスポンスの実装

リアルタイムで回答を表示したい場合は、ストリーミング機能を使用できます。

python# ストリーミング対応の Query Engine を作成
query_engine = index.as_query_engine(
    streaming=True,
    similarity_top_k=3
)

# ストリーミングで質問を実行
streaming_response = query_engine.query(
    "LlamaIndex の使い方を教えてください"
)

# リアルタイムで回答を表示
for text in streaming_response.response_gen:
    print(text, end="", flush=True)

この実装により、ChatGPT のようなリアルタイム表示が可能になります。

完全な実装例

ここまでの内容をまとめた完全な実装例を示します。

pythonfrom llama_index.core import (
    VectorStoreIndex,
    SimpleDirectoryReader,
    Settings,
    StorageContext,
    load_index_from_storage
)
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
import os

# API キーの設定
os.environ["OPENAI_API_KEY"] = "your-api-key-here"

# LLM と埋め込みモデルの設定
Settings.llm = OpenAI(model="gpt-4", temperature=0.1)
Settings.embed_model = OpenAIEmbedding(
    model="text-embedding-3-small"
)

データの読み込みとインデックス作成を行います。

python# Data Loader: ドキュメントの読み込み
documents = SimpleDirectoryReader(
    input_dir="./data",
    recursive=True
).load_data()

print(f"読み込んだドキュメント数: {len(documents)}")

# Index: ベクトルインデックスの作成
index = VectorStoreIndex.from_documents(
    documents,
    show_progress=True
)

# インデックスの永続化
index.storage_context.persist(persist_dir="./storage")

Retriever と Query Engine を使用して質問に回答します。

python# Query Engine の作成
query_engine = index.as_query_engine(
    similarity_top_k=3,
    response_mode="compact"
)

# 質問と回答
questions = [
    "このドキュメントの主なトピックは何ですか?",
    "具体的な実装例を教えてください",
    "注意すべきポイントはありますか?"
]

for question in questions:
    print(f"\n質問: {question}")
    response = query_engine.query(question)
    print(f"回答: {response.response}\n")
    print("-" * 50)

この実装により、4 つのコア概念を統合した完全な RAG システムが構築できました。

以下の図は、実装における処理の流れを示しています。

mermaidstateDiagram-v2
    [*] --> Setup: 初期設定
    Setup --> LoadData: Data Loader
    LoadData --> CreateIndex: Index 作成
    CreateIndex --> SaveIndex: 永続化
    SaveIndex --> LoadIndex: 読み込み
    LoadIndex --> CreateRetriever: Retriever 作成
    CreateRetriever --> CreateQueryEngine: Query Engine 作成
    CreateQueryEngine --> ProcessQuery: 質問処理
    ProcessQuery --> RetrieveContext: コンテキスト取得
    RetrieveContext --> GenerateResponse: LLM で回答生成
    GenerateResponse --> ReturnAnswer: 回答返却
    ReturnAnswer --> ProcessQuery: 次の質問
    ReturnAnswer --> [*]: 終了

図で理解できる要点:

  • 初期設定からデータ読み込み、インデックス作成までが一連の流れ
  • 一度インデックスを作成すれば、読み込みから開始できる
  • 質問処理は繰り返し実行可能

まとめ

本記事では、LlamaIndex の 4 つのコア概念について詳しく解説してまいりました。それぞれの役割と連携を理解することで、効率的な RAG システムを構築できるようになります。

4 つのコア概念のまとめ:

#コンポーネント役割主な機能
1Data Loaderデータ読み込み多様なデータソースから統一形式で読み込み
2Indexデータ構造化ベクトル化して検索可能な形式で保存
3Retriever情報検索質問に関連する情報を効率的に取得
4Query Engine回答生成LLM と連携して質問に回答

実装のポイント:

  • Data Loader で多様なデータソースに対応できる
  • Index の永続化により、再起動時の処理を高速化できる
  • Retriever のパラメータ調整で検索精度を向上できる
  • Query Engine のカスタマイズで用途に応じた回答形式を実現できる

LlamaIndex を使用することで、複雑な RAG システムをシンプルなコードで実装できます。まずは小規模なデータセットから始めて、徐々に機能を拡張していくことをお勧めいたします。

今後は、より高度な機能として、カスタムノードパーサーの実装や、複数のインデックスを組み合わせた検索、エージェント機能の活用なども検討してみてくださいね。

関連リンク