PROMETHEUS OS
NATIVE AI OPERATING SYSTEM
[INIT] Prometheus OS loading...
[CORE] Neural interface initializing...
[AI] LLM router connecting...
[MEM] Memory graph loading...
[AGT] Agent chains on standby...
[SEC] Security protocols active...
[NET] Provider mesh established...
[UI] Workspace rendering...
[DONE] All systems nominal.
Welcome to PROMETHEUS OS.
READY · OFFLINE · SECURE · PRIVATE
🎨 THEME ENGINE
PROMETHEUS OS · 23 THEMES
Trial version: Dark + Light themes only.
Full theme access with PROMETHEUS OS license.
Dark theme active
Experience all 23 themes in PROMETHEUS OS

GraphRAG and Knowledge Graphs

Advanced 15 min· 4 exercises

Model knowledge as entities and typed relations, build a knowledge graph from text with LLM extraction, explain how Microsoft's GraphRAG uses community detection for global and local search, and decide when graph retrieval beats vector retrieval and what it costs.

By the end of this lesson you will be able to
  • Explain entities, relations and triples, and compare the RDF and labelled property graph data models.
  • Describe an LLM-based knowledge-graph construction pipeline, including entity resolution, provenance and validation.
  • Explain Microsoft's GraphRAG indexing pipeline (extraction, Leiden communities, community reports) and its global and local search modes.
  • Identify question types where graphs outperform vector retrieval, such as multi-hop and aggregation, and where they do not.
  • Write basic Cypher and SPARQL queries and implement multi-hop traversal in Python with networkx.
  • Estimate the indexing cost of a GraphRAG-style pipeline and design a combined graph-plus-vector architecture.

Before this: Advanced Retrieval: Hybrid Search, Reranking and Query Rewriting, Embeddings and Retrieval-Augmented Generation

Vector search finds passages that look like the question. Some questions are not like any single passage: "Which of our suppliers are owned by companies that a sanctioned person controls?" or "What are the main themes across these 2,000 interview transcripts?" The first needs facts chained across documents; the second needs a view of the whole corpus. Knowledge graphs and GraphRAG target exactly these gaps. They are used in finance (ownership and risk networks), life sciences (drug, gene and disease relations), enterprise search, fraud investigation and intelligence analysis.

The picture: a detective's corkboard

A vector index is like a filing cabinet with a brilliant clerk who can find the folder most similar to your request. A knowledge graph is the detective's corkboard: photos of people, companies and places pinned up, with red string between them labelled "CEO of", "acquired", "lives in". You can follow the string from one pin to the next, which is how you answer chains of questions. GraphRAG adds a further step: step back from the board, notice the clusters of tightly connected pins, and write a summary card for each cluster, so you can answer "what is going on overall?" without reading every folder.

Knowledge graphs: the formal model

A knowledge graph (KG) represents knowledge as entities (nodes: people, organizations, products, concepts) and relations (typed, directed edges between them). The atomic unit is the triple (subject, predicate, object), for example (Nimbus Labs, ACQUIRED, Kestrel AI). A set of triples defines a directed, labelled multigraph G=(V,E)G = (V, E) with E⊆V×R×VE \subseteq V \times \mathcal{R} \times V, where R\mathcal{R} is the set of relation types.

Two data models dominate:

  • RDF (Resource Description Framework), a W3C standard. Every subject and predicate is an IRI (a global identifier), and objects are IRIs or literals (strings, numbers, dates). Schemas and ontologies are expressed in RDFS and OWL, enabling formal reasoning (for example inferring that a subsidiary of a subsidiary is also owned). Queried with SPARQL. Strong for data integration and published vocabularies; Wikidata, a large public knowledge base, is published as RDF and queried with SPARQL.
  • Labelled property graphs (LPG), used by graph databases such as Neo4j and others. Nodes have labels and key-value properties; edges have a type and their own properties (for example ACQUIRED {year: 2023, price_usd: 1.2e8}). Queried with Cypher, or with GQL (ISO/IEC 39075:2024), the ISO-standard property-graph query language published in 2024, which draws heavily on Cypher. Strong for application development and graph algorithms.

An ontology or schema defines allowed entity types and relation types, for example "a Person can be CEO_OF an Organization". Without one, extracted graphs drift into thousands of near-synonymous relation names.

Building a KG with LLM extraction

Classic KG construction used named-entity recognition, relation classifiers and hand-written rules. LLMs make extraction far more flexible:

  1. Chunk documents (smaller chunks yield more extracted facts per token, at higher cost).
  2. Extract: prompt an LLM to return entities (name, type, description) and relations (source, target, type, description, optional strength) as structured JSON, ideally constrained to your schema. A second "did you miss anything?" pass (sometimes called gleaning) increases recall at extra cost.
  3. Resolve entities: merge "Nimbus Labs", "Nimbus Labs Inc." and "Nimbus" into one node, using normalization, string similarity, embeddings of descriptions and, for hard cases, an LLM judge. Entity resolution errors are the most damaging failure in KG construction: missed merges fragment the graph, wrong merges invent connections.
  4. Attach provenance: every node and edge stores the chunk IDs it came from, so answers can cite sources and you can audit or delete.
  5. Validate: check types against the schema, flag low-confidence facts, and sample for human review.

Extracted facts are LLM outputs and can be hallucinated; provenance and validation are what make the graph trustworthy.

GraphRAG: Microsoft's approach

"GraphRAG" is used loosely for any retrieval that uses a graph. The best-known specific method is from Microsoft Research: Edge et al., "From Local to Global: A Graph RAG Approach to Query-Focused Summarization" (arXiv, April 2024), released as the open-source graphrag library. Its indexing pipeline:

  1. Split the corpus into text units (chunks).
  2. Use an LLM to extract entities, relationships and claims from each chunk, and summarize the descriptions of each entity and relationship across all mentions.
  3. Build the entity graph and run hierarchical community detection with the Leiden algorithm, which partitions the graph into communities of densely connected nodes, then recursively partitions those communities, giving levels from a few broad communities to many fine-grained ones.
  4. Use an LLM to write a community report (summary) for every community at every level, bottom-up, describing its key entities, relations and claims.

It then offers two main query modes:

  • Global search answers corpus-wide "sensemaking" questions ("What are the main themes in this dataset?"). It works map-reduce style: each community report at a chosen level is given to the LLM with the question to produce a partial answer with a helpfulness score (map); the most helpful partial answers are combined into a final answer (reduce). Ordinary top-kk vector RAG struggles here because no single chunk contains "the main themes".
  • Local search answers questions about specific entities. It finds the entities relevant to the question (via embeddings of entity descriptions), then gathers their neighbours, relationships, claims, related community reports and source text units into the context window.

The paper evaluated global search on query-focused summarization over corpora of roughly a million tokens (podcast transcripts and news articles), using an LLM judge to compare answers, and reported that GraphRAG answers were more comprehensive and diverse than those from a conventional vector RAG baseline. Note what this does and does not show: it is evidence for global sensemaking questions, judged by an LLM, not a claim that graphs beat vectors on ordinary fact lookup. The project has continued to add query modes and cheaper indexing variants since; check its documentation for the current set.

When graphs beat vectors

Question typeVector RAGGraph-based retrieval
Single fact stated in one passageStrongUnnecessary overhead
Multi-hop: facts chained across documentsWeak: each hop's passage may not resemble the questionStrong: follow edges
Aggregation: "how many", "list all", "which most"Weak: top-kk truncates the setStrong: query the whole graph
Global sensemaking: themes across a corpusWeakStrong (community summaries)
Relationship and path questions ("how is X connected to Y?")WeakStrong: shortest paths
Fuzzy semantic similarity, paraphraseStrongDepends on extraction quality

Worked example of multi-hop. Suppose these facts sit in four different documents: Ada Chen is CEO of Nimbus Labs; Nimbus Labs acquired Kestrel AI; Kestrel AI develops Falcon OCR; Falcon OCR is used by Harbor Bank. Question: "Which banks use products built by companies Ada Chen runs?" The question's embedding resembles the first document (it mentions Ada Chen) and maybe the last (it mentions banks), but the two middle documents mention neither, so top-kk retrieval likely misses at least one link and the LLM cannot complete the chain. In the graph, the answer is a four-hop traversal: Ada Chen to Nimbus Labs to Kestrel AI to Falcon OCR to Harbor Bank.

import networkx as nx

triples = [  # (subject, relation, object) as an LLM extractor might emit them
    ("Ada Chen", "CEO_OF", "Nimbus Labs"),
    ("Nimbus Labs", "ACQUIRED", "Kestrel AI"),
    ("Kestrel AI", "DEVELOPS", "Falcon OCR"),
    ("Falcon OCR", "USED_BY", "Harbor Bank"),
    ("Harbor Bank", "HEADQUARTERED_IN", "Rotterdam"),
    ("Ben Ortiz", "CTO_OF", "Kestrel AI"),
    ("Nimbus Labs", "PARTNER_OF", "Orchid Cloud"),
    ("Orchid Cloud", "HOSTS", "Harbor Bank"),
]
G = nx.MultiDiGraph()
for s, r, o in triples:
    G.add_edge(s, o, relation=r)

def follow(node, relation):
    return [o for _, o, r in G.out_edges(node, data="relation") if r == relation]

for company in follow("Ada Chen", "CEO_OF"):
    for sub in follow(company, "ACQUIRED"):
        for prod in follow(sub, "DEVELOPS"):
            print(company, "->", sub, "->", prod, "->", follow(prod, "USED_BY"))
# Nimbus Labs -> Kestrel AI -> Falcon OCR -> ['Harbor Bank']

# Communities on the undirected graph (GraphRAG uses Leiden; networkx
# provides Louvain, an older relative that optimizes the same objective)
U = nx.Graph(G.to_undirected())
for i, c in enumerate(nx.community.louvain_communities(U, seed=42)):
    print(i, sorted(c))

On this toy graph Louvain finds three communities: {Ada Chen, Nimbus Labs, Orchid Cloud}, {Ben Ortiz, Kestrel AI, Falcon OCR} and {Harbor Bank, Rotterdam}. In GraphRAG, an LLM would write a report for each, such as "Kestrel AI and its OCR product line", which global search would then map-reduce over. (Exact community assignments can vary with the seed and library version.) Both Louvain and Leiden maximize modularity, which compares the density of edges inside communities with what a random graph of the same degrees would have; Leiden adds a refinement step that guarantees communities are internally connected, a known weakness of Louvain.

Query languages: Cypher and SPARQL basics

The same four-hop question in Cypher, which uses ASCII-art patterns, (node)-[:RELATION]->(node):

MATCH (p:Person {name: "Ada Chen"})-[:CEO_OF]->(c:Organization)
      -[:ACQUIRED]->(s:Organization)-[:DEVELOPS]->(prod:Product)
      -[:USED_BY]->(b:Organization)
RETURN c.name, s.name, prod.name, b.name

An aggregation that vector search cannot do, counting customers per product:

MATCH (prod:Product)-[:USED_BY]->(customer)
RETURN prod.name, count(customer) AS customers
ORDER BY customers DESC LIMIT 10

In SPARQL, patterns are triples with ?variables, and prefixes abbreviate IRIs (ex: here is a made-up example namespace):

PREFIX ex: <http://example.org/>
SELECT ?company ?product ?bank WHERE {
  ex:AdaChen  ex:ceoOf    ?company .
  ?company    ex:acquired ?sub .
  ?sub        ex:develops ?product .
  ?product    ex:usedBy   ?bank .
}

An LLM can translate natural-language questions into Cypher or SPARQL (text-to-query), given the schema in its prompt. Treat generated queries like generated SQL: validate them, run them read-only with timeouts and row limits, and enforce access control in the database, not in the prompt.

Costs

GraphRAG-style indexing is expensive because it calls an LLM on every chunk and again for every community. An illustrative estimate (the assumptions are ours, not the library's defaults): a 1-million-token corpus split into 600-token chunks gives about 1,000,000/600≈1,6671{,}000{,}000 / 600 \approx 1{,}667 chunks. If each extraction call sends the chunk plus a 1,500-token instruction prompt, input is about 1,667×2,100≈3.51{,}667 \times 2{,}100 \approx 3.5 million tokens, plus perhaps 400 output tokens per chunk (≈0.67\approx 0.67 million). One gleaning pass roughly doubles that. Then entity and relationship description summaries and community reports add further calls; with, say, 500 communities across all levels at 3,000 input and 800 output tokens each, that is another 1.5 million input and 0.4 million output tokens. The total is on the order of 10 million tokens processed, versus embedding 1 million tokens for vector RAG, which is one to two orders of magnitude cheaper. Multiply by your model's per-token price, and remember re-indexing when documents change: inserting new documents can alter communities and require regenerating their reports.

Other costs: graph database operations, entity resolution engineering, schema design, and query latency for global search (one LLM call per community report in the map step).

Ways to reduce cost: use a small, cheap model for extraction and a strong one for answering; restrict extraction to a schema; build the graph only over the subset of documents where relationships matter; and use structured sources (databases, CRMs, existing ontologies) directly instead of extracting facts that already exist in structured form.

Combining graph and vector retrieval

The strongest systems use both:

  • Vector entry, graph expansion: embed the question, find the most similar entities or chunks, then expand along graph edges (11 to 22 hops) to collect connected facts and their source chunks. This is essentially GraphRAG's local search.
  • Graph-filtered vector search: use the graph to compute a candidate set (for example all documents about subsidiaries of a company) and run vector search inside it.
  • Router: classify the question; send fact lookups to vector RAG, aggregations and multi-hop questions to text-to-Cypher or text-to-SPARQL, and corpus-wide questions to community summaries.
  • Vectors inside the graph: many graph databases store embeddings on nodes and offer vector indexes, so one query can combine pattern matching and similarity.

Common pitfalls

  • Building a graph because it is fashionable. If your questions are single-fact lookups, a graph adds cost and failure modes without benefit. Classify a sample of real questions first.
  • No schema: thousands of relation types like ACQUIRED, BOUGHT, TOOK_OVER, ACQUISITION_OF that queries cannot rely on.
  • Weak entity resolution: duplicate nodes break multi-hop paths; over-merging (two different "John Smith"s) invents false connections.
  • Dropping provenance, making answers uncitable and errors untraceable.
  • Treating extracted triples as ground truth: they inherit LLM extraction errors.
  • Ignoring update cost: a corpus that changes daily may not afford full community re-summarization.
  • Unvalidated text-to-query, leading to wrong answers that look authoritative or to expensive runaway queries.

In practice

  • Start with strong hybrid vector retrieval and an evaluation set. Add a graph when the evaluation shows failures on multi-hop, aggregation or global questions, and measure the improvement.
  • When your organization already has structured data (ownership tables, product catalogues, org charts), load it as a graph directly; use LLM extraction for what exists only in text.
  • Define a small schema, enforce it in extraction prompts with structured outputs, and invest in entity resolution.
  • For corpus-wide sensemaking over a fixed corpus (research archives, investigations, survey responses), GraphRAG-style community summaries are worth evaluating despite the indexing cost.
  • Keep provenance end to end so every answer can cite the chunks behind each fact.

Key takeaways

  • A knowledge graph stores facts as typed, directed edges between entities, which makes multi-hop chains, paths and aggregations directly queryable.
  • LLMs make knowledge-graph extraction from text practical, but entity resolution, a schema and provenance determine whether the graph can be trusted.
  • Microsoft's GraphRAG clusters the extracted entity graph into a hierarchy of communities with Leiden, summarizes each, and answers corpus-wide questions by map-reduce over those summaries.
  • Graphs beat vectors on multi-hop, aggregation, path and global sensemaking questions; for single-fact lookups they add cost without benefit.
  • GraphRAG-style indexing typically costs one to two orders of magnitude more than embedding the same corpus, and updates are expensive.
  • The strongest systems combine both: vector search to find entry points, graph traversal to expand, and routing by question type.

Terms in this lesson

Knowledge graph
A graph whose nodes are entities and whose typed, directed edges are relations between them, often with properties and provenance.
Triple
The atomic fact (subject, predicate, object), such as (Nimbus Labs, ACQUIRED, Kestrel AI).
RDF
The W3C Resource Description Framework, a graph data model of IRI-identified triples queried with SPARQL.
Labelled property graph
A graph model in which nodes have labels and properties and edges have a type and properties, typically queried with Cypher or GQL.
Entity resolution
Deciding which mentions refer to the same real-world entity and merging them into one node.
Leiden algorithm
A community-detection method that improves on Louvain by guaranteeing well-connected communities, used by GraphRAG to build a community hierarchy.
Global search (GraphRAG)
Answering corpus-wide questions by asking an LLM to answer from each community report and then combining the most useful partial answers.
Multi-hop question
A question whose answer requires chaining several facts, typically from different documents.

Check your understanding

  1. 1.

    Which question is GraphRAG's global search specifically designed for?

  2. 2.

    What role does the Leiden algorithm play in Microsoft's GraphRAG?

  3. 3.

    Your extracted graph contains separate nodes 'Nimbus Labs', 'Nimbus Labs Inc.' and 'Nimbus'. Which problem is this, and what does it break?

  4. 4.

    A 2-million-token corpus is split into 500-token chunks. Each extraction call sends the chunk plus a 1,500-token instruction prompt. How many input tokens does one extraction pass consume?

  5. 5.

    Which Cypher query finds the products used by the customers that Nimbus Labs' partners host?

Exercises

Work each problem before opening the solution.

  1. Exercise 1Core

    Using the schema Person, Organization, Product with relations WORKS_FOR, FOUNDED, ACQUIRED, MAKES, extract triples from: "Maria Lopez founded Tidewave in 2019. Tidewave, which makes the Swell sensor, was bought by Corvid Systems last year. Lopez now works at Corvid." Then list two entity-resolution decisions the pipeline must make.

    Show solution

    Triples:

    • (Maria Lopez: Person, FOUNDED, Tidewave: Organization), property year: 2019
    • (Tidewave: Organization, MAKES, Swell sensor: Product)
    • (Corvid Systems: Organization, ACQUIRED, Tidewave: Organization), property when: "last year", which must be resolved against the document date to an actual year, or stored as unresolved
    • (Maria Lopez: Person, WORKS_FOR, Corvid Systems: Organization)

    Note that "was bought by" must be mapped to the schema relation ACQUIRED with subject and object swapped, which is exactly what a schema-constrained extraction prompt enforces.

    Entity-resolution decisions: (1) "Lopez" and "Maria Lopez" refer to the same person (within-document coreference), and she must be matched against any existing "Maria Lopez" nodes without merging a different person of the same name, using context such as employer. (2) "Corvid" and "Corvid Systems" are the same organization. Each triple should also store the source chunk ID for provenance.

  2. Exercise 2Core

    Write (a) a Cypher query returning, for each organization, the number of products it makes, highest first; (b) a Cypher query finding people who work for a company that acquired a company they founded; (c) a SPARQL query for (b), using the prefix ex: and predicates ex:worksFor, ex:acquired, ex:founded.

    Show solution

    (a)

    MATCH (o:Organization)-[:MAKES]->(p:Product)
    RETURN o.name AS organization, count(p) AS products
    ORDER BY products DESC
    

    (b)

    MATCH (person:Person)-[:FOUNDED]->(startup:Organization)
          <-[:ACQUIRED]-(acquirer:Organization)<-[:WORKS_FOR]-(person)
    RETURN person.name, startup.name, acquirer.name
    

    Reusing the variable person at both ends of the pattern forces it to be the same node, forming a cycle.

    (c)

    PREFIX ex: <http://example.org/>
    SELECT ?person ?startup ?acquirer WHERE {
      ?person   ex:founded  ?startup .
      ?acquirer ex:acquired ?startup .
      ?person   ex:worksFor ?acquirer .
    }
    

    On the previous exercise's data, both return Maria Lopez, Tidewave, Corvid Systems.

  3. Exercise 3Core

    Write a networkx function explain_connections(G, source, target, max_hops) that returns every simple path of at most max_hops edges between two entities as a human-readable chain of relations, following edges in either direction and marking reversed edges. Run it from Ada Chen to Harbor Bank on the lesson's graph.

    Show solution
    import networkx as nx
    
    G = nx.MultiDiGraph()
    for s, r, o in [("Ada Chen", "CEO_OF", "Nimbus Labs"),
                    ("Nimbus Labs", "ACQUIRED", "Kestrel AI"),
                    ("Kestrel AI", "DEVELOPS", "Falcon OCR"),
                    ("Falcon OCR", "USED_BY", "Harbor Bank"),
                    ("Nimbus Labs", "PARTNER_OF", "Orchid Cloud"),
                    ("Orchid Cloud", "HOSTS", "Harbor Bank"),
                    ("Ben Ortiz", "CTO_OF", "Kestrel AI")]:
        G.add_edge(s, o, relation=r)
    
    def explain_connections(G, source, target, max_hops=3):
        U = G.to_undirected(as_view=True)        # allow following edges either way
        results = []
        for path in nx.all_simple_paths(U, source, target, cutoff=max_hops):
            steps = []
            for a, b in zip(path, path[1:]):
                if G.has_edge(a, b):             # forward edge a -> b
                    rel = next(iter(G.get_edge_data(a, b).values()))["relation"]
                    steps.append(f"{a} -[{rel}]-> {b}")
                else:                            # reverse edge b -> a
                    rel = next(iter(G.get_edge_data(b, a).values()))["relation"]
                    steps.append(f"{a} <-[{rel}]- {b}")
            results.append(steps)
        return sorted(results, key=len)          # shortest explanations first
    
    for p in explain_connections(G, "Ada Chen", "Harbor Bank", max_hops=4):
        print(" ; ".join(p))
    

    Output (shortest first):

    Ada Chen -[CEO_OF]-> Nimbus Labs ; Nimbus Labs -[PARTNER_OF]-> Orchid Cloud ; Orchid Cloud -[HOSTS]-> Harbor Bank
    Ada Chen -[CEO_OF]-> Nimbus Labs ; Nimbus Labs -[ACQUIRED]-> Kestrel AI ; Kestrel AI -[DEVELOPS]-> Falcon OCR ; Falcon OCR -[USED_BY]-> Harbor Bank
    

    The function searches an undirected view so that "how is X connected to Y" questions find links regardless of edge direction, then looks up the original direction to label each step. Chains like these are ideal LLM context: each step is a fact with provenance. Caveats: all_simple_paths grows exponentially with hops on dense graphs, so cap max_hops (2 to 4), restrict relation types, or use shortest paths only; with several parallel edges between two nodes, this version reports only the first relation.

  4. Exercise 4Challenge

    Estimate the indexing token budget for a GraphRAG-style pipeline on a 2-million-token corpus with these assumptions: 500-token chunks; extraction prompt of 1,500 tokens plus the chunk; 300 output tokens per extraction; one gleaning pass that repeats extraction at the same cost; 800 community reports at 4,000 input and 600 output tokens each. Ignore description-summarization calls. (a) Total input and output tokens. (b) Using a hypothetical price of 0.50 per million input tokens and 2.00 per million output tokens, the cost. (c) Compare with embedding the corpus once and suggest two ways to cut the cost.

    Show solution

    (a) Chunks: 2,000,000/500=4,0002{,}000{,}000 / 500 = 4{,}000.

    • Extraction: input 4,000×2,000=8.04{,}000 \times 2{,}000 = 8.0M; output 4,000×300=1.24{,}000 \times 300 = 1.2M.
    • Gleaning doubles this: input 16.016.0M, output 2.42.4M in total for extraction.
    • Community reports: input 800×4,000=3.2800 \times 4{,}000 = 3.2M; output 800×600=0.48800 \times 600 = 0.48M.
    • Total: input 19.219.2M, output 2.882.88M.

    (b) 19.2×0.50+2.88×2.00=9.60+5.76=15.3619.2 \times 0.50 + 2.88 \times 2.00 = 9.60 + 5.76 = 15.36 currency units.

    (c) Embedding processes the corpus once, 2M tokens, with a small embedding model: roughly 22.08/2≈1122.08 / 2 \approx 11 times fewer tokens even before accounting for embedding models being far cheaper per token than generative LLMs, so in cost terms the gap is typically one to two orders of magnitude. Ways to cut cost: use a small, cheap model for extraction and gleaning (and skip gleaning if measured recall is sufficient); restrict extraction to a fixed schema and to the documents where relationships matter; use larger chunks (fewer calls, so less repeated prompt overhead, at some loss of extraction recall); generate community reports only for the levels your queries use; and cache the instruction prompt where the provider supports prompt caching. Re-indexing frequency matters too: a corpus that changes daily multiplies these costs.

Further reading

Go further

Sources

Fact-checked and last reviewed 2026-11-05

Stay in the loop
Prometheus OS™ · Native AI OS · Built for the future © 2025 - 2026