teachyou.ai academy
← All posts
RAGKnowledge GraphsVector DatabasesRetrieval Engineering

Graph RAG vs Vector RAG

Pramod Dutta · Jul 5, 2026 · 13 min read

The short answer to graph rag vs vector rag is that vector RAG retrieves text with similar meaning, while Graph RAG retrieves connected facts and paths. Use vector RAG for semantic search over documents, and prefer Graph RAG when answers depend on relationships, multi-hop reasoning, provenance, or global structure. Many production systems should use both, because vectors find relevant language and graphs constrain how entities are connected.

Graph RAG vs Vector RAG at a Glance

Vector RAG splits source documents into chunks, embeds each chunk, stores those vectors, and retrieves nearest neighbors for a query. Its central question is, "Which passages are semantically similar to this request?" The implementation is compact, ingestion is usually straightforward, and the retrieved text preserves useful local context.

Graph RAG extracts entities, relationships, and sometimes claims from source material. It stores nodes such as services, people, products, incidents, or regulations, plus edges such as DEPENDS_ON, OWNS, AFFECTED, and SUPERSEDES. Its central question is, "Which connected facts explain this request?"

The practical decision depends on query shape:

  • Use vector retrieval for policy lookup, documentation search, support answers, code examples, and questions answered by one or two nearby passages.
  • Use graph retrieval for dependency analysis, fraud patterns, organizational ownership, root-cause investigation, supply chains, and questions requiring several connected facts.
  • Use hybrid retrieval when users begin with vague natural language but expect a relationship-aware, auditable answer.

Neither architecture performs reasoning by itself. Retrieval supplies evidence, then a language model synthesizes an answer. A graph can make relationships explicit, but weak extraction creates false edges. A vector index can surface excellent passages, but similarity alone does not prove that two entities are related.

Build a Minimal Vector RAG Pipeline

Start with a small, transparent baseline. This example uses Python, Sentence Transformers, and NumPy. It runs locally and avoids coupling the basic retrieval experiment to a hosted database.

python -m venv .venv
source .venv/bin/activate
pip install sentence-transformers numpy

Create vector_rag.py:

from sentence_transformers import SentenceTransformer
import numpy as np

documents = [
    "Checkout API depends on Redis for idempotency keys.",
    "Redis latency increased after the cache shard migration.",
    "Payment retries caused duplicate queue messages during incident INC-42.",
    "The Identity team owns the OAuth gateway.",
    "The Payments team owns Checkout API and the retry worker.",
]

model = SentenceTransformer("all-MiniLM-L6-v2")
doc_vectors = model.encode(documents, normalize_embeddings=True)

def retrieve(query: str, k: int = 3):
    query_vector = model.encode([query], normalize_embeddings=True)[0]
    scores = doc_vectors @ query_vector
    indexes = np.argsort(scores)[::-1][:k]
    return [(documents[i], float(scores[i])) for i in indexes]

if __name__ == "__main__":
    query = "What contributed to duplicate payment processing?"
    for text, score in retrieve(query):
        print(f"{score:.3f}  {text}")

Run it:

python vector_rag.py

This baseline exposes the main mechanics. Normalized embeddings make the dot product equivalent to cosine similarity. In production, replace the in-memory matrix with a vector-capable database, attach stable document and chunk identifiers, and filter by tenant, permissions, language, product, or effective date before ranking.

Chunking often matters more than switching vector databases. Keep headings with their paragraphs, avoid separating definitions from qualifiers, and preserve source metadata. For prose, test structure-aware chunks before choosing a fixed token window. For code, split around functions, classes, and modules. Overlapping every chunk can improve recall, but it also creates near-duplicates that waste context.

Add lexical retrieval when exact identifiers matter. An embedding may not reliably distinguish INC-42 from INC-24, while keyword search can. Reciprocal rank fusion is a reasonable way to combine lexical and vector result lists without pretending their raw scores share a scale.

Build a Minimal Graph RAG Pipeline

A useful graph starts with a domain schema, not an unrestricted request to "extract a knowledge graph." For the incident example, model services, teams, infrastructure, and incidents. Define a small relationship vocabulary and reject edges outside it.

Run Neo4j locally:

docker run --name rag-neo4j --rm \
  -p 7474:7474 -p 7687:7687 \
  -e NEO4J_AUTH=neo4j/change-this-password \
  neo4j:latest

Install the driver:

pip install neo4j

Create graph_rag.py:

from neo4j import GraphDatabase

URI = "neo4j://localhost:7687"
AUTH = ("neo4j", "change-this-password")

driver = GraphDatabase.driver(URI, auth=AUTH)

seed = """
MERGE (checkout:Service {name: 'Checkout API'})
MERGE (redis:Infrastructure {name: 'Redis'})
MERGE (worker:Service {name: 'Retry Worker'})
MERGE (payments:Team {name: 'Payments'})
MERGE (incident:Incident {id: 'INC-42'})
MERGE (checkout)-[:DEPENDS_ON]->(redis)
MERGE (payments)-[:OWNS]->(checkout)
MERGE (payments)-[:OWNS]->(worker)
MERGE (redis)-[:CONTRIBUTED_TO]->(incident)
MERGE (worker)-[:CONTRIBUTED_TO]->(incident)
"""

query = """
MATCH (i:Incident {id: $incident_id})
MATCH path=(cause)-[:CONTRIBUTED_TO]->(i)
OPTIONAL MATCH (team:Team)-[:OWNS]->(cause)
RETURN cause.name AS cause,
       labels(cause) AS types,
       team.name AS owner,
       [n IN nodes(path) | coalesce(n.name, n.id)] AS evidence_path
ORDER BY cause
"""

with driver.session() as session:
    session.run(seed).consume()
    records = session.run(query, incident_id="INC-42")
    for record in records:
        print(record.data())

driver.close()

Run it:

python graph_rag.py

The returned paths become structured context for the generator. Serialize them compactly, for example, Redis -> CONTRIBUTED_TO -> INC-42. Include node identifiers and source references so the application can cite the documents that justified each edge.

Real ingestion needs entity resolution. "Checkout," "Checkout API," and svc-checkout-prod might name the same service. Use deterministic identifiers when available, then aliases, normalization rules, and model-assisted matching. Never merge entities only because their names are semantically similar. A bad merge can contaminate every downstream traversal.

Store provenance on claims or edges. Useful fields include source_id, source_span, observed_at, valid_from, valid_to, extractor, and confidence. If sources disagree, preserve separate claims instead of overwriting history with one supposedly canonical fact.

How Retrieval Differs in Practice

Vector retrieval starts from the query embedding and searches a geometric neighborhood. It is good at paraphrases. "Who maintains payment checkout?" may retrieve a passage saying "The Payments team owns Checkout API" even though the wording differs.

Graph retrieval usually starts by resolving entities, selecting an intent or query template, and traversing allowed relationships. A request such as "Which team owns services that contributed to INC-42?" maps naturally to Incident <- CONTRIBUTED_TO - Service <- OWNS - Team. The path expresses why an answer qualifies.

That strength introduces more moving parts:

  • Entity linking must map query mentions to the correct nodes.
  • The query planner must choose safe edge types and traversal depth.
  • Extraction must preserve negation, time, and direction.
  • High-degree nodes must not explode the result set.
  • The context serializer must translate subgraphs into concise evidence.

Vector systems have different failure modes:

  • Top results can be redundant chunks from one document.
  • Similar terminology can retrieve the wrong product, customer, or period.
  • A relevant fact may be split across distant passages.
  • Nearest-neighbor scores are not calibrated truth probabilities.
  • Metadata filtering can silently remove the only useful evidence.

A reranker helps both designs. For vectors, rerank candidate chunks against the full query. For graphs, rerank paths or communities based on the query, path length, source quality, and recency. Keep deterministic constraints outside the model, especially authorization and tenant isolation.

Graph RAG vs Vector RAG Architecture Tradeoffs

Vector RAG normally has the lower operational burden. The core artifacts are chunks, embeddings, metadata, and an approximate nearest-neighbor index. Updates are localized: change a document, regenerate its affected chunks, and upsert their vectors.

Graph RAG requires schema governance, extraction, entity resolution, edge validation, and graph query design. Updating one source can change aliases, relationships, summaries, or communities. The extra work is justified when connections are themselves the product requirement.

Consider these engineering tradeoffs:

  • Latency: Vector lookup is typically one bounded retrieval operation. Graph lookup may include entity linking and several traversals. Cache resolved entities and parameterized query results where correctness permits.
  • Freshness: Vector chunks are easy to replace. Graph updates need temporal semantics so deleted or superseded facts do not remain active.
  • Explainability: A chunk shows supporting prose. A graph path shows a relationship chain. The strongest answer often includes both.
  • Schema evolution: Vector metadata can evolve incrementally. Renaming graph labels or relationship semantics requires migrations and query compatibility.
  • Security: Both need document-level authorization. Graph traversal additionally needs controls that prevent reaching restricted nodes through allowed starting points.
  • Cost: Do not compare only database usage. Include extraction calls, embedding jobs, reprocessing, entity review, evaluation, and incident response.

A Hybrid Pipeline That Uses Both

Hybrid retrieval is not simply concatenating two lists. Give each retriever a job. Vector search can discover candidate entities and source passages, while the graph expands validated relationships. The final context can contain paths plus the passages supporting their edges.

A practical request flow is:

  1. Classify the query as local semantic lookup, relationship lookup, or broad synthesis.
  2. Apply tenant and access constraints before retrieving content.
  3. Run lexical and vector search to find candidate passages and entity mentions.
  4. Resolve candidate entities to graph identifiers.
  5. Execute a bounded, parameterized traversal selected from approved templates.
  6. Fetch source chunks attached to the returned nodes or edges.
  7. Rerank evidence, remove duplicates, and fit it to a token budget.
  8. Generate an answer that distinguishes facts, inferences, and missing evidence.

The routing logic can begin with rules. Queries containing an incident identifier and words such as "depends," "owner," "path," or "related" can prefer graph expansion. Broad thematic questions can use community summaries. Ordinary how-to questions can remain vector-first. Later, train or evaluate a lightweight classifier using labeled production queries.

Keep the graph traversal bounded. Avoid accepting arbitrary model-generated Cypher directly against production. Select a reviewed template and supply validated parameters:

QUERY_TEMPLATES = {
    "incident_causes": """
        MATCH (cause)-[:CONTRIBUTED_TO]->(i:Incident {id: $id})
        RETURN cause.name AS cause, labels(cause) AS types
        LIMIT 25
    """,
    "service_dependencies": """
        MATCH (s:Service {name: $name})-[:DEPENDS_ON*1..3]->(dep)
        RETURN DISTINCT dep.name AS dependency, labels(dep) AS types
        LIMIT 50
    """,
}

def choose_template(intent: str, params: dict):
    if intent not in QUERY_TEMPLATES:
        raise ValueError("Unsupported graph intent")
    allowed = {"id", "name"}
    if not set(params).issubset(allowed):
        raise ValueError("Unexpected query parameter")
    return QUERY_TEMPLATES[intent], params

Treat model-produced entity names as untrusted input. Parameterization prevents syntax injection, but authorization still requires explicit checks. Enforce limits, timeouts, allowed labels, allowed relationship types, and maximum traversal depth at the application and database layers.

Evaluate Graph RAG vs Vector RAG

Evaluate retrieval separately from answer generation. Otherwise, a fluent model can hide weak retrieval, or a poor prompt can make good evidence look useless. Build a golden set from real query patterns, including simple lookups, paraphrases, ambiguous entities, multi-hop questions, temporal questions, access-control cases, and unanswerable requests.

For vector retrieval, track metrics such as recall at k, precision at k, mean reciprocal rank, and normalized discounted cumulative gain. Judge relevance at the chunk level and source level. Source-level recall reveals whether duplicate chunks make the metric look better than the user experience.

For graph retrieval, evaluate entity-linking accuracy, edge correctness, path recall, path precision, and constraint compliance. A path can contain the right destination but the wrong relationship direction, so exact node overlap is insufficient. For global summaries, check whether cited community evidence supports each generated claim.

Use a shared end-to-end rubric:

  • Does the answer resolve the user intent?
  • Is every material claim supported by retrieved evidence?
  • Does it confuse entities with similar names?
  • Does it respect time, tenant, and authorization boundaries?
  • Does it state when evidence is missing or conflicting?
  • Are citations traceable to immutable source identifiers?

Measure latency by stage: query classification, embedding, candidate search, entity resolution, traversal, reranking, context construction, and generation. Compare quality at fixed context budgets and realistic concurrency. Do not publish a single aggregate score without showing the query categories, because one architecture may win easy lookups and lose multi-hop cases.

Production Hardening Checklist

Start with observability that connects each answer to one trace. Record the sanitized query, router decision, retrieval configuration, candidate identifiers, scores, selected graph template, traversal parameters, final evidence identifiers, model configuration, latency, and failure category. Avoid logging confidential source text by default.

Use deterministic ingestion where possible:

  • Give every source, chunk, entity, edge, and extraction run a stable identifier.
  • Version chunking, embedding, extraction, and schema configurations.
  • Make upserts idempotent and deletion behavior explicit.
  • Run extraction validation before publishing graph changes.
  • Keep a dead-letter queue for malformed or unresolved records.
  • Support rollback to the previous index and graph snapshot.

For graph extraction, validate output with a strict schema. Check allowed node labels, relationship types, required identifiers, direction, and evidence spans. Send low-confidence merges to review when their impact is high. A suspicious alias on a central entity deserves more scrutiny than an isolated leaf node.

For vector ingestion, detect empty chunks, encoding failures, excessive duplication, missing metadata, and embedding dimension changes. Deploy new embedding models into a separate index, replay the evaluation set, and shift traffic only after comparing retrieval quality and latency.

Choosing the Right Starting Point

Choose vector RAG first when documents are the source of truth and most questions map to a compact passage. It gives a fast path to a measurable baseline. Add keyword retrieval, metadata filters, reranking, and better chunking before assuming that a graph is necessary.

Choose Graph RAG first when the domain already has reliable structured relationships, such as a service catalog, identity graph, bill of materials, or fraud network. Reuse those authoritative identifiers and edges. Add documents as provenance and explanatory context rather than extracting everything again.

Choose hybrid retrieval when users need both discovery and precise navigation. A support engineer may describe a symptom semantically, then need ownership and dependency paths. An analyst may find a policy passage, then ask which controls and systems it governs.

Run a small bake-off before committing. Select representative queries, implement the vector baseline, add the smallest graph schema that can answer the multi-hop subset, and compare retrieval evidence. The graph should earn its additional operational complexity through measurable improvements on important query classes.

FAQ

Is Graph RAG always more accurate than vector RAG?

No. Graph RAG can be more precise for relationship and multi-hop questions, but its accuracy depends on extraction, entity resolution, schema quality, and traversal logic. Vector RAG often performs better for prose-heavy questions whose answers already exist in coherent passages.

Can I add Graph RAG without replacing my vector database?

Yes. Keep the vector index for semantic discovery and add a graph store for entities and relationships. Connect them with stable source, chunk, entity, and edge identifiers so paths can retrieve supporting passages.

Does Graph RAG require an LLM during retrieval?

Not necessarily. Entity extraction during ingestion may use a model, but retrieval can use deterministic entity matching and parameterized graph queries. A model can help classify intent or resolve ambiguity, provided you validate its output.

How deep should graph traversal go?

Use the smallest depth justified by domain questions. One to three hops covers many operational cases. Unbounded traversal increases latency, noise, and security risk. Evaluate depth per relationship type rather than adopting one global value.

What should I prototype first?

Build a vector baseline and a golden query set. Then model only the entities and edges needed by questions the baseline misses. This exposes whether the real limitation is missing relationships, poor chunking, weak metadata, or inadequate ranking.

How do I prevent hallucinated graph facts?

Require provenance for every extracted edge, validate against an allowed schema, preserve uncertainty, and retrieve original supporting text with each path. In the answer prompt, require the model to use supplied evidence and explicitly report unsupported or conflicting claims.

When is a knowledge graph unnecessary?

It is unnecessary when queries are mostly local document lookups, relationships are rarely requested, or the organization cannot maintain entity identity and schema quality. In those cases, strong vector and lexical retrieval is simpler and often sufficient.