Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🏦 Loan Underwriting Copilot

A stateful multi-agent underwriting copilot built with LangGraph that assists bank underwriters in processing loan applications end-to-end. The system orchestrates 10 specialized AI agents for document analysis, policy retrieval, fraud detection, risk scoring, and report generation — with a human-in-the-loop approval gate.

The system does not approve loans automatically. Final approval always remains with the human underwriter.


🏗️ Architecture

START
  │
  ▼
[Supervisor Agent]  ← validates input, creates workflow plan
  │
  ├──────────────┬──────────────┬──────────────┐
  ▼              ▼              ▼              ▼
[Document     [Policy        [External       [Fraud
 Agent]        Agent]         Intelligence    Agent]
               (AlloyDB       Agent]
               Hybrid RAG)    (Tavily)
  │              │              │              │
  └──────┬───────┴──────┬───────┘              │
         ▼              ▼                       │
  [Financial Analysis Agent] ◄─────────────────┘
         │
         ▼
  [Risk Scoring Agent]
         │
         ▼
  [Recommendation Agent]
         │
         ▼
  [Human Approval Node]  ← interrupt() here
         │
         ▼
  [Report Agent]
         │
         ▼
        END

Parallel execution: Document, Policy, External Intelligence, and Fraud agents run concurrently.


🛠️ Tech Stack

Layer Technology
Agent Framework LangGraph
LLM Gemini 1.5 Pro / Flash (Vertex AI)
OCR / Document Extraction Google Cloud Document AI
Embeddings text-embedding-004 (Vertex AI)
Vector DB + Hybrid Search AlloyDB for PostgreSQL (pgvector + ScaNN + tsvector + ai.hybrid_search())
Reranker Vertex AI Ranking API
Checkpointing Firestore (LangGraph checkpointer)
Memory (Long-term) Firestore
Document Storage Google Cloud Storage (GCS)
Web Search Tavily Search API
API Layer FastAPI
Frontend Streamlit
Monitoring LangSmith

📁 Project Structure

Loan_Underwriter/
├── README.md
├── requirements.txt
├── .env.example
├── pytest.ini
├── docker-compose.yml               # AlloyDB Omni local emulator
│
├── config/
│   └── settings.py                  # All GCP + API keys (from .env)
│
├── agents/
│   ├── supervisor_agent.py          # Workflow orchestrator + validator
│   ├── document_agent.py            # Document AI + Gemini Vision fallback
│   ├── policy_agent.py              # AlloyDB Hybrid RAG
│   ├── external_intelligence_agent.py  # Tavily web search
│   ├── fraud_agent.py               # Fraud indicator detection
│   ├── financial_analysis_agent.py  # DTI, FOIR, EMI calculation
│   ├── risk_scoring_agent.py        # Composite risk score (0–100)
│   ├── recommendation_agent.py      # Approve/Reject/Conditional
│   └── report_agent.py              # PDF report + GCS upload
│
├── graph/
│   ├── state.py                     # UnderwritingState TypedDict
│   ├── graph_builder.py             # LangGraph graph assembly
│   └── human_approval_node.py       # interrupt() HITL node
│
├── rag/
│   ├── alloydb_store.py             # AlloyDB client + hybrid_search()
│   ├── chunker.py                   # Semantic policy chunking
│   ├── embedder.py                  # Vertex AI text-embedding-004
│   └── policy_ingestion.py          # PDF → AlloyDB ingestion pipeline
│
├── memory/
│   ├── firestore_checkpointer.py    # LangGraph Firestore checkpointer
│   └── long_term_memory.py          # Past decisions + exception patterns
│
├── tools/
│   ├── tavily_tool.py               # Tavily search tool
│   ├── document_ai_tool.py          # GCP Document AI extraction
│   └── report_tool.py               # PDF generation (ReportLab)
│
├── api/
│   ├── main.py                      # FastAPI app entry point
│   ├── schemas.py                   # Pydantic request/response models
│   └── routes/
│       ├── applications.py          # POST/GET /applications
│       ├── approvals.py             # POST /applications/{id}/approve
│       └── reports.py               # GET /applications/{id}/report
│
├── frontend/
│   └── app.py                       # Streamlit 4-page UI
│
├── data/
│   └── sample_policies/             # Sample lending policy PDFs
│
├── evaluation/
│   ├── rag_eval.py                  # Ragas RAG evaluation
│   └── agent_eval.py               # Agent task completion metrics
│
└── tests/
    ├── test_agents.py
    ├── test_rag.py
    └── test_api.py

🚀 Quick Start

1. Clone and install

git clone <repo>
cd Loan_Underwriter
pip install -r requirements.txt

2. Configure environment

cp .env.example .env
# Edit .env with your GCP project, Tavily API key, LangSmith API key

3. Demo / Mock Mode (no GCP needed)

# Set USE_MOCK=true to run fully offline
export USE_MOCK=true

# Start the API
uvicorn api.main:app --reload --port 8000

# In another terminal, start the frontend
streamlit run frontend/app.py

Open http://localhost:8501 for the Streamlit UI.
API docs: http://localhost:8000/docs

4. Production Mode (GCP)

# Configure .env with real credentials
# Start AlloyDB Omni locally for development
docker-compose up -d

# Initialise AlloyDB schema
psql -h localhost -U postgres -d underwriting -f scripts/init_alloydb.sql

# Ingest sample policies
python -m rag.policy_ingestion

# Start API
uvicorn api.main:app --host 0.0.0.0 --port 8000

# Start frontend
streamlit run frontend/app.py --server.port 8501

📡 API Endpoints

Method Path Description
POST /applications Submit application → triggers underwriting graph
GET /applications/{id} Full application state + all agent outputs
GET /applications/{id}/status Lightweight status poll
POST /applications/{id}/approve Underwriter decision (resumes LangGraph)
GET /applications/{id}/report Get signed GCS URL for PDF report
GET /applications/{id}/report/download Download PDF directly (local mode)
GET /health Health check

Example: Submit Application

curl -X POST http://localhost:8000/applications \
  -H "Content-Type: application/json" \
  -d '{
    "applicant_name": "Priya Sharma",
    "loan_type": "Personal Loan",
    "requested_amount": 1500000,
    "annual_income": 1200000,
    "employer_name": "Infosys Limited",
    "employment_type": "Salaried",
    "existing_emi": 15000,
    "loan_tenure_months": 60,
    "interest_rate": 12.5
  }'

Example: Underwriter Approval

curl -X POST http://localhost:8000/applications/APP-XXXXXXXX/approve \
  -H "Content-Type: application/json" \
  -d '{
    "status": "APPROVED",
    "underwriter_id": "UW-001",
    "notes": "Strong income profile, stable employer"
  }'

🤖 Agent Details

Risk Scoring Weights

Component Weight
Financial Metrics (DTI, FOIR, stability) 40%
Fraud Risk 25%
Policy Compliance 20%
External Signals (Tavily) 15%

Risk Level Mapping

Score Level
0–30 LOW
31–60 MEDIUM
61–80 HIGH
81–100 CRITICAL

Recommendation Logic

Condition Recommendation
Risk < 35, no fraud, no breaches APPROVE
Risk 35–60 or minor breaches CONDITIONAL_APPROVE
Risk > 60 or HIGH fraud REJECT
CRITICAL risk or missing docs ESCALATE

Human-in-the-Loop Triggers

  • Risk score ≥ 70
  • Fraud risk = HIGH
  • Recommendation = ESCALATE or REJECT
  • Risk level = CRITICAL

🧪 Testing

# Run all tests
pytest -v

# Run specific test suite
pytest tests/test_agents.py -v
pytest tests/test_rag.py -v
pytest tests/test_api.py -v

# Run evaluation
python evaluation/agent_eval.py
python evaluation/rag_eval.py

📊 AlloyDB Hybrid Search

AlloyDB is used as the single vector + keyword search database:

-- Hybrid search: dense vector + sparse keyword → RRF fusion
SELECT * FROM ai.hybrid_search(
  search_inputs => ARRAY[
    jsonb_build_object(
      'data_type', 'vector',
      'table_name', 'policy_chunks',
      'vec_column', 'embedding',
      'query_vector', '[...embedding...]',
      'limit', 10
    ),
    jsonb_build_object(
      'data_type', 'text',
      'table_name', 'policy_chunks',
      'query_text', 'maximum FOIR personal loan',
      'limit', 10
    )
  ]
);

🔍 Observability (LangSmith)

Set in .env:

LANGCHAIN_TRACING_V2=true
LANGCHAIN_API_KEY=your_key
LANGCHAIN_PROJECT=loan-underwriter

All agent executions, tool calls, and graph transitions are traced in LangSmith.


🐳 Local Development with AlloyDB Omni

# Start AlloyDB Omni container
docker-compose up -d

# Check status
docker-compose ps

# View logs
docker-compose logs -f alloydb

# Stop
docker-compose down

📋 Environment Variables

See .env.example for all configurable variables.

Key variables:

Variable Description Default
USE_MOCK Offline demo mode (no GCP) false
GCP_PROJECT_ID Google Cloud project ID required
TAVILY_API_KEY Tavily search API key required
LANGCHAIN_API_KEY LangSmith API key optional
ALLOYDB_DSN AlloyDB connection string localhost
GCS_BUCKET_NAME GCS bucket for docs/reports optional

⚠️ Disclaimer

This system is an AI assistant for loan underwriters. All AI recommendations are advisory only. Final loan decisions must be made by qualified human underwriters following applicable banking regulations and internal credit policies.


Azure-Native Deployment Notes

For Azure deployments, set CLOUD_PROVIDER=azure. The recommended Azure-native stack is:

Capability Azure service
LLM and embeddings Microsoft Foundry / Azure OpenAI deployments
Policy RAG Azure AI Search hybrid + semantic search
Document extraction Azure AI Document Intelligence
Reports and uploaded documents Azure Blob Storage
Application state Azure Database for PostgreSQL
LangGraph checkpointing Azure Database for PostgreSQL via langgraph-checkpoint-postgres
Optional cache Azure Cache for Redis
Secrets Azure Key Vault + Managed Identity
Observability Azure Monitor + Application Insights

Chat history is intentionally disabled for the default workflow. This app submits applications, runs the underwriting graph, captures human approval state, and returns reports; it does not need conversational memory unless a future underwriter chat interface is added.

Graph RAG is also not required for the initial Azure migration. Policy lookup is best served by Azure AI Search hybrid retrieval. Consider Graph RAG later only if the product needs relationship-heavy reasoning across applicants, employers, policies, exceptions, historical decisions, and connected fraud patterns.

About

Automated financial loan underwriting engine with credit decisioning and risk analytics.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages