Prescriptive churn analytics for telecom-style retention work.
Aegis is not another "predict churn and stop there" demo. It is a working Python + Next.js system for turning a churn score into an operational retention decision:
- who is likely to churn,
- why the model thinks so,
- what intervention could plausibly change the outcome,
- whether the intervention is worth its cost,
- what evidence was used for uplift/CATE claims.
The current runtime uses the IBM/Kaggle Telco Customer Churn dataset as a small smoke-test dataset. That dataset is useful for local development and UI demos, but it does not contain real treatment/control assignment. Aegis therefore treats its built-in uplift flow as simulation evidence unless a real randomized or audited observational treatment column is provided.
- Litestar API with prediction, explanation, reporting, uplift, metrics, health, and sandbox routes.
- XGBoost/LightGBM/CatBoost training path with calibrated churn probabilities.
- Polars preprocessing with persisted feature state for train/serve consistency.
- SHAP and DiCE-style explanation paths.
- Expected-value retention decisions using churn probability, CLTV, action cost, margin, and WACC.
- T-learner uplift engine with feature-contract checks and explicit treatment-evidence metadata.
- Redis/Postgres/DuckDB integration paths with graceful local fallbacks.
- JWT auth with RBAC scopes, request-scoped tenant context, rate limiting in staging/production.
- File-backed artifact and policy registries for pilot-grade replay evidence.
- Privacy-safe JSONL audit events with redaction for sensitive fields.
- Real treatment/control validation guardrails for causal datasets.
- Next.js dashboard for operations, SHAP, DiCE, uplift, policy, drift, governance, and customer drill-down.
- Python test suite and frontend lint/build validation.
Aegis does not claim that IBM Telco proves causal uplift. The bundled treatment generator is a documented simulation so the product flow can be exercised locally.
For causal-grade CATE, use a dataset with real treatment assignment, such as a randomized campaign dataset. The dataset registry already marks stronger migration targets:
- Criteo Uplift Modeling Dataset
- Hillstrom Email Analytics Challenge
- KKBox churn for large temporal churn work
- KDD Cup 2009 Orange for wide-table telecom CRM modeling
Production-facing notes:
flowchart LR
A[Customer data] --> B[Polars preprocessor]
B --> C[Calibrated churn model]
C --> D[Risk score]
D --> E[SHAP / DiCE explanation]
D --> F[Expected value decision]
G[Treatment evidence] --> H[T-learner uplift]
H --> F
F --> I[Retention recommendation]
I --> J[Dashboard / API response]
The important boundary is between prediction and prescription. Churn probability is not enough. Aegis only recommends an intervention when the expected value clears the configured decision threshold.
python -m venv .venv
.venv\Scripts\activate
python -m pip install --upgrade pip
pip install -e ".[dev]"Prepare data and model artifacts:
python run.py setup
python run.py train --trials 5Start the API:
python run.py apiStart the frontend:
cd frontend
npm ci
npm run devDefault local URLs:
- API docs:
http://localhost:8000/docs - Dashboard:
http://localhost:3000 - Metrics:
http://localhost:8000/api/metrics
Backend:
ruff check .
python -m pytest -q
python -m pytest tests/test_suite.py::TestUpliftEngine tests/test_suite.py::TestUpliftEvaluator -qFrontend:
cd frontend
npm run lint
npm run buildDocker:
docker compose up --buildThe uplift engine trains two outcome models:
CATE = P(churn | treatment) - P(churn | control)
Negative CATE means the treatment is estimated to reduce churn.
The engine now enforces:
- no target, treatment, outcome, or customer-id leakage in model features,
- minimum row count per treatment arm,
- minimum positive and negative examples per arm,
- stored training feature contract,
- prediction-time feature alignment,
- treatment evidence metadata in API responses.
Example response metadata:
{
"evidence": {
"treatment_source": "simulation",
"is_causal_grade": false,
"rows": 7043,
"control_rows": 3180,
"treatment_rows": 3863,
"feature_count": 24,
"warning": "Treatment labels are simulated; CATE values are useful for demo flow only, not causal evidence."
}
}Simulation CATE is not used as production causal proof. In the prediction route, simulated uplift is skipped for decision success-rate estimation and the system falls back to segment-level assumptions.
Core routes:
POST /api/predictGET /api/customer/idsGET /api/customer/{customer_id}GET /api/customer/cohortPOST /api/explain/localPOST /api/explain/counterfactualGET /api/v1/customers/{customer_id}/upliftPOST /api/v1/targeting/portfolioGET /api/analytics/metricsGET /api/analytics/backtestGET /api/analytics/driftPOST /api/admin/artifactsGET /api/admin/artifacts/{artifact_id}POST /api/admin/policiesGET /api/admin/policies/{policy_id}/{policy_epoch}GET /api/health
src/churn/api/ Litestar API routes, auth, middleware
src/churn/data/ Polars loader and preprocessor
src/churn/models/ model training and bootstrap
src/churn/causal/ uplift, segmentation, profit optimization
src/churn/explainability/ SHAP, DiCE, causal explanations
src/churn/validation/ time split and uplift metrics
src/churn/db/ Postgres, DuckDB, audit logging
src/churn/policy/ immutable policy snapshots and digests
src/churn/security/ RBAC, audit events, KMS helper
src/churn/services/ artifact registry and service adapters
frontend/ Next.js dashboard
tests/ backend tests
docs/ rework notes and dataset direction
Good first places to read:
src/churn/data/preprocessor.pysrc/churn/api/routes/predict.pysrc/churn/causal/uplift_engine.pyfrontend/src/app/DashboardClient.tsxtests/test_suite.py
High-value contribution areas:
- replace simulated uplift labels with a real randomized uplift dataset,
- add temporal churn benchmarks from KKBox,
- add stricter feature leakage checks around post-churn columns,
- improve frontend evidence views for CATE provenance,
- add typed API contract tests between backend and frontend.
Current validation on the maintained workspace:
- Backend:
185 passed - Backend lint:
ruff checkclean - Frontend lint: clean
- Frontend production build: successful
This repo is best read as a serious churn-prescription workbench: complete enough to run, honest about where the evidence is strong, and structured so the next dataset upgrade can make the causal layer genuinely stronger.
Built as a two-person collaboration: product direction and all backend/frontend implementation by one developer (this repository's maintainer), with a dedicated teammate running QA and test scenarios. The prediction/prescription boundary described above — recommend only when expected value clears the threshold — was a product decision made and implemented end to end by the same person who owns this repo.
