企業のナレッジマネジメントにおいて、LLMを用いたRAG(Retrieval-Augmented Generation)の導入が急速に進んでいます。しかし、単一の巨大なベクトルデータベースから一度に検索を行う従来の「Naive RAG」方式では、社内規定やPC貸出ルールなど、多種多様なドキュメントが混在する場合に検索ノイズが増加し、検索精度の低下やハルシネーション(根拠のない回答)を招くという課題があります。
この課題を解決する画期的なアプローチが、LlamaIndexを用いた**「Router Agent(エージェントRAG)」**です。ドメインごとにインデックスを分割し、ユーザーの質問をLLMが分析して最適なナレッジソースを自律的に選択・実行させることで、検索精度を飛躍的に向上させることができます。本記事では、LlamaIndexを用いたRouter Agentの構築方法と、外部知識を効率的に取り込む実装ロードマップを解説します。
Router Agentを構成する4つのコアコンポーネント
Router Agentを構築するにあたり、理解しておくべき主要なコンポーネントは以下の4つです。
-
QueryEngine: 各データソース(就業規則、PC貸出ルールなど)に対して個別に構築されたRAG検索エンジンです。 -
QueryEngineTool: クエリエンジンをエージェント用の「ツール」としてカプセル化するクラスです。LLMが自律的に選択できるよう、ツールの役割を「説明(description)」として記述します。 -
OpenAIAgentWorker: OpenAIのFunction Calling機能を利用して、どのツールをどのような引数で呼ぶべきかという「自律的な思考」を管理する推論モジュールです。 -
AgentRunner: エージェントの状態管理やユーザーとの対話ループをコントロールする実行器です。
Router Agent実装の4ステップ
自律的にデータソースを切り替えるエージェントRAGは、以下の4つのステップで実装します。
Step 1: データの読み込みとインデックス化
性質の異なるドキュメント(就業規則のPDFや、PC貸出ルールのテキストなど)をそれぞれ個別に読み込み、個別のベクトルインデックス(VectorStoreIndex)として構築します。
Step 2: 個別クエリエンジンの構築
作成した各インデックスから、自然言語で問い合わせが可能な QueryEngine を作成します。
Step 3: QueryEngineToolによるツール化
各クエリエンジンをツールに変換します。この際、description に詳細な役割を記載することが極めて重要です。LLMはこの説明を読み、ユーザーの質問に対してどちらのツールを使うべきかを判断します。
Step 4: Router Agentの初期化と実行
OpenAIAgentWorker にツール群を渡し、AgentRunner でエージェントを起動します。これにより、ユーザーの質問に応じて自律的にソースを切り替えながら回答する対話エージェントが完成します。
Pythonによる実装コード例
以下は、LlamaIndex(v0.10.x以降)を用いたRouter Agentの具体的な実装例です。
import os
from llama_index.core import SimpleDirectoryReader, VectorStoreIndex, Settings
from llama_index.llms.openai import OpenAI
from llama_index.core.tools import QueryEngineTool, ToolMetadata
from llama_index.agent.openai import OpenAIAgentWorker
from llama_index.core.agent import AgentRunner
# 1. APIキーの設定とLLMの初期化
os.environ["OPENAI_API_KEY"] = "your-openai-api-key"
Settings.llm = OpenAI(model="gpt-4o-mini", temperature=0.1)
# 2. 異なるデータソースの読み込みとインデックスの作成
# data/ 配下に rules.txt (就業規則) と pc_policy.txt (PC貸出ルール) があると想定
rules_docs = SimpleDirectoryReader(input_files=["data/rules.txt"]).load_data()
pc_docs = SimpleDirectoryReader(input_files=["data/pc_policy.txt"]).load_data()
rules_index = VectorStoreIndex.from_documents(rules_docs)
pc_index = VectorStoreIndex.from_documents(pc_docs)
# 3. クエリエンジンの作成
rules_query_engine = rules_index.as_query_engine(similarity_top_k=3)
pc_query_engine = pc_index.as_query_engine(similarity_top_k=3)
# 4. クエリエンジンを「ツール」として定義(ここでの description がルーティングの鍵)
query_engine_tools = [
QueryEngineTool(
query_engine=rules_query_engine,
metadata=ToolMetadata(
name="employment_rules_tool",
description=(
"会社の就業規則、福利厚生、有給休暇、勤務時間などに関する質問に答えるために使用します。"
"入力には、ユーザーの質問をそのままテキストとして指定してください。"
)
)
),
QueryEngineTool(
query_engine=pc_query_engine,
metadata=ToolMetadata(
name="pc_rental_policy_tool",
description=(
"社内PCや周辺機器の貸出ルール、申請手続き、返却期限、紛失時の対応などに関する質問に答えるために使用します。"
"入力には、ユーザーの質問をそのままテキストとして指定してください。"
)
)
)
]
# 5. OpenAIAgentWorker と AgentRunner を用いた自律的エージェントの作成
agent_worker = OpenAIAgentWorker.from_tools(
tools=query_engine_tools,
llm=Settings.llm,
verbose=True # 動作の思考プロセスを表示
)
agent = AgentRunner(agent_worker)
# 6. テスト実行
print("--- クエリ 1: PC貸出ルールへの自動ルーティング ---")
response1 = agent.chat("社内PCの貸出申請はどのように行えばよいですか?期限はありますか?")
print(f"回答: {response1}\n")
print("--- クエリ 2: 就業規則への自動ルーティング ---")
response2 = agent.chat("来月、有給休暇を3日間取得したいのですが、何日前までに申請が必要ですか?")
print(f"回答: {response2}\n")
外部知識を効率的に取り込むための応用アプローチ
構築したRouter Agentを実務レベルへスケールさせ、より複雑な社内ドキュメントや膨大なナレッジベースに対応させるためには、効率的に外部知識を取り込むためのアプローチが必要です。
1. LlamaParseの活用による高度なドキュメントパース
就業規則や申請手順書といった企業文書は、PDF形式で記述されていることが多く、その中には図表や複雑なレイアウトが含まれています。単なるテキストとして抽出すると、ドキュメントの構造が崩れて検索精度が著しく低下します。この課題に対しては、LlamaIndex公式の提供する**LlamaParse**を利用します。データをMarkdown形式にパースしてからインデックス化することで、図表や見出しの構造を維持した高品質な知識ソースを構築し、エージェントの理解度を向上させられます。
2. ObjectIndexによる動的ツール選択(数多くのツールを扱う場合)
登録するツールの数が「経費精算」「セキュリティ規定」「出張申請」など数十個以上に増えると、すべてのツールの説明をLLMのコンテキスト(プロンプト)に含めることができなくなります。これはAPIコストの増加や、LLMが迷ってしまい適切な選択ができなくなる精度の低下を招きます。この場合は、LlamaIndexの**ObjectIndex**機能を使用します。ツール自体をベクトルインデックス化することで、ユーザーの質問に親和性の高い「上位数個のツール」だけを動的に取得し、LLMに渡す構成にできます。
3. LangGraphとの協調による状態管理と自己修正ループ
より複雑な自律型RAG、例えば「検索結果が不十分だった場合に、検索クエリを書き換えて別のツールで再実行する」といった「自己修正ループ」を含む高度なエージェントを構築したい場合があります。このような、複雑な状態管理や反復処理を制御したいときには、LlamaIndexのデータ接続機能と、状態管理フレームワークである**LangGraph**を協調させる設計が強力な解決策となります。