高度なRAG:LongRAG、Self-RAGおよびGraphRAGの解説

LongRAG、Self-RAG、GraphRAG - 次世代の技術

目次

リトリーバル・オーガナイズド・ジェネレーション (RAG) は単純なベクトル類似性検索を超えています。 LongRAG、Self-RAG、GraphRAGはこれらの能力の最先端を代表しています。

現代のRAGシステムは、大量のドキュメントを処理し、複雑なエンティティの関係性を理解するなど、さらに多くの機能が必要です。

a-pirate-captain この素晴らしい画像は、AIモデル Flux 1 devによって生成されました。

基本的なRAGを超えた進化

伝統的なRAGシステムは単純なパターンに従います:ドキュメントをチャンクに分割し、ベクトルに埋め込み、コサイン類似性を使って類似したチャンクを検索し、LLMに渡します。多くのユースケースでは効果的ですが、このアプローチは次の3つの重要なシナリオで苦労します:

  1. 長距離依存関係:重要な文脈は複数のチャンクにまたがる数千のトークンにわたる場合があります
  2. 検索信頼性:システムは検索された内容が実際に関連しているかどうかを評価する方法がありません
  3. 関係の複雑さ:ベクトル類似性はエンティティ間の複雑な関係を捉えることができません

高度なRAGのバリエーションは、特定の課題に特化したアーキテクチャでこれらの制限に対処します。

LongRAG: 拡張された文脈の征服

アーキテクチャ概要

LongRAGは拡張された文脈窓を持つLLM(32K、100K、あるいは1Mトークン)を活用して、チャンキング戦略を根本的に見直します。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では、評価を信頼できるようにするため、批評コンポーネントを訓練する必要があります。これは通常以下を行います:

  1. 監督付き微調整:関連性の判断が注釈付けされたデータセットを使用
  2. 強化学習:正確な予測に対して報酬を与える
  3. 対照学習:サポートされている声明とサポートされていない声明を区別する

反省トークンは以下のように実装できます:

  • ボキャブラリ内の特別なトークン(例:[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"エンティティ: {node.text}")
            context_parts.append(f"タイプ: {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はコミュニティの要約を生成する独自のアプローチを採用しています:

  1. 初期グラフの構築:LLMベースのエンティティ/関係抽出を使用してドキュメントから
  2. コミュニティの検出:Leidenアルゴリズムなど
  3. コミュニティの要約の生成:LLMを使用して
  4. 階層構造:複数レベルのコミュニティの抽象化を構築
  5. クエリ時:関連するコミュニティを検索し、特定のエンティティに到達

このアプローチは特に次のシナリオで効果的です:

  • 探索的なクエリ(「このコーパスの主なテーマは何ですか?」)
  • マルチホップの推論(「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には不十分です:

語義的なチャンキング:自然な境界(段落、セクション、トピックの変化)で分割 再帰的なチャンキング:親-子関係を持つ階層的なチャンクを作成 スライディングウィンドウ:境界で文脈を保持するための重複チャンクを使用 構造を意識したチャンキング:ドキュメント構造(マーカーの見出し、XMLタグ、コードブロック)を尊重

Pythonベースの実装では、LangChainやLlamaIndexなどのライブラリがこれらのチャンキング戦略のビルトインサポートを提供しています。

リランキングの統合

リランキングはすべてのRAGバリエーションの検索品質を劇的に改善します。初期検索後、専用のリランキングモデルがクエリ-ドキュメントの相互作用の特徴に基づいて結果を再スコアリングします。これは慎重に統合された場合、最小限のレイテンシーの影響で10-20%の精度向上をもたらします。

本番環境へのスケーリング

インデックス作成パイプライン

  • 大規模なドキュメントコーパスに対して分散処理(Ray、Dask)を使用
  • リアルタイム更新のためのインクリメンタルインデックス作成を実装
  • ベクトルデータベース(Pinecone、Weaviate、Qdrant)に最適化された埋め込みを保存

クエリ最適化

  • 頻繁なクエリとその結果をキャッシュ
  • クエリルーティング(異なるクエリタイプごとに異なるRAGバリエーション)
  • 亜線形スケーリングを実現するための近似最近隣検索の使用

モニタリング

  • 検索関連性スコアを追跡
  • Self-RAGでの反省決定をモニタリング
  • グラフトラバーサルのパスと深さを測定
  • 信頼度スコアとユーザーのフィードバックをログ

実際の応用

技術ドキュメンテーション検索

主要なクラウドプロバイダーがGraphRAGをドキュメンテーションに実装しました:

  • エンティティ:APIエンドポイント、パラメータ、エラーコード、サービス名
  • 関係:依存関係、バージョン互換性、移行パス
  • 結果:サポートチケット数が35%減少、解決時間が45%短縮

法的発見

法テック会社がSelf-RAGとLong-RAGを組み合わせました:

  • Self-RAGが早期に無関係なドキュメントをフィルタリング
  • Long-RAGが保持されたドキュメントの文脈を保持
  • 律師が誤検出の50%少なくレビュー
  • 重要な文脈の保持が71%から94%に改善

研究文献レビュー

ハイブリッドアプローチを使用した学術検索エンジン:

  • GraphRAGが引用ネットワークと研究コミュニティを特定
  • LongRAGが方法論の文脈を保持するフルセクションを取得
  • 関連論文の発見が40%改善
  • 文献レビューの時間は週から日に短縮

高度なトピック

マルチモーダルRAG

画像、表、コードを処理するこれらのバリエーションの拡張:

  • 視覚的基準:ドキュメント内の画像とテキストエンティティをリンク
  • 表の理解:構造化データをグラフ形式に解析
  • コード分析:コードベースから依存関係グラフを構築

適応型RAG

クエリの特性に基づいてRAG戦略を動的に選択:

  • クエリ複雑度分類器
  • ドキュメントタイプ検出器
  • 戦略選択のコスト-利益最適化器

プライバシー保護型RAG

プライバシー制約の下でこれらのバリエーションを実装:

  • データの分離されたシロップ間でのフェデレーテッド検索
  • 埋め込みへの差分プライバシー
  • 暗号化された類似性検索

Getting Started

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(Mean Reciprocal Rank)
  • NDCG(Normalized Discounted Cumulative Gain)

生成メトリクス:

  • ROUGE、BLEUによるテキストの類似度
  • BERTScoreによる意味的類似度
  • 人間による評価による品質評価

エンドツーエンドメトリクス:

  • タスク成功率
  • ユーザー満足度スコア
  • ラテンシーのパーセンタイル(p50、p95、p99)

結論

RAGシステムの風景は、基本的なベクトル類似性検索を超えて大幅に成熟しました。LongRAG、Self-RAG、GraphRAGはそれぞれ、従来のアプローチの特定の限界に対処しています:

LongRAGは、拡張されたコンテキストウィンドウと最小限のチャンク化を採用することで、コンテキストの断片化の問題を解決します。ドキュメントの連続性が重要であり、大規模なコンテキストを処理するための計算リソースがある場合に最適な選択肢です。

Self-RAGは、検索システムに重要な自己認識を追加します。自身の決定を反映することで、誤検出を減らし、信頼性を向上させます。正確性がスピードよりも重要となる高リスクのアプリケーションにおいて必須です。

GraphRAGは、構造化された知識表現の力にアクセスします。ドメインがエンティティ間の複雑な関係を含む場合、グラフベースの検索はベクトル類似性検索では完全に見逃される接続を明らかにします。

RAGの未来は、これらのバリエーションの強みを組み合わせたハイブリッドアプローチがもたらす可能性があります。本番環境のシステムでは、GraphRAGを使って関連するエンティティクラスタを特定し、Self-RAGを使って検索結果をフィルタリング・検証し、LongRAGを使ってLLMに連続的なコンテキストを組み立てることがあります。

LLMが継続的に改善し、コンテキストウィンドウが拡大するにつれて、さらに高度なRAGのバリエーションが出現するでしょう。鍵は、特定のユースケースの要件(ドキュメント構造、クエリパターン、精度要求、計算制約)を理解し、適切な技術またはその組み合わせを選択することです。

ツールエコシステムは急速に成熟しており、LangChain、LlamaIndex、Haystackなどのフレームワークは、これらの高度なパターンに対するますます洗練されたサポートを提供しています。強力なローカルLLMランタイムとエンベディングモデルと組み合わせることで、RAGシステムの実験と本番環境への展開はこれまでになく容易になりました。

基本から始め、性能を厳密に測定し、要件に応じてアーキテクチャを進化させてください。ここに記載された高度なRAGのバリエーションは、その進化のための道標となります。

有用なリンク

外部参照