10月8日、Machine Learning Masteryが「Evaluating Graph-RAG vs. Standard RAG: A Hallucination Benchmark on Fact-Dense Queries」と題した記事を公開した。Graph-RAGと標準RAGを事実密度の高いクエリで定量比較し、幻覚(ハルシネーション)の発生率にどれほど差が出るかを検証した実験を詳しく解説している。
Graph-RAGとは何か——なぜ注目されているのか
Graph-RAGは、Microsoftが提唱したRAG(Retrieval-Augmented Generation)の拡張手法だ。従来の標準RAGがベクトルDBから類似テキストを検索してLLMに渡すのに対し、Graph-RAGはナレッジグラフ(エンティティ間の関係を構造化したグラフDB)を併用する。エンティティ同士の関係性を明示的に保持できるため、「誰が何をしたか」「ある値の出典はどこか」といった事実密度の高いクエリへの回答精度が向上すると期待されている。
特に、ベクトル検索では避けられない「文脈のノイズ」——1つのドキュメントに複数の数値や矛盾する記述が混在する状況——においても、グラフDBに格納された構造化された事実を「絶対的真実」として参照できる点が強みとされてきた。この設計思想から、Graph-RAGは標準RAGよりもハルシネーションを抑制できるという期待が業界に広まっている。今回の実験は、その期待を実際のベンチマークで検証したものだ。
予想外の結果:Graph-RAGが標準RAGに負けた
実験の結論から先に述べる。**標準Vector-RAGの正解率が96.0%、3-Tiered Graph-RAGが92.0%**だった。Graph-RAGの方が低かった。
この結果は直感に反する。Graph-RAGはナレッジグラフに「絶対的な真実」を格納し、ベクトルDBの汚染されたコンテキストに依存しない設計だ。にもかかわらず標準RAGに劣ったのには、明確な理由がある。使用したLLMが小さすぎた。
実験に使ったのはgoogle/flan-t5-base(パラメータ数:250M)。このモデルは単純な読解タスクには強いが、「グラフの情報を優先し、ベクトルDBの情報を無視せよ」という複雑な指示に従う能力が不足している。標準RAGのシンプルなプロンプトとの相性は良くても、Graph-RAGが要求するコンフリクト解消(矛盾する情報源の優先順位付け)は荷が重すぎた。
記事はこの結果について「プロンプトの複雑さとモデルの処理能力を合わせなければならない」という教訓として整理している。Graph-RAGの構造的優位性を活かすには、LlamaやGPT-4クラスの推論能力を持つモデルが必要だということだ。
実験の設計:意図的にノイズを混入させたデータセット
実験はバスケットボール選手50人の合成データセットを使って構成された。設計のポイントは「グラフDBには正確な値を、ベクトルDBには意図的に矛盾する記述を格納する」という点だ。
たとえば、あるプレイヤーの本当の1試合平均得点(PPG)が23だとする。ベクトルDBに格納されるテキストはこうなる:
"During the recent game, Player_7 had a terrible first half, scoring only 4 points. Historically, his career average sat around 12. However, his official season average PPG is currently 23."
1つのドキュメントに3つの異なる数字が登場する。標準RAGがトップ3チャンクを取得してまとめると、モデルはどの数字が「公式平均」なのかを読み解かなければならない。現実のRAGシステムが直面するノイズをそのまま再現した設計だ。
グラフDBには(Player_7, season_ppg, 23, Sports_DB)というSPOC形式(Subject-Predicate-Object-Context:主語・述語・目的語・文脈の4要素で事実を表現するトリプルストアの拡張形式)で正確な値だけを格納する。このSimpleQuadStoreは実験用に自作したシンプルな集合ベースのクラスであり、外部ライブラリではない。主語(Subject)でフィルタするだけのクエリAPIを持ち、グラフDBの役割を最小限の実装で再現している。
2つの検索関数の実装
標準RAGはChromaDBからクエリに近いチャンクを3件取得し、それを結合してLLMに渡す:
def standard_vector_rag(q):
results = collection.query(query_texts=[q], n_results=3)
context = " ".join(results["documents"][0])
prompt = f"Context: {context}\nQuestion: {q}\nAnswer strictly with the exact number:"
return llm(prompt)
3-Tiered Graph-RAGはまずグラフDBを参照し、その値を「絶対的真実」としてプロンプトに明示した上で、ベクトルDBの結果をフォールバックとして付与する:
def deterministic_3_tier_rag(q, entity):
graph_res = qs.query(entity)
p1_context = f"{entity} season average PPG is {graph_res[0][2]}" if graph_res else "None"
p3_context = collection.query(query_texts=[q], n_results=1)["documents"][0][0]
prompt = f"""Context 1 (Absolute Truth): {p1_context}
Context 2 (Fallback Text): {p3_context}
Question: {q}
Answer strictly using Context 1 with the exact number:"""
return llm(prompt)
プロンプト構造の複雑さの違いが一目でわかる。Graph-RAGは情報源の優先順位をLLMに言語で教示しているが、これが小規模モデルの処理能力の限界を超えてしまった。
環境構築
依存ライブラリは2つだけだ:
pip install -q chromadb transformers
ローカルでflan-t5-baseを動かすため、HuggingFaceのtransformersライブラリを使う。Colabでもそのまま動作する構成だ。ベクトルDBにはChromaDBを採用している。
実装者への示唆
この実験が明らかにしたのは、Graph-RAGの設計思想は正しいが、その恩恵を受けられるかはLLMの能力に依存するという点だ。構造的に正確な情報をグラフに持っていても、LLMがプロンプト指示に従えなければ意味がない。
本番環境でGraph-RAGを採用する場合は、コンフリクト解消の指示に耐えられる十分な推論能力を持つモデルを選ぶ必要がある。小型ローカルモデルでコストを抑えたい場合は、プロンプトをシンプルな標準RAG構成に留める方が実際の精度は高くなる可能性がある。
詳細はEvaluating Graph-RAG vs. Standard RAG: A Hallucination Benchmark on Fact-Dense Queriesを参照していただきたい。




