論文概要(Abstract)
本論文は、大規模テキストコーパス全体にわたる「グローバルな問い」に対応するため、LLMを用いてナレッジグラフを事前構築し、Leidenアルゴリズムによるコミュニティ検出とMapReduce的な2段階要約で回答を生成するGraphRAGを提案する。従来のNaive RAGが局所的なチャンク検索に限定される一方、GraphRAGはコーパス全体のテーマ・構造・関係性を俯瞰する問いに対して包括性(Comprehensiveness)で72%の勝率を達成した。Microsoft Researchが開発し、NAACL 2025に採択されている。
この記事は Zenn記事: LlamaIndex v0.14実践ガイド:AgentWorkflowで本番RAGを構築する の深掘りです。
情報源
- arXiv ID: 2404.16130
- URL: https://arxiv.org/abs/2404.16130
- 著者: Darren Edge, Ha Trinh, Newman Cheng, Joshua Bradley, Alex Chao, Apurva Mody, Steven Truitt, Jonathan Larson (Microsoft Research)
- 発表年: 2024(NAACL 2025採択)
- 分野: cs.CL, cs.IR
背景と動機(Background & Motivation)
RAGは外部知識を検索してLLMに注入することで幻覚を抑制する技術だが、従来のNaive RAGはベクトル類似度ベースのtop-k検索に依存している。この手法は「XYZについて教えて」のような特定エンティティに関する問いには有効だが、「このデータセットの主要なテーマは何か?」「どのようなトレンドが見られるか?」といったコーパス全体を俯瞰するグローバルな問いには本質的に答えられない。
Zenn記事で紹介されているLlamaIndex v0.14のPropertyGraphIndexは、まさにこのGraphRAGの思想をフレームワークレベルで実装したものである。本論文はその理論的基盤と実験的エビデンスを提供する。
主要な貢献(Key Contributions)
- 貢献1: テキストコーパスからLLMを用いてエンティティ・関係グラフを自動構築し、Leidenアルゴリズムで階層的コミュニティ構造を検出する手法を提案
- 貢献2: Local Search(エンティティ近傍検索)とGlobal Search(コミュニティ要約のMapReduce)の2つの検索モードを設計
- 貢献3: LLM-as-Judgeによる評価で、Global SearchがNaive RAGに対して包括性72%・多様性62%の勝率を達成
- 貢献4: コミュニティレベル(C0-C3)によるコストと品質のトレードオフを定量的に分析
技術的詳細(Technical Details)
Phase 1: グラフ構築パイプライン
GraphRAGのインデックス構築は以下の4ステップで行われる。
ステップ1: テキスト分割とエンティティ抽出
テキストコーパスをチャンク(2400トークン、100トークンオーバーラップ)に分割し、各チャンクからLLMを用いてエンティティと関係を抽出する。
\[\text{Extract}(c_i) \rightarrow \{(e_j, \text{type}_j, \text{desc}_j)\}, \{(e_s, e_t, \text{rel}, w)\}\]ここで、
- $c_i$: $i$番目のテキストチャンク
- $e_j$: エンティティ(名前・タイプ・説明文のトリプル)
- $(e_s, e_t, \text{rel}, w)$: ソースエンティティ、ターゲットエンティティ、関係記述、強度スコア($w \in [1, 10]$)
Gleanings: 同一チャンクに対して複数回の抽出を実行し、見逃しを低減する手法。デフォルトは1回だが、重要なコーパスでは2-3回実行することで抽出漏れを防ぐ。
ステップ2: エンティティ統合(Entity Summarization)
同名エンティティが複数チャンクに出現した場合、LLMが各記述を統合して単一のサマリーを生成する。
1
2
3
4
5
6
7
8
9
10
11
12
13
def merge_entity_descriptions(
entity_name: str,
descriptions: list[str]
) -> str:
"""同名エンティティの複数記述を統合"""
prompt = f"""
以下は '{entity_name}' についての複数の記述です。
これらを統合した簡潔なサマリーを生成してください。
記述:
{chr(10).join(f'- {d}' for d in descriptions)}
"""
return llm.generate(prompt)
ステップ3: コミュニティ検出(Leiden Algorithm)
構築されたグラフにLeidenアルゴリズムを適用し、階層的なコミュニティ構造を検出する。Leidenアルゴリズムはモジュラリティ最適化に基づくコミュニティ検出手法で、Louvain法を改良したものである。
\[Q = \frac{1}{2m} \sum_{ij} \left[ A_{ij} - \frac{k_i k_j}{2m} \right] \delta(c_i, c_j)\]ここで、
- $Q$: モジュラリティ
- $A_{ij}$: 隣接行列の要素
- $k_i$: ノード$i$の次数
- $m$: 総エッジ数
- $c_i$: ノード$i$が属するコミュニティ
- $\delta(c_i, c_j)$: $c_i = c_j$なら1、そうでなければ0
コミュニティは階層的に検出され、C0(最大・最も抽象的)からC3(最小・最も具体的)まで4段階に分かれる。
ステップ4: コミュニティレポート生成
各コミュニティについて、LLMがメンバーエンティティと関係を基にレポートを生成する。レポートにはタイトル、主要な知見、詳細な説明が含まれる。
Phase 2: 検索アルゴリズム
Local Search(局所検索):
1
2
3
4
Query → エンティティ特定(ベクトル類似度)
→ 近傍ノード・エッジ・コミュニティレポート収集
→ コンテキスト構築
→ LLM回答生成
特定エンティティの詳細情報を取得するクエリ(例:「TransformerのAttention機構とは?」)に適する。Naive RAGと同等〜やや上回る性能。
Global Search(グローバル検索):
1
2
3
4
Query → コミュニティレポートをシャッフル・バッチ化
→ 各バッチにLLMが中間回答生成(Map step)
→ 全中間回答をLLMが統合・要約(Reduce step)
→ 最終回答
コーパス全体の俯瞰的な理解を要するクエリ(例:「このデータセットの主要テーマは?」)に最適。MapReduceパターンにより、コーパスサイズに対してスケーラブルな処理が可能。
コミュニティレベルの選択
| レベル | 粒度 | クエリトークン消費 | 用途 |
|---|---|---|---|
| C0 | 最も粗い | 300K+ | 超抽象的な概要 |
| C1 | 中粗い | 100K | 推奨: コストと品質のバランス最良 |
| C2 | 中程度 | 30K | やや具体的なテーマ分析 |
| C3 | 最も細かい | 10K | 具体的なサブトピック |
論文はC1レベルを推奨している。C0はコスト過大、C3は抽象度が低すぎてグローバルクエリへの包括的回答が困難。
実装のポイント(Implementation)
Microsoft公式実装
1
pip install graphrag
ディレクトリ構成:
1
2
3
4
graphrag/
├── index/ # インデックス構築(グラフ構築・コミュニティ検出)
├── query/ # 検索実行(local/global)
└── llm/ # LLM接続レイヤー(OpenAI / Azure OpenAI対応)
出力ファイル:
entities.parquet: エンティティ一覧(名前、タイプ、記述)relationships.parquet: 関係一覧(ソース、ターゲット、記述、強度)communities.parquet: コミュニティ定義(メンバー、階層レベル)community_reports.parquet: コミュニティ要約レポート
主要ハイパーパラメータ
| パラメータ | デフォルト値 | 説明 |
|---|---|---|
chunk_size | 2400トークン | テキスト分割サイズ |
chunk_overlap | 100トークン | チャンク間オーバーラップ |
max_gleanings | 1 | エンティティ抽出の反復回数 |
community_level | C1 | 検索時のコミュニティレベル |
llm_model | GPT-4 Turbo | 抽出・生成に使用するLLM |
LlamaIndex PropertyGraphIndexとの関係
Zenn記事のStage 3で紹介されているPropertyGraphIndexは、GraphRAGのLocal Searchに近い機能を提供する。ただし、GraphRAGのGlobal Search(コミュニティレポートベース)は2024年時点ではLlamaIndexに直接統合されていない。
1
2
3
4
5
6
7
8
9
10
11
12
from llama_index.core import PropertyGraphIndex
# PropertyGraphIndex: GraphRAGのLocal Search相当
index = PropertyGraphIndex.from_documents(
documents,
embed_model=embed_model,
llm=llm,
)
# エンティティ検索(Local Search)
query_engine = index.as_query_engine()
response = query_engine.query("TransformerのAttention機構について")
コスト最適化のポイント
- インデックス構築コスト: GPT-4使用時、1MBテキストで数十ドル。GPT-4o-miniへの切替で80%削減可能
- グリーニング回数: 1回がコスト効率最良。精度重視なら2回まで
- コミュニティレベル: C1が推奨。C0は不要なコスト増
Production Deployment Guide
AWS実装パターン(コスト最適化重視)
トラフィック量別の推奨構成:
| 規模 | 月間リクエスト | 推奨構成 | 月額コスト | 主要サービス |
|---|---|---|---|---|
| Small | ~3,000 (100/日) | Serverless | $80-200 | Lambda + Bedrock + Neptune Serverless |
| Medium | ~30,000 (1,000/日) | Hybrid | $500-1,200 | ECS Fargate + Neptune + ElastiCache |
| Large | 300,000+ (10,000/日) | Container | $3,000-8,000 | EKS + Neptune + OpenSearch |
Small構成の詳細 (月額$80-200):
- Lambda: 2GB RAM, 120秒タイムアウト ($30/月) — グラフトラバーサル処理用
- Bedrock: Claude 3.5 Haiku ($80/月) — エンティティ抽出・回答生成
- Neptune Serverless: グラフストレージ ($50/月) — ナレッジグラフ永続化
- S3: コミュニティレポートキャッシュ ($5/月)
コスト削減テクニック:
- インデックス構築はBedrock Batch API(50%割引)で夜間バッチ実行
- コミュニティレポートはS3にキャッシュし、クエリ時のLLM呼び出しを削減
- Neptune Serverlessでアイドル時の自動スケールダウン
コスト試算の注意事項:
- 上記は2026年2月時点のAWS ap-northeast-1(東京)リージョン料金に基づく概算値
- グラフ構築のLLMコスト(初回のみ)は別途発生(1MBテキストあたり$2-10)
- 最新料金は AWS料金計算ツール で確認してください
Terraformインフラコード
Small構成 (Serverless): Lambda + Bedrock + Neptune Serverless
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# --- Neptune Serverless(ナレッジグラフ) ---
resource "aws_neptune_cluster" "graphrag" {
cluster_identifier = "graphrag-knowledge-graph"
engine = "neptune"
serverless_v2_scaling_configuration {
min_capacity = 1.0 # 最小: コスト削減
max_capacity = 8.0 # 最大: バースト対応
}
storage_encrypted = true
}
resource "aws_neptune_cluster_instance" "graphrag" {
cluster_identifier = aws_neptune_cluster.graphrag.id
instance_class = "db.serverless"
engine = "neptune"
}
# --- Lambda関数(GraphRAGクエリハンドラ) ---
resource "aws_lambda_function" "graphrag_query" {
filename = "graphrag_query.zip"
function_name = "graphrag-query-handler"
role = aws_iam_role.graphrag_lambda.arn
handler = "index.handler"
runtime = "python3.12"
timeout = 120
memory_size = 2048 # グラフトラバーサルにメモリが必要
environment {
variables = {
NEPTUNE_ENDPOINT = aws_neptune_cluster.graphrag.endpoint
BEDROCK_MODEL_ID = "anthropic.claude-3-5-haiku-20241022-v1:0"
COMMUNITY_LEVEL = "1" # C1推奨
S3_REPORT_CACHE = aws_s3_bucket.report_cache.id
}
}
vpc_config {
subnet_ids = module.vpc.private_subnets
security_group_ids = [aws_security_group.lambda_sg.id]
}
}
# --- S3(コミュニティレポートキャッシュ) ---
resource "aws_s3_bucket" "report_cache" {
bucket = "graphrag-community-reports"
}
resource "aws_s3_bucket_lifecycle_configuration" "report_cache" {
bucket = aws_s3_bucket.report_cache.id
rule {
id = "expire-old-reports"
status = "Enabled"
expiration { days = 30 }
}
}
# --- IAMロール ---
resource "aws_iam_role" "graphrag_lambda" {
name = "graphrag-lambda-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "lambda.amazonaws.com" }
}]
})
}
運用・監視設定
CloudWatch Logs Insights — グラフ構築監視:
1
2
3
4
fields @timestamp, entity_count, relationship_count, community_count
| stats sum(entity_count) as total_entities,
sum(relationship_count) as total_relationships
by bin(1d)
コスト異常検知(Python):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import boto3
cloudwatch = boto3.client('cloudwatch')
cloudwatch.put_metric_alarm(
AlarmName='graphrag-neptune-cost',
ComparisonOperator='GreaterThanThreshold',
EvaluationPeriods=1,
MetricName='ServerlessDatabaseCapacity',
Namespace='AWS/Neptune',
Period=3600,
Statistic='Average',
Threshold=4.0, # NCU 4超過でアラート
AlarmDescription='Neptune Serverless容量異常'
)
コスト最適化チェックリスト
- インデックス構築はBedrock Batch API活用(50%割引、夜間バッチ)
- コミュニティレポートはS3キャッシュ(クエリ時LLMコスト削減)
- Neptune Serverless最小容量を1.0 NCUに設定
- コミュニティレベルC1を使用(C0はコスト過大)
- エンティティ抽出にGPT-4o-miniを使用(80%コスト削減)
- S3ライフサイクルポリシーで古いキャッシュ自動削除
- AWS Budgets月額予算設定
実験結果(Results)
データセット:
- Podcast transcript(1,669,713トークン)
- News articles(エンロン事件報道、3,197,802トークン)
評価手法: LLM-as-Judge(GPT-4 Turbo)による対戦形式
| 評価軸 | GraphRAG C1 vs NaiveRAG 勝率 |
|---|---|
| Comprehensiveness(包括性) | 72% win |
| Diversity(多様性) | 62% win |
| Empowerment(意思決定支援) | 65% win |
| Directness(直接性) | 38% win(GraphRAGは冗長な傾向) |
コミュニティレベル別のコスト効率:
| レベル | クエリトークン消費 | Comp. 勝率 vs NaiveRAG |
|---|---|---|
| C0 | 300K+ | 72% |
| C1 | 100K | 72%(コスト効率最良) |
| C2 | 30K | 68% |
| C3 | 10K | 60% |
分析ポイント:
- グローバルクエリ(テーマ分析、トレンド把握)でNaive RAGを大幅に上回る
- Directnessが低いのはMapReduceの要約ステップで回答が冗長になるため
- C1がコストと品質のスイートスポット(C0と同等品質で1/3のトークン消費)
実運用への応用(Practical Applications)
LlamaIndex PropertyGraphIndexとの対応
| GraphRAG概念 | LlamaIndex実装 |
|---|---|
| Entity Extraction | PropertyGraphIndexのエンティティ抽出 |
| Local Search | PropertyGraphIndex.as_query_engine() |
| Global Search | 未直接対応(カスタム実装が必要) |
| Community Detection | 未直接対応(NetworkX等で別途実装) |
適用が有効なユースケース
- 社内ナレッジベースの全体分析: 「過去1年間の技術的課題のトレンドは?」
- 調査報告書の要約: 「このレポート全体の主要な知見は?」
- 競合分析: 「市場の主要プレイヤーとその関係性は?」
適用が不向きなユースケース
- リアルタイムチャット: インデックス構築コストが高く、即時性が求められるケース
- 単純なFAQ: Naive RAGで十分な場合にGraphRAGは過剰設計
- 頻繁に更新されるコーパス: 差分更新が未対応(→LightRAGが解決)
関連研究(Related Work)
- LightRAG (2412.15605): GraphRAGの差分更新問題を解決し、デュアルレベル検索を導入した軽量版。インクリメンタル更新とコスト削減が特徴
- HippoRAG (2408.08921): 海馬の記憶統合メカニズムに着想を得たRAG。Personalized PageRankでマルチホップ推論を実現
- CRAG (Corrective RAG): 検索品質の自己評価メカニズム。GraphRAGのGrader機能と相補的
まとめと今後の展望
GraphRAGは「コーパス全体の理解」という従来RAGの盲点を解消する画期的な手法である。LlamaIndex v0.14のPropertyGraphIndex(Stage 3)は本論文の思想を実装レベルで取り入れており、特にエンティティ間の関係性が重要なドメインで威力を発揮する。
一方で、インデックス構築のLLMコスト(大規模コーパスで数百〜数千ドル)と差分更新の困難さが実用上の最大の課題である。後者はLightRAGによるインクリメンタル更新で大幅に改善されている。
参考文献
- arXiv: https://arxiv.org/abs/2404.16130
- Code: https://github.com/microsoft/graphrag (MIT License)
- Related Zenn article: https://zenn.dev/0h_n0/articles/62e946539206db
- LlamaIndex PropertyGraphIndex: https://developers.llamaindex.ai/python/framework/