RAG vs CAG vs KAG: Choosing the Right Context Layer in HazelJS

A practical cross-comparison of RAG, Cache-Augmented Generation, Knowledge-Augmented Generation, and Memory — when each wins, how HazelJS ships them, and offline labs in Meridian Ops.
Retrieval-Augmented Generation (RAG) solved a real problem: LLMs alone don't know your documents. But not every agent question is “find the right passage.” Sometimes you need stable context reused across thousands of requests. Sometimes you need structured entities, facts, and relationships with provenance. That is why HazelJS treats context as four complementary layers — RAG, CAG, KAG, and Memory — and ships first-class packages for each.
This post is a practical cross-comparison: what each layer is for, when it wins or loses, how we built @hazeljs/rag, @hazeljs/cag, and @hazeljs/kag, and real use cases you can run offline in hazeljs-meridian-ops.
Packages & Links
- @hazeljs/rag — retrieval, loaders, vector stores, GraphRAG (docs)
- @hazeljs/cag — Cache-Augmented Generation (docs)
- @hazeljs/kag — Knowledge-Augmented Generation (docs)
- @hazeljs/context — unified planner that combines them (docs)
- @hazeljs/memory — agent/user continuity (distinct from domain knowledge)
Runnable labs (no API keys): meridian-ops/examples — example:rag · example:cag · example:kag · example:context
One Picture
HazelJS Agent
│
▼
Context / Reasoning
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
RAG CAG KAG
│ │ │
└─────────────┼─────────────┘
│
Memory
│
▼
LLM
| Layer | Core question | Typical answer shape |
|---|---|---|
| RAG | What information should I retrieve for this query? | Ranked passages + citations |
| CAG | What context can I preload, reuse, and cache? | Fingerprinted policy / schema blobs |
| KAG | What entities, facts, and relationships do I know? | Evidence path + provenance |
| Memory | What has this agent learned or experienced with this user? | Preferences, session facts |
They are complementary. CAG is not a RAG wrapper. KAG is not “GraphRAG renamed.” Memory is not a knowledge graph.
What Each Layer Is
RAG — Retrieval-Augmented Generation
Idea: At request time, embed the question, search a vector (and optionally keyword) index, optionally rerank, then stuff passages into the prompt.
Request → Embedding → Vector / hybrid search → Documents → LLM
HazelJS surface: @hazeljs/rag — loaders, chunking, embeddings, vector stores, hybrid search, GraphRAG for document-derived graphs, RAGService / RAGPipeline. Meridian’s helpdesk agent uses the same mental model with an in-memory KB (no Qdrant required to learn the pattern).
Best when: answers live in a large, changing corpus and relevance depends on the query.
Lab: examples/rag-helpdesk-agent → npm run example:rag
CAG — Cache-Augmented Generation
Idea: Relatively stable knowledge is prepared once, fingerprinted, cached (memory / Redis / provider prompt-cache hints), then reused — without embedding + search on every request.
Knowledge sources → Prepare → Context cache → Request → Cached context → LLM
HazelJS surface: @hazeljs/cag — CagModule / CagService, sources, fingerprints, TTL / version invalidation, token budgets, optional compression, multi-tenant keys.
Best when: the same policies, schemas, catalogs, or tenant config are needed on almost every turn.
Lab: examples/cag-support-agent → npm run example:cag
KAG — Knowledge-Augmented Generation
Idea: Domain knowledge as typed entities, relations, and time-scoped facts — with provenance, confidence, contradictions, and traversable evidence — then optional LLM phrasing of that evidence.
Data → Extract / ingest → Knowledge graph → Traverse / plan → Evidence → LLM
HazelJS surface: @hazeljs/kag — KnowledgeGraph / KagService, in-memory tenant store, entity resolution, temporal facts, heuristic (industry-pluggable) query planner, ask({ explain: true }), GraphRAG import adapters (not a GraphRAG fork).
Best when: the question is multi-hop, causal, or “why did X change?” over structured domain state — not “which PDF page mentions X?”
Lab: examples/kag-commerce-agent → npm run example:kag
Memory — continuity (not knowledge)
Memory answers “what do we already know about this user / this run?” It is orthogonal to RAG/CAG/KAG. Promote a preference into KAG only when it becomes validated domain truth.
Cross Comparison
Capability matrix
| Dimension | RAG | CAG | KAG | Memory |
|---|---|---|---|---|
| Unit of context | Passages / chunks | Assembled stable text blocks | Entities, relations, facts | Episodes / preferences |
| Hot path | Embed + search every request | Cache hit → assemble | Graph traverse / plan | Lookup by user/session |
| Data freshness | Great for frequently updated docs | Great for slowly changing knowledge | Great for operational / transactional truth | Great for conversational state |
| Latency profile | Search + embed cost | Near-instant on hit | Traversal (cheap at small/medium scale) | Near-instant |
| Explainability | Citations to passages | Source id + version / fingerprint | Evidence path + provenance | “You said X earlier” |
| Multi-tenancy | Namespaces / metadata filters | Cache keys include tenantId | Store partitioned by tenantId | Scoped by user / agent |
| Failure mode | Wrong or missing passage | Stale cache if invalidation is wrong | Weak graph / low confidence | Stale or over-trusted preference |
| Typical cost driver | Embeddings + vector DB | Prompt tokens (mitigated by cache) | Ingestion + occasional LLM phrasing | Storage + retrieval of history |
| Update cadence | Continuous ingest | Version bump / TTL / invalidate | Event-driven graph writes | Per turn / session |
| HazelJS package | @hazeljs/rag | @hazeljs/cag | @hazeljs/kag | @hazeljs/memory |
Same question, four wrong tools
| User asks | If you only use… | What goes wrong |
|---|---|---|
| “What’s our return policy?” | RAG | You pay embed+search every time for text that never changes per tenant version. |
| “How do I rotate staging API keys?” | CAG | You either miss the right runbook or stuff the entire handbook into every prompt. |
| “Why was settlement €1,420 short?” | RAG | Passages mention refunds; they don’t reconstruct merchant→order→payment→refund→settlement with sources. |
| “Remember I exclude VAT.” | KAG | You pollute the domain graph with a personal preference that isn’t operational truth. |
| Mix of policy + ledger + preference | Any single layer | Incomplete answer — needs @hazeljs/context. |
Pros and cons (short)
| Wins | Loses when | |
|---|---|---|
| RAG | Large unstructured corpora; query-dependent relevance; mature tooling | Multi-hop structured “why”; always-on policy paragraphs |
| CAG | Latency/cost for stable tenant context; fingerprints; priority budgets | Query-specific doc lookup; forgotten invalidation |
| KAG | Causal paths, contradictions, provenance, industry profiles | Free-form prose; “drop PDFs and go” |
| Memory | Continuity and personalization | Treating preferences as ledger truth |
How HazelJS Built These Packages
We did not invent three parallel frameworks. We extended the Agent OS context substrate with clear boundaries and heavy reuse.
- Composition over duplication — CAG reuses RAG loaders for files/URLs when present; it does not reimplement vector search. KAG can import GraphRAG graphs; it does not own community detection or embeddings.
- Native HazelJS shape —
@HazelModule,forRoot/forRootAsync,@Service(), typedXxxError+ codes, Jest, peer@hazeljs/core. - Multi-tenant by default where it matters — CAG cache keys and KAG store scopes always include
tenantId. - Agent OS ports, not hard couples —
AgentRuntimeConfigtakes duck-typedragService/cagService/kagService/contextService. DNA declares intent; apps inject services. - Toward a unified planner —
@hazeljs/contextcalls the same contributor shape (contribute) so auto strategy can pick CAG / RAG / KAG / Memory per query.
Real-World Use Cases
Each scenario below maps to a Meridian lab (or live Meridian route). Run the lab after reading the section.
1) Help center / FAQ over a large handbook → RAG
Industries: SaaS support, internal IT, compliance Q&A, product docs.
Problem: Hundreds of docs, changelogs, and runbooks. The right answer depends on the question wording and may live in an obscure page updated last week.
// Pattern (production: @hazeljs/rag ingest + vector store)
const docs = await rag.search('How do I rotate API keys on staging?', { topK: 3 });
Why not CAG? You cannot sensibly preload the entire doc set into every prompt.
Why not KAG? The knowledge is narrative procedure text, not a fact graph (unless you later extract entities).
Lab / live: Offline rag-helpdesk-agent mirrors Meridian’s helpdesk KB. In the running app: POST /api/helpdesk/chat.
npm run example:rag
# ask: "What is the refund SLA?" → hits refund-sla doc, not the whole policy blob
2) Multi-merchant support: always-on return policy → CAG
Industries: Marketplaces, payment platforms, white-label SaaS, any multi-tenant agent.
Problem: Every merchant chat asks about returns, hours, and fee schedules. Running RAG every time burns embeddings and latency for text that rarely changes — and tenant A must never see tenant B’s policy.
cag.register(
{
id: 'return-policy',
priority: 100,
compressible: false,
version: '2026-10',
source: { type: 'text', content: loadMerchantPolicy(tenantId) },
},
'support'
);
const { context, hit } = await cag.getContext({
namespace: 'support',
tenantId: 'merchant-123',
});
Why not RAG? The answer is not “find a similar paragraph” — it is “always include this policy.”
Why not KAG? There is no multi-hop entity reasoning; it is static prose.
Lab: cag-support-agent — cold miss → warm hit, tenant isolation, version bump invalidates cache.
npm run example:cag
# firstHit: false, secondHit: true, otherTenantHit: false, afterVersionBumpHit: false
Other CAG fits: OpenAPI / tool schemas stuck in every tool-calling turn; compliance disclaimers; product catalog summaries that change weekly under a version key.
3) Ops / finance: “Why was settlement short?” → KAG
Industries: Commerce / payments, logistics (shipment→exception→claim), healthcare ops (patient→encounter→claim), ITSM (incident→change→outage).
Problem: The answer is a path through merchant → orders → payments → refunds → fees → settlement, with source systems and confidence — not a single handbook paragraph.
const knowledge = new KnowledgeGraph({
tenantId: 'merchant-123',
industry: COMMERCE_INDUSTRY_PROFILE, // opt-in vocabulary
});
await knowledge.relate({ from: 'payment-1', to: 'refund-1', type: 'HAS_REFUND' });
await knowledge.fact({
subject: 'refund-1',
predicate: 'amount',
object: 1420,
confidence: 0.98,
source: { type: 'database', id: 'refund-456', authority: 90 },
});
const result = await knowledge.ask(question, { explain: true });
// result.evidence + result.sources — auditable, not hidden CoT
Why not RAG alone? Passages might mention refunds but won’t reliably reconstruct the causal path with provenance.
Why not CAG alone? The graph changes with every day’s ledger; caching a frozen narrative goes stale immediately.
Lab: kag-commerce-agent — builds the graph, asks the settlement question, prints evidence + contradiction handling.
npm run example:kag
# explanation includes merchant → order → payment → refund → settlement + sources
4) Hybrid desk: policy + ledger + handbook + preference → @hazeljs/context
Industries: Any production support desk that already has RAG and needs policy + ops truth.
Problem: “Why was settlement impacted by a refund, given our refund policy and my VAT preference?”
| Slice of the question | Layer |
|---|---|
| Stable refund policy | CAG |
| Relational “why” on the ledger | KAG |
| Optional handbook passage | RAG |
| “exclude VAT” preference | Memory |
const context = new ContextService({
strategy: 'auto',
sources: ['cache', 'rag', 'knowledge', 'memory'],
cag,
kag,
rag,
memory,
});
const assembled = await context.assemble({
query,
tenantId: 'merchant-123',
userId: 'ops-1',
});
// assembled.plan.rationale explains which layers fired
Lab: context-auto-agent — auto-plans all four contributors and prints labeled assembly.
npm run example:context
# plan.sources: cag, rag, kag, memory — with per-layer rationale
5) Industry cheat sheet
| Domain | Prefer RAG | Prefer CAG | Prefer KAG |
|---|---|---|---|
| E‑commerce / payments | Help center, shipping FAQs | Merchant return/fee policy | Settlement, chargeback, refund causality |
| SaaS / developer platforms | API docs, changelogs | Per-tenant feature flags / schemas in prompt | Entitlement → usage → invoice disputes |
| Healthcare ops | Clinical guidelines PDFs | Facility policy snippets | Encounter → claim → denial paths |
| Logistics | Carrier manuals | Lane / SLA templates | Shipment → exception → claim chains |
| Internal IT | Runbooks, wiki | Org security policy always-on | Asset → ticket → change → outage |
When two columns apply in one utterance, reach for @hazeljs/context.
6) Quick decision matrix
| If the user asks… | Prefer | Lab |
|---|---|---|
| “What’s in the docs / handbook about X?” | RAG | example:rag |
| “What is our always-on policy / schema / catalog?” | CAG | example:cag |
| “Why did entity A’s metric change given related entities?” | KAG | example:kag |
| “Remember that I prefer…” | Memory | (wired inside example:context) |
| A mix of the above | @hazeljs/context auto | example:context |
Common Mistakes
- Using RAG for static policies — you pay retrieval forever for text that should be CAG.
- Using CAG for the whole knowledge base — prompts explode; retrieval exists for a reason.
- Treating GraphRAG as KAG — GraphRAG is document-extraction RAG; KAG is operational knowledge with facts, time, and contradictions.
- Auto-merging uncertain entities — HazelJS KAG keeps candidates below threshold; don’t turn that off casually.
- Skipping tenant in cache/graph keys — never acceptable for multi-merchant agents.
- Exposing raw model chain-of-thought as “explanation” — KAG explanations are evidence (entities, relations, sources).
- Putting user preferences only in Memory forever — promote to KAG when they become validated domain rules; keep Memory for soft personalization.
Where This Is Going
HazelJS is moving from “we have RAG, and also CAG, and also KAG” to a single context substrate:
Query → ContextPlanner → CAG | RAG | KAG | Memory → Assembly → LLM
@hazeljs/context is the first cut of that planner. Each package remains independently useful for teams that only need one layer. Meridian Ops is the teaching app: DNA + Skillgate + HITL in the live server, and offline context labs under examples/.
Try It
# from hazeljs-meridian-ops/
npm run example:install
npm run example:rag # handbook retrieval
npm run example:cag # cached policy + tenant isolation
npm run example:kag # settlement evidence path
npm run example:context # auto planner over all four
Docs: RAG · CAG · KAG · Context
RAG optimizes retrieval. CAG optimizes reuse. KAG optimizes structured knowledge. Memory optimizes continuity. Together they are the context engine of HazelJS Agent OS.