高度な RAG: LongRAG、Self-RAG、GraphRAG の解説
LongRAG、Self-RAG、GraphRAG - 次世代の技術
検索拡張生成 (RAG) は、単純なベクトル類似度検索の域を遥かに超えて進化しました。LongRAG、Self-RAG、GraphRAG は、これらの能力の最先端を代表しています。
最新の RAG システム は、膨大なドキュメントを処理し、複雑なエンティティ間の関係を理解するなど、より多くの要件に対応する必要があります。
この素敵な画像は、AI モデル Flux 1 dev によって生成されました。
基本的な RAG を超える進化
従来の RAG システムは、ドキュメントをチャンク化し、ベクトルに変換し、コサイン類似度によって類似したチャンクを検索して、LLM に渡すという単純なパターンに従います。多くのユースケースで効果的ですが、このアプローチは 3 つの重要なシナリオにおいて課題を抱えています。
検索、ディープ検索、ディープリサーチの区別を理解することは、複雑な情報検索タスクにおいてなぜ高度な RAG 変種が必要なのかを明確にするのに役立ちます。これらの概念について詳しく知りたい場合は、検索 vs ディープ検索 vs ディープリサーチ をご覧ください。
- 長距離依存関係: 重要なコンテキストは、複数のチャンクにわたる数千トークンにまたがることがあります
- 検索の信頼性: システムは、検索されたコンテンツが実際に関連しているかどうかを評価する方法を持っていません
- 関係性の複雑さ: ベクトル類似度だけでは、エンティティ間の複雑なつながりを捉えることはできません
高度な RAG 変種は、これらの制限に対処するために、特定の課題に合わせた専門的なアーキテクチャを採用しています。
LongRAG:拡張されたコンテキストへの挑戦
アーキテクチャの概要
LongRAG は、拡張されたコンテキストウィンドウ(32K、100K、あるいは 1M トークンなど)を持つ LLM を活用することで、チャンク化戦略を根本から再考します。ドキュメントを小さな 512 トークンのチャンクに分割するのではなく、LongRAG は階層的なアプローチを採用します。
ドキュメントレベルの埋め込み: 全文(または非常に大きなセクション)を単一のユニットとして処理します。ドキュメントレベルの埋め込みは全体的な意味的意味を捉える一方で、下流の処理のために全文を維持します。
最小限の断片化: チャンク化が必要な場合でも、LongRAG ははるかに大きなチャンク(4K-8K トークン)を使用し、大幅な重複(20-30%)を持たせます。これにより物語の流れが保存され、コンテキストの断片化が軽減されます。
コンテキストの組み立て: 検索時、LongRAG は散らばった断片ではなく、完全なドキュメントまたは大きな一貫したセクションを返します。LLM は、構造的および意味的な関係を維持する連続したコンテキストを受信します。
実装戦略
以下は、Python と最新の埋め込みモデルを使用した概念的な実装例です。
from typing import List, Dict
import numpy as np
class LongRAGRetriever:
def __init__(self, model, chunk_size=8000, overlap=1600):
self.model = model
self.chunk_size = chunk_size
self.overlap = overlap
self.doc_embeddings = []
self.documents = []
def create_long_chunks(self, text: str) -> List[str]:
"""重複する大きなチャンクを作成"""
chunks = []
start = 0
while start < len(text):
end = start + self.chunk_size
chunk = text[start:end]
chunks.append(chunk)
start += (self.chunk_size - self.overlap)
return chunks
def index_document(self, doc: str, metadata: Dict):
"""階層的埋め込みでドキュメントをインデックス登録"""
# 全文を埋め込み
doc_embedding = self.model.embed(doc)
# 重複を含む大きなチャンクを作成
chunks = self.create_long_chunks(doc)
chunk_embeddings = [self.model.embed(c) for c in chunks]
self.doc_embeddings.append({
'doc_id': len(self.documents),
'doc_embedding': doc_embedding,
'chunk_embeddings': chunk_embeddings,
'chunks': chunks,
'full_text': doc,
'metadata': metadata
})
self.documents.append(doc)
def retrieve(self, query: str, top_k: int = 3) -> List[Dict]:
"""関連する長文形式のコンテンツを検索"""
query_embedding = self.model.embed(query)
# まずドキュメントレベルでスコアリング
doc_scores = [
np.dot(query_embedding, doc['doc_embedding'])
for doc in self.doc_embeddings
]
# 上位のドキュメントを取得
top_doc_indices = np.argsort(doc_scores)[-top_k:][::-1]
results = []
for idx in top_doc_indices:
doc_data = self.doc_embeddings[idx]
# 各ドキュメントについて最良のチャンクを見つける
chunk_scores = [
np.dot(query_embedding, emb)
for emb in doc_data['chunk_embeddings']
]
best_chunk_idx = np.argmax(chunk_scores)
# 最良のチャンク周辺の拡張コンテキストを返す
context_chunks = self._get_extended_context(
doc_data['chunks'],
best_chunk_idx
)
results.append({
'text': ''.join(context_chunks),
'score': doc_scores[idx],
'metadata': doc_data['metadata']
})
return results
def _get_extended_context(self, chunks: List[str],
center_idx: int) -> List[str]:
"""関連するチャンク周辺の拡張コンテキストを取得"""
start = max(0, center_idx - 1)
end = min(len(chunks), center_idx + 2)
return chunks[start:end]
使用例とパフォーマンス
LongRAG は、コンテキストが重要なシナリオで優れた性能を発揮します。
- 法的文書の分析: 契約書や法的要約書は、数十ページにわたる依存関係を持っていることがよくあります
- 研究論文の検索: 方法論を理解するには、孤立した段落ではなく、一貫したセクションが必要です
- コードリポジトリ: 関数やクラスは、そのモジュールのコンテキスト内で理解される必要があります
パフォーマンス特性:
- レイテンシ: 大きなチャンクを処理するため高くなります(標準的な RAG より 2-5 倍遅い)
- 精度: 長文形式の QA ベンチマークで 15-25% 向上
- メモリ: コンテキストウィンドウのため、メモリ使用量が 3-4 倍必要
Self-RAG:反省的な検索
基本原理
Self-RAG は、RAG システムにメタ認知のレイヤーを導入します。盲目に検索して生成するのではなく、システムは特別な反省トークンを通じて自身のプロセスを能動的に反芻します。
検索トークン: 与えられたクエリに対して検索が必要かどうかを決定します 関連性トークン: 検索されたドキュメントが実際に関連しているかを評価します サポートトークン: 生成された回答が検索されたコンテンツによって支持されているかを確認します 批判トークン: 生成されたレスポンスの全体的な品質を評価します
アーキテクチャの構成要素
Self-RAG アーキテクチャは、3 つの交互するフェーズで構成されます。
class SelfRAGSystem:
def __init__(self, retriever, generator, critic):
self.retriever = retriever
self.generator = generator
self.critic = critic
def generate_with_reflection(self, query: str,
max_iterations: int = 3):
"""自己反省を伴う回答生成"""
# フェーズ 1: 検索が必要かどうかを決定
retrieve_decision = self.critic.should_retrieve(query)
if not retrieve_decision:
# 検索なしで直接生成
return self.generator.generate(query)
# フェーズ 2: 検索と関連性の評価
retrieved_docs = self.retriever.retrieve(query)
relevant_docs = []
for doc in retrieved_docs:
relevance_score = self.critic.assess_relevance(
query, doc
)
if relevance_score > 0.7: # しきい値
relevant_docs.append(doc)
if not relevant_docs:
# 検索なしでの生成にフォールバック
return self.generator.generate(query)
# フェーズ 3: 生成とサポートの確認
best_answer = None
best_score = -1
for _ in range(max_iterations):
# 候補回答を生成
answer = self.generator.generate(
query, context=relevant_docs
)
# サポートと品質を評価
support_score = self.critic.check_support(
answer, relevant_docs
)
quality_score = self.critic.assess_quality(answer)
total_score = 0.6 * support_score + 0.4 * quality_score
if total_score > best_score:
best_score = total_score
best_answer = answer
# 高品質が達成されたら早期停止
if total_score > 0.9:
break
return {
'answer': best_answer,
'confidence': best_score,
'sources': relevant_docs,
'reflections': {
'retrieved': retrieve_decision,
'relevance': len(relevant_docs),
'support': support_score
}
}
反省メカニズムのトレーニング
Self-RAG は、信頼できる評価を行うためにクリティコンポーネントのトレーニングを必要とします。これには通常、以下のプロセスが含まれます。
- 関連性の判断が注釈付けられたデータセットでの教師あり微調整
- 正確な予測に対する報酬を用いた強化学習
- 支持された主張と支持されていない主張を区別するための対比学習
反省トークンは、以下のように実装できます。
- 語彙内の特別なトークン(
[RETRIEVE]、[RELEVANT]など) - モデル上の独立した分類器ヘッド
- 外部のクリティモデル(アンサンブルアプローチ)
本番環境での考慮事項
Self-RAG を本番システムにデプロイする際に考慮すべき点:
レイテンシのトレードオフ: 各反省ステップは推論オーバーヘッドを 20-40% 増加させます。徹底性とレスポンス時間の要件のバランスを調整する必要があります。
信頼性のしきい値: 使用ケースに応じて反発しきい値を調整します。法律や医療アプリケーションは、一般的なチャットボットよりも高い信頼性を必要とします。
モニタリング: 反発決定を追跡してパターンを特定します。検索がほとんど必要ない場合、よりシンプルなアーキテクチャの方が有益である可能性があります。
GraphRAG:知識グラフ強化型検索
概念的基盤
GraphRAG は、検索問題をベクトル類似度からグラフトラバーサルへと変換します。意味的に類似したテキストチャンクを見つけるのではなく、GraphRAG は関連するエンティティと関係性のサブグラフを特定します。
エンティティ抽出: 固有のエンティティ、概念、およびそのタイプを特定 関係性マッピング: エンティティ間の関係性(時間的、因果的、階層的)を抽出 グラフ構築: エンティティをノード、関係性をエッジとして持つ知識グラフを構築 サブグラフ検索: クエリに対して、関連する接続されたサブグラフを検索
グラフ構築パイプライン
構造化されていないテキストから知識グラフを構築するには、いくつかの段階が必要です。
class GraphRAGBuilder:
def __init__(self, entity_extractor, relation_extractor):
self.entity_extractor = entity_extractor
self.relation_extractor = relation_extractor
self.graph = NetworkGraph()
def build_graph(self, documents: List[str]):
"""ドキュメントから知識グラフを構築"""
for doc in documents:
# エンティティを抽出
entities = self.entity_extractor.extract(doc)
# エンティティをノードとして追加
for entity in entities:
self.graph.add_node(
entity['text'],
entity_type=entity['type'],
context=entity['surrounding_text']
)
# 関係性を抽出
relations = self.relation_extractor.extract(
doc, entities
)
# 関係性をエッジとして追加
for rel in relations:
self.graph.add_edge(
rel['source'],
rel['target'],
relation_type=rel['type'],
confidence=rel['score'],
evidence=rel['text_span']
)
def enrich_graph(self):
"""派生関係性とメタデータを追加"""
# ノードの重要性を計算(PageRank など)
self.graph.compute_centrality()
# コミュニティ/クラスターを特定
self.graph.detect_communities()
# タイムスタンプがあれば時間的順序を追加
self.graph.add_temporal_edges()
グラフを用いたクエリ処理
GraphRAG クエリは、知識グラフ上でのマルチホップ推論を含みます。
class GraphRAGRetriever:
def __init__(self, graph, embedder):
self.graph = graph
self.embedder = embedder
def retrieve_subgraph(self, query: str,
max_hops: int = 2,
max_nodes: int = 50):
"""クエリに関連するサブグラフを検索"""
# クエリ内のシードエンティティを特定
query_entities = self.entity_extractor.extract(query)
# グラフ内の一致するノードを検索
seed_nodes = []
for entity in query_entities:
matches = self.graph.find_similar_nodes(
entity['text'],
similarity_threshold=0.85
)
seed_nodes.extend(matches)
# トラバーサルによってサブグラフを拡張
subgraph = self.graph.create_subgraph()
visited = set()
for seed in seed_nodes:
self._expand_from_node(
seed,
subgraph,
visited,
current_hop=0,
max_hops=max_hops
)
# 関連性でノードをランク付け
ranked_nodes = self._rank_subgraph_nodes(
subgraph, query
)
# コンテキストを抽出してフォーマット
context = self._format_graph_context(
ranked_nodes[:max_nodes],
subgraph
)
return context
def _expand_from_node(self, node, subgraph, visited,
current_hop, max_hops):
"""再帰的にサブグラフを拡張"""
if current_hop >= max_hops or node in visited:
return
visited.add(node)
subgraph.add_node(node)
# 近隣ノードを取得
neighbors = self.graph.get_neighbors(node)
for neighbor, edge_data in neighbors:
# サブグラフにエッジを追加
subgraph.add_edge(node, neighbor, edge_data)
# 再帰的に拡張
self._expand_from_node(
neighbor,
subgraph,
visited,
current_hop + 1,
max_hops
)
def _format_graph_context(self, nodes, subgraph):
"""サブグラフをテキストコンテキストに変換"""
context_parts = []
for node in nodes:
# ノードコンテキストを追加
context_parts.append(f"Entity: {node.text}")
context_parts.append(f"Type: {node.entity_type}")
# 関係性情報を追加
edges = subgraph.get_edges(node)
for edge in edges:
context_parts.append(
f"- {edge.relation_type} -> {edge.target.text}"
)
return "\n".join(context_parts)
Microsoft の GraphRAG 実装
Microsoft の GraphRAG は、コミュニティサマリーを生成する独自の手法を採用しています。
- LLM ベースのエンティティ/関係性抽出を用いてドキュメントから初期グラフを構築
- Leiden アルゴリズムなどを用いてコミュニティを検出
- LLM を用いて各コミュニティのサマリーを生成
- 階層的構造: 複数のレベルのコミュニティ抽象化を構築
- クエリ時: 関連するコミュニティを検索し、特定のエンティティにトラバース
この手法は、以下のような場合に特に効果的です。
- 探索的クエリ(「このコーパスの主なテーマは何か?」)
- マルチホップ推論(「A は B を通じて C にどのように接続されているか?」)
- 時間的分析(「このエンティティの関係性はどのように進化してきたか?」)
比較分析
各変種を使用するタイミング
LongRAG を使用するべき場合:
- ドキュメントに強い内部的な一貫性がある場合
- LLM のコンテキストウィンドウが大きな入力(32K 以上)をサポートする場合
- 回答に長距離依存関係の理解が必要な場合
- 構造化されたドキュメント(レポート、論文、書籍)を扱っている場合
Self-RAG を使用するべき場合:
- 精度と信頼性が重要な場合
- 説明可能な検索決定が必要な場合
- 関連性の低い検索による誤検出がコスト高い場合
- クエリの複雑さが大きく異なる場合(一部は検索が必要、他は不要)
GraphRAG を使用するべき場合:
- ドメインに豊かなエンティティ関係性がある場合
- クエリがマルチホップ推論を含む場合
- 時間的または階層的な関係性が重要な場合
- エンティティ間の接続を理解する必要がある場合
パフォーマンス指標の比較
| 指標 | 標準 RAG | LongRAG | Self-RAG | GraphRAG |
|---|---|---|---|---|
| インデックス作成時間 | 1x | 0.8x | 1.1x | 3-5x |
| クエリレイテンシ | 1x | 2-3x | 1.4x | 1.5-2x |
| メモリ使用量 | 1x | 3-4x | 1.2x | 2-3x |
| 精度 (QA) | ベースライン | +15-25% | +20-30% | +25-40%* |
| 解釈可能性 | 低 | 中 | 高 | 高 |
*GraphRAG の向上はドメインに大きく依存します
ハイブリッドアプローチ
最も強力な本番システムは、複数の技術を組み合わせたものがよくあります。
LongRAG + GraphRAG: グラフ構造を用いて関連するドキュメントクラスタを特定し、断片ではなく全文を検索します
Self-RAG + GraphRAG: 反発メカニズムをグラフトラバーサルの決定(どの経路をたどる、いつ拡張を止めるか)に適用します
3 段階のパイプライン: GraphRAG を初期エンティティベースの検索に使用 → Self-RAG を関連性フィルタリングに使用 → LongRAG をコンテキスト組み立てに使用
実装の考慮事項
埋め込みモデル
異なる RAG 変種には異なる埋め込み要件があります。
LongRAG: ドキュメントレベルとチャンクレベルの両方で機能する埋め込みが必要です。長いシーケンスで対比学習トレーニングされたモデルを検討してください。
Self-RAG: 微細な関連性評価のために意味的なニュアンスを捉える埋め込みから利益を受けます。
GraphRAG: エンティティに意識的な埋め込みが必要です。エンティティリンクタスクで微調整されたモデルの方がパフォーマンスが良いです。
埋め込みモデルの選択はパフォーマンスに大きな影響を与えます。ローカルモデルを使用する際、Ollama のようなツールは、本番デプロイにコミットする前にさまざまな埋め込みモデルを実験するための簡単な手段を提供します。
チャンキング戦略の再考
従来の固定サイズチャンキングでは、高度な RAG には不十分です。
意味的チャンキング: 自然な境界(段落、セクション、トピックの転換)で分割 再帰的チャンキング: 親子関係を持つ階層的なチャンクを作成 スライディングウィンドウ: 境界でのコンテキストを保存するために重複するチャンクを使用 構造感知: ドキュメント構造(Markdown ヘッダー、XML タグ、コードブロック)を尊重
Python ベースの実装では、LangChain や LlamaIndex などのライブラリが、これらの チャンキング戦略 に対して組み込みのサポートを提供します。
リランキングの統合
リランキングは、すべての RAG 変種において検索品質を劇的に向上させます。初期検索後、専門のリランキングモデルがクエリ - ドキュメント相互作用の特徴に基づいて結果を再スコアリングします。これを思いやりを持って統合することで、最小限のレイテンシ影響で大きな精度向上(10-20%)をもたらします。
本番環境へのスケーリング
インデックス作成パイプライン:
- 大規模なドキュメントコーパスには分散処理(Ray、Dask)を使用
- リアルタイム更新のために増分インデックス作成を実装
- 埋め込みを最適化されたベクトルデータベース(Pinecone、Weaviate、Qdrant)に保存
クエリ最適化:
- 頻繁なクエリとその結果をキャッシュ
- クエリルーティングを実装(異なるクエリタイプに対して異なる RAG 変種を使用)
- 亜線形スケーリングのために近似近傍検索を使用
モニタリング:
- 検索関連スコアを追跡
- Self-RAG での反発決定を監視
- グラフトラバースのパスと深さを測定
- 信頼性スコアとユーザーフィードバックをログ出力
実世界でのアプリケーション
技術ドキュメント検索
大手クラウドプロバイダーは、ドキュメントのために GraphRAG を実装しました。
- エンティティ: API エンドポイント、パラメータ、エラーコード、サービス名
- 関係性: 依存関係、バージョン互換性、移行パス
- 結果: サポートチケットが 35% 減少、解決時間が 45% 短縮
法的開示
法的テック企業は、Self-RAG と LongRAG を組み合わせています。
- Self-RAG が早期に関連性の低いドキュメントをフィルタリング
- LongRAG が保持されたドキュメントのコンテキストを保存
- 弁護士は 60% 少ない誤検出を確認
- 重要なコンテキスト保存率が 71% から 94% に向上
研究文献レビュー
ハイブリッドアプローチを使用する学術検索エンジン。
- GraphRAG が引用ネットワークと研究コミュニティを特定
- LongRAG が方法論のコンテキストを維持した全文セクションを検索
- 関連する論文の発見が 40% 向上
- 文献レビューまでの時間が数週間から数日に短縮
高度なトピック
マルチモーダル RAG
画像、テーブル、コードを処理するためにこれらの変種を拡張。
- 視覚的グラウンディング: テキストエンティティをドキュメント内の画像にリンク
- テーブル理解: 構造化データをグラフ形式にパース
- コード分析: コードベースから依存関係グラフを構築
適応型 RAG
クエリ特性に基づいた RAG 戦略の動的選択。
- クエリ複雑性分類器
- ドキュメントタイプ検出器
- 戦略選択のための費用対効果最適化
プライバシー保護 RAG
プライバシー制約とともにこれらの変種を実装。
- データサイロ間での連合検索
- 埋め込みにおける微分プライバシー
- 暗号化類似度検索
入門ガイド
Python でのクイックスタート
これらの手法を実装したい方にとって、堅固な Python の基礎から始めることが不可欠です。機械学習のための Python の豊富なエコシステムは、RAG 開発にとって自然な選択です。
実験のためのシンプルな出発点:
# 依存関係のインストール
# pip install sentence-transformers faiss-cpu langchain
from sentence_transformers import SentenceTransformer
import faiss
import numpy as np
# 大きなチャンクを実験するための基本的なセットアップ
model = SentenceTransformer('all-MiniLM-L6-v2')
documents = [
# ここに長文形式のドキュメントを配置
]
# 埋め込みを作成
embeddings = model.encode(documents)
# FAISS インデックスを構築
dimension = embeddings.shape[1]
index = faiss.IndexFlatL2(dimension)
index.add(embeddings.astype('float32'))
# クエリ
query = "ここに質問を入力"
query_embedding = model.encode([query])
distances, indices = index.search(
query_embedding.astype('float32'), k=3
)
フレームワークの選択
LangChain: 迅速なプロトタイピング、広範な統合に最適 LlamaIndex: ドキュメントインデックス作成と検索に最適化 Haystack: 本番環境準備済み、強力なパイプライン抽象化 カスタム: 完全な制御と最適化が必要な場合
評価フレームワーク
本番デプロイ前に厳格な評価を実装します。
検索指標:
- Precision@K、Recall@K、MRR(平均逆ランク)
- NDCG(正規化割引累積ゲイン)
生成指標:
- テキスト類似性の ROUGE、BLEU
- 意味的類似性の BERTScore
- 品質評価のための人間評価
エンドツーエンド指標:
- タスク成功率
- ユーザー満足度スコア
- レイテンシパーセンタイル(p50、p95、p99)
結論
RAG システムの風景は、基本的なベクトル類似度検索から大きく成熟しました。LongRAG、Self-RAG、GraphRAG は、それぞれ従来のアプローチの特定の制限に対処します。
LongRAG は、拡張されたコンテキストウィンドウと最小限の チャンキング を採用することで、コンテキスト断片化の問題を解決します。ドキュメントの一貫性が重要で、大きなコンテキストを処理する計算リソースがある場合の選択です。
Self-RAG は、検索システムに重要な自己意識を追加します。自身の決定を反芻することで、誤検出を減らし、信頼性を向上させます。これは、速度よりも精度が重要なハイステークスのアプリケーションに不可欠です。
GraphRAG は、構造化された知識表現の力を解き放ちます。ドメインがエンティティ間の複雑な関係性を扱う場合、グラフベースの検索は、ベクトル類似度が完全に見落とすつながりを表面化します。
RAG の未来には、これらの変種の強みを組み合わせたハイブリッドアプローチが含まれるでしょう。本番システムは、関連するエンティティクラスタを特定するために GraphRAG を使用し、検索をフィルタリングして検証するために Self-RAG を使用し、LLM のための一貫したコンテキストを組み立てるために LongRAG を使用することがあります。
LLM が引き続き改善され、コンテキストウィンドウが拡大するにつれて、さらに洗練された RAG 変種が登場するでしょう。鍵は、ドキュメント構造、クエリパターン、精度要件、計算制約など、特定のユースケース要件を理解し、適切な技術またはその組み合わせを選択することです。
ツールエコシステムは急速に成熟しており、LangChain、LlamaIndex、Haystack などのフレームワークは、これらの高度なパターンに対してますます洗練されたサポートを提供しています。強力なローカル LLM ランタイムと埋め込みモデルと組み合わせて、生産グレードの RAG システムを実験し、デプロイするのはこれまで以上に簡単になりました。
基礎から始めて、パフォーマンスを厳格に測定し、要件が示すようにアーキテクチャを進化させましょう。ここで取り上げられている高度な RAG 変種は、その進化へのロードマップを提供します。
参考リンク
- 埋め込みモデルを用いたリランキング
- LLM と構造化出力: Ollama、Qwen3 & Python または Go
- クラウド LLM プロバイダー
- LLM 比較: Qwen3:30b vs GPT-OSS:20b
- ステップバイステップ RAG チュートリアル
- RAG におけるチャンキング比較
- RAG 向けベクトルデータベースの比較