LlamaIndexのクエリエンジンを活用した高精度RAGシステムの構築手法

LlamaIndex Query Engine API詳解:RAG精度を支配する制御変数の全貌

この記事は急速に進化する技術について解説しています。最新情報は公式ドキュメントをご確認ください。

約20分で読めます
文字サイズ:
LlamaIndex Query Engine API詳解:RAG精度を支配する制御変数の全貌
目次

「チュートリアル通りにRAGを構築してみたものの、実務で求められるレベルの回答精度に到達しない」

AI導入プロジェクトにおいて、PoC(概念実証)から本番運用へ移行する際、このような壁に直面する開発チームは決して珍しくありません。LlamaIndexは非常に洗練されたフレームワークであり、わずか数行のコードを記述するだけで「動くRAG」を構築できます。しかし、プロジェクトマネジメントの観点から見ると、その圧倒的な手軽さこそが落とし穴にもなります。抽象化が進んでいるゆえに、裏側でどのようなデータ処理や検索プロセスが実行されているかが見えにくく、いざ精度を向上させようとした時に「具体的にどのパラメータを調整すべきか分からない」という膠着状態に陥りやすいのです。

例えば、「無関係なドキュメントを参照してハルシネーションを起こす」「非構造化データへの接続が不十分で回答がズレる」「レスポンスが遅すぎてユーザー体験を損なう」といった課題です。AIはあくまでビジネス課題を解決するための手段であり、ROI(投資対効果)を最大化するためには、これらの課題を体系的に解決しなければなりません。近年のRAG領域ではエージェント型チャンキングなどの高度な手法も注目を集めていますが、こうした複雑な問題は単純なプロンプトの微調整だけでは解決しないケースがほとんどです。根本的な改善を図るには、RAGパイプラインの中核を担う「Query Engine(クエリエンジン)」のアーキテクチャと制御フローを正確に把握する必要があります。また、フレームワークの仕様は継続的にアップデートされるため、公式ドキュメントで最新のクラス構成や変更点を定期的に確認するアプローチも不可欠です。

本記事では、開発プロセスで不透明になりがちなQuery Engineの内部構造を論理的に解剖します。検索精度と生成品質を自在にコントロールし、実用的なAIシステムを構築するためには、どのクラスのどのパラメータに介入すべきなのか。具体的なAPIの挙動やデータフローの視点から、実務で直面する精度向上のボトルネックを解消するための実践的なアプローチを提示します。

LlamaIndex Query Engine API概論

まず、LlamaIndexにおける「Query Engine」とは何者なのか、技術的な定義から整理します。これを単なる「検索機能」と捉えると実態を見誤ってしまいます。Query Engineは、検索(Retrieval)と生成(Synthesis)を統合管理し、ユーザーからの入力を最終的な出力へと変換するオーケストレーターとして機能します。RAGパイプラインの心臓部と言える重要なコンポーネントです。

BaseQueryEngineのクラス階層と役割

すべてのクエリエンジンは、抽象基底クラスであるBaseQueryEngineを継承して実装されています。このクラスが保証する契約(インターフェース)は極めてシンプルです。「自然言語のクエリ文字列(str)またはQueryBundleを受け取り、最終的にResponseオブジェクトを返す」。基本的にはこれだけです。

しかし、その内部では以下の複雑なプロセスがカプセル化され、自動的に実行されています。

  1. クエリ処理: ユーザーの質問文に対する前処理や埋め込み(Embedding)ベクトル化
  2. 検索実行: RetrieverによるVector Storeからの関連ノード(Chunk)取得
  3. 後処理: Node Postprocessorによるノードの選別・加工(リランキング、フィルタリング等)
  4. 回答合成: Response Synthesizerによるプロンプト生成とLLMの実行

開発者やプロジェクトマネージャーとして重要なのは、このパイプラインの中で「どこまでをフレームワークのデフォルトに任せ、どこを手動で制御(Customization)するか」を見極めることです。開発リソースと期待する精度のバランスを取ることが、プロジェクト成功の鍵となります。LlamaIndexはRAG特化フレームワークとして進化を続けており、内部処理は高度に最適化されています。

また、回答合成フェーズで利用するLLMのモデル選定には注意が必要です。公式情報によると、OpenAI APIを利用する場合、2026年2月13日をもってGPT-4oやGPT-4.1、o4-miniといった旧モデルが廃止されました。現在は、長い文脈理解やツール実行能力が向上したGPT-5.2(InstantおよびThinking)が主力モデルへと移行しています。LlamaIndexの実装において、廃止された旧モデルをハードコードしていると実行時エラーとなるため、速やかにGPT-5.2などの最新モデルへ設定を更新し、移行を完了させることが不可欠です。

同期実行(query)と非同期実行(aquery)の仕様

Web APIとしてRAGシステムを提供する際、最初に見直すべきアーキテクチャ上のポイントが実行メソッドの選択です。プロダクション環境では、スループット(単位時間あたりの処理能力)がユーザー体験と直結し、システムのビジネス価値を大きく左右します。

  • query(str_or_query_bundle): 同期実行メソッドです。Jupyter Notebookでの実験的なコード実行や、順次処理で問題ないバックグラウンドのバッチ処理に向いています。しかし、サーバーサイドのAPIでこれを使用すると、LLMの応答待ちやデータベースへの問い合わせの間、スレッドが完全にブロックされてしまいます。
  • aquery(str_or_query_bundle): asyncioを用いた非同期実行メソッドです。FastAPIなどの非同期対応フレームワークでRAGを構築する場合は、迷わずこちらを選択してください。LLMのAPI呼び出しやVector DBへのI/O待ち時間をノンブロッキングで効率的に処理できるため、複数ユーザーからの同時アクセス時におけるパフォーマンスが劇的に向上します。

StreamingResponseの扱い方

ChatGPTのUIで馴染み深い「文字がパラパラとリアルタイムに表示される」体験は、ユーザーの体感待ち時間(TTFB: Time To First Byte)を減らし、AIアプリケーションの定着率を高める上で非常に有効なアプローチです。GPT-5.2への移行によりLLM自体の生成速度や応答性能は向上していますが、複雑な推論(Thinkingプロセス)を伴うタスクや長文生成時の待機ストレスを軽減するためには、依然としてストリーミングが必須のテクニックと言えます。

LlamaIndexにおいて、これはメソッドの引数で都度指定するのではなく、エンジンの初期化時に設定を組み込むのが一般的です。

streaming=Trueを設定して初期化すると、戻り値のResponseオブジェクトはイテレータとして振る舞うようになります。response_gen属性を通じて生成されたトークンを順次取得できるため、フロントエンドに対してSSE(Server-Sent Events)などでリアルタイム転送する仕組みが容易に構築可能です。

※APIの細かな仕様やクラス名はバージョンアップにより変更される可能性があります。実装の際は必ずLlamaIndex公式ドキュメントで最新情報をご確認ください。

RetrieverQueryEngine:基本構成要素の仕様解説

LlamaIndexでRAG(検索拡張生成)システムを構築する際、多くの場合で中核を担うのがRetrieverQueryEngineクラスです。このクラスはコンポーネント指向で設計されており、各パーツをブロックのように柔軟に組み合わせたり差し替えたりすることで、システムの挙動を細かくカスタマイズできます。

コンストラクタ引数詳解

インスタンス化の際に渡す引数は、RAGのパフォーマンス(回答精度と処理速度)を大きく左右します。ここでは、特に重要な主要コンポーネントの役割を整理します。

  • retriever (BaseRetriever):
    必須のコンポーネントです。「どのデータソースから(Index)」「どのような手法で(ベクトル検索やキーワード検索など)」「どれだけの件数を(top_k)」取得するかを決定します。この段階で必要な情報を取りこぼすと、後続の処理で挽回することは不可能なため、非常に重要な役割を持ちます。

  • response_synthesizer (BaseSynthesizer):
    任意のコンポーネントですが、回答の質を高める上で欠かせません。検索して取得したドキュメント群を「どのようにLLM(大規模言語モデル)に解釈させるか」を制御します。指定を省略した場合はデフォルトの動作(通常はcompactモード)が適用されますが、より高い精度や特定のフォーマットを追求するのであれば、明示的なチューニングが不可欠です。

  • node_postprocessors (List[BaseNodePostprocessor]):
    精度向上のための最大のチューニングポイントと言える設定項目です。検索結果のチャンク(Nodes)に対して、関連度スコアによる足切り(フィルタリング)や、より高度な再ランク付け(Rerank)を行うプロセッサのリストを指定します。

node_postprocessorsの設定と実行順序

このリストに渡されたPostprocessorは、指定した順序通りにシーケンシャル(直列)に実行されます。この仕様を正しく理解し活用することで、APIコストの削減とレスポンスタイムの向上という、プロジェクトの重要KPIに直結する大幅な最適化が可能になります。

例えば、[SimilarityPostprocessor(similarity_cutoff=0.7), LLMRerank(top_n=3)]という順序でプロセッサを設定したと仮定します。

  1. まず、類似度スコアが0.7未満の関連性の低いノイズデータを、計算コストの安いSimilarityPostprocessorで除外します。
  2. 次に、残った有望なノードのみを対象として、計算コスト(API料金や処理時間)の高いLLMRerankを実行し、最終的な上位3件に絞り込みます。

もしこの順序を逆にしてしまうと、不要なノイズデータに対しても高価なRerank処理が実行されてしまい、APIコストとレイテンシ(遅延)が無駄に増加してしまいます。「計算の軽い処理で絞り込んでから、重い処理を適用する」というパイプライン設計の鉄則を守ることが重要です。

callback_managerによる可観測性の確保

RAGシステムが「なぜその回答を生成したのか」という根拠やプロセスを追跡できないと、運用時のデバッグは困難を極め、保守コストの増大を招きます。callback_managerを適切に設定することで、LangSmithやLlamaTraceといった専用のトレーシングツールへ内部イベントをリアルタイムに送信できます。

特に近年のLangSmithなどのツールは、単なるログ収集から大きく進化しています。例えば、取得したトレースデータを「Agent Builder」によるAIエージェントの構築やMemory(記憶)機能の改善に直結させたり、「Aligned Evals」機能を用いて人間の評価とLLM-as-a-Judge(LLMによる自動評価)の基準をすり合わせたりすることが可能です。さらに、MCP(Model Context Protocol)を活用したツール連携により、CLIからトレースを取得してコードの自動修復を支援するといった高度な運用も一般的になりつつあります。

どのドキュメントがRetrieve(検索)され、どのNodeがPostprocess(後処理)で除外され、最終的なプロンプトがどう構築されたのか。この可観測性(Observability)を確保し、データ駆動でシステムを反復改善できる状態を作ることが、PoCの壁を越えて本番環境で安定稼働させるための最短ルートとなります。

高度なQuery Engineクラスの比較と選定

RetrieverQueryEngine:基本構成要素の仕様解説 - Section Image

単純な「質問→検索→回答」のフローでは太刀打ちできない複雑なタスクもあります。そのような場合に検討すべき、専用のQuery Engineクラス群を紹介します。

SubQuestionQueryEngine:複雑な質問の分解

例えば、「第一事業部と第二事業部の2023年の売上を比較して」といった複合的な質問に対し、単一のベクトル検索では両方の情報が十分に取れないことがあります。

SubQuestionQueryEngineは、LLMを使って元の質問を「第一事業部の2023年の売上は?」「第二事業部の2023年の売上は?」というサブ質問に分解し、それぞれ検索を実行します。

  • メリット: 複雑な質問に対する網羅性が高い。
  • デメリット: LLM呼び出し回数が増えるため、レスポンスが遅くなる。また、分解の精度自体がLLMの能力に依存する。

RouterQueryEngine:複数エンジンの動的切り替え

質問の種類に応じて、使用するツールやインデックスを切り替えたい場合に有効です。例えば、要約タスクなら「要約エンジン」、詳細検索なら「ベクトル検索エンジン」へと振り分けます。

  • selector: クエリを分析し、最適なツールを選択するモジュール。LLMSingleSelector(LLMが判断)やPydanticSingleSelector(構造化出力で判断)などがあります。

LLMベースのセレクターは柔軟ですが遅延が発生します。キーワードマッチングのような軽量なロジックで代用できないか、設計段階で検討する価値があります。

MultiStepQueryEngine:段階的推論の仕様

一度の検索で答えが出ない難問に対し、中間的な回答を生成し、それに基づいて次の検索を行う「Chain-of-Thought(思考の連鎖)」的なアプローチです。

研究用途や、非常に深い調査が必要なエージェントシステムでは強力ですが、一般的なQ&Aボットとしては応答時間が長すぎる(数十秒〜数分)ケースが多いため、ビジネス要件とユーザー体験のバランスを慎重に見極め、オーバースペックにならないよう注意する必要があります。

Node Postprocessor API:精度向上のためのフィルタリング仕様

RAGの精度改善において、多くのプロジェクトで真っ先に着手されるのがこのNodePostprocessorの調整です。ベクトルデータベースから検索された「生の素材(ノード)」を、LLMに渡して回答を生成する前に厳選する工程と言えます。AI導入において「Garbage in, garbage out(無価値なデータからは無価値な結果しか得られない)」と言われるように、この「下ごしらえ」の質が最終的な出力の精度を大きく左右します。最新のRAGアーキテクチャでは、単なる検索だけでなく、このフィルタリングプロセスをいかに最適化するかが重要視されています。

SimilarityPostprocessor:類似度スコアによる足切り

ベクトル検索の特性上、どんなにクエリと無関係な情報であっても、システムは計算上「最も近い」と判断したドキュメントを返してしまいます。この無関係なコンテキストの混入が、LLMによるハルシネーション(幻覚)を引き起こす主要因となります。

  • similarity_cutoff: 類似度の閾値を0.0〜1.0の範囲で設定し、基準に満たないノードを切り捨てます。例えばOpenAIのEmbeddingモデル(text-embedding-3-smallなど)を使用する場合、一般的に0.75〜0.80付近がノイズを除外する有効な境界線となる傾向があります。ただし、最適な数値は扱うデータのドメイン(専門用語の多さなど)や選択した埋め込みモデルによって大きく変動します。そのため、本番環境への導入前には、必ず実際の評価データセットを用いた入念なチューニングが不可欠です。

KeywordNodePostprocessor:必須/除外キーワード制御

ベクトル検索が持つ「意味的な近さ」の判定だけでは、業務上の厳密な要件を完全に捕捉できないケースは珍しくありません。そこで、古典的でありながら確実なキーワードマッチングを組み合わせることで、検索結果を補完・制御します。

  • required_keywords: ノード内に必ず含まれていなければならない単語のリストを指定します。
  • exclude_keywords: ノード内に含まれていてはいけない単語のリストを指定します。

具体的な活用例として、社内規定を参照するRAGシステムにおいて、「旧規定」や「廃止」といった単語が含まれる古い情報を強制的に除外する運用などが挙げられます。すべてをAIの確率的な判断に任せるのではなく、コンプライアンスや業務上絶対に外せない確実なルールを明示的に適用できる点は、エンタープライズシステムにおいて極めて重要です。

LLMRerank:LLMを用いた再ランク付けの引数設定

Cohere RerankやColBERTなどの専用モデル、あるいはLLM自体を活用して、一度取得した検索結果の関連度をさらに高度なロジックで再評価(リランキング)します。これはRetrieve(検索)の精度を劇的に向上させる、現代のRAGパイプラインにおける強力な切り札です。

  • top_n: リランク処理の後に最終的に残すノード数を指定します。一般的なベストプラクティスとしては、最初のRetrieverフェーズで多めにノードを取得(top_k=10〜20程度)しておき、このRerankフェーズで真に関連性の高い精鋭ノード(top_n=3〜5程度)に絞り込む構成が推奨されます。これにより、計算コストを抑えつつ高い精度を維持する最適なバランスを実現できます。
  • choice_batch_size: LLMに一度の処理で渡すノードの数を制御します。モデルのコンテキストウィンドウの上限を超えてエラーが発生するのを防ぎ、効率的なバッチ処理を行うために細かく調整します。

Response Synthesizer API:回答生成の制御

Node Postprocessor API:精度向上のためのフィルタリング仕様 - Section Image

検索結果がどれほど完璧であっても、それをLLMにどのように「読ませる」かによって、最終的な回答の質は大きく変動します。Response Synthesizerは、検索で得られたContext(コンテキスト)とユーザーからのPrompt(指示)を結合する戦略を決定する、RAGパイプラインの最終関門です。ここで適切な設定を行うことが、幻覚(ハルシネーション)の抑制やトークン消費量の最適化に直結します。

response_modeの種類と挙動

主要なモードの挙動を正しく理解し、システムの要件に合わせて最適なものを選択することが重要です。最新の仕様や追加されたモードについては、公式ドキュメント(docs.llamaindex.ai)も併せて確認することをお勧めします。

  1. compact (デフォルト):
    可能な限り多くのノードを連結し、コンテキストウィンドウの制限内で1つのプロンプトに詰め込みます。LLMへのAPI呼び出し回数を最小化できるため、ランニングコストと応答速度のバランスに優れています。プロジェクトの初期段階では、まずこのモードから検証を始めるのが定石です。

  2. refine:
    ノードごとに順次LLMを呼び出し、回答を段階的に洗練(Refine)させていくアプローチです。「1つ目のノードを見て初期回答を作成」し、「2つ目のノードを見てその回答を修正・追記」するという処理を繰り返します。非常に詳細で精度の高い回答が得られる反面、ノードの数だけAPIコールが直列で発生するため、応答遅延(レイテンシ)が大きくなる点に注意が必要です。

  3. tree_summarize:
    ノード群から部分的な要約を並列で作成し、それらをツリー状に統合して最終的な回答を生成します。複数の異なるドキュメントから情報を抽出し、網羅的な要約を作成するタスクに極めて有効です。複雑な情報統合を伴うエージェント的なアプローチを組み込む際にも、強力な基盤として機能します。

structured_answer_filteringの仕様

「回答は必ずJSON形式で出力してほしい」「指定されたステータスコードの中から選んでほしい」といった、構造化されたデータ出力が求められる場面で活躍する機能です。内部的にPydanticを活用し、LLMの出力が事前に定義したスキーマに適合するかを厳密に検証します。

もし出力フォーマットが崩れていた場合には、再生成を試みるリトライロジックが組み込まれています。そのため、単なるチャットボットの枠を超え、後続のシステムやアプリケーションとのAPI連携を前提とするRAGシステムを構築する際には、必須のオプションと言えます。

プロンプトテンプレートの注入方法

LlamaIndexに組み込まれているデフォルトのプロンプトは英語ベースで設計されていることが多いため、精度の高い日本語RAGを実現するにはプロンプトの上書きが不可欠です。

具体的には、get_response_synthesizerファクトリ関数のtext_qa_templateおよびrefine_template引数に対して、日本語で最適化したPromptTemplateオブジェクトを渡します。これにより、LLMに対する指示を根底からカスタマイズできます。

「あなたはプロのコンサルタントとして振る舞ってください」「回答は必ず日本語の敬体で記述し、不明な場合は推測せず『わからない』と答えてください」といったペルソナ設定や厳密な制約条件も、ここで一元管理することで、安定した出力を担保できます。

実装パターン別パラメータ設定リファレンス

ここまで解説したAPI仕様を踏まえ、実際のプロジェクト要件に応じた推奨設定パターンをコード例と共に紹介します。ビジネス課題に合わせて最適なアーキテクチャを選択してください。パラメータの組み合わせ次第で、RAGの挙動とパフォーマンスは大きく変化します。

高精度重視型(Re-ranking + Tree Summarize)の設定例

契約書レビューや技術文書検索など、情報の正確性が最優先され、多少の計算コストや処理時間は許容されるケースに最適です。

from llama_index.core import VectorStoreIndex, get_response_synthesizer
from llama_index.core.retrievers import VectorIndexRetriever
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core.postprocessor import SimilarityPostprocessor, LLMRerank

# 1. 広めに検索 (top_k=20)
retriever = VectorIndexRetriever(
    index=index,
    similarity_top_k=20,
)

# 2. 厳密にフィルタリングと並び替え
# ノイズとなる情報を除去してから、高精度なリランクにかける
postprocessors = [
    SimilarityPostprocessor(similarity_cutoff=0.78),
    LLMRerank(top_n=5, llm=gpt4_llm)  # 高性能なLLMで文脈を評価し選別
]

# 3. 情報を統合して回答 (tree_summarize)
# 複数の文脈を階層的に統合し、網羅的で要約的な回答を生成
response_synthesizer = get_response_synthesizer(
    response_mode="tree_summarize",
    summary_template=custom_jp_template
)

query_engine = RetrieverQueryEngine(
    retriever=retriever,
    node_postprocessors=postprocessors,
    response_synthesizer=response_synthesizer,
)

この構成では、最初に多くの候補を取得し、LLMを用いたリランキングで本当に必要な情報だけを抽出します。最新のRAGアーキテクチャにおいても、この「広めに拾って厳密に絞る」アプローチは回答精度の要となります。

低遅延重視型(Compact + Streaming)の設定例

社内ヘルプデスクやカスタマーサポートのチャットボットなど、レスポンス速度がユーザー体験(UX)に直結するケースに適しています。

# 1. 必要最小限を検索 (top_k=3)
# 取得数を絞ることで検索と生成の処理時間を短縮
retriever = VectorIndexRetriever(
    index=index,
    similarity_top_k=3,
)

# 2. 軽量なフィルタリングのみ
# LLMを使わない、計算コストの低いベクトル類似度フィルタのみを採用
postprocessors = [
    SimilarityPostprocessor(similarity_cutoff=0.75)
]

# 3. 最速モードでストリーミング (compact)
# コンテキストを可能な限り1つのプロンプトに詰め込み、ストリーミングで即座に表示開始
response_synthesizer = get_response_synthesizer(
    response_mode="compact",
    streaming=True,
    text_qa_template=custom_jp_template
)

query_engine = RetrieverQueryEngine(
    retriever=retriever,
    node_postprocessors=postprocessors,
    response_synthesizer=response_synthesizer,
)

検索範囲を限定し、LLMによる評価プロセスを省くことで遅延を最小限に抑えます。ユーザーを待たせないことが重要なシステムでは、このシンプルで高速なパイプラインが威力を発揮します。

デバッグ・評価用設定(CallbackManager + Tracing)

開発フェーズや精度改善のプロセスでは、システム内部で何が起きているかを可視化することが不可欠です。以下のように設定することで、処理のトレースが可能になり、ボトルネックの特定が容易になります。

from llama_index.core.callbacks import CallbackManager, LlamaDebugHandler
import llama_index.core

# デバッグハンドラのセットアップ
# 処理の開始と終了、各ステップの入出力をコンソールに出力
llama_debug = LlamaDebugHandler(print_trace_on_end=True)
callback_manager = CallbackManager([llama_debug])

# グローバル設定に適用(推奨)
llama_index.core.Settings.callback_manager = callback_manager

# またはQuery Engine構築時にコンテキスト経由で明示的に渡すことも可能

トレース情報を確認することで、意図しないドキュメントが検索されていないか、プロンプトに正しいコンテキストが渡されているかを正確に把握できます。

まとめ

取得数を絞ることで処理時間を短縮 - Section Image 3

LlamaIndexのQuery Engineは、デフォルト設定のままでも手軽に動作します。しかし、それではRAGの真のポテンシャルを引き出せているとは言えません。RetrieverQueryEngineを中心に、NodePostprocessorによるノイズの除去、Response Synthesizerによる回答生成戦略の最適化を組み合わせることで、初めて業務の要求水準を満たす精度とパフォーマンスに到達します。

特に重要なのは、「パラメータ一つでパイプライン全体の挙動とコストが劇的に変化する」という事実です。今回解説したAPIの仕様を深く理解し、自社のデータ特性やビジネス要件に合わせて適切なクラスと引数を選定することは、プロジェクトマネジメントの観点からも、ROI(投資対効果)を最大化するための重要なポイントとなります。

実際の開発現場において、「自社のデータにはどの設定パターンが最適かわからない」「パラメータ調整を繰り返しても精度が頭打ちになっている」という課題に直面することは珍しくありません。PoC(概念実証)に留まらず、実用的なAI導入を成功させるためには、技術的なチューニングだけでなく、ビジネス課題の解決を第一に考えたアプローチが求められます。そのような場合は、専門家への相談で導入リスクや開発の停滞を軽減できます。個別のデータ特性やシステム要件に応じた客観的なアドバイスを得ることで、ボトルネックの根本的な原因を特定し、より効果的なアーキテクチャ設計が可能になります。精度の高いRAGシステム構築への確実なステップとして、外部の専門的な知見を活用することも有効な手段です。

LlamaIndex Query Engine API詳解:RAG精度を支配する制御変数の全貌 - Conclusion Image

コメント

コメントは1週間で消えます
コメントを読み込み中...