A knowledge graph represents entities, relationships, and claims as a connected model. It becomes valuable for AI when the application must traverse explicit relationships, preserve evidence lineage, enforce domain constraints, or aggregate facts across a corpus. It is not synonymous with a graph database, and GraphRAG is not a universal replacement for lexical, vector, or hybrid retrieval.

This guide designs the graph as a governed derived view: every claim points back to a source span and revision, every retrieval path inherits authorization, and every GraphRAG mode must beat a simpler baseline on the target workload.

Table of Contents

Key Takeaways

  • A knowledge graph is a representation of knowledge; a graph database is one implementation choice.
  • Store extracted statements as versioned claims with provenance, not as timeless truth.
  • Treat relation extraction and entity resolution as measurable, reversible data pipelines.
  • Use GraphRAG only where graph context beats lexical, vector, or hybrid baselines.
  • Never execute unrestricted LLM-generated Cypher or trust query-supplied authorization fields.
  • A graph path shows what retrieval used. It does not reveal hidden model reasoning or prove correctness.
  • Evaluate extraction, resolution, graph quality, retrieval, generation, security, and lifecycle independently.

Knowledge Graph, Graph Database, and Ontology

A knowledge graph is a connected representation of entities and claims, while a graph database is software for storing and querying graph-shaped data. Keeping those concepts separate prevents storage choices from defining the knowledge model.

Concept What it defines What it does not guarantee
Knowledge graph Entities, claims, relationships, identifiers, and semantics Truth, completeness, or a specific database
Graph database Storage, indexes, transactions, and traversal queries Ontology quality or source reliability
RDF graph A set of subject-predicate-object triples identified with IRIs Rich inference unless an entailment regime is added
Property graph Nodes and relationships with labels and properties Shared semantics across systems
Ontology Vocabulary, classes, properties, and sometimes constraints or rules Correct instance data
GraphRAG Retrieval architectures that use graph-derived context Better answers on every query

The stable W3C RDF 1.1 Concepts Recommendation defines an RDF graph as a set of triples and an RDF dataset as a default graph plus zero or more named graphs. RDF 1.2 work may add capabilities, but a production contract should not describe a Working Draft as a final standard.

Property graphs make traversal-oriented application data convenient. RDF emphasizes global identifiers, graph exchange, and explicit semantics. Relational databases remain excellent for transactions, constraints, and bounded joins. A production system may use more than one model, provided identity, source lineage, and deletion semantics remain consistent.

Production Architecture

A production knowledge graph separates source records, extraction, resolution, graph publication, retrieval, and answer generation. This separation lets teams reprocess one layer without silently changing every downstream answer.

flowchart LR A["Versioned source documents"] --> B["Parse and segment"] B --> C["Extract candidate entities and claims"] C --> D["Validate vocabulary and source spans"] D --> E["Resolve entity identities"] E --> F["Publish versioned graph view"] F --> G["Lexical, vector, and graph indexes"] G --> H["Policy-aware query router"] H --> I["Bounded retrieval"] I --> J["Answer with source citations"] J --> K["Layered evaluation and audit"] K --> C

The offline path creates derived artifacts. The online path retrieves only artifacts the caller may access. The application should be able to answer four questions for every generated claim:

  1. Which source spans support it?
  2. Which extractor and resolver versions produced the graph artifacts?
  3. Which authorization decision allowed retrieval?
  4. Which graph and index snapshots were active?

If any answer is missing, the system has an observability gap rather than an explainability feature.

Model Claims and Provenance

The safest graph model treats an extracted edge as a claim supported by evidence, not as an unconditional fact. This matters when sources conflict, change over time, or apply to different scopes.

json
{
  "claim_id": "clm_01J...",
  "subject_id": "org:acme",
  "predicate": "HEADQUARTERED_IN",
  "object_id": "place:singapore",
  "source_document_id": "doc_842",
  "source_revision": "sha256:8f1...",
  "source_span": {"start": 418, "end": 476},
  "extractor_version": "relations-2026-08-01",
  "valid_from": "2026-04-01",
  "valid_to": null,
  "tenant_id": "tenant_17",
  "policy_scope": ["research"],
  "state": "candidate"
}

The source document and revision are the citation boundary. Community summaries, embeddings, resolved entities, and graph edges are derived aids. They must point back to that boundary rather than cite one another recursively.

The following dependency-free validator demonstrates a minimum ingestion contract:

python
from dataclasses import dataclass
from typing import Optional

ALLOWED_RELATIONS = {"HEADQUARTERED_IN", "OWNS", "DEPENDS_ON"}

@dataclass(frozen=True)
class Claim:
    subject_id: str
    predicate: str
    object_id: str
    source_document_id: str
    source_revision: str
    span_start: int
    span_end: int
    extractor_version: str
    tenant_id: str
    valid_to: Optional[str] = None

def validate_claim(claim: Claim, source_length: int) -> None:
    if claim.predicate not in ALLOWED_RELATIONS:
        raise ValueError("relationship type is not in the versioned vocabulary")
    if not (0 <= claim.span_start < claim.span_end <= source_length):
        raise ValueError("source span is outside the source revision")
    required = (
        claim.subject_id,
        claim.object_id,
        claim.source_document_id,
        claim.source_revision,
        claim.extractor_version,
        claim.tenant_id,
    )
    if any(not value.strip() for value in required):
        raise ValueError("claim lineage and tenant scope are required")

example = Claim(
    subject_id="org:acme",
    predicate="HEADQUARTERED_IN",
    object_id="place:singapore",
    source_document_id="doc_842",
    source_revision="sha256:8f1",
    span_start=10,
    span_end=42,
    extractor_version="relations-v3",
    tenant_id="tenant_17",
)
validate_claim(example, source_length=100)
print("valid")
# Output: valid

In production, also record confidence, review state, temporal scope, ingestion time, policy labels, and supersession links. Confidence is a ranking signal, not permission to publish unsupported claims.

Control Extraction and Entity Resolution

Extraction and entity resolution are noisy inference tasks, so they need explicit schemas, test sets, review queues, and rollback. A generic pretrained language model with an invented label list is not a relation extractor.

Use a constrained contract:

  1. Define a versioned relationship vocabulary and the valid subject/object types.
  2. Require source spans for every candidate entity and relation.
  3. Reject unknown predicates instead of creating new edge types at runtime.
  4. Resolve entities with stable identifiers and auditable match features.
  5. Route low-confidence or high-impact merges to review.
  6. Keep candidate claims separate from accepted and rejected claims.

Entity resolution deserves its own release gate. A harmful merge combines two different entities and can contaminate many paths; a harmful split creates duplicate identities and hides evidence. Track pairwise precision/recall alongside harmful merge and split rates, segmented by entity type and language.

flowchart LR A["Candidate"] -->|Schema and evidence pass| B["Accepted"] A -->|Ambiguous identity or high impact| C["Review"] C -->|Reviewer confirms| B C -->|Reviewer rejects| D["Rejected"] B -->|Newer source revision| E["Superseded"] B -->|Source deletion or policy change| F["Withdrawn"]

Do not overwrite conflicts. Preserve claim-level source and temporal scope so retrieval can expose disagreement or select the claim valid for a specific time.

Choose RDF, Property Graph, or Relational Storage

Choose a storage model from workload and governance requirements, not a vendor-size slogan. Benchmark representative data, traversals, updates, and authorization filters before committing.

Decision factor RDF store Property graph Relational or hybrid
Shared identifiers and standards-based exchange Strong fit Requires conventions Requires mapping
Ontology and explicit entailment Strong fit with selected semantics Usually application-managed Usually application-managed
Traversal-heavy application queries Capable; test SPARQL workload Often ergonomic Good for bounded relationships
Transactional records and strict constraints Product-dependent Product-dependent Strong default
Existing operational platform May add a new stack May add a new stack Often lowest incremental cost
Fine-grained authorization Must be designed and tested Must be designed and tested Mature options, model-dependent
Portability RDF standards help exchange Query dialects and features vary SQL is broad; extensions vary

For database selection, evaluate query language compatibility, transaction and consistency guarantees, index behavior, traversal fan-out, bulk and incremental ingestion, backup/restore, observability, policy enforcement, managed versus self-hosted operations, and measured total cost. Product names alone do not answer those questions.

Route GraphRAG by Query Type

GraphRAG is a family of graph-enhanced retrieval designs, and each query mode serves a different task. Route by evaluated query class instead of sending every request through the most expensive pipeline.

Microsoft's GraphRAG query documentation distinguishes:

  • Basic Search: a baseline vector RAG path.
  • Local Search: combines text chunks with graph entities, relationships, and related reports.
  • Global Search: map-reduce synthesis over community reports for corpus-level questions.
  • DRIFT Search: expands local search with community information.

The original GraphRAG paper evaluates global sensemaking on particular corpora and criteria. It does not prove that graph retrieval dominates every task. Independent WildGraphBench results similarly indicate that benefits depend on query type: graph methods can help multi-fact aggregation, while BM25 remains competitive for straightforward lookup and graph aggregation can lose fine detail.

Query class Start with Promote to graph retrieval when
Exact identifier or quoted fact Lexical/BM25 Entity aliases or linked records improve recall
Local semantic question Vector or hybrid Neighbor relations add supported evidence
Connected multi-fact question Hybrid baseline Bounded traversal raises evidence coverage
Corpus-wide themes Aggregation baseline Community reports improve completeness at acceptable cost
Transactional lookup Authoritative database/API Usually do not route through GraphRAG

The router should use deterministic features where possible: requested operation, identified entities, expected aggregation scope, and policy. An LLM may classify intent, but its output still needs schema validation and a safe fallback.

Execute Graph Queries Safely

Safe graph retrieval maps validated intent to pre-reviewed query templates. It never sends arbitrary model-generated query text to the database.

python
from dataclasses import dataclass
from enum import Enum
from typing import Any

class Intent(str, Enum):
    ENTITY_PROFILE = "entity_profile"
    DEPENDENCY_NEIGHBORS = "dependency_neighbors"

@dataclass(frozen=True)
class QueryPlan:
    cypher: str
    parameters: dict[str, Any]

TEMPLATES = {
    Intent.ENTITY_PROFILE: """
        MATCH (e:Entity {tenant_id: $tenant_id, id: $entity_id})
        WHERE e.policy_scope IN $allowed_scopes
        RETURN e.id AS id, e.name AS name, e.type AS type
        LIMIT 1
    """,
    Intent.DEPENDENCY_NEIGHBORS: """
        MATCH (e:Entity {tenant_id: $tenant_id, id: $entity_id})
              -[r:DEPENDS_ON]->(n:Entity {tenant_id: $tenant_id})
        WHERE e.policy_scope IN $allowed_scopes
          AND n.policy_scope IN $allowed_scopes
        RETURN n.id AS id, n.name AS name, r.claim_id AS claim_id
        ORDER BY n.id
        LIMIT $row_limit
    """,
}

def build_plan(
    intent: Intent,
    *,
    entity_id: str,
    authoritative_tenant_id: str,
    authoritative_scopes: list[str],
    requested_limit: int = 25,
) -> QueryPlan:
    if not entity_id or len(entity_id) > 128:
        raise ValueError("invalid entity identifier")
    if not authoritative_scopes:
        raise PermissionError("no authorized policy scope")
    return QueryPlan(
        cypher=TEMPLATES[intent],
        parameters={
            "entity_id": entity_id,
            "tenant_id": authoritative_tenant_id,
            "allowed_scopes": authoritative_scopes,
            "row_limit": min(max(requested_limit, 1), 100),
        },
    )

plan = build_plan(
    Intent.DEPENDENCY_NEIGHBORS,
    entity_id="service:checkout",
    authoritative_tenant_id="tenant_17",
    authoritative_scopes=["operations"],
    requested_limit=500,
)
print(plan.parameters["row_limit"])
# Output: 100

The Neo4j Cypher injection guidance explains why literal values should be parameters: query text and values are compiled separately. That control is necessary but incomplete.

Apply all of these controls:

  • Derive tenant, object, and purpose scope from trusted identity and policy services.
  • Use read-only, least-privilege database roles.
  • Allowlist query templates, labels, relationship types, and sortable properties.
  • Bound traversal depth, fan-out, returned rows, time, and memory.
  • Parameterize literal values.
  • Redact database errors before they reach a model or client.
  • Log template ID, policy decision, snapshot, parameters after redaction, latency, and row count.

Parameters prevent literal-value injection. They do not authorize a query. Dynamic labels, relationship types, property names, and query clauses cannot be made safe merely by placing user text in a parameter.

Preserve Authorization and Evidence

Authorization must produce the same decision across source text, vector chunks, graph claims, and generated summaries. Otherwise, the graph becomes a side channel around document permissions.

Apply policy before and during retrieval:

  1. Resolve caller identity, tenant, purpose, and object grants from authoritative services.
  2. Filter candidate indexes by those grants.
  3. Enforce tenant and policy scope inside graph templates.
  4. Recheck every source span before prompt assembly.
  5. Reject generated citations that point outside the authorized evidence set.
  6. Record the policy decision and graph/index snapshot for audit.

Never let the model invent tenant_id, allowed_scopes, or an access-control predicate. Query text is not an identity source.

Community reports and other graph summaries require the same controls as their source documents. A summary that combines allowed and denied documents cannot be safely returned by hiding its citations; it must be rebuilt for the permitted evidence scope or excluded.

Evaluate Each Layer Separately

A single answer score cannot reveal whether a regression came from extraction, entity resolution, retrieval, generation, or policy. Use layered gates and keep a simpler retrieval baseline in every release evaluation.

Layer Example measures Critical failure
Extraction Entity and relation precision/recall/F1, source-span validity, vocabulary violations Unsupported or untraceable claim
Resolution Pairwise precision/recall/F1, harmful merge rate, harmful split rate Cross-entity contamination
Graph quality Provenance coverage, stale-edge rate, orphan rate, duplicate-entity rate Accepted edge without current evidence
Retrieval Evidence Recall@K, path/evidence coverage, citation coverage Authorized evidence missed or denied evidence returned
Generation Correctness, claim support, completeness, abstention, contradiction handling Unsupported material claim
Security Denied-content retrieval rate, cross-tenant leakage, template bypass attempts Any unauthorized disclosure
Operations p50/p95 latency, indexing cost, refresh lag, update amplification SLO or deletion deadline breach

This dependency-free example calculates evidence recall and citation precision without conflating them:

python
def retrieval_metrics(
    required_evidence: set[str],
    retrieved_evidence: list[str],
    cited_evidence: list[str],
) -> dict[str, float]:
    retrieved = set(retrieved_evidence)
    cited = set(cited_evidence)
    recall = len(required_evidence & retrieved) / max(len(required_evidence), 1)
    citation_precision = len(cited & retrieved) / max(len(cited), 1)
    return {
        "evidence_recall": round(recall, 3),
        "citation_precision": round(citation_precision, 3),
    }

print(retrieval_metrics(
    required_evidence={"span:A", "span:B"},
    retrieved_evidence=["span:A", "span:C"],
    cited_evidence=["span:A", "span:X"],
))
# Output: {'evidence_recall': 0.5, 'citation_precision': 0.5}

Segment results by query class, language, tenant, entity type, graph depth, and source freshness. Report quality alongside latency and cost. A gain in corpus-level synthesis does not justify routing direct lookups through a slower, less precise path.

For a broader release-gate design, use the production RAG evaluation guide. The advanced GraphRAG engineering guide goes deeper on extraction contracts and baseline experiments, while the semantic search guide covers hybrid retrieval and authorization filtering.

Operate Updates, Deletions, and Rollbacks

A production graph is a versioned derived view, so updates and deletions must propagate through every derivative. Updating only the source document leaves stale claims, embeddings, community reports, and caches active.

Maintain a dependency manifest:

text
source revision
  -> segments
  -> candidate claims
  -> resolved entities
  -> accepted graph snapshot
  -> lexical/vector indexes
  -> community assignments
  -> generated summaries
  -> retrieval and answer caches

For each source change:

  1. Create a new immutable source revision.
  2. Re-extract affected spans and compare claim deltas.
  3. re-run entity resolution only for impacted candidates.
  4. Publish a new graph/index snapshot atomically.
  5. Invalidate summaries and caches that depend on changed claims.
  6. Keep the previous snapshot available for rollback.

For deletion, tombstone the source immediately, block it from retrieval, remove or withdraw dependent claims, rebuild affected summaries, purge caches, and verify propagation within a defined deadline. Track deletion coverage and lag as release metrics.

Common Failure Modes

The most damaging failures cross layer boundaries and therefore remain invisible to a single answer-quality metric.

Failure Why it happens Production control
Knowledge graph treated as truth Extracted edges lose source context Claim nodes, source spans, review states, temporal scope
Distinct entities are merged Weak identity features or global thresholds Type-specific thresholds, review queue, rollback
One entity is split Alias and multilingual matching gaps Stable IDs, alias provenance, pairwise evaluation
GraphRAG replaces every baseline Architecture chosen before workload evaluation Query-class router and baseline gates
Path shown as explanation Retrieval artifact confused with reasoning Label paths as evidence; cite original spans
Generated Cypher is executed Model output crosses the trust boundary Pre-reviewed templates and strict parameters
Tenant filter comes from the prompt Query data is mistaken for authorization Authoritative identity and policy service
Deleted source still affects answers Derived artifacts lack dependency lineage Deletion manifest, propagation SLO, verification
Community summary becomes authority Generated text is cited instead of sources Summary-to-span lineage and citation checks

Frequently Asked Questions

Is every knowledge graph based on RDF?

No. RDF is a standardized graph data model based on triples and IRIs, but knowledge graphs can also use property graphs, relational representations, search indexes, or combinations of them. The representation should preserve identity, semantics, provenance, and lifecycle requirements regardless of storage.

Is an ontology required?

Not always. A small application may start with a constrained entity and relationship schema. An ontology becomes useful when teams need shared semantics, interoperability, inference rules, or formal constraints. An ontology does not validate source truth or eliminate entity-resolution errors.

Can GraphRAG reduce hallucinations?

It can provide better evidence for some tasks, but it cannot guarantee factual output. Hallucination risk still depends on extraction quality, retrieval coverage, authorization, prompt construction, model behavior, and claim-level verification. Measure supported-claim rate and abstention rather than assuming the graph fixes generation.

How deep should graph traversal be?

There is no universal depth. Use fixed, allowlisted templates for the relationships the product actually needs, then benchmark coverage, fan-out, latency, and false associations. Unbounded or model-selected depth increases cost, leakage risk, and irrelevant paths.

When should a team avoid GraphRAG?

Avoid it when direct database lookup, BM25, vector, or hybrid retrieval already satisfies quality and latency targets; when source permissions cannot be propagated to graph artifacts; or when the team cannot operate extraction, entity resolution, updates, deletion, and layered evaluation.

References

Summary

Knowledge graphs create durable AI value when they make identity, relationships, provenance, policy, and lifecycle explicit. Build the graph as a versioned derivative of authoritative sources, keep extraction and resolution reversible, route GraphRAG only where evaluation proves value, and execute graph queries through bounded templates. The result is not automatic truth or hidden-reasoning visibility; it is a more inspectable retrieval system with measurable evidence and governance contracts.