Skip to content
01 / ENGINEERING CASE STUDYMulti-Tenant · Self-Healing · Platform

CHATBOT

One codebase, any number of clients across 4 chat platforms + embedded widget — with RAG, escalation, and a self-healing knowledge pipeline.

BUILT BY DAVID RAJNOHA  ·  JAN–APR 2026
Scroll
02
Project Index
Role
Solo Builder & Platform Architect
Industry
Customer Support SaaS
Year
2026
Duration
<45 active days12 weeks · 257 commits
0
Chat Adapters
0
AI Assistant Tools
0
Tests Passing
0×
Languages
03
The Problem
Context
Customer support automation
Surface area
4 platforms · 9 intents · 5 tools
Cost of alternatives
Per-client subscription + infra

Restaurants needed chat support automation across messaging channels. Off-the-shelf tools had three blockers — every new client meant a new install, a new subscription, and a new knowledge base to babysit.

Predefined FAQs or raw GPT hallucinations. Binary handoff — bot answers everything or escalates instantly. What was missing was a platform that learned from its own failures.

“A chatbot that forgets its mistakes is a template. One that learns from them is a platform.”
The Challenge

A platform has to behave like infrastructure, not a chatbot.

New client = 1 DB record. New chat platform = 1 adapter. New use case = 1 tool class. Zero duplication, zero code changes per tenant.

Single-tenant SaaS collapses under 10 clients. Template-based FAQs can't handle 6 languages and 9 intents. Binary handoff destroys trust. And everything has to be multi-tenant-safe — one leaked RLS policy = compliance breach across every customer.

The Solution

Three architectural patterns hold the system together — Adapter, LEGO Orchestrator, Self-Healing.

Platforms are isolated in adapters. Tools route through a priority-based orchestrator keyed on detected intent. Every low-confidence turn feeds a closed feedback loop that proposes KB edits — gated by human-in-the-loop approval.

Result: adding a new platform (Smartsupp, Discord) is one adapter file. Adding a capability (Ordermage API, news feed) is one tool class + a feature flag in clients.features JSONB. The bot that answered yesterday's vague question gets a proposed KB entry tomorrow morning.

04
System Architecture

Three patterns, one closed feedback loop.

FIG. 01 — PLATFORM TOPOLOGY
Platform & Tool Graph
message → adapter → orchestrator → tools → response · fan-out: 5 + 42
Multi-tenant chatbot platform architecture diagramA message enters via one of five platform adapters (HelpCrunch, Telegram, WhatsApp, Chatwoot, Widget), flows to the LEGO Orchestrator which routes to the highest-priority backend tool (of five) based on detected intent. Low-confidence outputs feed the Self-Healing pipeline that proposes KB edits for human approval.CustomerMESSAGE5 ADAPTERS · PlatformAdapter interfaceHelpCrunch302 LOCTelegram372 LOCWhatsApp366 LOCChatwoot320 LOCWidget163 LOChandler-orchestrator.tsIntent → Router → ComposerINTENTROUTERCOMPOSER1,073 LOC · 9 intent types · LRU cache 5 min5 BACKEND TOOLS · priority-sortedAdminBotp=300admin_commandsHandoffBotp=200handoffOrdermageBotp=150ordermageNewsFeedBotp=100news_feedRAGBotp=50rag_v2Responsevia SAME adapterSELF-HEALING · closed feedback loopLOW CONFIDENCEgap-detector < 0.5POSTGRES TABLEknowledge_gapsGPT-4O-MINIsuggestion-generatorDASHBOARD · HUMANapprove → KBDASHBOARDAI Assistant · 42 tools23 read · 18 write · 1 destructiveOpenAI Model Registry · 10 models · 11 slotsgpt-4o-mini (chat) · gpt-4.1-nano (translation) · text-embedding-3-small (RAG)webhookconfidence<0.5pick highest-priority
L1 · Adapter Pattern

Platform adapters

5 adapters — 4 external chat platforms (HelpCrunch, Telegram, WhatsApp, Chatwoot) plus an embedded web widget. Each implements the PlatformAdapter interface in 150–400 LOC; Edge Function wrappers stay thin (40–72 LOC for chat platforms; the widget carries session + auth at ~240) — all meaningful logic lives in the shared handler factory.

L2 · LEGO Orchestrator

Priority-based tool routing

5 backend tools (AdminBot, Handoff, Ordermage, News, RAG) — ranging 89–635 LOC depending on domain depth, each declared with capability + required feature. Tool router picks highest-priority match. Turn on a feature flag → behavior changes, no code change per tenant.

L3 · Self-Healing Pipeline

KB learns from its own failures

Low confidence (<0.5) → gap-detector writes knowledge_gaps → suggestion-generator calls GPT → dashboard admin approves → pattern-analyzer updates intent patterns → weekly-digest emits a health score. Human-in-the-loop by default.

/supabase/functions/_shared/handler-orchestrator.ts
// One-line Edge Function per platformexport const handler = createHandler({  platform: "chatwoot",  adapter: chatwootAdapter,  verify: verifyHmacSha256WithTimestamp,});// 1,073-line orchestrator behind a thin platform wrapperexport async function createHandler<TEvent>(config: PlatformConfig<TEvent>) {  /* intent → tool router → response composer */}
/dashboard/src/app/api/assistant/chat/route.ts
// AI Assistant: multi-step agent over 42 toolsconst result = streamText({  model: openai(resolveModel("assistant")),  tools: scopedTools,  stopWhen: stepCountIs(3),  onStepFinish: (s) => logToolCall(s),  messages: [smartContext, ...turn.messages],});
05
Stack
PLATFORM · 01
Supabase
+ Edge Functions
Postgres + pgvector, 89 migrations, 87 RPC functions, 121 indexes. RLS per client_id. Deno runtime for Edge webhook handlers. One database, zero other services.
APP · 02
Next.js 16 + TypeScript
App Router, Server Actions, dashboard with AI Assistant. Strict TS, thin edge layer.
MODELS · 03
OpenAI Model Registry
10 models across 11 slots: gpt-4o-mini (chat), gpt-4.1-nano (translation/news), dall-e-3 (image), text-embedding-3-small (RAG). Per-client override via DB.
SDK · 04
Vercel AI SDK v6
Streaming, tool calls, stopWhen stepCountIs(3).
UI · 05
Tailwind 4 + tokens
COLOR_CONCEPTS — static metric vs dynamic threshold.
QA · 06
Promptfoo eval suite
40 cases · 40/40 PASS vs live.
06
The 12-Week Build

Under 45 active days, 257 commits, foundation to post-incident evals.

Honest framing — not a 5-day sprint, not a full-time three-month run either. Part-time development from 23 Jan → 15 Apr 2026 — under 45 active dev days across 12 weeks. March alone held 173 of the 257 commits — the core sprint phase.

01
JAN · FOUNDATION
Base schema + first HelpCrunch handler
17 commitsbase schema1 platform

Set up Supabase, pgvector, the first adapter interface, and the initial HelpCrunch handler. Drafted the multi-tenant contract on paper: new client = 1 DB record, not a deploy.

02
FEB · V2 MIGRATION
Intent patterns, OrdermageBot, Analytics V2, i18n
41 commitsOrdermage live6 languages

V2 migrations, Dashboard wiring, per-client intent patterns (9 types, LRU-cached), OrdermageBot (two-step HMAC-SHA256 flow), Analytics V2 with cost + confidence tracking, Design System, 6-language detection (CS/EN/DE/SK/PL/HU).

03
MAR · CORE SPRINT
API Layer, AI Assistant, WhatsApp, Chatwoot, Self-Healing
173 commits42 AI tools4 external platforms live

API Layer (68 routes, 3-step auth pipeline) · MCP Server (auto-generated tools from OpenAPI) · AI Assistant (42 tools, 3-tier approval, SSE streaming) · Production Hardening · Notification Center · Tenant isolation · WhatsApp + Chatwoot adapters · Self-Healing 1–3 · Cost Protection.

04
APR · HARDENING
Multilingual patterns, security fix, LLM eval suite
26 commitseval suitezero rollbacks

Multilingual hardening (DE/PL/SK/HU patterns). security_invoker=true views migration fixing an RLS bypass. Semantic handoff detector. Amici 'podsednik' incident (KB hallucination) → Promptfoo LLM eval suite (40 cases, 40/40 PASS against live).

07
Live Demo

Watch the AI Assistant triage a morning dashboard.

Dashboard AI Assistant — multi-step KB gap triage demoTerminal UI mockup showing an admin asking the AI Assistant to surface unresolved knowledge-base gaps for Amici. Two auto-executed read tools (get_kb_gaps, get_conversations) and one write tool (create_kb_suggestion) with an amber approval banner demonstrate the 3-tier approval system and stopWhen stepCountIs(3) multi-step agent loop.dashboard · ai assistant · amici● CONNECTEDCLIENTSAmicihelpcrunch · liveObjednamechatwoot · liveai_news_cz_bottelegram · channelmaster-barberwidget · demoTOOL TIERS 23 READ auto 18 WRITE approve 1 DESTRUCT. confirmKB HEALTHentries 9,714 · gaps 3confidence 0.72ADMIN · 09:14Show me unresolved KB gaps for Amici anddraft a suggestion for the top one.AI ASSISTANT · 09:14 · gpt-4o-miniget_kb_gapsREADclientId="amici"OK · 3 rows · 47msget_conversationsREADintent="menu_query" confidence<0.5OK · 12 rows · 61mscreate_kb_suggestionWRITEgap_id="gluten-free-menu"⚠ requires approval — [approve] [edit] [reject]GAP ANALYSIS3 KB gaps · all from Amici conversations with confidence < 0.5.Top gap: 5 turns asking about gluten-free menu options (last 7 days).DRAFT · KB ENTRY (pending approval)Title: "Gluten-free menu options"Body: "Amici offers gluten-free pizza (GF dough +30 Kč), salads, andseveral antipasti. The kitchen uses a dedicated prep area to avoid…"APPROVE ⏎EDITREJECT↳ stopWhen: stepCountIs(3) · SSE stream · logged to notificationsAsk the AI Assistant…⌘ + ⏎
↳ Dashboard AI Assistant preview · read/write/destructive tool tiers · SSE streaming.
08
Results & Learnings

Results

  • R/01Two production clients onboarded in parallel — Amici food delivery (HelpCrunch) and Objedname (Chatwoot). Adding client #2 cost 1 DB record + 0 LOC — the multi-tenant architecture delivered on its promise.
  • R/0242 AI Assistant tools with 3-tier approval (23 read auto-execute, 18 write amber banner, 1 destructive red + confirm) and role scoping per user type.
  • R/035,400+ tests + 40/40 LLM eval cases PASS against live production — Vitest unit + integration + E2E + Promptfoo LLM eval suite (4 platform datasets + 1 Amici regression).
  • R/0489 migrations · 12 CI workflows · zero rollbacks across 257 commits. Multi-tenant by construction — 87 RPC functions, 11 views (security_invoker=true), 121 indexes.

Learnings

  • L/01
    Rule #1 has more leverage than any framework.

    “Before shipping: how does this repeat for the next client?” filtered every PR across 257 commits. Sprint 15 review caught 7 CRITICAL bugs — IDOR, missing auth, wrong RPC — before they touched prod.

  • L/02
    security_invoker = true is not optional.

    Postgres default `security_invoker = false` means views run as table owner — bypassing RLS. Migration 20260310000002 flipped 8 views. In a multi-tenant setup, always explicit.

  • L/03
    Hallucinations forced eval-driven dev.

    The Amici 'podsednik' incident (2026-04-12): scraper wrote nonsense into KB, bot repeated it. Response: Promptfoo suite with platform and regression layers. Every future bug becomes one YAML case.

  • L/04
    Split god modules on schedule, not when they burn.

    handler-orchestrator.ts hit 1,146 LOC before Sprint 23 extracted analytics-merger, auto-handoff, ab-manager — down to 932 LOC. Subsequent features pushed it back to 1,073, but with clean responsibilities. Refactor cadence > refactor heroics.