Naive RAGが現実の壁にぶつかる時
深夜2時、上司から「昨夜のAppleのイベントについて、ボットの回答がデタラメなんだけど?」とメッセージが届く。AI開発に携わる方なら、誰もが経験のある光景でしょう。たとえ1週間前にデータをVector Databaseにインデックスしていたとしても、時間単位で変化する情報に対して、従来のRAGシステムは無力であり、自信満々に「ハルシネーション(もっともらしい嘘)」を吐き始めます。
問題の本質は、Naive RAGが線形的で受動的なプロセスである点にあります。これを解決するには、Agentic RAGへのアップグレードが必要です。Naive RAGが単なる辞書検索機だとすれば、Agentic RAGは「思考するパートナー」のようなものです。回答する前に、「社内データだけで十分か?Webで再確認する必要はないか?」と自問自答します。
徹底比較:Naive RAG vs Agentic RAG
なぜ、あえて仕組みを複雑にする必要があるのでしょうか?これら2つのアプローチの決定的な違いを見てみましょう。
1. Naive RAG(従来型)
- 処理フロー: Query → Vector Search → LLM → Answer.
- 長所: 非常に高速(通常2秒未満)、運用コストが低い。
- 弱点: 自己検証能力がない. DB内のデータが古い場合、AIは気づかずに誤った結果を返します。
2. Agentic RAG(次世代型)
- 処理フロー: Query → LLMによる推論 → ツールの選択 (DB/Web検索) → 結果の検証 → 必要に応じて繰り返し → 回答。
- 長所: 自己修正能力があり、リアルタイムな世界の知識を統合できる。
- トレードオフ: レイテンシが高い(複雑さに応じて5〜15秒程度)、LLMが多段階で「思考」するためトークン消費量が増える。
なぜLangGraphとTavilyの組み合わせが「最適解」なのか?
かつてLangChainでエージェントを構築すると、コードが「スパゲッティ状態」になり管理が困難になることがよくありました。LangGraphは、その混乱を整理するために登場しました。プロセス全体をステートマシン(状態遷移図)として扱うことで、どのノードの次にどのノードを実行するか、いつ前のステップに戻ってデータを再取得するかを正確に制御できます。
検索エンジンには、Google検索ではなくTavilyを選択します。TavilyはAI専用に最適化されており、不要なHTMLタグではなく、クリーンなテキストと凝縮されたスニペットを返します。これにより、コンテキストウィンドウを最大40%節約し、LLMへのノイズを大幅に削減できます。
詳細な実装ガイド
ステップ1:環境構築
コアライブラリをインストールします。LangGraphの安定性を確保するため、Python 3.9以上を使用することを推奨します。
pip install langchain-openai langgraph tavily-python chromadb langchain-community
OpenAI APIキーとTavily APIキーを用意してください(Tavilyは毎月1,000回まで無料で検索可能です。テストには十分な回数です)。
ステップ2:社内ナレッジベースの構築
ChromaDBを使用して、会社の規定を含む擬似的なVector Databaseを構築します。
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_core.documents import Document
# 社内データのシミュレーション
docs = [
Document(page_content="2024年度のポリシー:従業員には12日間の有給休暇と3日間の特別休暇が付与されます。", metadata={"source": "HR"}),
Document(page_content="KPI賞与は毎年3月の給与時に支払われます。", metadata={"source": "Finance"})
]
vectorstore = Chroma.from_documents(documents=docs, embedding=OpenAIEmbeddings())
retriever = vectorstore.as_retriever()
ステップ3:エージェントの「武器(ツール)」を定義する
エージェントには2つのツールが必要です。1つは社内データを掘り下げるためのもの、もう1つは外部の世界を調べるためのものです。
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain.tools import tool
web_search_tool = TavilySearchResults(k=3)
@tool
def retrieve_internal_info(query: str):
"""会社の規定、ポリシー、社内ドキュメントを検索します。"""
docs = retriever.get_relevant_documents(query)
return "\n".join([d.page_content for d in docs])
tools = [retrieve_internal_info, web_search_tool]
ステップ4:LangGraphによる思考フローの設定
ここでAIの「脳」を設計します。agentノードが行動を決定し、actionノードがツールを実行します。
from langgraph.prebuilt import ToolNode
from langgraph.graph import StateGraph, END
class AgentState(TypedDict):
messages: Annotated[Sequence[BaseMessage], operator.add]
model = ChatOpenAI(model="gpt-4o").bind_tools(tools)
def agent(state):
return {"messages": [model.invoke(state['messages'])]}
workflow = StateGraph(AgentState)
workflow.add_node("agent", agent)
workflow.add_node("action", ToolNode(tools))
workflow.set_entry_point("agent")
def should_continue(state):
last_message = state['messages'][-1]
return "continue" if last_message.tool_calls else "end"
workflow.add_conditional_edges("agent", should_continue, {"continue": "action", "end": END})
workflow.add_edge("action", "agent")
app = workflow.compile()
実力検証:期待を超えるパフォーマンス
「私の休暇ポリシーはどうなっていますか?また、今日の金価格の変動はどうなっていますか?」と尋ねてみましょう。
通常のRAGでは、AIはフリーズするか、後半の質問に対して誤った回答をします。しかし、Agentic RAGであれば、retrieve_internal_infoを起動して有給休暇の日数を取得し、その後にweb_search_toolを呼び出して金価格を確認します。最終的に、それらを統合して、正確で包括的な一つの回答を作成します。
実装における「鉄則」と注意点
多くの実プロジェクトを経て得られた、重要な3つの教訓を共有します。
- 無限ループの防止: アプリを実行する際は、必ず
recursion_limit(10〜15程度)を設定してください。適切な回答が見つからない場合、エージェントが「迷走」してツールを呼び出し続ける可能性があります。 - ツールの説明が鍵: LLMはあなたが記述した説明(docstring)に基づいてツールを選択します。「検索ツール」と書くのではなく、「最新の株価やニュース、時事問題を更新するために使用する」といった具合に具体的に記述しましょう。
- コストの最適化: GPT-4oは非常に強力ですが高価です。単純な推論タスクには
gpt-4o-miniを使用することで、トークンコストを最大80%節約できます。
おわりに
Agentic RAGは単なるコーディング手法ではなく、AIにツールの使い方を教え、情報を検証させるための方法論です。LangGraphとTavilyを組み合わせることで、チャットボットは単なる「オウム返し」を脱却し、真のアシスタントへと進化します。基礎的なRAGをマスターしたなら、今日からAgentic RAGへのアップグレードに挑戦してみてください。ボットが自らデータを探しに行き、回答を完成させる様子を見るのは、本当に素晴らしい体験ですよ!

