Skip to content

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

βš•οΈ MedScan AI

MedScan AI Landing Page

An AI-powered medical report analyzer that turns confusing lab reports into plain-English insights.
Built with Hybrid RAG Β· PubMedBERT Β· Microsoft Presidio Β· FastAPI Β· React

πŸ’‘ Why I Built This

Every time I got a blood test back, I'd stare at rows of numbers β€” "MCHC 31.2 g/dL", "TSH 6.8 Β΅IU/mL" β€” and have no idea what any of it meant. I'd end up Googling each value, getting scared by WebMD, and then waiting days for a doctor's appointment just to hear "it's fine."

I thought: what if an AI could read the entire report, understand what's abnormal, pull in real medical context, and explain it to me like a human would?

That's MedScan AI. You upload a lab report PDF, and it:

  1. Extracts every test result (even from messy, multi-format PDFs)
  2. Scrubs your personal info before anything touches an LLM
  3. Looks up each abnormal result against a curated medical knowledge base
  4. Explains what's going on in plain English with lifestyle tips

No more Googling "is my creatinine high" at 2 AM.


πŸ–ΌοΈ What It Looks Like

MedScan AI Dashboard

The dashboard after analyzing a blood test β€” biomarker gauges, AI-generated summary, and an interactive chat to ask follow-up questions.


πŸ”¬ How It Actually Works (The Pipeline)

When you upload a PDF, here's exactly what happens under the hood:

PDF Upload
    β”‚
    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  1. PDF EXTRACTION          β”‚  pdfplumber extracts text + tables
β”‚     (pdfplumber)            β”‚  Falls back to Gemini Vision for scanned PDFs
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚
              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  2. PII SCRUBBING           β”‚  Microsoft Presidio masks names, phone numbers,
β”‚     (Presidio + spaCy)      β”‚  SSNs, emails β€” BEFORE the text reaches any LLM
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚  "Patient: <PERSON>, Ph: <PHONE_NUMBER>"
              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  3. LLM PARSING             β”‚  The LLM reads the messy text and extracts
β”‚     (Gemini / Groq)         β”‚  structured data: test names, values, units,
β”‚                             β”‚  reference ranges, status (High/Low/Normal)
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚  Structured JSON (validated by Pydantic)
              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  4. HYBRID RAG RETRIEVAL    β”‚  For each abnormal result, we query:
β”‚     Dense: ChromaDB +       β”‚   β†’ PubMedBERT vectors (semantic meaning)
β”‚            PubMedBERT       β”‚   β†’ BM25 keyword index (exact medical terms)
β”‚     Sparse: BM25            β”‚  Results fused via Reciprocal Rank Fusion
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚  Relevant medical context from 17 biomarker guides
              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  5. RAG-GROUNDED ANALYSIS   β”‚  The LLM generates a patient-friendly summary
β”‚     (Gemini / Groq)         β”‚  using ONLY the retrieved medical context.
β”‚                             β”‚  No hallucination β€” every tip is traceable.
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚
              β–Ό
        Dashboard UI
   (gauges, chat, summary)

🧠 The Technical Details

Why Hybrid RAG? (Not Just Vector Search)

Standard vector search works great for general questions, but medical queries are different. If a patient asks about "MCHC" or "RDW-CV", a dense embedding model might not have strong representations for these abbreviations β€” it dilutes them into general "blood test" semantics.

My solution: combine two retrieval strategies and fuse them.

Strategy Good At Example
Dense (PubMedBERT + ChromaDB) Semantic similarity β€” understanding intent "I feel tired all the time" β†’ retrieves hemoglobin, iron, B12 docs
Sparse (BM25) Exact keyword matching "MCHC 31.2" β†’ retrieves the exact MCHC biomarker guide

The results are combined using Reciprocal Rank Fusion (RRF), which gives a fair score to documents that appear in both lists.

Why PubMedBERT? (Not Generic Embeddings)

General models like all-MiniLM-L6-v2 don't know that "HbA1c" is related to "diabetes management" or that "eGFR" is about kidney function. I use pritamdeka/S-PubMedBert-MS-MARCO, which was trained on biomedical literature and understands medical vocabulary natively.

PII Scrubbing β€” Why It Matters

Before any text is sent to Gemini or Groq, Microsoft Presidio scans and masks all personal identifiers:

Before: "Patient Name: Rahul Singh, Phone: 9876543210, DOB: 15/03/1995"
After:  "Patient Name: <PERSON>, Phone: <PHONE_NUMBER>, DOB: 15/03/1995"

Important design choice: Age, DOB, and Gender are intentionally not scrubbed. The AI needs these to contextualize results correctly β€” hemoglobin reference ranges for a 12-year-old are very different from those of a 50-year-old adult.

The LLM Parser (Instead of Regex)

Most lab report parsers use regex, which breaks the moment you switch from a Lal PathLabs report to an Apollo or Thyrocare format. Instead, I use the LLM itself as a semantic parser β€” it reads unstructured text and extracts structured JSON, validated by Pydantic schemas. The prompt engineering is the parsing logic, and it handles format variability gracefully.

RAGAS Evaluation

I don't just trust the RAG pipeline blindly. The project includes a scripts/evaluate_rag.py script that uses the RAGAS framework to measure:

  • Faithfulness β€” Is the answer grounded in the retrieved context?
  • Answer Relevancy β€” Does the answer actually address the question?
  • Context Precision β€” Did we retrieve the right documents?
  • Context Recall β€” Did we retrieve enough of the right documents?

πŸ—οΈ Architecture Diagram

graph TD
    classDef frontend fill:#EA580C,stroke:#333,stroke-width:2px,color:#fff
    classDef backend fill:#2563EB,stroke:#333,stroke-width:2px,color:#fff
    classDef db fill:#16A34A,stroke:#333,stroke-width:2px,color:#fff
    classDef llm fill:#9333EA,stroke:#333,stroke-width:2px,color:#fff

    UI["React / Vite Frontend"]:::frontend
    API["FastAPI Backend"]:::backend
    Chroma[("ChromaDB Vector Store")]:::db
    SQLite[("SQLite - Report History")]:::db
    Presidio{"Microsoft Presidio"}:::backend
    LLM["Groq / Gemini LLMs"]:::llm
    BERT["PubMedBERT Embeddings"]:::llm
    BM25["BM25 Keyword Index"]:::backend

    UI -- "Upload PDF" --> API
    API -- "Extract Text" --> API
    API -- "Mask PII" --> Presidio
    Presidio -. "Anonymized Text" .-> API

    UI -- "User Question" --> API
    API -- "Embed Query" --> BERT
    BERT -. "768d Vector" .-> Chroma
    API -- "Keyword Search" --> BM25
    Chroma -. "Semantic Results" .-> API
    BM25 -. "Keyword Results" .-> API
    API -- "RRF Fusion" --> API

    API -- "Context + Prompt" --> LLM
    LLM -. "Structured JSON" .-> API
    API -- "Save" --> SQLite
    API -- "Response" --> UI
Loading

πŸ› οΈ Tech Stack

Layer Technology Why I Chose It
Frontend React 18, Vite Fast HMR, modern DX
Backend Python 3.11, FastAPI Async-first, auto-generated API docs
Vector DB ChromaDB (Persistent) Lightweight, no server needed, Python-native
Embeddings S-PubMedBert-MS-MARCO Domain-specific medical embeddings
Sparse Search BM25 (rank-bm25) Exact keyword matching for medical abbreviations
LLM Groq (Llama 4 Scout), Gemini 2.0 Flash Provider-agnostic client β€” switch with 1 env var
Privacy Microsoft Presidio + spaCy Enterprise-grade PII detection
Evaluation RAGAS Industry-standard RAG quality metrics
Database SQLite + SQLAlchemy Simple persistence for report history
Containerization Docker + Docker Compose One-command deployment

πŸ“ Project Structure

MedScan-AI/
β”œβ”€β”€ backend/
β”‚   β”œβ”€β”€ app/
β”‚   β”‚   β”œβ”€β”€ api/
β”‚   β”‚   β”‚   β”œβ”€β”€ main.py              # FastAPI app factory + CORS + health check
β”‚   β”‚   β”‚   └── routes/
β”‚   β”‚   β”‚       β”œβ”€β”€ reports.py        # Upload β†’ Extract β†’ Scrub β†’ Parse β†’ Analyze
β”‚   β”‚   β”‚       └── chat.py           # Follow-up Q&A over analyzed reports
β”‚   β”‚   β”œβ”€β”€ core/
β”‚   β”‚   β”‚   β”œβ”€β”€ config.py             # Pydantic settings (env-driven, validated at startup)
β”‚   β”‚   β”‚   β”œβ”€β”€ db.py                 # SQLAlchemy engine + session
β”‚   β”‚   β”‚   └── db_models.py          # PatientReport model
β”‚   β”‚   β”œβ”€β”€ services/
β”‚   β”‚   β”‚   β”œβ”€β”€ pdf_extractor.py      # pdfplumber + Gemini Vision fallback
β”‚   β”‚   β”‚   β”œβ”€β”€ pii_scrubber.py       # Presidio-based PII masking
β”‚   β”‚   β”‚   β”œβ”€β”€ report_parser.py      # LLM-as-parser (prompt-engineered JSON extraction)
β”‚   β”‚   β”‚   β”œβ”€β”€ report_analyzer.py    # RAG-grounded analysis orchestrator
β”‚   β”‚   β”‚   β”œβ”€β”€ rag_engine.py         # Hybrid retrieval (Dense + Sparse + RRF)
β”‚   β”‚   β”‚   β”œβ”€β”€ vector_store.py       # ChromaDB + PubMedBERT embeddings
β”‚   β”‚   β”‚   └── llm_client.py         # Provider-agnostic LLM client (Gemini/Groq/Ollama)
β”‚   β”‚   β”œβ”€β”€ knowledge/
β”‚   β”‚   β”‚   β”œβ”€β”€ biomarker_info/       # 17 curated medical guides (CBC, lipid, thyroid, etc.)
β”‚   β”‚   β”‚   β”œβ”€β”€ lifestyle_tips/       # Actionable health recommendations
β”‚   β”‚   β”‚   └── reference_ranges.json # Structured normal ranges by age/gender
β”‚   β”‚   β”œβ”€β”€ schemas/                  # Pydantic models (LabReport, TestResult, Analysis)
β”‚   β”‚   └── utils/
β”‚   β”‚       └── logger.py             # Loguru-based structured logging
β”‚   β”œβ”€β”€ scripts/
β”‚   β”‚   β”œβ”€β”€ evaluate_rag.py           # RAGAS evaluation pipeline
β”‚   β”‚   β”œβ”€β”€ compare_embeddings.py     # PubMedBERT vs generic embedding comparison
β”‚   β”‚   └── generate_kb.py            # Knowledge base generation script
β”‚   β”œβ”€β”€ tests/                        # Unit + integration tests
β”‚   β”œβ”€β”€ Dockerfile
β”‚   β”œβ”€β”€ requirements.txt
β”‚   └── .env.example
β”œβ”€β”€ frontend/
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ App.jsx                   # Main shell + health check indicator
β”‚   β”‚   β”œβ”€β”€ components/
β”‚   β”‚   β”‚   β”œβ”€β”€ FileUploadView.jsx    # Landing page + PDF upload + progress animation
β”‚   β”‚   β”‚   └── DashboardView.jsx     # Stats, biomarker gauges, markdown chat
β”‚   β”‚   └── index.css                 # Full design system (warm hybrid theme)
β”‚   β”œβ”€β”€ Dockerfile
β”‚   └── nginx.conf
β”œβ”€β”€ docker-compose.yml
└── README.md

πŸš€ Getting Started

Prerequisites

1. Clone the repo

git clone https://github.com/rahulsgh21/MedScan-AI.git
cd MedScan-AI

2. Set up the backend

cd backend

# Create and activate a virtual environment
python -m venv venv
venv\Scripts\activate        # Windows
# source venv/bin/activate   # Mac/Linux

# Install dependencies
pip install -r requirements.txt

# Download the spaCy language model (needed for PII detection)
python -m spacy download en_core_web_sm

# Set up your environment variables
copy .env.example .env       # Windows
# cp .env.example .env       # Mac/Linux

Now open backend/.env and paste in your API keys:

LLM_PROVIDER=groq                    # or "gemini"
GEMINI_API_KEY=your_key_here
GROQ_API_KEY=your_key_here

3. Start the backend

uvicorn app.api.main:app --reload

The API will be running at http://localhost:8000. You can check the auto-generated docs at http://localhost:8000/docs.

4. Set up the frontend (in a new terminal)

cd frontend
npm install
npm run dev

The frontend will be live at http://localhost:5173.

5. Upload a report

Open http://localhost:5173, drag in a lab report PDF, and watch the magic happen.


🐳 Or Just Use Docker

If you have Docker installed, skip all the above and run:

# Copy your env file first
copy backend/.env.example backend/.env
# Edit backend/.env with your API keys

docker-compose up --build
  • Frontend: http://localhost:80
  • Backend: http://localhost:8000

πŸ§ͺ Running the RAGAS Evaluation

To evaluate how well the RAG pipeline is performing:

cd backend
python scripts/evaluate_rag.py

This will:

  1. Run medical questions through the full Hybrid RAG pipeline
  2. Use Gemini as an impartial judge to score faithfulness, relevancy, precision, and recall
  3. Save a detailed CSV to backend/ragas_eval_results.csv

πŸ—ΊοΈ What I'd Build Next

  • PDF Export β€” Let users download their analysis as a clean PDF report
  • Multi-language support β€” Hindi, Tamil, etc. for Indian lab reports
  • Report comparison β€” "How has my cholesterol changed since last year?"
  • Doctor handoff β€” Generate a concise clinical summary for the patient's physician

πŸ“„ License

This project is licensed under the MIT License.


A note on deployment: The full PubMedBERT stack needs ~2GB RAM to run comfortably. Most 512MB free-tier cloud platforms (like Render) can't handle it. If you want to deploy this for cheap, swap the embedding model in config.py to all-MiniLM-L6-v2 β€” you'll lose some medical-specific accuracy but it'll run anywhere.

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages