FastAPI backend. Suggests APPROVE / REJECT / REFER_FOR_MANUAL_REVIEW on insurance proposals, with confidence % + template-based explanation (no LLM, deterministic).
data/generate_synthetic_data.py builds 10k synthetic proposals using a hand-coded
risk formula (age, smoker, BMI, credit score, claims history, occupation risk, etc)
- noise. Swap this for real historical proposal data when you get it -> same pipeline (train_model.py) retrains without other code changes, as long as column names match FEATURES list.
data/generate_synthetic_data.py->underwriting_data.csvml/train_model.py-> trains RandomForestClassifier ->model.pkl+feature_meta.json(feature_meta.json stores feature_importances_ + risk thresholds, used by explanation engine)app/-> FastAPI serving layermodel_service.py: loads model, predicts, maps probability -> suggestion + confidenceexplain.py: template engine, flags risky/positive features per applicant, ranks by model's feature importance, fills sentence templatesmain.py:/api/v1/underwritePOST endpoint
- predict_proba(approve) > 0.58 -> APPROVE
- predict_proba(approve) < 0.42 -> REJECT
- in between -> REFER_FOR_MANUAL_REVIEW (model itself isn't confident -> don't let it decide)
confidence = how far probability sits from the decision boundary, scaled 0-100. risk_score = 100 - approve_probability*100 (0=safest, 100=riskiest).
pip install -r requirements.txt
cd data && python generate_synthetic_data.py && cd ..
cd ml && python train_model.py && cd ..
uvicorn app.main:app --reload --port 8000Docs: http://localhost:8000/docs Endpoint: POST http://localhost:8000/api/v1/underwrite
{
"age": 45, "annual_income": 800000, "sum_assured": 5000000,
"bmi": 29.5, "smoker": 1, "alcohol_consumption": 1,
"pre_existing_disease": 0, "family_medical_history": 1,
"occupation_risk": 1, "credit_score": 610,
"num_previous_claims": 1, "years_with_insurer": 0
}- Map your real columns to the FEATURES list in
train_model.py(rename or extend). - Replace
underwriting_data.csvwith real historical approved/rejected proposals. - Rerun
train_model.py-> new model.pkl auto picked up by API, zero other changes. - Update
explain.pyrisk_thresholds to match real underwriting policy cutoffs (currently guessed: age 55, BMI 30, credit 650, claims >2).