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
- Knowledge Graph, Graph Database, and Ontology
- Decide Whether You Need a Knowledge Graph
- Production Architecture
- Model Claims and Provenance
- Control Extraction and Entity Resolution
- Choose RDF, Property Graph, or Relational Storage
- Route GraphRAG by Query Type
- Execute Graph Queries Safely
- Preserve Authorization and Evidence
- Evaluate Each Layer Separately
- Ship a Minimum Viable Knowledge Graph
- Operate Updates, Deletions, and Rollbacks
- Common Failure Modes
- Frequently Asked Questions
- References
Key Takeaways
- A knowledge graph is a representation of knowledge; a graph database is one implementation choice.
- Start from recurring questions and required evidence paths, not from a database product or a universal ontology.
- 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 Concepts is a Candidate Recommendation Snapshot that adds features such as triple terms. Use RDF 1.1 as the interoperability baseline unless every producer and consumer explicitly supports the selected RDF 1.2 features.
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.
Decide Whether You Need a Knowledge Graph
A knowledge graph is justified when named relationships, shared identity, or evidence paths are part of the product requirement. It is not justified merely because the source corpus is large or an LLM is involved.
| User task | Start with | Add a graph only when |
|---|---|---|
| Fetch a current account balance or order status | Authoritative API or relational query | Related-party or dependency paths are part of the answer |
| Find an identifier, exact phrase, or policy clause | Lexical retrieval | Aliases and linked records materially improve recall |
| Find paraphrases in documents | Vector or hybrid retrieval | Explicit entity relations contribute missing evidence |
| Trace ownership, lineage, impact, or fraud rings | Relational joins or a graph benchmark | Repeated, variable-depth paths are a core workload |
| Summarize themes across a corpus | Hierarchical or map-reduce baseline | Graph communities improve supported coverage at acceptable cost |
Before modeling entities, write a query contract for each important question:
- The user decision the answer supports.
- The authoritative source records and freshness requirement.
- The entities and relationship path needed to retrieve evidence.
- The maximum traversal depth, fan-out, latency, and cost.
- The authorization scope and required citations.
- The non-graph baseline and acceptance metrics.
This reverses a common failure pattern. Instead of loading every noun into a graph and searching for a use case later, the team builds only the identity and relationship layer required by measured questions.
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.
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:
- Which source spans support it?
- Which extractor and resolver versions produced the graph artifacts?
- Which authorization decision allowed retrieval?
- 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.
{
"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_scopes": ["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. For interoperable RDF systems, PROV-O provides a vocabulary for exchanging provenance, while SHACL validates structural conditions over RDF data graphs. Neither standard proves that a real-world claim is true; source review and domain controls still own that decision.
The following dependency-free validator demonstrates a minimum ingestion contract:
package main
import (
"fmt"
"strings"
)
var allowedRelations = map[string]struct{}{
"HEADQUARTERED_IN": {},
"OWNS": {},
"DEPENDS_ON": {},
}
type Claim struct {
SubjectID string
Predicate string
ObjectID string
SourceDocumentID string
SourceRevision string
SpanStart int
SpanEnd int
ExtractorVersion string
TenantID string
}
func (c Claim) Validate(sourceLength int) error {
if _, ok := allowedRelations[c.Predicate]; !ok {
return fmt.Errorf("relationship %q is not in the versioned vocabulary", c.Predicate)
}
if c.SpanStart < 0 || c.SpanStart >= c.SpanEnd || c.SpanEnd > sourceLength {
return fmt.Errorf("source span [%d,%d) is outside revision length %d",
c.SpanStart, c.SpanEnd, sourceLength)
}
required := []string{
c.SubjectID,
c.ObjectID,
c.SourceDocumentID,
c.SourceRevision,
c.ExtractorVersion,
c.TenantID,
}
for _, value := range required {
if strings.TrimSpace(value) == "" {
return fmt.Errorf("claim lineage and tenant scope are required")
}
}
return nil
}
func main() {
claim := Claim{
SubjectID: "org:acme",
Predicate: "HEADQUARTERED_IN",
ObjectID: "place:singapore",
SourceDocumentID: "doc_842",
SourceRevision: "sha256:8f1",
SpanStart: 10,
SpanEnd: 42,
ExtractorVersion: "relations-v3",
TenantID: "tenant_17",
}
if err := claim.Validate(100); err != nil {
panic(err)
}
fmt.Println("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:
- Define a versioned relationship vocabulary and the valid subject/object types.
- Require source spans for every candidate entity and relation.
- Reject unknown predicates instead of creating new edge types at runtime.
- Resolve entities with stable identifiers and auditable match features.
- Route low-confidence or high-impact merges to review.
- 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.
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.
package main
import (
"fmt"
"strings"
)
type Intent string
const (
EntityProfile Intent = "entity_profile"
DependencyNeighbors Intent = "dependency_neighbors"
)
type QueryPlan struct {
Cypher string
Parameters map[string]any
}
var queryTemplates = map[Intent]string{
EntityProfile: `
MATCH (e:Entity {tenant_id: $tenant_id, id: $entity_id})
WHERE any(scope IN e.policy_scopes WHERE scope IN $allowed_scopes)
RETURN e.id AS id, e.name AS name, e.type AS type
LIMIT 1`,
DependencyNeighbors: `
MATCH (e:Entity {tenant_id: $tenant_id, id: $entity_id})
-[r:DEPENDS_ON {tenant_id: $tenant_id}]->(n:Entity {tenant_id: $tenant_id})
WHERE any(scope IN e.policy_scopes WHERE scope IN $allowed_scopes)
AND any(scope IN r.policy_scopes WHERE scope IN $allowed_scopes)
AND any(scope IN n.policy_scopes WHERE 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`,
}
func buildPlan(
intent Intent,
entityID string,
tenantID string,
allowedScopes []string,
requestedLimit int,
) (QueryPlan, error) {
query, ok := queryTemplates[intent]
if !ok {
return QueryPlan{}, fmt.Errorf("unsupported query intent %q", intent)
}
if strings.TrimSpace(entityID) == "" || len(entityID) > 128 {
return QueryPlan{}, fmt.Errorf("invalid entity identifier")
}
if strings.TrimSpace(tenantID) == "" || len(allowedScopes) == 0 {
return QueryPlan{}, fmt.Errorf("authoritative tenant and policy scope are required")
}
rowLimit := requestedLimit
if rowLimit < 1 {
rowLimit = 1
}
if rowLimit > 100 {
rowLimit = 100
}
return QueryPlan{
Cypher: query,
Parameters: map[string]any{
"entity_id": entityID,
"tenant_id": tenantID,
"allowed_scopes": append([]string(nil), allowedScopes...),
"row_limit": rowLimit,
},
}, nil
}
func main() {
plan, err := buildPlan(
DependencyNeighbors,
"service:checkout",
"tenant_17",
[]string{"operations"},
500,
)
if err != nil {
panic(err)
}
fmt.Println(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:
- Resolve caller identity, tenant, purpose, and object grants from authoritative services.
- Filter candidate indexes by those grants.
- Enforce tenant and policy scope inside graph templates.
- Recheck every source span before prompt assembly.
- Reject generated citations that point outside the authorized evidence set.
- 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:
package main
import "fmt"
type RetrievalMetrics struct {
EvidenceRecall float64
CitationPrecision float64
}
func toSet(values []string) map[string]struct{} {
set := make(map[string]struct{}, len(values))
for _, value := range values {
set[value] = struct{}{}
}
return set
}
func overlapCount(left, right map[string]struct{}) int {
count := 0
for value := range left {
if _, ok := right[value]; ok {
count++
}
}
return count
}
func retrievalMetrics(required, retrieved, cited []string) RetrievalMetrics {
requiredSet := toSet(required)
retrievedSet := toSet(retrieved)
citedSet := toSet(cited)
requiredCount := len(requiredSet)
if requiredCount == 0 {
requiredCount = 1
}
citedCount := len(citedSet)
if citedCount == 0 {
citedCount = 1
}
return RetrievalMetrics{
EvidenceRecall: float64(overlapCount(requiredSet, retrievedSet)) /
float64(requiredCount),
CitationPrecision: float64(overlapCount(citedSet, retrievedSet)) /
float64(citedCount),
}
}
func main() {
metrics := retrievalMetrics(
[]string{"span:A", "span:B"},
[]string{"span:A", "span:C"},
[]string{"span:A", "span:X"},
)
fmt.Printf("evidence_recall=%.3f citation_precision=%.3f\n",
metrics.EvidenceRecall, metrics.CitationPrecision)
}
// Output: evidence_recall=0.500 citation_precision=0.500
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.
Ship a Minimum Viable Knowledge Graph
The smallest production-worthy knowledge graph supports one bounded query family end to end. It should prove retrieval value and lifecycle control before the team expands the ontology or imports the entire enterprise catalog.
| Delivery step | Required artifact | Release evidence |
|---|---|---|
| 1. Fix the workload | Versioned questions, expected evidence, and no-answer cases | Reviewers agree that each question maps to a real user decision |
| 2. Define identity | Stable entity IDs, aliases, merge/split policy, and relationship vocabulary | Labeled resolution set passes the agreed harmful-merge gate |
| 3. Publish claims | Source revision, span, extractor version, policy scope, and claim state | Every accepted claim is traceable to current authorized evidence |
| 4. Keep a baseline | Lexical, vector, relational, or API result for the same questions | Graph path wins the target slice without regressing direct lookups |
| 5. Shadow the path | Versioned graph/index snapshots and per-layer telemetry | Quality, latency, cost, and denied-content tests pass together |
| 6. Enable one route | Feature flag, rollback snapshot, deletion drill, and accountable owner | Rollback and deletion propagation are demonstrated before launch |
Expansion should follow failed or newly valuable query slices. Adding an entity type without a query, owner, source contract, and deletion path increases maintenance surface without proving user value.
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:
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:
- Create a new immutable source revision.
- Re-extract affected spans and compare claim deltas.
- re-run entity resolution only for impacted candidates.
- Publish a new graph/index snapshot atomically.
- Invalidate summaries and caches that depend on changed claims.
- 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
- RDF 1.1 Concepts and Abstract Syntax — W3C Recommendation
- RDF 1.2 Concepts and Abstract Data Model — W3C Candidate Recommendation
- Shapes Constraint Language (SHACL) — W3C Recommendation
- PROV-O: The PROV Ontology — W3C Recommendation
- GraphRAG Query Overview — Microsoft
- From Local to Global: A Graph RAG Approach to Query-Focused Summarization
- WildGraphBench: Benchmarking Graph-Based Retrieval-Augmented Generation
- Protecting against Cypher Injection — Neo4j
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.