Skip to content

notsooamit/CogniQuote

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 

Repository files navigation

CogniQuote -- AI Underwriting Pipeline

CogniQuote is a six-node processing pipeline that accepts insurance application PDFs and returns a risk assessment with a calculated premium and a routing decision.

Problem

Manual insurance underwriting is slow and inconsistent. Underwriters read multi-page application forms, cross-reference policy manuals, identify PII that must not leave the organisation, calculate premiums from actuarial tables, and route applications to the right queue. An LLM alone cannot do this reliably -- it hallucinates numbers, mishandles privacy, and has no mechanism to run deterministic formulas. CogniQuote splits the work between an LLM (for unstructured extraction) and deterministic code (for premium calculation and routing), with a privacy layer sitting between them so that PII never reaches the cloud model.

Architecture

The pipeline is a linear six-node state machine built on LangGraph and executed inside a FastAPI BackgroundTask. Each node reads from and writes to a typed shared state dictionary.

PDF Upload
   |
   v
[Node 1: OCR] -----> raw_text
   |                  (AWS Textract, or mock text in dev)
   v
[Node 2: Privacy] -> anonymized_text + token_map
   |                  (spaCy + Presidio -- runs fully offline)
   v
[Node 3: RAG] -----> retrieved_context
   |                  (pgvector similarity search over policy manual chunks)
   v
[Node 4: LLM] -----> risk_assessment (JSON)
   |                  (Groq Llama 3.3-70B with structured output)
   v
[Node 5: De-anon] -> restored_assessment
   |                  (PII tokens swapped back to original values)
   v
[Node 6: Calculate] -> calculated_premium + routing_decision
   |                  (deterministic Python, no LLM)
   v
Response (stored in PostgreSQL, polled by frontend)

The privacy shield (Node 2) pseudonymises names, emails, phone numbers, locations, credit card numbers, and IBANs before the text leaves the server. The LLM (Node 4) receives only placeholder tokens like <PERSON_1>. After the LLM returns its structured result, Node 5 restores the original values in the JSON payload. This means the cloud LLM never sees real PII.

The risk calculator (Node 6) is a set of hardcoded rules. It looks up a base premium and a risk multiplier from a policy-type table, applies the LLM's risk score to the formula base * (1 + risk_score * multiplier), then picks a routing bucket:

  • risk_score <= 40: INSTANT_QUOTE
  • risk_score <= 70: RISK_SUMMARY
  • risk_score > 70 or unknown policy type: HUMAN_REVIEW

Key Design Decisions

  • Mock mode for local development. Setting MOCK_MODE=true in the environment bypasses AWS Textract and uses a hardcoded sample insurance form. No AWS credentials needed for local work.
  • Structured LLM output. The LLM is forced to return a Pydantic-validated JSON schema via LangChain's with_structured_output(). The risk_score is constrained to 0--100, the recommendation is a Literal type, and the whole object is validated before it enters the deterministic stage.
  • Selective retries. Both the OCR service and the LLM service use tenacity with exponential backoff, but the LLM retry logic explicitly skips non-transient errors (rate limiting, auth failures, token limit errors) and only retries on 5xx server errors or connection failures.
  • Input truncation. Application text is capped at 4,000 characters and RAG context at 5,000 characters before being sent to the LLM, keeping token usage within Groq free-tier limits.
  • Lazy vector store initialisation. The PGVector connection is created on first use rather than at import time, so the API starts even if the database container is still booting.
  • Single-page frontend with polling. The React dashboard uploads a file, receives a job_id immediately, then polls /api/v1/job-status/{job_id} every two seconds until the pipeline completes. There is no WebSocket or SSE -- this keeps the architecture simple.

Tech Stack

  • FastAPI (Python 3.11)
  • LangGraph, LangChain Core
  • Groq API (Llama 3.3-70B-Versatile)
  • spaCy (en_core_web_lg) + Microsoft Presidio
  • AWS Textract (OCR, with mock fallback)
  • HuggingFace sentence-transformers (all-MiniLM-L6-v2)
  • PostgreSQL with pgvector
  • SQLAlchemy ORM
  • tenacity (retry logic)
  • Docker, Docker Compose
  • Next.js 16, React 19, TypeScript
  • Tailwind CSS v4, Framer Motion
  • Axios

Project Structure

CogniQuote_Project/
  cogniquote_backend/
    app/
      agent/
        graph.py              # LangGraph pipeline definition
        state.py              # TypedDict for pipeline state
      db/
        models.py             # SQLAlchemy Document & Assessment models
        session.py            # Database connection and session factory
      services/
        llm.py                # Groq LLM integration with structured output
        ocr.py                # AWS Textract integration with mock mode
        privacy.py            # PII detection and pseudonymisation
        storage.py            # Database CRUD helpers
      main.py                 # FastAPI application and endpoints
    policy_manuals/           # HDFC ERGO policy PDFs for RAG ingestion
    scripts/
      ingest_policies.py      # PDF chunking and vector store population
    docker-compose.yml
    Dockerfile
    requirements.txt
    rules.md
    .env.example
  cogniquote_frontend/
    app/
      layout.tsx              # Root layout with metadata
      page.tsx                # Underwriter dashboard (single page)
      globals.css
    package.json
    tsconfig.json
    next.config.ts
  .gitignore

Setup and Run

Prerequisites

  • Docker and Docker Compose
  • Node.js 18 or later
  • A Groq API key (for LLM inference)
  • AWS credentials (optional -- only needed for live Textract)

Backend

  1. Copy the environment template and fill in your keys:
cd cogniquote_backend
cp .env.example .env

Required variables:

  • GROQ_API_KEY -- your Groq API key
  • MOCK_MODE -- set to true for local development (bypasses AWS Textract)
  • AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY -- only needed if MOCK_MODE=false
  • AWS_S3_BUCKET -- only needed if MOCK_MODE=false
  • DATABASE_URL -- defaults to the Docker Compose service; override if needed
  1. Start the API and the pgvector database:
docker-compose up --build -d

The API will be available at http://localhost:8000. The database runs on port 5432.

  1. (First run only) Ingest the policy manuals into the vector store:
docker-compose exec api python scripts/ingest_policies.py

This loads the PDFs from policy_manuals/, chunks them, embeds them with all-MiniLM-L6-v2, and stores the vectors in PostgreSQL.

Frontend

cd cogniquote_frontend
npm install
npm run dev

The dashboard will be available at http://localhost:3000.

API Endpoints

POST /api/v1/process-document

Upload an insurance document for processing. Returns immediately with a job_id.

Request: multipart/form-data with a file field (PDF, PNG, JPG, or TIFF).

Response (200):

{
  "job_id": "a1b2c3d4-...",
  "status": "PROCESSING"
}

GET /api/v1/job-status/{job_id}

Poll for the result of a processing job.

Response while processing:

{
  "job_id": "a1b2c3d4-...",
  "status": "PROCESSING",
  "filename": "application.pdf"
}

Response when complete:

{
  "job_id": "a1b2c3d4-...",
  "status": "COMPLETED",
  "filename": "application.pdf",
  "anonymized_text": "INSURANCE APPLICATION FORM...\nApplicant Name: <PERSON_1>...",
  "pipeline_trace": [
    "ocr_node: extracted text",
    "privacy_node: PII pseudonymized",
    "rag_retrieval_node: retrieved 3 chunks",
    "llm_node: risk assessed",
    "de_anonymize_node: PII restored",
    "calculate_risk_node: premium 15750.00, route INSTANT_QUOTE"
  ],
  "assessment": {
    "policy_type": "Comprehensive Motor Insurance",
    "vehicle_value": 1250000,
    "claim_history_summary": "1 claim in last 3 years...",
    "risk_score": 25,
    "recommendation": "APPROVE",
    "reasoning": "...",
    "calculated_premium": 15750.0,
    "routing_decision": "INSTANT_QUOTE"
  }
}

GET /api/v1/history

Returns all past assessments, ordered by most recent first.

{
  "status": "success",
  "count": 5,
  "results": [...]
}

Current Status

  • The six-node pipeline runs end-to-end: OCR, PII pseudonymisation, RAG retrieval, LLM analysis, PII restoration, and deterministic routing.
  • Mock mode allows local development and testing without AWS credentials.
  • The vector store is populated from seven HDFC ERGO policy manuals covering motor, health, critical illness, personal accident, and wellbeing products.
  • The policy-type rules in POLICY_RULES_DB (in graph.py) are hardcoded for three policy types plus a default fallback. Adding a new policy type requires editing the dictionary and restarting.
  • The frontend polls for results. There is no push notification or real-time socket.
  • The LLM prompt is tuned for motor insurance applications. Other lines of business (e.g. health insurance forms) may produce lower-quality assessments without prompt adjustments.
  • Input truncation at 4,000 characters means very long application forms (30+ pages) may lose tail content. This is a trade-off for staying within Groq free-tier token limits.
  • There is no authentication layer on the API or the frontend.

License

MIT

About

Async AI underwriting microservice — LangGraph, pgvector RAG, PII privacy shield, deterministic premium calculation.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors