- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 標準RAGが失敗する場面
- GraphRAGとは?(Microsoft Research、2024)
- コア構造の理解
- コードで見るGraphRAG
- ローカル vs グローバル検索:使い分け
- 知識グラフを直接見る
- GraphRAG vs 標準RAG:性能比較
- コストの現実:率直な計算
- GraphRAGが適切な場合 vs 標準RAGで十分な場合
- LightRAG:より実用的な代替案
- まとめ:GraphRAGはツールであり、全ての答えではない
標準RAGが失敗する場面
ベクター検索ベースのRAGを運用していると、特定の質問パターンで一貫して失敗することに気づきます。
標準RAGが壊れる質問:
- 「この四半期のレポート全体から、主なリスク要因を要約して」
- 「製品レビューで繰り返し現れる不満のパターンは?」
- 「CEO交代後の会社戦略の変化を説明して」
- 「製品Aと製品Bの共通する欠陥の特徴は?」
なぜ失敗するのでしょうか?これらの質問に共通するのは、単一のチャンクでは答えられず、ナレッジベース全体を横断する理解が必要という点です。
標準RAGの仕組みを考えると:
- 質問をエンベッド
- コサイン類似度で最も関連性の高いチャンクを3〜5件取得
- そのチャンクから回答を生成
「これらのレポート全体の主要なリスク要因は?」と聞くと、RAGは1つのレポートの1セクションを取得するだけで、全文書にまたがるパターンを見ることができません。これが根本的な限界です。
GraphRAGとは?(Microsoft Research、2024)
Microsoft Researchが2024年に発表した論文「From Local to Global: A GraphRAG Approach to Query-Focused Summarization」(Edge et al., 2024)で紹介された手法です。
コアアイデアはシンプルです:単にテキストをチャンクに分割するのではなく、文書からエンティティと関係を抽出して知識グラフを構築し、その上で検索する。
標準RAG:
文書 → チャンク分割 → エンベッド → ベクターDB → 類似度検索 → 関連チャンク
GraphRAG:
文書 → エンティティ抽出 → 知識グラフ構築 → コミュニティ検出 →
→ 階層的サマリー生成 → 多段階検索(ローカル + グローバル)
コア構造の理解
1. エンティティと関係の抽出
GraphRAGはLLMを使って文書からエンティティ(人物、組織、場所、概念)とその関係を抽出します。
入力テキスト:
「サムスンは2024年にGalaxy S25を発売し、AppleのiPhone 16と直接競合しています。
Galaxy S25はQualcomm Snapdragon 8 Eliteチップを搭載しています。」
抽出されるエンティティ:
- サムスン(組織)
- Galaxy S25(製品)
- Apple(組織)
- iPhone 16(製品)
- Qualcomm(組織)
- Snapdragon 8 Elite(製品/技術)
抽出される関係:
- サムスン --[発売]--> Galaxy S25
- Galaxy S25 --[競合]--> iPhone 16
- Galaxy S25 --[搭載]--> Snapdragon 8 Elite
- Qualcomm --[製造]--> Snapdragon 8 Elite
2. コミュニティ検出
エンティティグラフにLeidenアルゴリズム等を適用し、密接に連結されたエンティティのグループ(コミュニティ)を見つけます。
例:スマートフォン市場コミュニティ、半導体コミュニティ、ソフトウェアエコシステムコミュニティ
3. 階層的サマリー
各コミュニティに対して複数の粒度レベルでサマリーを生成します:
Level 0(最詳細):個別エンティティ/関係のサマリー
Level 1:小規模コミュニティのサマリー
Level 2:中規模コミュニティのサマリー(最もよく使われる)
Level 3(最包括的):グローバルトピックのサマリー
コードで見るGraphRAG
Microsoftの公式graphragライブラリを使います。
# インストール
pip install graphrag
# プロジェクト初期化
mkdir my-graphrag-project
graphrag init --root ./my-graphrag-project
生成されるsettings.yamlの主要設定:
# settings.yamlの主要設定
llm:
api_key: ${GRAPHRAG_API_KEY}
type: openai_chat
model: gpt-4o-mini # インデックス作成用(コスト削減のためmini使用)
model_supports_json: true
embeddings:
llm:
model: text-embedding-3-small
input:
type: file
file_type: text
base_dir: "input" # ここにドキュメントを配置
chunks:
size: 1200
overlap: 100
# インデックス作成(知識グラフの構築)
# graphrag index --root ./my-graphrag-project
# 内部的に行われること:
# 1. ドキュメントのチャンク分割
# 2. エンティティと関係の抽出(LLMの呼び出し)
# 3. グラフの構築
# 4. コミュニティ検出の実行
# 5. 各コミュニティのサマリー生成(LLMの呼び出し)
# Python APIでのクエリ
import asyncio
from graphrag.query.api import local_search, global_search
# ローカル検索:特定エンティティ/関係中心の質問
async def search_local(query: str):
result = await local_search(
config_dir="./my-graphrag-project",
data_dir="./my-graphrag-project/output",
root_dir="./my-graphrag-project",
community_level=2,
response_type="multiple paragraphs",
query=query,
)
return result.response
# グローバル検索:ナレッジベース全体の合成が必要な質問
async def search_global(query: str):
result = await global_search(
config_dir="./my-graphrag-project",
data_dir="./my-graphrag-project/output",
root_dir="./my-graphrag-project",
community_level=2,
response_type="multiple paragraphs",
query=query,
)
return result.response
# 使用例
local_result = asyncio.run(search_local(
"サムスンとTSMCの関係はどうなっていますか?"
))
global_result = asyncio.run(search_global(
"これらの文書における半導体産業の主なトレンドは何ですか?"
))
ローカル vs グローバル検索:使い分け
2つのクエリモードを理解することがGraphRAG活用の鍵です。
| クエリタイプ | 最適モード | 例 |
|---|---|---|
| 特定エンティティへの質問 | ローカル検索 | 「田中さんの役割は?」 |
| 2エンティティの関係 | ローカル検索 | 「サムスンとTSMCの関係は?」 |
| 全体的なトレンド把握 | グローバル検索 | 「この文書群の主なテーマは?」 |
| 文書横断のパターン分析 | グローバル検索 | 「業界全体のリスク要因は?」 |
| 時系列の変化 | 両方を活用 | 「5年間の戦略変化は?」 |
ローカル検索の内部処理:
- クエリから関連エンティティを特定
- そのエンティティに連結したテキストチャンク、関係、コミュニティサマリーを収集
- LLMで最終回答を生成
グローバル検索の内部処理:
- 全コミュニティサマリーを複数の「バッチ」に分割
- 各バッチに対して部分回答を生成(Mapフェーズ)
- 部分回答を統合して最終回答を生成(Reduceフェーズ)
知識グラフを直接見る
GraphRAGが生成したグラフを可視化することで洞察が得られます。
import pandas as pd
import networkx as nx
# GraphRAG出力ファイルからエンティティと関係をロード
entities_df = pd.read_parquet("./output/entities.parquet")
relationships_df = pd.read_parquet("./output/relationships.parquet")
print(f"総エンティティ数:{len(entities_df)}")
print(f"総関係数:{len(relationships_df)}")
# NetworkXグラフの構築
G = nx.DiGraph()
for _, entity in entities_df.iterrows():
G.add_node(entity["title"], type=entity["type"])
for _, rel in relationships_df.iterrows():
G.add_edge(
rel["source"],
rel["target"],
weight=rel["weight"],
description=rel["description"]
)
# 最も連結度の高いエンティティ(ハブ)を探す
top_hubs = sorted(G.degree(), key=lambda x: x[1], reverse=True)[:10]
print("最も重要なエンティティ Top 10:")
for entity, degree in top_hubs:
print(f" {entity}:{degree}個の接続")
GraphRAG vs 標準RAG:性能比較
Microsoftの原論文より(HotPotQA、MuSiQueデータセット):
グローバルクエリ(全体要約が必要):
- 標準RAG: 網羅性40%、多様性57%
- GraphRAG: 網羅性72%、多様性62%
→ 網羅性が80%向上!
ローカルクエリ(特定事実の検索):
- 標準RAG: 精度 約65%
- GraphRAG: 精度 約70%
→ わずかな改善
速度:
- 標準RAG: 0.5〜2秒
- GraphRAG: 3〜10秒(コミュニティサマリーの集約のため)
結論:グローバルクエリではGraphRAGが圧倒的に優れますが、特定の事実検索では標準RAGと同程度か遅くなります。
コストの現実:率直な計算
GraphRAGの最大の欠点はインデックス作成コストです。
# GraphRAGインデックスコストの見積もり
# 前提:1,000件のドキュメント、各1,000トークン
num_documents = 1000
tokens_per_doc = 1000
total_tokens = num_documents * tokens_per_doc # 100万トークン
# インデックス作成でのLLM呼び出し回数の見積もり
entity_extraction_calls = total_tokens / 1200 # チャンクごとに1回
# 各呼び出し:約800入力トークン + 約400出力トークン
community_summary_calls = 200 # コミュニティ数 × レベル
# 各呼び出し:約2,000入力トークン + 約500出力トークン
# GPT-4o-mini基準($0.15/1M入力、$0.60/1M出力)
entity_input_cost = (entity_extraction_calls * 800 / 1_000_000) * 0.15
entity_output_cost = (entity_extraction_calls * 400 / 1_000_000) * 0.60
community_cost = (community_summary_calls * 2500 / 1_000_000) * 0.40
total_indexing_cost = entity_input_cost + entity_output_cost + community_cost
print(f"推定インデックスコスト:${total_indexing_cost:.2f}")
# 1,000件のドキュメント:約$1〜5(GPT-4o-mini使用時)
# 10,000件のドキュメント:約$10〜50
# 100,000件のドキュメント:約$100〜500
# クエリコスト(グローバル検索)
# グローバル検索は各クエリで全コミュニティサマリーを処理
# 200コミュニティ × 500トークン = クエリあたり約10万トークン処理
global_query_cost = (100_000 / 1_000_000) * 2.50 # GPT-4o価格
print(f"グローバル検索のクエリあたりコスト:${global_query_cost:.3f}") # $0.25/クエリ
この費用が許容できるかの判断基準:
- 初期インデックス作成:文書が頻繁に変更されない場合は一回限りのコスト
- クエリコスト:グローバル検索は標準RAGの10〜100倍高くなることがある
GraphRAGが適切な場合 vs 標準RAGで十分な場合
GraphRAGが価値を発揮する場合:
- 数百〜数千件の文書にまたがるパターン/トレンド分析
- 財務報告書、特許、法律文書の分析
- ナレッジベースがほぼ変更されない場合(インデックスコストが一回限り)
- 「この会社について何を知っているか?」のような広範な質問が多い場合
標準RAGで十分な場合:
- 特定の事実検索(「この契約書の有効期限は?」)
- 高速レスポンスが必要なリアルタイムサービス
- 文書が頻繁に更新される場合(再インデックスのコスト)
- 小規模ナレッジベース(100件未満)
- コスト予算が厳しい場合
LightRAG:より実用的な代替案
MicrosoftのGraphRAGが複雑すぎるか費用が高いと感じるなら、LightRAGを検討してください。
# pip install lightrag-hku
from lightrag import LightRAG, QueryParam
from lightrag.llm import gpt_4o_mini_complete
rag = LightRAG(
working_dir="./lightrag-storage",
llm_model_func=gpt_4o_mini_complete,
)
# ドキュメントの追加
with open("document.txt", "r") as f:
rag.insert(f.read())
# クエリ(naive、local、global、hybridの4モード)
result = rag.query(
"主なトレンドは何ですか?",
param=QueryParam(mode="global") # hybridモードも効果的
)
LightRAGは実装がシンプルで費用も安く、小〜中規模のナレッジベースでは十分な性能を発揮します。
まとめ:GraphRAGはツールであり、全ての答えではない
GraphRAGは強力ですが、全ての状況に適しているわけではありません。
重要なポイント:
- グローバルクエリでは標準RAGを大きく上回る
- インデックスコストが高いため、ナレッジベースが安定している場合に適している
- クエリコストも標準RAGより高い
- 手軽に始めるにはMicrosoftの
graphragライブラリまたはLightRAGを活用
投資対効果を考えましょう。「このコーパス全体のパターンを理解する必要がある」という明確な要件がある時にGraphRAGを導入し、特定の事実検索が主な用途なら、よく調整された標準RAGの方がコスト効率が良いです。