GraphRAG スキル
フラットなチャンクではなく、ナレッジグラフ上での検索拡張生成。 エンティティとリレーションシップはドキュメントから抽出され、 グラフデータベースに格納され、クエリ時にLLMがグラフ構造に基づいて推論します。
なぜベクトルではなくグラフなのか
ベクトル検索は類似したチャンクを見つけます。関連をたどることはできません。
- 「プラントXの停止の影響を受けたサプライヤーは?」は
停止 → 影響 → プラント → 供給元 → サプライヤーという2ホップの関係を必要としますが、単一のチャンクには含まれていません。 - 「プロジェクトYについて知っているすべてを要約して」は、最も類似した10個の段落ではなく、Yを中心としたエンティティのコミュニティを必要とします。
質問が単一の事実を調べるものであれば、ベクターRAG(よりシンプルです)を使い続けてください。質問が関係性にまたがる場合は、GraphRAGに手を伸ばしてください。
スタック
- Kuzu — 埋め込みグラフデータベース(
pip install kuzu)、サーバーは不要、Cypherクエリ言語。ここから始めてください。 - Neo4j — 埋め込みデータベースでは手狭になった場合:マルチユーザー、より大きなグラフ、Bloom可視化。同じCypherスキルがそのまま使えます。
- LLM抽出 — エンティティ/リレーションシップ抽出のための構造化出力を備えた、任意の強力なモデル。
パイプライン
1. 最初にスキーマを定義する
抽出器に自由にタイプを発明させないでください — 同じリレーションシップの40通りの綴りができてしまいます。ノードとエッジのタイプを事前に宣言します:
CREATE NODE TABLE Person(name STRING, PRIMARY KEY(name));
CREATE NODE TABLE Project(name STRING, status STRING, PRIMARY KEY(name));
CREATE NODE TABLE Document(title STRING, date DATE, PRIMARY KEY(title));
CREATE REL TABLE WORKS_ON(FROM Person TO Project, role STRING);
CREATE REL TABLE MENTIONS(FROM Document TO Person);
CREATE REL TABLE MENTIONS_PROJECT(FROM Document TO Project);
スキーマは小さく保ちます(5〜10ノードタイプ)。拡張は可能ですが、乱雑なグラフを安価に抽出し直すことはできません。
2. エンティティとリレーションシップを抽出する
ドキュメントをチャンク化し(500〜1000トークン、オーバーラップあり)、その後抽出器にプロンプトを与えます:
以下のテキストからエンティティとリレーションシップを抽出してください。
許可されるノードタイプ:Person, Project, Document
許可されるエッジタイプ:WORKS_ON (Person→Project), MENTIONS (Document→Person),
MENTIONS_PROJECT (Document→Project)
ルール:
- 名前を正規化する(「J. Smith」と「John Smith」は同じPersonとする)
- テキストに明示されているか、明確に暗示されているリレーションシップのみを抽出する
- JSONで返す:{"nodes": [...], "edges": [...]}
テキスト:
<chunk>
10〜20チャンクを手動でスポットチェックします。抽出品質は、その後のすべての処理の上限です — ノイズの多いグラフはノイズの多い回答を生みます。
3. Kuzuにロードする
import kuzu
db = kuzu.Database("./knowledge.kz")
conn = kuzu.Connection(db)
# スキーマを作成し、次に抽出されたノード/エッジごとにパラメータ化されたMERGE/CREATEを実行
ロード時に重複排除する(主キーに対してMERGE)— 同じエンティティが多くのチャンクに現れます。
4. クエリパターン
ローカルルックアップ — エンティティに接続されているすべて:
MATCH (p:Person {name: $name})-[r]-(neighbor)
RETURN type(r), neighbor;
マルチホップ推論 — グラフ全体のリレーションシップをたどる:
// 停止についてのドキュメントで言及されているプロジェクトで働いている人は誰か?
MATCH (d:Document)-[:MENTIONS_PROJECT]->(proj:Project)<-[:WORKS_ON]-(p:Person)
WHERE d.title CONTAINS 'outage'
RETURN DISTINCT p.name, proj.name;
グローバル要約 — コミュニティ検出を行い、各コミュニティを要約する:
// 密に接続されたクラスタを見つける(一度実行し、コミュニティIDをノードに保存)
CALL show_tables(); // その後、グラフのコミュニティ検出を使用
各コミュニティをLLMで要約する:「これらのエンティティはトピックXを中心にクラスタリングされています。」これがGraphRAGの「グローバルクエリ」です — 「これらのドキュメントの主要テーマは何か?」といった質問です。
5. LLMで回答する
機能するクエリパターン:
- エージェントが質問を1〜3のCypherクエリに変換する
- 結果が構造化された行として返される
- LLMが行から回答を合成し、ノード/エッジの証拠を引用する
- クエリが何も返さない場合、エージェントは再定式化する — グラフの内容を決して幻覚しない
時間的知識(エージェントメモリ)
セッションをまたぐエージェントメモリのために、エッジに時間を追加します:VALID_FROM / VALID_TOタイムスタンプ、上書きされるのではなく、取って代わられた事実にマークを付けます。これが時間的ナレッジグラフパターンです(Graphiti/Zepを参照):「ユーザーは以前はXを好んでいたが、今はYを好む」が、上書きされる代わりにクエリ可能なままになります。
アンチパターン
- スキーマなしでの抽出。 自由形式の抽出は、誰もクエリできないグラフを生成します。
- すべてにGraphRAGを使う。 クリーンなドキュメントに対する単一の事実検索? ベクター検索の方が安価で優れています。関係性が重要な場合にグラフを使用してください。
- 抽出を盲目的に信頼する。 すべてのパイプラインにはスポットチェックループが必要です。抽出エラーは静かに蓄積されます。
- 巨大なチャンク。 エンティティ抽出は約1000トークンを超えると品質が低下します — モデルがリレーションシップを見逃し始めます。より小さなチャンクを、より多く用意してください。
- ロード時の重複排除なし。 「John Smith」、「J. Smith」、「Smith」が3つのノードとして存在すると、マルチホップクエリが静かに壊れます。