← Back to the portfolio
Case study · Resilience architecture
Designing for AI Failure
RAG and BullMQ fallback architecture
Every AI feature fails eventually: the provider times out,
rate-limits you, or just has a bad day. Most teams answer with a
try/catch and an apology message. I built a three-tier fallback
ladder instead — retry inside the queue,
RAG fallback over the tenant’s own data, then a
rule-based floor — so an AI outage never reaches
the user as an error. Five AI-backed capabilities sit behind one
Unified AI Service and inherit the same reliability pattern: one
contract, one queue, one fallback ladder.
05
Implementation
UnifiedAIService.ts · TypeScript / NestJS
export type LlmTier = 'retry' | 'rag' | 'floor';
export type Confidence = 'high' | 'medium' | 'low';
export type LlmResponse<T> =
| { tier: 'retry'; degraded: false; confidence: 'high'; content: T;
providerMeta: ProviderMeta; invariants: InvariantReport }
| {
tier: 'rag' | 'floor';
degraded: true;
confidence: 'medium' | 'low';
readonly _content: T;
providerMeta: ProviderMeta;
invariants: InvariantReport;
acceptDegraded(): T;
};
/**
* Degraded responses do not expose `.content` at all. The payload is only
* returned after `acceptDegraded()` runs the invariant check and audit log.
*/
export function acceptDegraded<T>(res: LlmResponse<T>): T {
if (res.degraded) {
if (!res.invariants.passed) {
throw new DegradedInvariantViolation(res.invariants.violated);
}
logger.warn('degraded_accept', {
tier: res.tier,
confidence: res.confidence,
violations: res.invariants.violated,
});
return res._content;
}
return res.content;
}
// Gateway side — invariant validation runs AFTER a Tier-1 success.
const raw = await this.dispatchPrimaryLLM(req, tenantContext);
const invariants = this.invariantEngine.check(raw, req.domainRules);
if (!invariants.passed) {
// Tier 1 "succeeded" but violated a domain rule. Downgrade.
return this.ruleEngine.extractHeuristicData(req.rawDocument, 'floor', invariants);
}
return { tier: 'retry', degraded: false, confidence: 'high', content: raw, providerMeta, invariants };