AI-Solutions-KK/grabon-ai-merchant-underwriting-agent
๐ค GrabOn AI Merchant Underwriting Agent
Production-deployed AI underwriting system โ risk scoring, Claude AI decisions, and real WhatsApp offer delivery. Live at โ [huggingface.co/spaces/AI-Solutions-KK/grabon-ai-merchant-underwriting-agent](https://huggingface.co/spaces/AI-Solutions-KK/grabon-ai-merchant-underwriting-agent)
๐ฒ Live WhatsApp Test โ 3 Steps
Evaluators: You can receive a real WhatsApp underwriting offer message in under 60 seconds.
๐๏ธ System Architecture
flowchart TD
subgraph CLIENT["๐ค Client Layer"]
A[Admin / Evaluator Browser]
WA[๐ฑ WhatsApp User]
end
subgraph API["๐ API Layer โ FastAPI"]
R[routes.py\nREST API]
D[dashboard.py\nAdmin Dashboard]
ADM[admin.py\nEngine Controls]
end
subgraph ORCH["๐ฏ Orchestration Layer"]
ORC[Orchestrator\norchestrator.py]
end
subgraph ENGINES["โ๏ธ Engine Layer"]
RE[Risk Engine\nDeterministic Scoring\n13 Business Rules]
DE[Decision Engine\nApprove / Reject /\nConditional]
OE[Offer Engine\nCredit + Insurance\nOffer Builder]
UA[Claude AI Agent\nLLM Explanation +\nRisk Narrative]
end
subgraph SERVICES["๐ง Service Layer"]
MS[Merchant Service]
ES[Engine Service\nBatch Processor]
MON[Monitor Service\nBackground Thread\nMD5 Change Detection]
WAS[WhatsApp Service\nTwilio + retry logic]
CS[Config Service\nEngine State + Cache]
APP[Application Service\nRisk Score Persistence]
AGT[Underwriting Agent\nClaude API Wrapper]
end
subgraph DB["๐๏ธ Data Layer โ SQLite"]
M[(merchants)]
RS[(risk_scores)]
SC[(system_config\nEngine state +\nFingerprints)]
end
subgraph INFRA["โ๏ธ Infrastructure"]
TW[Twilio\nWhatsApp Sandbox]
ANT[Anthropic\nClaude 3.5 Sonnet]
HF[Hugging Face Spaces\nDocker Container]
end
A -->|HTTP| D
A -->|HTTP| ADM
WA -->|receives offer| TW
D --> ORC
ADM --> MON
ADM --> ES
ORC --> RE
ORC --> DE
ORC --> OE
ORC --> UA
RE --> ES
DE --> ES
OE --> ES
UA --> AGT
ES --> MON
MON --> WAS
MON --> CS
MON --> APP
WAS --> TW
AGT --> ANT
MS --> M
APP --> RS
CS --> SC
ES --> RS
HF --> API๐ Overview
This system automates merchant underwriting for GrabCredit and GrabInsure products. It takes raw merchant data, runs it through a deterministic risk scoring pipeline, gets an AI-generated explanation from Claude, makes a final credit/insurance offer decision, and delivers the offer directly to the merchant via WhatsApp โ all from an admin dashboard with real-time engine controls.
What makes this production-grade (not a demo):
- โ Real WhatsApp delivery via Twilio sandbox โ not mocked
- โ Real Claude AI calls โ not canned responses
- โ Persistent SQLite DB โ state survives restarts
- โ MD5 fingerprint change detection โ re-processes only changed merchants
- โ
Background daemon engine with 60s polling (
ALWAYS_ONmode) - โ Docker-containerized and deployed on Hugging Face Spaces
- โ Rate limit detection with human-readable error reporting
- โ Idempotent runs โ won't spam merchants already messaged
โ Latest Update โ Deterministic Profile-Derived Credit Scoring
The underwriting pipeline now computes credit_score from merchant profile data in Orchestrator Step 0 using a deterministic weighted CreditEngine.
Updated flow
Merchant Profile (API / Seed / SQL)
โ
โผ
[1] CreditEngine (weighted deterministic model)
โ credit_score: 300โ850
โ
โผ
[2] RiskEngine (hard rules + weighted scoring)
โ risk_score: 0โ100
โ
โผ
[3] DecisionEngine (single authority)
โ APPROVED / APPROVED_WITH_CONDITIONS / REJECTED
โ
โผ
[4] OfferEngine
โ
โผ
[5] Persist + Optional WhatsAppFormula (deterministic)
Let factor scores be in $[0,100]$:
- $P$ = payment-history proxy
- $A$ = amounts-owed proxy
- $L$ = length/history proxy
- $M$ = credit-mix proxy
- $N$ = new-credit proxy
$$ W = 0.35P + 0.30A + 0.15L + 0.10M + 0.10N $$
$$ credit\_score = clamp(300 + 5.5W,\ 300,\ 850) $$
Implementation notes:
- Input
credit_scoreremains backward-compatible but is recomputed in orchestration. - Seed/monitor/batch paths rebuild
MerchantInputwithcredit_score=None. - Fingerprint logic excludes derived
credit_scorefrom trigger comparison.
๐ง High-Level Flow
๐ System Architecture
1๏ธโฃ External Layer
- WhatsApp User
๐ Decision Flow
sequenceDiagram
participant Admin
participant Dashboard
participant Orchestrator
participant RiskEngine
participant DecisionEngine
participant OfferEngine
participant ClaudeAI
participant MonitorService
participant WhatsAppService
participant Merchant
Admin->>Dashboard: Click "Run Once"
Dashboard->>MonitorService: POST /admin/engine/on
MonitorService->>Orchestrator: process_merchant(id)
Orchestrator->>RiskEngine: score(merchant_data)
RiskEngine-->>Orchestrator: RiskProfile {score, flags, tier}
Orchestrator->>ClaudeAI: explain(risk_profile)
ClaudeAI-->>Orchestrator: AI narrative
Orchestrator->>DecisionEngine: decide(risk_profile + ai_rec)
DecisionEngine-->>Orchestrator: APPROVED / REJECTED / CONDITIONAL
Orchestrator->>OfferEngine: build_offer(decision)
OfferEngine-->>Orchestrator: CreditOffer + InsuranceOffer
Orchestrator-->>MonitorService: UnderwritingDecision
MonitorService->>WhatsAppService: send_offer(merchant_number)
WhatsAppService-->>Merchant: ๐ฑ WhatsApp message delivered
MonitorService-->>Dashboard: stats {processed, approved, wa_sent, ...}
Dashboard-->>Admin: Engine summary banner + per-merchant breakdownโ๏ธ Engine Modes
The underwriting engine has 3 operating modes controlled from the dashboard:
Change Detection Logic
Each merchant is fingerprinted across 13 fields (revenue, GMV, chargeback rate, mobile number, etc.) using MD5. Stored in system_config as fp_{merchant_id}. A run only triggers Orchestrator + WhatsApp for merchants whose fingerprint differs from last run.
๐ฏ Decision Authority Model
flowchart LR
RE[Risk Engine\n13 deterministic rules\nOutputs: score 0-100 + flags]
AI[Claude AI Agent\nOutputs: recommendation + narrative]
DE{Decision Engine\nSingle Authority}
OUT_A[โ
APPROVED\nCredit + Insurance Offer]
OUT_R[โ REJECTED\nReason + Guidance]
OUT_C[โก CONDITIONAL\nApproved with conditions]
RE --> DE
AI --> DE
DE --> OUT_A
DE --> OUT_R
DE --> OUT_COnly the Decision Engine produces final outcomes. Risk Engine and Claude AI are inputs only โ this eliminates distributed decision ambiguity and ensures full auditability.
๐ Risk Scoring โ 13 Business Rules
Final score 0โ100 โ maps to: LOW / MEDIUM / HIGH / CRITICAL risk tier.
๐ Unique Features
๐ Problems Faced & How They Were Solved
๐ Project Structure (Active Files)
grabon-assignment/
โโโ Dockerfile # HF Spaces Docker deploy
โโโ requirements.txt # Pinned production deps
โโโ alembic.ini # DB migrations config
โโโ .env # API keys (not committed)
โโโ .env.example # Template for env setup
โ
โโโ app/
โ โโโ main.py # FastAPI app + lifespan + routers
โ โ
โ โโโ api/
โ โ โโโ routes.py # REST underwriting endpoints
โ โ โโโ dashboard.py # Admin dashboard + inline edit
โ โ โโโ admin.py # Engine control endpoints
โ โ
โ โโโ engines/
โ โ โโโ risk_engine.py # 13-rule deterministic scorer
โ โ โโโ decision_engine.py # Final decision authority
โ โ โโโ offer_engine.py # Credit + insurance offer builder
โ โ
โ โโโ orchestrator/
โ โ โโโ orchestrator.py # Pipeline coordinator
โ โ
โ โโโ services/
โ โ โโโ monitor_service.py # 3-state engine + MD5 change detection
โ โ โโโ engine_service.py # Batch merchant processor
โ โ โโโ merchant_service.py # Merchant CRUD
โ โ โโโ application_service.py # Risk score persistence
โ โ โโโ config_service.py # system_config key-value store
โ โ โโโ whatsapp_service.py # Twilio WA with retry + rate limit guard
โ โ โโโ underwriting_agent.py # Claude AI wrapper
โ โ
โ โโโ models/
โ โ โโโ merchant.py # Merchant SQLAlchemy model
โ โ โโโ risk_score.py # Risk score + WA status
โ โ โโโ system_config.py # Engine state + fingerprints
โ โ
โ โโโ schemas/
โ โ โโโ merchant_schema.py # Merchant Pydantic schema
โ โ โโโ decision_schema.py # Decision + offer schemas
โ โ
โ โโโ db/
โ โ โโโ base.py # SQLAlchemy declarative base
โ โ โโโ session.py # Engine + SessionLocal
โ โ โโโ init_db.py # Table creation + seeding
โ โ
โ โโโ scripts/
โ โ โโโ seed_merchants.py # Seeds 10 test merchants
โ โ
โ โโโ templates/
โ โโโ merchant_list.html # Main dashboard (segmented engine UI)
โ โโโ merchant_detail.html # Individual merchant detail view
โ
โโโ tests/
โโโ test_decision_engine.py
โโโ test_risk_engine.py๐ฆ Tech Stack
๐ง Local Setup
# 1. Clone
git clone https://huggingface.co/spaces/AI-Solutions-KK/grabon-ai-merchant-underwriting-agent
cd grabon-ai-merchant-underwriting-agent
# 2. Virtual environment
python -m venv .venv
.venv\Scripts\activate # Windows
# source .venv/bin/activate # Linux/Mac
# 3. Install dependencies
pip install -r requirements.txt
# 4. Configure environment
cp .env.example .env
# Edit .env with your API keys (see below)
# 5. Run (no --reload โ required for background threads)
python -m uvicorn app.main:app --port 8000
# 6. Open dashboard
# http://localhost:8000/dashboardRequired .env Keys
# Database
DATABASE_URL=sqlite:///./underwriting.db
# Claude AI
ANTHROPIC_API_KEY=sk-ant-...
CLAUDE_MODEL=claude-3-5-sonnet-20241022
# Twilio WhatsApp
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_WHATSAPP_NUMBER=whatsapp:+14155238886
# App
SECRET_KEY=your-secret-key-here
APP_ENV=production๐ณ Docker
# Build
docker build -t grabon-underwriting .
# Run (maps HF port 7860 โ local 8000)
docker run -p 8000:7860 \
-e ANTHROPIC_API_KEY=sk-ant-... \
-e TWILIO_ACCOUNT_SID=ACxxx \
-e TWILIO_AUTH_TOKEN=xxx \
-e TWILIO_WHATSAPP_NUMBER=whatsapp:+14155238886 \
grabon-underwriting๐ Production Considerations
- No `--reload` in production โ kills background daemon threads
- SQLite persistence โ
underwriting.dbpersists across container restarts on HF Spaces - Idempotent engine runs โ MD5 fingerprints prevent duplicate WA messages
- Rate limit short-circuit โ 63038 triggers
_rate_limitedflag, skips remaining Twilio calls - REJECTED guard โ approved-only WhatsApp dispatch, no false notifications
- Claude retry policy โ exponential backoff with timeout on LLM calls
- Structured DB state โ all engine decisions persisted in
risk_scorestable - Webhook idempotency โ signature validation, deduplication guard
๐ Development Phases
๐ License
Apache 2.0
Built for GrabOn AI Engineering Assignment โ Production deployment on Hugging Face Spaces with live WhatsApp integration.
