A three-tier fraud detection system. Cheap models clear the obvious cases; expensive models earn their keep on the ambiguous ones. Truly uncertain transactions get five specialized AI judges, who deliberate in turn and issue a reasoned verdict — then place a voice call and auto-draft the dispute paperwork.
Transaction
↓
┌──────────────────────────┐
│ Tier 1: XGBoost (CPU) │ ~few ms
│ Kaggle 284K txns │ Auto-clears the easy cases
│ ROC-AUC ~0.98 │
└─────────┬────────────────┘
↓ uncertain (0.30 < score < 0.95)
┌──────────────────────────┐
│ Tier 2: Sequence │ ~tens of ms
│ Transformer (GPU) │ Catches behavioural anomalies
│ 6.5M params · AP ~0.83 │ across the last N transactions
└─────────┬────────────────┘
↓ still uncertain (0.20 < score < 0.85)
┌──────────────────────────┐
│ Tier 3: 5 AI Judges │ mock: ~instant
│ (Pattern · Geography · │ real: ~5–10s with Qwen-72B
│ Temporal · Merchant · │
│ Chief) │
└─────────┬────────────────┘
↓
BLOCK + per-judge ElevenLabs voice alert + auto-dispute letter
backend/ FastAPI + the three tiers
frontend/ Next.js 15 landing pages + demo console
sampleui/ Source HTML/CSS the landing pages were ported from
cd backend
pip install -e .
# GPU box (Tier 2 training + optional Tier 3 real judges):
pip install -e ".[gpu]"Grab the Kaggle creditcardfraud dataset and drop it at:
backend/data/raw/creditcard.csv (~144 MB). Either drag-upload, or on the pod with kaggle CLI:
kaggle datasets download -d mlg-ulb/creditcardfraud -p data/raw --unzippython -m ml.train # Tier 1 XGBoost (CPU, ~30 s)
python -m ml.train_transformer # Tier 2 Transformer (GPU, ~5–15 min on MI300X)Mock mode (demo-safe, no GPU required for Tier 3):
SKIP_LLM=1 uvicorn main:app --host 0.0.0.0 --port 8000Real Qwen-72B judges (requires a 192 GB GPU and the weights cached):
export HF_HOME=/workspace/.cache/huggingface # writable cache
export ELEVENLABS_API_KEY=... # voice on every turn
export VOICE_CHIEF=... VOICE_PATTERN=... VOICE_GEOGRAPHY=...
export VOICE_TEMPORAL=... VOICE_MERCHANT=...
uvicorn main:app --host 0.0.0.0 --port 8000 # ~3 min cold loadIf the real-LLM path errors mid-request (OOM, timeout, anything), the judge route silently falls back to the scripted mock dialogue so /analyze never 500s.
GET /health— model load stateGET /dataset/random?fraud=true|false— returns a Kaggle row + synthesized merchant/location + prior user-history for Tier 2POST /analyze— full pipeline: ML scores + judge dialogue + voice audio + dispute letterPOST /ask-judge— follow-up question to a specific judge; returns voice audio
cloudflared tunnel --url http://localhost:8000
# or: ssh -R 80:localhost:8000 serveo.netThen in frontend/.env.local:
BACKEND_URL=https://<your-tunnel>.trycloudflare.com
cd frontend
npm install
npm run dev # http://localhost:3000Routes:
/— landing hero/mission— mission statement/technology— the 5-judge bento grid/demo— the real demo console (transaction picker → tier pipeline → court → ask-a-judge)
- Tier 2 on the Kaggle dataset is trained by clustering transactions into 2,000 pseudo-users (Kaggle has no user IDs) and feeding each new transaction as the last position of a length-50 sequence.
- Tier 3 mock dialogue uses
ml_scoreto pick from fraud / legit fragment pools; confidence is derived from the ML tier scores, not hardcoded. /dataset/random?fraud=truepre-filters to rows XGBoost catches confidently, so the demo never gets a fraud sample the model silently approves.