HazelJS CAG Package
@hazeljs/cag provides Cache-Augmented Generation — prepare, fingerprint, cache, and reuse stable context without embedding + search on every request.
Prepare once. Reuse on every request.
CAG is not a wrapper around RAG. Optional @hazeljs/rag loaders can supply file/URL sources; vector search stays in @hazeljs/rag. Store concepts reuse @hazeljs/cache via CagStore.
Use CAG for policies, catalogs, schemas, and tenant config that barely change. Use RAG when relevance depends on the query. Combine both (plus KAG / Memory) with @hazeljs/context.
Quick Reference
- Purpose: Preload and reuse relatively static context under a token budget with multi-tenant cache keys.
- When to use: Multi-merchant support bots, always-on compliance text, OpenAPI / tool schemas stuck in every turn, weekly catalogs keyed by
version. - Key APIs:
CagModule.forRoot/forRootAsync,CagService,register/registerClass,getContext,invalidate,getMetrics,@ContextSource,MemoryCagStore, Redis-shaped store viaredis.client,PromptCacheProvider. - Dependencies: Peer
@hazeljs/core,@hazeljs/cache. Optional:@hazeljs/ragloaders,@hazeljs/aipromptCachefield. - Common mistakes: Using CAG instead of RAG for dynamic handbook lookup; omitting
tenantIdwhenmultiTenantis on; compressing compliance text (priority >= 90/compressible: false); forgetting version bumps when policy content changes.
CAG vs RAG
| CAG | RAG | |
|---|---|---|
| Question | What context can I preload/reuse? | What information should I retrieve? |
| Typical data | Policies, catalogs, schemas, tenant config | Documents matched to a query |
| Hot path | Cache hit → assemble → LLM | Embed → search → passages → LLM |
| Failure mode | Stale cache if invalidation is wrong | Wrong or missing passage |
Full comparison (including KAG): RAG vs CAG vs KAG.
Installation
npm install @hazeljs/cag @hazeljs/core @hazeljs/cache
Quick Start
import { CagModule, CagService } from '@hazeljs/cag';
CagModule.forRoot({
store: 'memory',
ttl: '1h',
maxTokens: 50_000,
compression: true,
invalidation: { strategy: 'version' },
multiTenant: true,
});
// somewhere injectable:
cag.register(
{
id: 'return-policy',
priority: 100,
compressible: false,
version: '2026-10',
source: { type: 'text', content: 'Returns within 30 days…' },
},
'support'
);
const { context, hit } = await cag.getContext({
namespace: 'support',
tenantId: 'merchant-123',
});
// context.text → assembled policy blob
// hit → true on warm cache
Per-tenant function sources
Registration is shared by namespace; vary content by tenantId in a function source:
cag.register(
{
id: 'return-policy',
priority: 100,
compressible: false,
version: '2026-10',
source: {
type: 'function',
load: async ({ tenantId }) => loadMerchantPolicy(tenantId),
},
},
'support'
);
Context Sources
| Type | Use |
|---|---|
text / json | Inline stable strings / objects |
file / url | Prefer RAG loaders when installed; fs/fetch fallback |
function / custom / memory | Dynamic load with ContextLoadInput (includes tenantId) |
rag | Optional retrieve port for hybrid prep |
Declarative:
@ContextSource({ namespace: 'merchant-policy', ttl: '6h', priority: 100 })
export class MerchantPolicyContext {
async load(input: ContextLoadInput) {
return loadMerchantPolicies(input.tenantId);
}
}
Caching & Invalidation
- Memory store by default; Redis via injected
redis.client(ioredis-compatible) - Keys include
tenantId, namespace, version, and fingerprint - Invalidate with TTL, bumping
versionon sources, orcag.invalidate({ tenantId, namespace })
Token Budgets & Compression
tokenBudget: { max: 50_000, strategy: 'priority' }
// truncate | priority | compress | summarize | reject
Sources with compressible: false or priority >= 90 are never rewritten by compression.
Multi-Tenancy
multiTenant defaults to true. Cache keys always include tenantId. Tenant A cannot read Tenant B context.
LLM Provider Caching
PromptCacheProvider.prepare() returns opaque hints for OpenAI / Anthropic / Gemini. Pass them on @hazeljs/ai AICompletionRequest.promptCache. CAG does not call vendor SDKs.
Observability
- Events:
cag.context.hit|miss|build|invalidate|compress - Metrics:
cag.getMetrics()(hits, misses, hitRatio, tokensSaved, …)
Agent Integration
Inject CagService as AgentRuntimeConfig.cagService. Optional DNA:
{
"context": {
"cag": { "enabled": true, "namespace": "merchant" }
}
}
Prefer @hazeljs/context when the agent needs CAG + RAG + KAG + Memory in one plan.
Production Guide
- Prefer Redis with a real client in production
- Keep
multiTenant: true; add isolation tests - Mark compliance sources
compressible: falseand high priority - Set
strict: trueto fail closed on store errors - Monitor hit ratio via
getMetrics()
Try it offline
No API keys — Meridian Ops labs:
# from hazeljs-meridian-ops/
npm run example:install
npm run example:cag
cag-support-agent demonstrates cold miss → warm hit, tenant isolation, and version invalidation.
Related Resources
- RAG Package — query-time retrieval
- KAG Package — structured knowledge
- Context Package — unified planner
- Cache Package — underlying cache concepts
- Agent Package —
cagServiceruntime port - RAG vs CAG vs KAG — when to pick which layer
- Package README — npm-facing overview
License
Apache-2.0