RAGなしで始めるナレッジグラフQA——コンテナで再現する比較検証
RAG なしで始めるナレッジグラフ QA——コンテナで再現する比較検証
ねらい
「RAG なし」= RAG に含まれるベクトル検索部分を実装せず、ナレッジグラフ単独で論理的に答える最小構成を Docker だけで動かして確認する。差分・否定・経路・カウントなど、ベクトル検索が失敗しやすい質問で、KG(Cypher/SPARQL)が正確に答えられることを手元で再現する。
重要な注釈:RAG と KG は対立ではなく補完関係
本記事のタイトルは「RAG なし」ですが、これは「RAG を否定する」という意味ではありません。むしろ、KG と RAG の役割分担を理解するための比較実験 です。
実務では、KG と RAG は併用されます:
- RAG の役割:大量の非構造化テキスト(ドキュメント、記事、ログ)から関連情報を素早く検索し、文脈を提供する
- KG の役割:構造化された知識(エンティティ・関係・ルール)から論理的に推論し、正確な回答を導く
本記事では、KG が得意とする領域を明確にするために、敢えて KG 単独での動作を検証 しています。実装ではベクトル DB(Qdrant)も起動していますが、意図的に LLM を使わずに、データストアの本質的な特性の違い を浮き彫りにしています。
設計的な重要なポイント:なぜ LLM を使わないのか
この実験では、意図的に LLM を省いています。パイプラインは以下の通り:
- KG: 質問 → Cypher(手書き) → グラフ実行 → 回答
- ベクトル検索: 質問 → ベクトル埋め込み → テキスト検索 → キーワード抽出 → 回答
現実のシステムでは、ベクトル検索の後ろに LLM(ChatGPT など)がついて自然言語で回答を生成します(これが RAG)。しかし、その LLM でも データストアの根本的な特性(テキスト検索の曖昧性)は超えられません。詳しくは「RAG を超える知識統合」の5 つの意味質問型を参照してください。
エンジニアリング・リソースの長期的視点
RAG は「プロンプト調整」「リランキング」などで精度を上げられますが、継続的な調整が必要です。一方、KG は構造確定後は安定します:
| 観点 | RAG | KG |
|---|---|---|
| embedding モデル進化時 | 全データ再処理・再チューニング | 影響なし |
| LLM 変更時 | プロンプト再調整必須 | 影響なし |
| スケーリング(規模増大時) | ベクトル検索の計算コスト爆増 | Cypher クエリで線形時間実行 |
大規模運用での実例:Wikidata(8,800 万エンティティ)のような規模では、RAG のベクトル検索は実用的でなく、KG は高速に応答可能です。
実務的な結論:初期段階では RAG が低コストで有効。しかし、長期的・大規模運用を視野に入れると、KG への投資が ROI が高い。理想は「論理的クエリが必要なら KG、テキスト探索が必要なら RAG を補助」という役割分担です。
1. 何を用意するか(ローカルのみ)
- macOS(または Linux/WSL2)
- Docker / Docker Compose v2
-
curl(動作確認) - 使うもの:
- Neo4j 5(Property Graph, Cypher)
- Qdrant(ベクトル検索用のベクトル DB:比較対象。ベクトル検索は"失敗例"として参照)
この記事は ベクトル検索を"使わない"実装が主役ですが、比較のため最小のベクトル検索ベースラインも同時起動します。
2. 実験環境の構成
本実験は Docker コンテナで動作し、以下が起動します:
- Neo4j 5 — ナレッジグラフ(Property Graph)
- Qdrant — ベクトル DB(ベクトル検索のベースライン)
- FastAPI — 比較用 API
詳細なセットアップ手順、試験データセット(5 項目版・50 項目版)、試験質問については、GitHub の実装ガイドを参照してください。
このガイドには以下が含まれます:
- グラフ構造と試験データの詳細説明
- 5 つの試験質問(集合・差分・経路・否定・交差)
- クイックスタートコマンド
- API エンドポイントの使い方
- 50 項目データセットによるスケール依存性の実証
3. 5 つの意味質問型——KG が得意、ベクトル検索が失敗しやすい質問パターン
本実験で検証する 5 つの質問タイプを具体例で示します。
Q1-集合(Union) — 「A または B に該当するものは?」
// A または B に該当する人物を検索
Q: "ソフトウェアエンジニアまたはデータサイエンティストは誰ですか?"
KG (Cypher):
MATCH (p:Person)-[:hasRole]->(r:Role)
WHERE r.name IN ['SoftwareEngineer', 'DataScientist']
RETURN p.name
ベクトル検索:
→ ベクトル検索で "エンジニア" "データサイエンティスト" を検索
→ ノイズが多い場合、関連ドキュメントが多すぎて曖昧
Q2-差分(Set Difference) — 「A に属するが B には属さない者は?」
// A に属するが B には属さない人物を検索
Q: "プロジェクトXに参加しているがプロジェクトYには参加していない人は?"
KG (Cypher):
MATCH (p:Person)-[:participates]->(px:Project {name: 'X'})
WHERE NOT EXISTS { (p)-[:participates]->(:Project {name: 'Y'}) }
RETURN p.name
ベクトル検索:
→ 「プロジェクトX」と「プロジェクトY」の差を推論できない
→ テキスト検索では否定条件を正確に表現不可
Q3-経路(Path Traversal) — 「A から B へのつながりは?」
// A から B への経路を辿る
Q: "太郎がプロジェクトXを通じて、どの組織につながっているか?"
KG (Cypher):
MATCH path = (p:Person {name: '太郎'})-[:participates]->(:Project {name: 'X'})-[:belongsTo]->(org:Organization)
RETURN org.name, path
ベクトル検索:
→ 多段階のグラフ構造を追跡できない
→ テキスト検索は "点" 単位であり、"経路" を辿れない
Q4-否定(Negation) — 「A ではない条件を満たすのは?」
// A ではない条件を満たす人物を検索
Q: "東京オフィスに配属されていない従業員は?"
KG (Cypher):
MATCH (emp:Employee)
WHERE NOT EXISTS { (emp)-[:assignedTo]->(:Office {location: 'Tokyo'}) }
RETURN emp.name
ベクトル検索:
→ 「否定」という論理演算子をテキストから推論困難
→ ベクトル検索は類似度で判定するため、"ない" という条件が曖昧
Q5-交差(Intersection) — 「A かつ B の両方に属する者は?」
// A かつ B の両方の条件を満たす人物を検索
Q: "プロジェクトXにも参加し、かつ管理職でもある人は?"
KG (Cypher):
MATCH (p:Person)-[:participates]->(:Project {name: 'X'})
MATCH (p)-[:hasRole]->(r:Role {type: 'Manager'})
RETURN p.name
ベクトル検索:
→ 複数条件の交差をベクトル検索では不正確
→ スケール増加時にノイズが増えると精度が急落
結果の見え方
| 質問型 | KG | ベクトル検索(小規模) | ベクトル検索(大規模) | 理由 |
|---|---|---|---|---|
| Q1-集合 | ✅ 正確 | ⚠️ 変動 | ❌ ノイズで失敗 | OR は明確だが、ノイズに弱い |
| Q2-差分 | ✅ 正確 | ⚠️ 変動 | ⚠️ 変動 | 論理的な差分(A かつ NOT B)はベクトル検索に不向き |
| Q3-経路 | ✅ 正確 | ❌ 失敗 | ❌ 失敗 | 多段階経路をベクトル検索で追跡不可 |
| Q4-否定 | ✅ 正確 | ❌ 失敗 | ❌ 失敗 | 論理的否定が曖昧 |
| Q5-交差 | ✅ 正確 | ⚠️ 変動 | ❌ 失敗 | 複数条件の厳密な AND が難しい |
4. 実行結果の概要
ローカルで実行すると、以下の結果が得られます。
5 項目版
{
"summary": {
"kg_correct": 5,
"kg_total": 5,
"rag_correct": 2,
"rag_total": 5
}
}
解釈: KG は全問正解。RAG は集合と差分のみ正解(2/5)。
⚠️ 注釈:以下は手元で複数回実行した際の参考値です。RAG(ベクトル検索)の結果は実行ごとに変動します。この実例では Q1-集合と Q2-差分のみ成功しましたが、実行環境や embedding の初期値によって異なる結果が得られる可能性があります。
小規模データ(5 項目版)の結果だけでなく、規模を増やした 50 項目版でも傾向は変わりませんでした。
50 項目版
{
"summary": {
"kg_correct": 5,
"kg_total": 5,
"rag_correct": 1,
"rag_total": 5
}
}
解釈: KG は相変わらず全問正解。RAG は大幅に精度低下(ノイズが増えると精度が急落)。
⚠️ 注釈:以下は手元で複数回実行した際の参考値です。RAG(ベクトル検索)の結果は実行ごとに変動します。この実例では 1/5 の成功率でしたが、一般的には 0 ~ 1/5 の範囲で変動します。ベクトル空間における類似度計算の不確定性により、実行環境や初期条件によって結果が大きく異なります。
5. 結果の解釈
実験結果はすべての試験で一貫して同様の傾向を示しました。特に Q3(経路)と Q4(否定)は全試行でベクトル検索が失敗し、KG は常に正確な結果を返しました。
以下の表はその傾向を簡潔にまとめたものです。
| 質問型 | 傾向 |
|---|---|
| Q1-集合・Q2-差分・Q5-交差 | ベクトル検索は部分的に成功するが精度が不安定 |
| Q3-経路・Q4-否定 | ベクトル検索は全試行で失敗、KG は常に正確 |
KG の強み
- スケール不変性 — データが 5 件でも 50 件でも、Cypher クエリの結果は同じ
- 論理厳密性 — 集合、差分、否定、経路追跡を正確に実行
- 構造理解 — ドメイン知識をグラフ構造として記述すれば、複雑な関係も明確に表現可能
RAG の限界
- スケール依存性 — データが増えるとベクトル検索のノイズが増加
- 操作的な問い — 差分、否定、カウント、経路追跡は得意でない
- 曖昧性 — テキストが増えるほど意図を正確に拾いづらくなる
6. さらに学ぶために
理論的背景
本記事で実装した内容の理論的背景やアーキテクチャ的な位置づけについては、以下の記事をご覧ください:
-
「RAG を超える知識統合 ── ナレッジグラフで"つながる推論"を実現する」
- RAG と KG の本質的な違い
- GraphRAG と KG の関係性
- エンタープライズ知識グラフの戦略的な役割
技術資料
- Neo4j ドキュメント: https://neo4j.com/docs/
- Cypher 解説: https://neo4j.com/docs/cypher-manual/
- セマンティック Web との統合: SPARQL、RDF、OWL
- スケールアップ: Wikidata、DBpedia への連携
参考文献
- Neo4j (2024), Neo4j Documentation. https://neo4j.com/docs/
- Neo4j (2024), Cypher Query Language Manual. https://neo4j.com/docs/cypher-manual/
- Tech Blog (2024), kg-no-rag 再現環境リポジトリ. https://github.com/DevRev-JP/tech-blog/tree/main/experiments/kg-no-rag
更新履歴
- 2025-10-24 — 初版公開
- 2025-10-30 — 参考文献を追記
※本記事は AI を活用して執筆しています。
Discussion