⚠️ This project is under active development. Not production-ready as-is — see the Production Checklist before any real deployment.
Sentinel-X Credit is an AI-powered credit risk assessment system. Instead of relying on a simple credit score, it analyzes a loan applicant's full financial profile — income, debt, spending behavior, employment stability — and cross-references it against a database of historical loan outcomes to make an intelligent APPROVE / REJECT decision with reasoning.
Think of it as giving a loan officer an AI assistant that has studied thousands of past cases and can instantly say: "This applicant looks similar to 3 past cases we rejected — here's why."
When a loan application comes in, the system runs it through a 3-step pipeline:
1. Embed → Search similar past cases (Qdrant)
The applicant's financial narrative gets converted into a vector embedding using FastEmbed (BAAI/bge-small-en-v1.5). Qdrant then does a hybrid vector search across all historical loan cases to find the most similar past applicants and their outcomes (APPROVED / REJECTED).
2. RAG context → Feed evidence to Gemini
The retrieved historical cases become context for the AI. Gemini (gemini-flash-latest) receives both the applicant's data and the evidence from similar past cases, then reasons about the risk.
3. Structured decision → Back to the UI Gemini returns a structured JSON response with a decision, confidence level, reasoning, and specific risk factors. The loan officer sees all of this in the dashboard.
The system also learns over time — you can feed it new labeled outcomes via the /teach endpoint, which adds them to Qdrant and improves future decisions.
Next.js Dashboard
│ POST /api/v1/analyze
▼
FastAPI (risk engine)
│ build narrative → embed with FastEmbed
▼
Qdrant (hybrid vector search + RRF fusion)
│ top similar historical cases + their outcomes
▼
Gemini (gemini-flash-latest)
│ reason over applicant data + evidence
▼
{ decision, confidence, reasoning, risk_factors }
│
▼
Dashboard displays result to loan officer
| Module | Path | Description |
|---|---|---|
| Risk Analysis API | src/agents/risk_analysis |
FastAPI backend — embedding, search, AI decision |
| Frontend Dashboard | src/frontend |
Next.js loan officer UI |
cd src/agents/risk_analysis
cp .env.example .env
# Fill in your keys, then:
docker-compose up --buildRequired keys in .env:
GOOGLE_API_KEY=your_gemini_key
QDRANT_URL=https://your-cluster.qdrant.io
QDRANT_API_KEY=your_qdrant_key
COLLECTION_NAME=credit_risk_v1Once running:
- Health check:
http://localhost:8000/health - Interactive API docs:
http://localhost:8000/docs
Note: The Docker healthcheck uses
curl, which isn't inpython:3.11-slimby default. If health checks fail, install curl in the Dockerfile or remove the healthcheck block fromdocker-compose.yml.
The more historical cases in Qdrant, the better the RAG evidence quality. Seed it with the bundled dataset:
python scripts/init_db.pyThis reads data/history.json and loads all cases into your Qdrant collection.
cd src/frontend
npm install
npm run devOpen http://localhost:3000. Start in Demo mode first (no backend needed) to explore the UI, then switch to Live mode once the backend is running.
Check if the backend is up and connected to Qdrant.
{ "status": "operational", "mode": "cloud_connected" }Submit a loan application for AI risk assessment.
curl -X POST http://localhost:8000/api/v1/analyze \
-H "Content-Type: application/json" \
-d '{
"applicant_id": "new_120",
"net_income": 3200,
"monthly_debt": 1400,
"dti": 0.44,
"disposable_income": 800,
"emp_duration": "<1 yr",
"residential_status": "RENT",
"nsf_count": 2,
"overdraft_usage": 600,
"gambling_percent": 8,
"loan_amount": 9000
}'Response:
{
"decision": {
"decision": "REJECTED",
"confidence": "High",
"reasoning": "Applicant shows a DTI of 0.44 combined with recent NSF activity...",
"risk_factors": ["High DTI", "Recent NSF activity", "Short employment history"]
},
"evidence": "- Outcome: REJECTED (Score: 0.82)\n- Outcome: APPROVED (Score: 0.74)"
}Add a new labeled case to Qdrant. Use this after a loan outcome is known to continuously improve the system.
curl -X POST http://localhost:8000/api/v1/teach \
-H "Content-Type: application/json" \
-d '{
"applicant_id": "hist_9001",
"net_income": 7000,
"monthly_debt": 900,
"dti": 0.13,
"disposable_income": 4500,
"emp_duration": ">2 yr",
"residential_status": "OWN",
"banking_tenure": 72,
"nsf_count": 0,
"overdraft_usage": 0,
"gambling_percent": 0,
"loan_amount": 25000,
"loan_purpose": "home_improvement",
"outcome": "APPROVED"
}'# from src/agents/risk_analysis
pytest -v # runs against the app in-memory (no Docker needed)
TEST_MODE=docker pytest -v # runs against a live Docker container on localhost:8000cd src/agents/risk_analysis
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000Before deploying this anywhere real, make sure you handle the following:
🔒 Lock down CORS
Right now the API accepts requests from any domain (allow_origins=["*"]). Change it in app/main.py to your actual frontend URL:
app.add_middleware(
CORSMiddleware,
allow_origins=["https://yourdomain.com"],
allow_methods=["GET", "POST"],
allow_headers=["*"],
)🔁 Enable auto-restart
Uncomment restart: always in docker-compose.yml so the container recovers automatically from crashes.
🔑 Protect the /teach endpoint
Currently anyone who can reach port 8000 can call /teach and inject garbage into your Qdrant collection. Add API key authentication before going live.
💸 Rate-limit /analyze
Every call hits Gemini and costs money. Add rate limiting to avoid surprise bills.
🌐 Wire up the frontend API URL
Set NEXT_PUBLIC_API_URL in your frontend .env.local to your actual backend URL instead of hardcoding localhost.
