Advanced9 min read31 of 40

Knowledge Graph RAG - Structured Reasoning with Entity Graphs

Augment RAG with a knowledge graph of entities and relationships for multi-hop reasoning, precise entity lookups, and explainable answers.

Overview

Knowledge Graph RAG enriches standard vector retrieval with a graph of named entities and their relationships. When a query requires reasoning across multiple entities — "Who manages the team that built product X?" — a knowledge graph can traverse the path product X → built_by → team → managed_by → person in a single hop, something dense retrieval cannot do reliably.

Pattern #2 in the 37 RAG Patterns Collection

The Problem

Standard RAG retrieves documents by semantic similarity. This works well for single-entity lookups but breaks on relational queries:

Query: "Which VP oversees the team responsible for the Falcon payment gateway?" Standard RAG retrieval: Top doc 1: "Falcon Payment Gateway product brief" (mentions features, not org) Top doc 2: "Engineering team structure 2024" (lists teams, not reporting lines) Top doc 3: "Falcon integration guide" (technical docs, no org info) Result: LLM hallucinates an answer because no single doc has the full chain.

The answer requires traversing: Falcon → owned_by → Payments Team → reports_to → Sarah Chen (VP Eng) — a two-hop graph path that dense retrieval cannot surface from isolated document embeddings.

Architecture

graph TB
    A[User Query] --> B[Entity Extraction]
    B --> C{Query Type?}
    C -->|Relational| D[Graph Traversal]
    C -->|Semantic| E[Vector Search]
    D --> F[Multi-hop Path]
    F --> G[Reached Nodes]
    G --> E
    E --> H[Relevant Chunks]
    H --> I[Merge Context]
    I --> J[LLM Generation]

    style A fill:#f9f,stroke:#333
    style C fill:#ff9,stroke:#333
    style J fill:#9f9,stroke:#333

Graph Schema

Nodes represent entities; edges represent typed relationships:

Nodes: Person, Product, Team, Organization, Document Edges: manages, reports_to, built_by, owns, acquired, references

A small example graph fragment:

(Sarah Chen) --manages--> (Payments Team) (Payments Team) --built--> (Falcon Gateway) (Falcon Gateway) --acquired_by--> (Acme Corp) (Falcon Gateway) --documented_in--> [doc_chunk_42, doc_chunk_87]

Implementation

Step 1 — Build the Knowledge Graph

python
import networkx as nx
from sentence_transformers import SentenceTransformer

graph = nx.DiGraph()
embedder = SentenceTransformer("all-MiniLM-L6-v2")

def add_entity(name: str, entity_type: str, chunk_ids: list[str]):
    """Add a node with its type and associated document chunks."""
    graph.add_node(name, type=entity_type, chunks=chunk_ids)

def add_relation(source: str, relation: str, target: str):
    """Add a directed edge between two entities."""
    graph.add_edge(source, target, relation=relation)

# Populate from structured data or NER extraction
add_entity("Sarah Chen", "Person", ["chunk_12", "chunk_45"])
add_entity("Payments Team", "Team", ["chunk_88", "chunk_91"])
add_entity("Falcon Gateway", "Product", ["chunk_42", "chunk_87"])

add_relation("Sarah Chen", "manages", "Payments Team")
add_relation("Payments Team", "built", "Falcon Gateway")

Step 2 — Extract Entities from Query

python
def extract_entities(query: str, llm) -> list[str]:
    """Use an LLM to identify named entities in the query."""
    prompt = f"""Extract all named entities (people, products, teams, orgs)
from the following query. Return a JSON list of strings.

Query: {query}

Entities:"""
    result = llm.invoke(prompt, schema={"type": "array", "items": {"type": "string"}})
    return result

Step 3 — Multi-Hop Graph Traversal

python
def traverse_graph(seed_entities: list[str], max_hops: int = 2) -> set[str]:
    """
    Starting from seed entities, collect all reachable nodes within max_hops.
    Returns a set of chunk IDs associated with reached nodes.
    """
    visited_nodes = set()
    chunk_ids = set()
    frontier = set(seed_entities)

    for _ in range(max_hops):
        next_frontier = set()
        for node in frontier:
            if node not in graph or node in visited_nodes:
                continue
            visited_nodes.add(node)
            # Collect document chunks linked to this node
            chunk_ids.update(graph.nodes[node].get("chunks", []))
            # Expand to neighbours
            next_frontier.update(graph.successors(node))
            next_frontier.update(graph.predecessors(node))
        frontier = next_frontier - visited_nodes

    return chunk_ids


def knowledge_graph_rag(query: str) -> tuple[str, dict]:
    # Step 1: Extract entities mentioned in the query
    entities = extract_entities(query, llm)

    # Step 2: Traverse graph from those entities
    graph_chunk_ids = traverse_graph(entities, max_hops=2)

    # Step 3: Vector search for semantic similarity
    semantic_chunks = vector_store.search(query, top_k=5)

    # Step 4: Fetch graph-referenced chunks from the document store
    graph_chunks = [doc_store.get(cid) for cid in graph_chunk_ids if doc_store.has(cid)]

    # Step 5: Merge and deduplicate
    seen = set()
    merged = []
    for chunk in graph_chunks + semantic_chunks:
        if chunk["id"] not in seen:
            seen.add(chunk["id"])
            merged.append(chunk)

    context = "\n\n".join(c["content"] for c in merged[:8])
    response = llm.generate(f"Context: {context}\n\nQuestion: {query}")

    return response, {
        "entities_found": entities,
        "graph_chunks": len(graph_chunk_ids),
        "semantic_chunks": len(semantic_chunks),
        "merged_total": len(merged),
    }

Complete Notebook

Run the full implementation:

Knowledge Graph RAG AWS Notebook

Includes:

  • Graph construction from unstructured text with NER
  • Neo4j and NetworkX backend options
  • Multi-hop traversal with path explanations
  • AWS Bedrock integration
  • Evaluation against standard RAG on multi-hop benchmarks

Performance

Query TypeStandard RAGKG-RAG
Single-entity lookup79%81%
Two-hop relational44%81%
Three-hop relational21%68%
Latency (avg)~300ms~650ms

KG-RAG nearly doubles accuracy on multi-hop queries.

When to Use

Best For

  • Enterprise knowledge bases with clear entity relationships (org charts, product catalogs, project ownership)
  • Multi-hop reasoning — queries that require chaining two or more relationships
  • Explainability requirements — graph paths make reasoning traceable
  • Structured + unstructured hybrid — when you have both database records and documents

Combine With

  • Hierarchical RAG (#22) — use the graph for cross-document entity links and hierarchical chunking for within-document structure
  • Self-RAG (#14) — after graph traversal, validate that the retrieved chunks actually answer the question before generating

Related Papers

Next Steps

  1. Try it: Run the notebook
  2. Scale up: Move from NetworkX to Neo4j for graphs with millions of nodes
  3. Automate construction: Use an NER pipeline to extract entities and relations automatically from your document corpus
  4. Add validation: Layer Self-RAG (#14) to verify graph-retrieved chunks before generation

Part of: 37 RAG Patterns Collection See also: Complete Pattern Index

Go Deeper With Live Instruction

Covered in depth in our llm engineering program.