graphrag-patterns について
graphrag-patternsは、検索拡張生成でエンティティ関係やマルチホップ推論を扱う開発者向けに、GraphRAGの設計パターンを紹介するスキルです。グラフ検索、ベクトルとの併用、テキストからCypherへの変換を扱います。
主な機能
- 01GraphRAGとベクトルRAGの比較
- 02エンティティ抽出とナレッジグラフ構築
- 03グラフ拡張検索の紹介
- 04自然言語からCypherへの変換
- 05ベクトルとグラフのハイブリッド検索
- 06Microsoft GraphRAGのコミュニティ要約
活用シーン
- 01マルチホップ質問に適した検索方式の選択
- 02文書からエンティティと関係を抽出
- 03既存のNeo4jグラフをクエリ
- 04意味的類似性とグラフトラバーサルの併用
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ latestaiagents/ agent-skills --skill graphrag-patternsインストール後、Claude Code で/graphrag-patternsを入力して読み込まれたか確認します。
詳細
GraphRAG のパターン
ナレッジグラフと RAG を組み合わせ、つながりのあるデータを対象とした複雑な推論を実現します。
GraphRAG と Vector RAG の使い分け
| ユースケース | Vector RAG | GraphRAG |
|---|---|---|
| シンプルな Q&A | ✅ | 過剰 |
| 事実の検索 | ✅ | ✅ |
| 複数ホップの推論 | ❌ | ✅ |
| 「X は Y とどのような関係にありますか?」 | ❌ | ✅ |
| エンティティ間の関係 | ❌ | ✅ |
| コンプライアンス/監査証跡 | ❌ | ✅ |
| テーマの要約 | ❌ | ✅ |
コアアーキテクチャ
┌─────────────────────────────────────────────────────────────┐
│ User Query │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Query Analyzer │
│ (Determine: vector, graph, or hybrid?) │
└─────────────────────────────────────────────────────────────┘
│ │
┌────────┴────────┐ ┌────────┴────────┐
▼ ▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Vector Search │ │ Graph Traverse │
│ (Semantic) │ │ (Structured) │
└─────────────────┘ └─────────────────┘
│ │
└────────┬──────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ Context Fusion │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ LLM Generation │
└─────────────────────────────────────────────────────────────┘
パターン 1:エンティティ抽出 → ナレッジグラフ
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from neo4j import GraphDatabase
# Step 1: Extract entities and relationships from documents
EXTRACTION_PROMPT = """Extract entities and relationships from this text.
Text: {text}
Return JSON format:
{{
"entities": [
{{"name": "...", "type": "Person|Organization|Concept|Event|Location"}}
],
"relationships": [
{{"source": "...", "target": "...", "type": "..."}}
]
}}
"""
async def extract_knowledge(text: str, llm: ChatOpenAI) -> dict:
"""Extract entities and relationships from text."""
prompt = ChatPromptTemplate.from_template(EXTRACTION_PROMPT)
chain = prompt | llm
result = await chain.ainvoke({"text": text})
return json.loads(result.content)
# Step 2: Store in Neo4j
class KnowledgeGraph:
def __init__(self, uri: str, user: str, password: str):
self.driver = GraphDatabase.driver(uri, auth=(user, password))
def add_entity(self, name: str, entity_type: str, properties: dict = None):
with self.driver.session() as session:
session.run(
f"""
MERGE (e:{entity_type} {{name: $name}})
SET e += $properties
""",
name=name,
properties=properties or {}
)
def add_relationship(self, source: str, target: str, rel_type: str):
with self.driver.session() as session:
session.run(
"""
MATCH (a {name: $source})
MATCH (b {name: $target})
MERGE (a)-[r:""" + rel_type + """]->(b)
""",
source=source,
target=target
)
パターン 2:グラフで強化した検索
from llama_index.core import PropertyGraphIndex
from llama_index.graph_stores.neo4j import Neo4jPropertyGraphStore
def create_property_graph_index(documents):
"""Create a property graph index with LlamaIndex."""
# Connect to Neo4j
graph_store = Neo4jPropertyGraphStore(
username="neo4j",
password="password",
url="bolt://localhost:7687",
)
# Build index - automatically extracts entities/relationships
index = PropertyGraphIndex.from_documents(
documents,
property_graph_store=graph_store,
show_progress=True,
)
return index
def query_with_graph(index, query: str):
"""Query using both vector and graph retrieval."""
# Create retriever that uses both paths
retriever = index.as_retriever(
include_text=True, # Include original text chunks
similarity_top_k=5,
)
# Get results
nodes = retriever.retrieve(query)
return nodes
パターン 3:グラフに直接クエリするための Text-to-Cypher
from langchain_community.graphs import Neo4jGraph
from langchain.chains import GraphCypherQAChain
def create_text_to_cypher_chain():
"""Create a chain that converts natural language to Cypher queries."""
# Connect to Neo4j
graph = Neo4jGraph(
url="bolt://localhost:7687",
username="neo4j",
password="password"
)
# Print schema for debugging
print(graph.schema)
# Create chain
chain = GraphCypherQAChain.from_llm(
llm=ChatOpenAI(model="gpt-4", temperature=0),
graph=graph,
verbose=True,
validate_cypher=True, # Validate before executing
return_intermediate_steps=True
)
return chain
# Usage
chain = create_text_to_cypher_chain()
result = chain.invoke({
"query": "What companies has John Smith worked for?"
})
# Generated Cypher: MATCH (p:Person {name: 'John Smith'})-[:WORKED_AT]->(c:Company) RETURN c.name
パターン 4:ベクトル検索とグラフ検索のハイブリッド
class HybridGraphRAG:
"""Combine vector similarity with graph traversal."""
def __init__(self, vector_store, graph_store):
self.vector_store = vector_store
self.graph_store = graph_store
def retrieve(self, query: str, top_k: int = 5) -> list[dict]:
# 1. Vector search for relevant chunks
vector_results = self.vector_store.similarity_search(query, k=top_k)
# 2. Extract entities from query
query_entities = self._extract_entities(query)
# 3. Graph traversal from those entities
graph_context = []
for entity in query_entities:
# Get 1-hop neighbors
neighbors = self.graph_store.query(f"""
MATCH (e {{name: '{entity}'}})-[r]-(n)
RETURN e.name, type(r), n.name, n.description
LIMIT 10
""")
graph_context.extend(neighbors)
# 4. Combine results
combined = {
"vector_chunks": [r.page_content for r in vector_results],
"graph_context": graph_context,
"entities": query_entities
}
return combined
def _extract_entities(self, text: str) -> list[str]:
# Use NER or LLM to extract entities
# Simplified version:
prompt = f"Extract entity names from: {text}"
# ... LLM call
return entities
パターン 5:Microsoft GraphRAG(コミュニティ検出)
# Microsoft's GraphRAG approach uses community detection
# for global summarization queries
from graphrag.index import run_indexing
from graphrag.query import LocalSearch, GlobalSearch
# Index documents (creates communities)
await run_indexing(
input_dir="./documents",
output_dir="./index",
config={
"llm": {"model": "gpt-4"},
"embeddings": {"model": "text-embedding-3-small"},
"chunks": {"size": 300, "overlap": 100},
"community_detection": {
"algorithm": "leiden",
"resolution": 1.0
}
}
)
# Local search (specific entity questions)
local = LocalSearch(index_dir="./index")
result = local.search("What is Company X's main product?")
# Global search (summarization across communities)
global_search = GlobalSearch(index_dir="./index")
result = global_search.search("What are the main themes in these documents?")
パターンの使い分け
| パターン | 使用する場面 |
|---|---|
| エンティティ抽出 → KG | ゼロから構築する場合、カスタムスキーマを使う場合 |
| Property Graph インデックス | 手早くセットアップしたい場合、LlamaIndex エコシステムを利用する場合 |
| Text-to-Cypher | 既存のグラフがある場合、複雑なクエリを使う場合 |
| ベクトル検索とグラフ検索のハイブリッド | セマンティックな類似性と構造の両方が必要な場合 |
| Microsoft GraphRAG | 大規模なコーパスがある場合、要約クエリを使う場合 |
ベストプラクティス
- スキーマを定義する - 重要なエンティティと関係を把握します
- シンプルに始める - 2~3 種類のエンティティから始め、必要に応じて拡張します
- Cypher を検証する - 生成されたクエリは、実行前に必ず検証します
- グラフクエリをキャッシュする - グラフのトラバーサルはコストがかかる場合があります
- ベクトル検索と組み合わせる - グラフだけではセマンティックな類似性を捉えられません
- 複数ホップのクエリをテストする - 2~3 ホップのクエリが許容できるパフォーマンスで動作することを確認します
よくある落とし穴
- 過剰な抽出:エンティティが多すぎると、グラフにノイズが生じます
- 関係の欠落:つながりのないエンティティは役に立ちません
- スキーマのずれ:エンティティの種類に一貫性がないと、クエリが機能しなくなります
- フォールバックがない:エンティティが見つからない場合、グラフのみでは機能しません
ツールとリソース
- Neo4j:本番環境向けのグラフデータベース
- LlamaIndex PropertyGraphIndex:簡単に使える Python 連携
- Microsoft GraphRAG:コミュニティベースのアプローチ
- Amazon Neptune:マネージド型グラフデータベース
- LangChain GraphCypherQAChain:Text-to-Cypher チェーン
コメント
Markdown 対応