Graph RAG¶
Graph RAG augments vector retrieval with a knowledge graph, enabling multi-hop reasoning and global structural queries that flat vector search cannot handle — such as "how are concept A and concept B related?" or "what does this entity depend on?"
What you'll learn¶
- What knowledge graphs add that vector search cannot provide
- How to extract entities and relations from text using an LLM prompt
- The graph + vector hybrid retrieval pattern
- When to use GraphRAG and when plain vector RAG is enough
- Tools and trade-offs
Why graphs complement vectors¶
Vector search answers "what documents are semantically similar to this query?" — it operates on local similarity. A knowledge graph answers "how are these concepts connected?" — it captures global structure and multi-hop paths.
| Query type | Vector RAG | Graph RAG |
|---|---|---|
| "What is HNSW?" | Excellent | Overkill |
| "What indexes does ChromaDB support?" | Good | Good |
| "Which components depend on the embedding model?" | Weak | Strong |
| "Summarize all topics related to retrieval" | Weak (misses links) | Strong |
| "How does chunking affect indexing and then retrieval?" | Weak | Strong |
Entity and relation extraction¶
The first step in building a knowledge graph from text is extracting structured triples (subject, relation, object) using an LLM:
# extract_graph.py
import ollama
import json
EXTRACT_PROMPT = """Extract knowledge graph triples from the text below.
Return a JSON array of objects with keys: "subject", "relation", "object".
Extract only factual relationships stated in the text. Output ONLY valid JSON.
Text: {text}"""
def extract_triples(text: str) -> list[dict]:
r = ollama.chat(
model="llama3.2",
messages=[{"role": "user", "content": EXTRACT_PROMPT.format(text=text)}]
)
raw = r["message"]["content"].strip()
# Strip markdown fences if present
if raw.startswith("```"):
raw = raw.split("```")[1]
if raw.startswith("json"):
raw = raw[4:]
return json.loads(raw.strip())
sample = (
"ChromaDB is a vector database that supports HNSW indexing. "
"HNSW is an approximate nearest-neighbor algorithm. "
"ChromaDB can be used with sentence-transformers for embedding."
)
triples = extract_triples(sample)
for t in triples:
print(f" ({t['subject']}) --[{t['relation']}]--> ({t['object']})")
Expected output:
(ChromaDB) --[is a]--> (vector database)
(ChromaDB) --[supports]--> (HNSW indexing)
(HNSW) --[is an]--> (approximate nearest-neighbor algorithm)
(ChromaDB) --[used with]--> (sentence-transformers)
Graph + vector hybrid retrieval¶
flowchart LR
Q[Query] --> NER[Entity extraction\nfrom query]
NER --> Graph[(Knowledge\ngraph)]
Q --> Vector[(Vector\nstore)]
Graph -->|Neighboring nodes\n& relations| Merge[Context merger]
Vector -->|Top-K chunks| Merge
Merge --> LLM[LLM generation]
- Extract entities mentioned in the query
- Traverse the graph: fetch the subgraph around those entities (1–2 hops)
- Also retrieve top-K vector chunks as usual
- Merge graph context + chunk context into the prompt
- Generate
Building a simple in-memory graph¶
# simple_graph.py
from collections import defaultdict
class KnowledgeGraph:
def __init__(self):
self.edges: dict[str, list[tuple[str, str]]] = defaultdict(list)
def add_triple(self, subject: str, relation: str, obj: str):
self.edges[subject.lower()].append((relation, obj))
self.edges[obj.lower()].append((f"inverse:{relation}", subject))
def get_neighbors(self, entity: str, hops: int = 1) -> list[str]:
visited, queue = set(), [entity.lower()]
context = []
for _ in range(hops):
next_queue = []
for node in queue:
for rel, neighbor in self.edges.get(node, []):
fact = f"{node} --[{rel}]--> {neighbor}"
if fact not in visited:
visited.add(fact)
context.append(fact)
next_queue.append(neighbor.lower())
queue = next_queue
return context
kg = KnowledgeGraph()
kg.add_triple("ChromaDB", "supports", "HNSW indexing")
kg.add_triple("HNSW", "is a", "nearest-neighbor algorithm")
kg.add_triple("ChromaDB", "used with", "sentence-transformers")
print(kg.get_neighbors("chromadb", hops=2))
When to use Graph RAG¶
Use Graph RAG when
- Your domain has rich, explicit relationships (biomedical, legal, engineering)
- Users ask multi-hop questions: "what depends on what?"
- You need global summarization across connected concepts
- Precision on entity-specific queries is critical
Avoid Graph RAG when
- Your corpus is unstructured prose without clear entities and relations
- Query patterns are purely semantic similarity
- You need fast time-to-production — graph construction is expensive to maintain
Production tools¶
| Tool | Notes |
|---|---|
| Neo4j | Mature graph DB with Cypher query language; LangChain integration available |
| Microsoft GraphRAG | Open-source library for community-level graph summarization at scale |
| NetworkX | Pure-Python in-memory graphs; good for prototyping (used above) |
| Kuzu | Embeddable graph DB, good for local/offline use |
Extraction quality matters
The graph is only as good as your extraction. Use few-shot prompts, validate triples against a schema, and consider human review for high-stakes domains. Noisy graphs degrade answer quality.
Next steps¶
- Agentic RAG — agents can traverse knowledge graphs as one of several tools
- Evaluation — measure whether graph context improves faithfulness and relevance scores