複雑な問いをAIが分解して処理するSubQuestionQueryEngineの実装ステップ

比較質問に弱いRAGを救う?SubQuestionQueryEngineの実装と判断基準

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

約12分で読めます
文字サイズ:
比較質問に弱いRAGを救う?SubQuestionQueryEngineの実装と判断基準
目次

はじめに

例えば、「2つの製品のうち、セキュリティ機能で優れているのはどちらか?」と、苦労して構築したRAG(検索拡張生成)システムに問いかけたとき、期待外れの回答が返ってきたことはありませんか。あるいは、「一方の製品については把握していますが、もう一方については情報が見つかりませんでした」といった不完全な回答でお茶を濁された経験があるかもしれません。

複雑な比較質問にRAGが対応できないという課題は、エンタープライズ向けのナレッジベース構築において、多くのプロジェクトが直面する大きな壁です。単純なファクト検索(たとえば特定の製品の価格を問うようなケース)には高精度で答えられるRAGも、複数の情報を統合して比較・分析する必要がある質問には、驚くほど脆いという傾向があります。

これは、テキストの類似度のみに依存する一般的なベクター検索の仕組み上、ある種必然的な課題でもあります。しかし、ビジネスの現場で真に価値があるのは、単なる情報検索ではなく、ユーザーの意思決定を直接的に支援する「洞察」の提供です。AIはあくまでビジネス課題を解決するための手段であり、実用的な価値を生み出さなければ意味がありません。

かつて、この課題を技術的に突破するアプローチとして、LlamaIndexのSubQuestionQueryEngineを活用する手法が広く知られていました。複雑な問いをAIが自ら小さな問いに分解し、それぞれの答えを持ち寄って最終回答を生成するというアプローチです。しかし、生成AI周辺の技術進化は非常に早く、最新のLlamaIndexの設計思想では、固定的なクエリエンジンから、より柔軟なエージェントベースのアプローチ(エージェント型チャンキングや高度なツール呼び出し機能など)への移行が進んでいます。

そのため、旧来の特定の機能やクラスに依存した実装を見直し、よりモダンで柔軟なアーキテクチャへの移行を検討することが推奨されます。機能の統廃合や最新の代替手段、具体的な移行ステップの詳細については、実装前に必ずLlamaIndexの公式ドキュメント(docs.llamaindex.ai)で最新の状況を確認することが不可欠です。

本記事では、比較質問に弱いというRAGの弱点を克服し、「人間のような思考プロセス」をシステムに実装するための現代的なアプローチと、アーキテクチャを選定する際の判断基準について、プロジェクトマネジメントの視点も交えながら論理的に整理します。

なぜRAGは「AとBの違い」に答えられないのか?

実装に入る前に、まずは直面している課題の正体を論理的に整理しておきます。なぜ、標準的なRAG構成では複合的な質問に弱いのでしょうか。

単純なVectorStoreIndexの限界

一般的なRAGの実装では、ユーザーの質問をベクトル化し、データベース内のドキュメントチャンク(断片)と類似度が高いものを検索します(Top-k検索)。

例えば、「2つの製品の違い」について質問したと仮定します。
このとき、検索エンジンはそれぞれの製品名や「違い」というキーワードに関連するチャンクを探しに行きます。しかし、ドキュメントが製品ごとに分かれている場合、検索結果の上位には各製品の概要がバラバラにヒットします。

LLM(大規模言語モデル)に渡されるコンテキストは、これらバラバラの断片です。もし、一方の製品のセキュリティに関する記述が検索順位の低い(Top-kに入らない)場所にあったとしたらどうでしょう。LLMは「情報が足りない」と判断するか、不完全な情報に基づいてハルシネーション(幻覚)を起こす結果につながります。

「問いの分解」が必要な理由

人間がこの質問に答えるとき、無意識に以下のような思考ステップを踏んでいます。

  1. 「まず、一方の製品のセキュリティ機能を調べよう」
  2. 「次に、もう一方の製品のセキュリティ機能を調べよう」
  3. 「最後に、その2つを並べて違いを見つけよう」

つまり、複雑な質問(Complex Query)を、検索可能な単純な質問(Sub Questions)に分解しているのです。

単純なRAGシステムは、この「分解」のプロセスを飛ばして、いきなり答えを探そうとするため失敗します。「2つの製品の違い」という文章そのものに類似したドキュメントは存在しないことが多いからです。

かつてはGraphRAGのような複雑なナレッジグラフ構築アプローチが解決策として議論されることもありました。しかし、技術の移り変わりが激しい中、特定のフレームワークの公式な最新アップデートが不透明な状況では、それに過度に依存したシステム設計はリスクを伴います。現在、より現実的で確実な代替手段として注目されているのが、Amazon Bedrock Knowledge Basesのプレビュー機能(Amazon Neptune Analytics対応)を活用したグラフ構造の取り込みや、自律的に問いを立てるエージェント型RAGへの移行です。構造化されていない情報から高度な推論を行うには、単一の複雑な仕組みに頼るのではなく、検索の前段階でクエリを適切に分解・整理する堅牢なプロセス設計が不可欠だと言えます。

SubQuestionQueryEngineが解決する課題

LlamaIndexが提供するSubQuestionQueryEngineは、まさにこの人間の思考プロセスを模倣するコンポーネントです。ユーザーのクエリを受け取ると、まずLLMを使って「この質問に答えるためには、他にどんな質問を解決すればいいか」を論理的に考え、サブ質問を生成します。

そして、それぞれのサブ質問に対して適切なデータソース(インデックス)へ検索を行い、得られた回答を最後に統合します。これにより、単純な類似性検索では拾いきれない文脈を補完し、精度の高い論理的な回答生成が可能になります。これは一種のエージェント的な挙動であり、複雑なタスクを自律的に処理する現代的なAIアプリケーションの設計思想とも合致する実践的なアプローチです。大がかりなグラフデータベースの構築といった複雑な移行作業を伴わず、既存のシステム構成を活かしながらクエリ分解の恩恵を受けられる点が、現場での導入において大きな強みとなります。

概念理解:SubQuestionQueryEngineの仕組みを「料理長とスタッフ」で理解する

概念理解:SubQuestionQueryEngineの仕組みを「料理長とスタッフ」で理解する - Section Image

技術的な詳細に入る前に、この仕組みを厨房のオペレーションに例えてイメージを固めましょう。システム設計を考える上で、このメンタルモデルは非常に役立ちます。

メインAI(料理長)とサブAI(担当スタッフ)の役割分担

SubQuestionQueryEngineは、巨大なレストランの厨房のようなものです。

  • ユーザー: お客様。例えば「2つのランチメニューのうち、どちらがカロリーが低い?」と注文したとします。
  • SubQuestionQueryEngine(料理長): 全体を取り仕切る司令塔です。自分では直接冷蔵庫(データベース)を探しません。「カロリー比較」というオーダーを受けると、必要な情報を判断し、スタッフに指示を出します。
  • QueryEngineTool(担当スタッフ): 特定の分野に詳しい専門スタッフです。「和食担当(特定のインデックス)」や「洋食担当(別のインデックス)」がいます。

データソースの切り分け方

この仕組みがうまくいくための鍵は、担当スタッフ(データソース)の役割定義にあります。

料理長は、各スタッフが「何が得意か」を知っていなければなりません。LlamaIndexでは、これをToolMetadatadescription(説明文)として定義します。

もし、「和食担当」の胸に「なんでも屋」という名札がついていたら、料理長はいつ彼に指示を出せばいいか迷ってしまいます。「特定の技術仕様について詳しい」という明確な名札(メタデータ)があるからこそ、料理長は「そのスペックなら彼に聞こう」と判断できるのです。

回答統合のプロセス

  1. オーダー受信: お客様から「2つのメニューの比較」という注文が入る。
  2. 指示出し(分解): 料理長は「和食担当、一方のカロリーを教えて」「洋食担当、もう一方のカロリーを教えて」と指示(サブ質問)を出す。
  3. 調理(検索・生成): 各担当スタッフは自分の持ち場(インデックス)から情報を探し、答えを持ってくる。
  4. 仕上げ(統合): 料理長はスタッフから集まった情報を皿に盛り付け、「一方は500kcal、もう一方は600kcalなので、前者の方が低いです」とお客様に提供する。

この一連の流れを自動化するのが、今回の実装対象です。

導入前の比較検討:メリット・デメリットと評価軸

実践ステップ1:環境構築とデータソースの準備 - Section Image 3

「素晴らしい! すべてのRAGをこれに置き換えよう」と考えるのは早計です。プロジェクトマネジメントの観点から言えば、技術選定には常にシビアな視点が求められます。ROI(投資対効果)を最大化するためには、あえてデメリットも含めた客観的な判断基準を持つことが重要です。

RouterQueryEngine vs SubQuestionQueryEngine

似たような仕組みにRouterQueryEngineがあります。これは「質問に対して最適なツールを1つ選ぶ」仕組みです。「特定の製品について教えて」という入力であれば、該当するインデックスだけを的確に参照します。

一方、SubQuestionQueryEngineは「質問を分解して複数のツールを並行して使う」ことができます。複数のドキュメントにまたがる比較質問には後者のアプローチが必須ですが、単一のトピックに関する単純な質問であれば、前者のほうがリソース効率が高く無駄がありません。

精度・速度・コストのトレードオフ

SubQuestionQueryEngineを本番環境へ導入する際の最大の懸念点は、レイテンシー(応答速度)とAPIコストです。内部では以下のような処理が行われています。

  1. 分解ステップ: ユーザーの複雑な質問をサブ質問に分解するためにLLMを呼び出します。
  2. 実行ステップ: 分解された質問(例えば3つ)ごとに、独立した検索処理とLLMによる回答生成が走ります。
  3. 統合ステップ: 最後に得られた複数の回答を一つの自然な文章に統合するために、再度LLMを呼び出します。

単純計算でも、通常のRAGに比べてLLMの呼び出し回数が数倍に膨れ上がります。単純な検索が1秒程度で終わる環境であっても、このプロセスを通すことで数倍の処理時間がかかることは珍しくありません。

また、コストと運用面も無視できない要素です。OpenAIのAPIを利用する場合、2026年2月13日をもって旧モデル(GPT-4oやGPT-4.1など)が廃止され、現在はGPT-5.2(InstantおよびThinking)が主力となっています。公式情報によれば、GPT-5.2は長い文脈理解やツール実行能力が飛躍的に向上し、応答速度の面でも改善が見られます。しかし、SubQuestionQueryEngineの仕組み上、APIコールの回数に比例してトークン消費量が増加する構造自体は変わりません。

さらに、旧モデルの廃止に伴い、システム側でもモデル指定の確実な更新が必須となります。旧モデルに依存したコードが残っているとシステムエラーの原因となるため、計画的な移行作業が求められます。

これらのトレードオフを許容できるユースケースかどうかの見極めが重要です。ユーザーが即答を求めるチャットボットには不向きかもしれませんが、GPT-5.2の高度な構造化能力や要約力を活かした「専門的な調査レポート作成支援」のような業務であれば、数秒の待機時間は十分に許容範囲内と言えるでしょう。

導入すべきユースケース判定表

以下の表を参考に、システム要件と照らし合わせて導入を検討してください。

評価軸 単純なQueryEngine RouterQueryEngine SubQuestionQueryEngine
得意な質問 特定ドキュメントの単純検索 ドキュメントごとの明確な切り分け 複数ドキュメントに跨る比較・分析
応答速度 速い(<2秒) 普通(2-4秒) 相対的に遅い(並列処理で改善可)
コスト 高(APIコール数・トークン消費が増加)
実装難易度 高(モデル移行のメンテナンス含む)
推奨シーン FAQ検索、マニュアル検索 複数製品の個別サポート 競合比較、レポート作成支援、複雑な調査

実践ステップ1:環境構築とデータソースの準備

APIキーの設定 - Section Image

実装の第一歩として、Pythonによる開発環境のセットアップとデータ準備の手順を解説します。複雑な比較質問に対応するRAGを構築するため、ここでは例えば2つの異なる製品の仕様書PDFを比較・検証するケースを想定して進めます。

※前提条件:Python実行環境が整っており、OpenAI APIキーをすでに取得しているものとします。

LlamaIndexのインストールと設定

まず、LlamaIndexの最新バージョンをインストールします。LlamaIndexは非構造化データの接続やエージェント型チャンキングなど、RAGの高度な実装を支える機能が継続的にアップデートされています。そのため、最新の仕様や変更手順については、常に公式ドキュメント(docs.llamaindex.ai)を直接確認することを推奨します。

pip install llama-index llama-index-llms-openai

次に、必要なライブラリをインポートし、APIキーと基本設定を記述します。非同期処理を活用するため、nest_asyncioの設定も追加します。これはJupyter Notebookなどの対話型環境で実行する際に特に重要なステップです。

import os
import nest_asyncio
from llama_index.core import SimpleDirectoryReader, VectorStoreIndex, Settings
from llama_index.llms.openai import OpenAI

# 非同期処理のパッチ適用
nest_asyncio.apply()

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

# LLMの設定(問いの分解には論理的推論が得意なOpenAIの高性能モデルを推奨)
# テスト段階やコストを抑えたい場合は、gpt-4o-miniなどの軽量モデルも選択可能です
Settings.llm = OpenAI(model="gpt-4o-mini")

比較質問に弱いRAGを救う?SubQuestionQueryEngineの実装と判断基準 - Conclusion Image

コメント

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