Vibe Engines
YouTube
AI System Design

Design a GraphRAG System

Learn AI system design by building a GraphRAG system — knowledge-graph-augmented retrieval — step by step.

The numbers to beatLLM per chunkextract entities + relsmergeentity resolutionindex-time costbuild the graph once

The whole design, in writing

Learn AI system design by building a GraphRAG system — knowledge-graph-augmented retrieval — step by step. An interactive guide covering why vanilla vector RAG fails on global and multi-hop questions, building a knowledge graph by extracting entities and relationships, community detection and hierarchical summaries, local vs global query modes, hybrid graph+vector retrieval, indexing cost, and the unhappy paths (extraction errors, cost, when a graph helps).

Every step of the build above, written out: the problem each piece solves, the option that was taken and the ones that were not, the numbers, and how it fails in production.

The big idea

What problem does GraphRAG solve?

Vanilla RAG retrieves the top-k most similar chunks and hands them to the LLM. That's great for a local lookup ("what's the refund policy?") — but ask something that needs connecting facts across many documents ("how are these three incidents related?") or a global question ("what are the main themes across this whole corpus?") and it fails: the answer isn't in any single chunk, it's in the relationships between them.

Clienta question
New in this step: Client.

GraphRAG builds a knowledge graph (entities + relationships) from the corpus so retrieval can traverse connections, and clusters the graph into communities with pre-written summaries so global questions become a map-reduce over a few summaries. It adds structure and connectivity on top of RAG — answering the multi-hop and holistic questions similarity search can't.

What the new pieces do

Clientclient
Asks anything from a narrow fact ("what did X say?") to a global one ("what are the main themes across all these docs?").

Step 1 · Where vector RAG breaks

Isolated chunks, no connections

Vector RAG embeds the query, finds the nearest chunks, and stuffs them into the prompt. Name the class of questions it fundamentally can't answer well, and why.

Clienta question
The system as it stands at this step.

Vector RAG retrieves the top-k most similar chunks. What can't it answer well?

  1. Similarity search returns chunks that individually resemble the query, but questions like "how does A connect to C via B?" (multi-hop) or "what are the overall themes?" (global) require reasoning over connections spanning many documents. No single chunk contains that answer, and retrieving a handful of similar chunks misses the structure — you need the relationships, which vanilla RAG doesn't model.

  2. Those are exactly what vector RAG does well — a local fact usually lives in one similar chunk. Its weakness is connected/global questions, not simple lookups.

  3. Language is a separate concern (handled by multilingual embeddings). The structural gap is connected/multi-hop and global reasoning across documents.

Vector RAG returns chunks that individually resemble the query — perfect for a local fact living in one chunk. It fails when the answer requires connecting information across many chunks: multi-hop reasoning ("A relates to C through B", where A, B, C are in different documents) or global synthesis ("the main themes across everything"). Those answers live in the relationships and structure of the corpus, which isolated similarity search never captures.

Why this piece earns its place

The reason this deserves its own step is that the failure never reaches your telemetry. A multi-hop question does not error and does not time out — the retriever returns exactly k chunks with ordinary-looking scores, the model writes a fluent paragraph from them, and the only thing wrong is that the paragraph is thin. The eval harness is blind to it too: retrieval recall is scored against a known-correct chunk, and a question whose answer spans three documents has no single chunk to label, so the metric stays green while the answers get worse. The demand signal is suppressed as well. People learn quickly what a tool is bad at, so after a few vague replies they stop asking connected questions altogether, and the query log then under-reports exactly the traffic that would justify building a graph. So sample real questions by hand — from support threads, from the people who gave up, not from the log — and sort them into local, multi-hop and global. Keep that sample afterwards, because it is also the only before measurement you will ever get. Once the graph ships, the traffic changes shape: people discover the system can follow a chain and start asking things they never bothered to type, and a query log written by users who had already learned what not to ask cannot be the control group.

Step 2 · Add structure

Represent the corpus as a graph

If the missing thing is connections, what representation captures them, and how does that change what retrieval can do?

Clienta questionGraphRAG Svcindex · query
New in this step: GraphRAG Svc.

Build a knowledge graph over the corpus: entities (people, orgs, concepts, events) as nodes and their relationships as edges. Now retrieval can traverse — start at the entities in the question and follow edges to connected facts, gathering a connected context instead of a bag of similar chunks. The graph turns "find similar text" into "follow the relationships", which is exactly what multi-hop questions need.

Why this piece earns its place

The decision hiding inside this step is the schema: what counts as an entity, and which relationship labels are allowed. Fully open extraction — keep whatever the model finds — produces a graph whose node types are as varied as the prose that made them and whose edges are phrased in free text, where works_for, employed by and is staff at are three unrelated edges and no traversal can ask for all three. A closed schema traverses cleanly and silently discards anything it did not anticipate. Most real systems settle on a small fixed set of relationship types with the original wording carried alongside for display. Choose deliberately, because the schema is baked into every extraction you have already paid for: widening it later means re-reading the corpus, while adding another node or edge under the existing schema is just a write. The second thing the graph buys you is a doorway for data that was never prose. An org chart, a ticket system’s foreign keys, a product catalogue — those are already entities and relationships, so they merge into the same graph with no model in the loop and no extraction error to inherit.

What the new pieces do

GraphRAG Svcgateway
At index time builds the graph; at query time picks local vs global mode and assembles context for the LLM.

Step 3 · Build the graph

Entity & relationship extraction

Where does the graph come from? Your corpus is unstructured text — you have to derive entities and relationships from it. How, and at what cost?

Extractorentities + relsKnowledge Graphnodes + edges
New in this step: Extractor, Knowledge Graph.

Your corpus is plain text. How do you build a knowledge graph from it?

  1. Manual graph construction doesn't scale to a real corpus. You extract the graph automatically from the text.

  2. Titles are far too coarse — the entities and relationships live inside the text. You need to extract them from the chunk content.

  3. At indexing time, an LLM reads each chunk and extracts entities and their relationships (subject–relation–object), which are merged (deduping/aliasing entities) into a single knowledge graph. It's an LLM pass per chunk — expensive up front — but it's what turns unstructured text into a traversable graph.

At indexing time, run an LLM (or NER/relation-extraction models) over each chunk to extract entities and the relationships between them (subject–relation–object), then merge them — deduping and aliasing the same entity across chunks — into one knowledge graph. This is the expensive part: an LLM pass per chunk. But it's what converts unstructured text into a connected, traversable structure that the query side can exploit.

Why this piece earns its place

Two things about this pass only show up once you have run it. First, the blast radius of a mistake here is nothing like a mistake at query time. A bad retrieval spoils one answer; a bad extraction is written into the graph and quietly shapes every answer that ever traverses that edge, until somebody re-extracts. That is the argument against economising on the model here, which is otherwise the most tempting place to do it: a weaker extractor does not buy you a slightly worse answer, it buys you a permanently worse asset, and the saving is banked once while the damage is read back forever. Spend here and economise somewhere you can change your mind later. Second, make the pass resumable and idempotent before you start it rather than after. A pass over a whole corpus runs long enough to be interrupted — a rate limit, a deploy, one chunk that crashes the worker — and if the rerun does not skip chunks already done you pay the entire bill twice. Key the output by chunk id so a rerun is a no-op over finished work, and keep the raw triples as their own durable artifact, separate from the merged graph, so you can re-run the merge under better resolution rules without re-reading a single document.

  • LLM per chunkextract entities + rels
  • mergeentity resolution
  • index-time costbuild the graph once

What the new pieces do

Extractorservice
Indexing step: an LLM reads each chunk and extracts entities and the relationships between them.
Knowledge Graphdata
The extracted graph: entities as nodes, relationships as edges — a connected map of the corpus, not isolated chunks.

Step 4 · Answer the whole corpus

Communities & hierarchical summaries

Traversal handles multi-hop, but a truly global question ("what are the major themes across all 10,000 documents?") can't be answered by traversing from a few entities — it's about the whole corpus. Reading everything per query is impossible. How do you enable global questions?

Knowledge Graphnodes + edgesCommunitiescluster + summarize
New in this step: Communities.

Detect communities in the graph (clusters of densely-connected entities, via graph clustering like Leiden), then summarize each community with an LLM — and do it hierarchically (summaries of summaries) up to the whole corpus. Now a global question is answered by map-reduce over community summaries: answer it against each relevant community summary (map), then combine those partial answers (reduce). You get a holistic answer without reading every document at query time.

Why this piece earns its place

The thing to know about community detection is that the partition is not stable. Add documents, re-cluster, and the algorithm can redraw boundaries across the whole graph — one community splits, two merge, the numbering shifts. Community ids are therefore not durable identifiers, and anything treating them as such breaks quietly: a cached global answer, a saved link into a summary, a dashboard counting summaries per theme. Give each summary an identity derived from its members, or accept that everything downstream is re-derived on every re-clustering. The second point is that a summary is derived state three removes from the source — text, then graph, then clusters, then prose — so each of those can go stale on its own, and the errors compound without ever contradicting each other. A summary written from last month’s partition reads perfectly well and describes a grouping that no longer exists, which is the hardest kind of wrong to notice: fluency is not evidence of freshness, and nothing in the prose itself gives away its age. And treat the hierarchy level as a per-request choice: reducing over the top level is cheap and vague, reducing over the leaves is slow and specific, and that knob belongs in the query, not the config.

  • communitiesgraph clusters
  • summariesper community, hierarchical
  • global = map-reduceover summaries

What the new pieces do

Communitiesservice
Detects clusters (communities) in the graph and writes a summary of each, hierarchically — the key to global questions.

Step 5 · Two ways to ask

Local vs global query modes

Different questions need different retrieval. How does the system decide how to gather context for a given query?

Communitiescluster + summarizeRetrievertraverseLLMsynthesize
New in this step: Retriever, LLM.

How should retrieval differ for "what did entity X do?" vs "what are the overall themes?"

  1. Entity-centric (local) questions start at the mentioned entities and traverse their neighborhood in the graph to gather connected facts. Global/thematic questions use the community summaries via map-reduce. The system routes to the mode that fits the question (and can blend them).

  2. Traversing everything is expensive and unnecessary for a local question, and doesn't give the pre-summarized holistic view a global question needs. Match the mode to the question.

  3. Community summaries are for global questions; a precise local fact is better answered by traversing from the specific entity. You need both modes.

Two modes. Local (entity-centric): find the entities in the query, traverse the graph around them to gather connected facts (and the chunks backing them) — for specific, multi-hop questions. Global (thematic): map-reduce over community summaries — for holistic, "across everything" questions. The system routes each query to the right mode (or blends), assembling a connected context the LLM then synthesizes into a cited answer.

Why this piece earns its place

Routing looks like a classification problem and is mostly a budget problem. The two modes do not cost remotely the same: a local question is served out of one neighbourhood, while a global one spends a model call on every community summary it maps over before it can reduce them. So a misroute to global is not a slightly worse answer, it is a request whose price is set by how many communities the graph happens to contain — a number the person asking neither sees nor controls, and one that grows with the corpus. Any user can trigger it over and over just by phrasing things broadly. Global mode needs its own concurrency cap and its own spend limit, separate from the service’s overall rate limit. The quieter failure lives inside local mode. Before it can traverse, it has to match the names in the question to nodes in the graph — the same resolution problem as indexing, now on the critical path with no time to deliberate. If the question names something the graph canonicalised differently, traversal starts from nowhere and returns an empty neighbourhood, and the answer reports that there is no information rather than admitting it could not find the entity. Log the linked entities on every query; an empty link set is the signal, and nothing else exposes it.

  • localtraverse from entities
  • globalmap-reduce summaries
  • routemode per question

What the new pieces do

Retrieverservice
Query time: traverses the graph (local) or map-reduces community summaries (global), fused with vector chunks.
LLMservice
Reads the assembled graph + chunk context and writes the grounded, cited answer.

Step 6 · Best of both

Hybrid graph + vector retrieval

The graph is powerful for connections, but plain vector search is still great for finding the specific passage that states a fact. Should you throw away vector RAG? No. How do they combine?

Retrievergraph + vectorVector Chunksembeddings
New in this step: Vector Chunks.

Run hybrid retrieval: use the graph for structure and connections (traversal, communities) and keep vector search over chunks for pinpoint semantic matches, then fuse them into one context — graph-derived connected facts plus the exact supporting chunks (which also give you citations). The LLM gets both the "how things connect" from the graph and the "here's the passage that proves it" from vectors, producing answers that are connected and grounded.

Why this piece earns its place

Fusing two retrievers sounds like concatenation and is really a ranking problem, because the two scores are not comparable. Traversal gives you hop distance and edge weights; vector search gives you a similarity. No threshold means the same thing on both, so a merged list cannot simply be sorted by score. Either fuse at the level of rank rather than score, or put a single reranker over the union and let one model produce one ordering. The lazy version — take a fixed few from each side — is a budget split hardcoded in advance, generous to the graph on a simple lookup and stingy on a genuine multi-hop chain. The operational cost of keeping both indexes is that the corpus now has two write paths that can disagree. Delete a document, remove its chunks but not its edges, and you get an answer whose reasoning still holds and whose evidence has vanished — a claim with a dangling citation, which is worse than no citation because it looks checked. Decide once which store is authoritative for deletion, and reconcile the other toward it on a schedule.

  • graphconnections + global
  • vectorspinpoint + citations
  • fuseconnected AND grounded

What the new pieces do

Vector Chunksdata
The classic chunk embeddings, kept alongside the graph so retrieval can combine graph structure with semantic similarity.

Step 7 · Keeping it current

Indexing cost & incremental updates

Building the graph (LLM extraction per chunk + community summarization) is expensive and slow, and corpora change — new documents arrive constantly. Re-building the whole graph each time is untenable. How do you keep it fresh affordably?

ClientGraphRAG SvcExtractorKnowledge GraphCommunitiesRetrieverVector ChunksLLM
The system as it stands at this step. · swipe to pan the diagram

Treat graph construction as an expensive index build you amortize, and support incremental updates: when new documents arrive, extract their entities/relationships and merge into the existing graph (re-resolving entities), and re-summarize only the affected communities rather than the whole corpus. Cache and version the graph and summaries. Accept that indexing is the cost center of GraphRAG — you pay more at index time to get better answers to hard questions at query time.

Why this piece earns its place

What makes incremental updates genuinely hard is that a new document can change the meaning of old ones. A page establishing that two names refer to the same person retroactively merges nodes that were separate, rewiring edges written months earlier and moving entities between communities. The affected set is not the new document’s neighbourhood — it ripples, and the ripple has no clean bound. So the honest design runs both mechanisms: incremental merges for freshness, plus a scheduled full rebuild as the correctness backstop, with the rebuild producing a new graph version swapped in atomically rather than mutating the live one. Mutating in place means a query in flight traverses a half-updated graph and follows a hop that has disappeared by the time it fetches the supporting text. Pin the extractor model and prompt version to the graph version while you are there, or a rebuild meant only to add documents silently re-litigates every old edge under a different model and nobody can attribute the changed answers. That version is also the only unit a rollback can work in: you cannot un-merge a graph in place, so when a rebuild leaves answers worse than it found them, the recovery is to point traffic back at the previous version — which means keeping it, and its summaries, alive longer than it takes somebody to notice.

  • index buildexpensive, amortized
  • incrementalmerge new docs
  • re-summarizeaffected communities only

Step 8 · The sharp edges

Extraction errors, cost & when to use it

GraphRAG has real downsides: LLM extraction makes mistakes (wrong/missing entities and relations), indexing is costly, and it's overkill for many workloads.

ClientGraphRAG SvcExtractorKnowledge GraphCommunitiesRetrieverVector ChunksLLM
The system as it stands at this step. · swipe to pan the diagram

Manage extraction quality — errors propagate into a wrong graph, so use good prompts/models, entity resolution, and validation, and remember the graph is an approximation. Control cost (LLM per chunk + summarization) by scoping GraphRAG to corpora and question types that need it. Crucially, know when to use it: for mostly-local factual Q&A, vanilla vector RAG is cheaper and sufficient — GraphRAG earns its cost only when multi-hop or global/thematic questions matter (research synthesis, investigations, large connected corpora). Default to hybrid, and reach for the full graph when the questions demand connectivity.

Why this piece earns its place

The shape of the cost matters as much as its size. Vanilla RAG bills you for what people use; a graph bills you for what you own. Extraction runs over the corpus whether or not anybody ever asks a connected question, so an unused deployment costs about what a busy one does, and the money is committed before a single query has tested the premise. That should reorder the work: extract one representative slice of the corpus, see whether the answers off it are actually better, and only then hand over the archive to a pass you cannot un-spend. The second thing a single quality figure hides is that extraction is not uniformly good. Clean narrative prose hands a model the subject-relation-object shape it is looking for; transcripts, forms, tables and anything thick with pronouns hand it much less — and those are frequently the documents carrying the connections you built this for. Break the number out by source type, and expect a graph that is strongest where you needed it least. Then name an owner: retrieval treats the graph as an input, ingestion treats it as an output, and its quality belongs to neither of them until an answer embarrasses somebody.

You did it

You just designed a GraphRAG system.

ClientGraphRAG SvcExtractorKnowledge GraphCommunitiesRetrieverVector ChunksLLM
The finished design, end to end. · swipe to pan the diagram

Everything you assembled, in order

  • Vanilla vector RAG retrieves isolated similar chunks — it fails multi-hop and global questions.
  • Build a knowledge graph (entities + relationships) so retrieval can traverse connections.
  • Construct the graph by LLM-extracting entities/relations per chunk and merging (entity resolution).
  • Detect communities and write hierarchical summaries — this enables global/thematic questions.
  • Route queries: local (traverse from entities) vs global (map-reduce over community summaries).
  • Use hybrid graph + vector retrieval for connected reasoning with precise, citable evidence.
  • Indexing is the cost center — amortize it, update incrementally, and use GraphRAG only where it pays.

Deep cut · 20:17

Every page true. Answer false.

The interactive build above lays out GraphRAG: an extractor that reads every page once and turns names into entities and links into relationships, entity resolution that decides which names are the same thing, local search that walks outward from the question’s entities, community summaries that answer questions about everything by map-reduce, a router between the two, hybrid retrieval with a page behind every claim, and incremental updates. The film builds the same system one fix at a time in a made-up company’s paper room, then replays one wrong answer hop by hop to find the rule that caused it.

  • See the wreck: the assistant said Sam Lee, in sales, once signed a deal for Brightwell, and Sam was taken off the deal. Both pages it quoted were true. The extractor’s rule was “same name, same dot”, so Brightwell’s lawyer Sam Lee and Brisk’s Sam Lee became one entity, and the graph drew a path no page ever made.
  • Take it into the interview: merge two entities only when their context agrees (who they work for, what they do, which entities sit around them); keep a close call as a candidate match instead of a fact; keep every edge’s source page, and show every hop with its page in the answer, so a bad join is visible.

Where an interviewer pokes next

Getting the boxes right is the easy half. These are the questions that separate a candidate who drew the diagram from one who has run the thing. Answer each one out loud before you open it.

  1. What kinds of questions does vanilla vector RAG fail on, and why?

    Vector RAG works by embedding the query and retrieving the top-k chunks whose embeddings are most similar, then handing those chunks to the LLM. This is excellent for local factual questions, where the answer lives in a single passage that closely resembles the query. It fails on two related classes of question. The first is multi-hop reasoning: questions whose answer requires connecting facts that live in different documents — for example, "how is A related to C?" when the corpus only states that A relates to B in one place and B relates to C in another. Retrieving chunks that individually resemble the query won't assemble that chain, because the connecting chunk (about B) may not be similar to the query at all. The second is global or thematic questions like "what are the main themes across this entire corpus?" — there is no single similar chunk that contains the answer; it's a property of the whole collection. Both failures share a root cause: similarity search optimizes for local resemblance to the query and has no representation of the relationships and structure connecting facts, which is exactly what these questions require.

  2. How does building a knowledge graph change what retrieval can do?

    A knowledge graph represents the corpus as entities (people, organizations, concepts, events) as nodes and the relationships between them as edges, which makes connections first-class rather than implicit in scattered text. This fundamentally changes retrieval from "find text similar to the query" to "start at the entities mentioned in the query and traverse the edges to gather connected facts." That traversal can reach information that isn't textually similar to the query but is related to it a hop or two away — precisely the connecting facts that similarity search would never surface. For a multi-hop question, you follow the chain of relationships to assemble the reasoning path across documents. And because the graph captures structure, you can additionally cluster it and summarize those clusters to answer global questions. So the graph adds two capabilities vector search lacks: connectivity (traversal to related facts) and global structure (communities and their summaries), turning a bag-of-similar-chunks retriever into one that can reason over how the corpus's facts connect.

  3. How is the knowledge graph actually built from unstructured text, and why is that the expensive part?

    Because the corpus is plain text, the graph has to be derived from it automatically at indexing time. The standard approach runs an LLM (or dedicated named-entity-recognition and relation-extraction models) over each chunk of the corpus, having it extract the entities present and the relationships between them, typically as subject-relation-object triples. Those per-chunk extractions are then merged into a single graph, which crucially involves entity resolution — recognizing that "IBM" and "International Business Machines" across different chunks are the same node — so the graph is coherent rather than fragmented. This is the expensive, front-loaded cost of GraphRAG: it's an LLM pass over every chunk of the entire corpus at index time (plus the later community summarization), which is far more costly than vanilla RAG's simple embed-and-store indexing. The payoff is that this one-time, amortized construction cost turns unstructured text into a connected, traversable structure, enabling cheaper and richer answers to connected and global questions at query time — so GraphRAG inverts the usual economics, paying much more at indexing to unlock answers that were previously impossible.

  4. How do community detection and summaries make global questions answerable?

    Traversal from a few entities handles multi-hop questions, but a truly global question — "what are the major themes across all ten thousand documents?" — isn't about a few entities; it's about the whole corpus, and reading everything at query time is intractable. GraphRAG solves this by pre-computing the corpus's thematic structure at index time. It runs graph clustering (such as the Leiden algorithm) to detect communities — clusters of densely interconnected entities that tend to correspond to coherent topics or themes — and then uses an LLM to write a summary of each community, doing this hierarchically so you get summaries of communities, summaries of those summaries, and so on up to the whole corpus. At query time, a global question is then answered by a map-reduce over these community summaries: the question is applied against each relevant community summary to produce a partial answer (the map step), and those partial answers are combined into a final holistic answer (the reduce step). This lets the system answer "summarize everything" by reading a handful of pre-written summaries instead of the entire corpus, which is GraphRAG's signature capability beyond what vanilla RAG can do.

  5. When is GraphRAG worth its cost, and when should you just use vanilla RAG?

    GraphRAG is a powerful but expensive upgrade, so it should be applied deliberately rather than by default. Its costs are real: LLM extraction over every chunk plus community summarization make indexing far more expensive and slower than vanilla RAG, extraction can make mistakes that propagate into a wrong or incomplete graph (so the graph is an approximation that needs good models, entity resolution, and validation), and keeping it current requires incremental merging and re-summarization. Those costs are justified only when the questions you need to answer actually require connectivity — multi-hop reasoning that chains facts across documents, or global/thematic synthesis across a large connected corpus, as in research synthesis, investigations, or analyzing a big body of interrelated reports. For workloads that are mostly local factual question-answering, where each answer lives in a single relevant passage, vanilla vector RAG is cheaper, simpler, and entirely sufficient, and adding a graph is overkill that pays for capability you don't use. A sensible default is hybrid retrieval (vectors for precise, citable local matches) and reaching for the full graph, communities, and global mode specifically when connected and holistic questions matter — matching the tool's considerable cost to the questions that genuinely need it.

Check yourself — the answers, and why

Nine steps in, these are the calls you should be able to make cold. Pick one, then read why.

  1. Vanilla vector RAG fails on…

    Similarity search finds locally-similar chunks, not the connections multi-hop/global answers need.

  2. GraphRAG represents the corpus as…

    Making connections first-class lets retrieval follow relationships, not just similarity.

  3. The graph is built at index time by…

    An LLM pass per chunk (the expensive part) turns unstructured text into a traversable graph.

  4. Global/thematic questions are answered by…

    Community detection + hierarchical summaries make "summarize everything" tractable.

  5. GraphRAG is worth its cost when…

    Indexing is expensive; use the graph only where connectivity is needed, default to hybrid.

How you’d open this design in an interview

Before any boxes: agree what it must do, pin the qualities that shape everything, then build — naming each trade-off as you make it. The walkthrough above is that exact order.

What it must do

Agree on these before drawing a single box.

  • Answer local: a narrow factual question ("what did X say?").
  • Answer multi-hop: connect facts across documents ("how is A related to C via B?").
  • Answer global: synthesize themes across the whole corpus.
  • Build the graph: extract entities and relationships from unstructured text.
  • Cite: ground every answer in the exact supporting chunks.

The qualities that shape everything

Each one names the mechanism that buys it.

Follow connections, not just similarity
Represent the corpus as a knowledge graph — entities as nodes, relationships as edges — so retrieval traverses from the query’s entities to connected facts a hop or two away.
A graph from unstructured text
At index time an LLM (or NER/relation models) extracts entities and subject–relation–object triples per chunk, merged with entity resolution into one graph.
Answer "the whole corpus" without reading it
Detect communities (graph clustering like Leiden) and pre-summarize each hierarchically, so a global question becomes a map-reduce over a few summaries instead of every document.
The right retrieval per question
Route each query: local mode traverses the graph from its entities; global mode map-reduces over community summaries — or blends the two.
Connected reasoning with citable evidence
Hybrid retrieval fuses graph structure with vector chunk search, giving the LLM both how facts connect and the exact passages that prove them.
Stay current without full rebuilds
Treat graph construction as an amortized index build; merge new documents’ entities incrementally and re-summarize only the affected communities.

The trade-offs you say out loud

Senior signal isn’t the boxes — it’s naming what you gave up and why it was the right price.

A knowledge graph over the corpus over top-k vector similarity alone

Similarity search returns chunks that individually resemble the query, so multi-hop ("A→B→C" across documents) and global ("the main themes") questions have no single chunk to retrieve. The answer lives in relationships the graph makes first-class.

LLM entity/relation extraction over a hand-drawn graph

Manual graph construction doesn’t scale to a real corpus, and titles are far too coarse — the entities live inside the text. An LLM pass per chunk is expensive up front but is what turns unstructured text into a traversable graph.

Route local vs global over one retrieval mode for all

Traversing the whole graph is wasteful for a specific fact and can’t give the holistic view a global question needs, while community summaries are too coarse for a precise local lookup. Matching the mode to the question serves both from one index.

Hybrid graph + vector over replacing vector RAG entirely

The graph is powerful for connections but plain vector search still pinpoints the passage that states a fact and supplies citations. Fusing them yields answers that are connected via the graph and grounded in exact chunks.

GraphRAG only where it pays over a graph for every workload

LLM extraction per chunk makes indexing far more expensive and can propagate extraction errors. For mostly-local factual Q&A vanilla RAG is cheaper and sufficient — earn the graph’s cost only when multi-hop or global questions matter.

The answer, out loud

What a strong answer to “Design a GraphRAG System” sounds like, first question to last trade-off. It is about 7 minutes of talking; the whiteboard and the interviewer fill the rest of the 45. Read it aloud once, then close the page and give it yourself.

  1. 0–3 min

    Name the questions similarity search cannot reach

    I’d start by narrowing what we are actually being asked to fix, because ordinary RAG already works. If the questions are local — what did this person say, what is the policy — vector search finds the one chunk that resembles the query and we are done. What it cannot do is connect. Ask how A relates to C when one document says A relates to B and another says B relates to C: there is no single similar chunk, because the connecting document may not resemble the question at all. Ask what the main themes are across the corpus and there is nothing to fetch either — the answer is a property of the collection. So the first requirement I’d write down is retrieval that follows relationships, not just similarity.

  2. 3–8 min

    Why a graph, not a bigger prompt

    Before I reach for a graph I’d rule out the cheaper answers, because that’s the first thing I’d be asked. Retrieving more chunks only raises the odds the connecting document lands in the prompt by luck; it doesn’t make the system go looking for it. A longer context window has the same problem, and for a question about the whole corpus I’d be pasting the corpus. Both buy volume where the gap is structure. What I want is a representation where a connection is a thing in its own right — entities as nodes, relationships as edges — so retrieval can walk outward from whatever the question names instead of matching how it was worded. That’s a modelling decision, not an infrastructure one.

    Built in step 2: Represent the corpus as a graph
  3. 8–15 min

    What the extractor can and cannot see

    The graph has to come out of the text, so at index time I’d run a model over each chunk, pull out entities and subject-relation-object triples, and merge them. Two things I’d raise unprompted. The extractor only ever sees one chunk, so a relationship stated across a boundary — a name in one paragraph, the fact about it in the next — isn’t extracted wrongly, it’s never seen at all. That makes chunking a graph decision, not only a retrieval one. And the merge fails asymmetrically: leaving two nodes for one person costs me recall, while fusing two people who share a name invents a relationship nobody ever wrote. On a close call I’d take the duplicate.

    Built in step 3: Entity & relationship extraction
  4. 15–21 min

    Answer questions about everything

    A global question is different again, because it names nothing to start from. So I’d move that work off the query path — cluster the graph at index time, have a model write a summary per cluster and summaries over those, and let the question reduce over those instead of the corpus. The clustering runs over connectivity, not embeddings: the groups follow who is actually linked to whom rather than which pages sound alike, and that’s the partition a thematic question wants. A global answer is also weaker evidence than a local one — it’s built from generated summaries, not source text, so I’d cite it back to a community and say as much.

    Built in step 4: Communities & hierarchical summaries
  5. 21–27 min

    Who picks the mode

    That leaves two modes, and the part I’d want credit for isn’t that there are two, it’s deciding who picks. I wouldn’t start with a learned router: the first version lets the caller declare the mode, since an agent usually knows whether it’s chasing a fact or asking for a read on everything, and I’d train a classifier once there are real questions to train it on. The two also don’t belong behind the same promise. A local lookup is one traversal and one generation, so it answers like a search box; a global one has to visit the summaries before it can combine them, so it behaves like a job. I’d stream it rather than hold a blank page.

    Built in step 5: Local vs global query modes
  6. 27–33 min

    Keep the vectors

    I would not throw vector search away. The graph is what connects facts, but plain chunk embeddings are still the best way to find the exact passage that states one, and that passage is what I cite. So retrieval is hybrid: graph structure fused with vector chunk search, and the model receives both how things connect and the text proving each hop. Keeping a page behind every edge is also what makes a bad join visible — if I show every hop with its source, a reader can see where the chain went wrong instead of merely disagreeing with the conclusion.

    Built in step 6: Hybrid graph + vector retrieval
  7. 33–40 min

    How fresh does it have to be

    Then indexing, and the question I’d put back to the interviewer is how fresh this has to be, because that number picks the design. Incremental merging keeps the graph roughly current; it doesn’t make a document traversable the moment it lands. What I’d lean on is that the vector half of retrieval has no such lag — a new document is searchable as soon as it’s embedded, while its entities only join the graph at the next merge. So the promise I’d make is a plain question about brand-new material answered straight away, a connected one about it a little later. I’d rather say that than let someone assume the graph is live.

    Built in step 7: Indexing cost & incremental updates
  8. 40–45 min

    Close on the trade-off

    So the trade-off in a line: connected and thematic answers, bought with an index that’s expensive, slow to change and only ever an approximation of the corpus — and I wouldn’t propose it for a workload of plain factual lookups. With more time I’d go at two things. Access control first: which nodes and edges a user may traverse, enforced inside the traversal rather than by filtering the finished answer, because a path that ran through a document they can’t read has already leaked that the link exists. Then entity resolution properly, since that one decision quietly settles whether the graph is a map of the corpus or a slightly fictional version of it.

What this teaches

Learn AI system design by building a GraphRAG system — knowledge-graph-augmented retrieval — step by step. An interactive guide covering why vanilla vector RAG fails on global and multi-hop questions, building a knowledge graph by extracting entities and relationships, community detection and hierarchical summaries, local vs global query modes, hybrid graph+vector retrieval, indexing cost, and the unhappy paths (extraction errors, cost, when a graph helps).

Key takeaways

  • Vanilla vector RAG retrieves isolated similar chunks — it fails multi-hop and global questions.
  • Build a knowledge graph (entities + relationships) so retrieval can traverse connections.
  • Construct the graph by LLM-extracting entities/relations per chunk and merging (entity resolution).
  • Detect communities and write hierarchical summaries — this enables global/thematic questions.
  • Route queries: local (traverse from entities) vs global (map-reduce over community summaries).
  • Use hybrid graph + vector retrieval for connected reasoning with precise, citable evidence.
  • Indexing is the cost center — amortize it, update incrementally, and use GraphRAG only where it pays.

Concepts covered

  • What problem does GraphRAG solve?
  • Isolated chunks, no connections
  • Represent the corpus as a graph
  • Entity & relationship extraction
  • Communities & hierarchical summaries
  • Local vs global query modes
  • Hybrid graph + vector retrieval
  • Indexing cost & incremental updates
  • Extraction errors, cost & when to use it
RUN IT YOURSELF

The graph drew a path no document ever wrote

One corpus, one set of extracted triples, and the knowledge graph built from them five ways — running for real in your browser. Two people are called Sam Lee and one of them is also written S. Lee, so the merge decides both what the graph can answer and what it invents. Change SWEEP, hit Run, and watch the invented links appear and the real ones drop out.

HOW TO READ THE CODE — 5 IDEAS
  1. Merging is where the graph is actually built: the triples are identical in every run, only entity resolution changes (step 3).
  2. Merge mentions whose name matches exactly as written — the film’s “same name, same dot” — and the graph is wrong in both directions at once: it loses 4 of the 9 true links because S. Lee and Sam Lee stay apart, and still invents 2, because both Sam Lees were already one dot.
  3. Skip the context test and take every pair the blocking key proposes (ctx>=0.00) and invented links rise to 4. That row does not merge on the name: 7 of its 11 pairs have different names — the blocking key is surname plus first initial.
  4. Requiring the contexts to agree takes invented links to 0. Raise the threshold further and true links start falling instead — the failure is asymmetric, and a duplicate is the cheaper mistake (step 3).
  5. The closing block is the catch: 0.20 works only because each document names 2–3 entities. Give every document 2 more of its own and the resolver merges nothing (8/9 → 3/9); repeat boilerplate across them and the impostors merge too (4 invented, back). A fixed global threshold is the wrong knob — keep a close call as a candidate, with the source page on every edge (steps 5–6).
CPython · WebAssembly
built to connect facts across a whole corpus, not just fetch the nearest chunk — make the calls, drop the graph, run the gauntlet.
Finished this one? 0 / 65 AI System Designs done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More AI System Designs