Skip to content

Latest commit

Β 

History

20 Commits

Folders and files

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

Repository files navigation

🩺 Clinic RAG Bot

A domain-specific medical Retrieval-Augmented Generation (RAG) system that answers health-related questions strictly based on retrieved evidence from a local vector database. Built with Python 3.12, PubMedBERT embeddings, ChromaDB, and a dual-LLM backend (Groq API + Local Qwen 2.5).

Core principle: If the answer is not in the database, the bot says so. No hallucination. No general-knowledge guessing.


✨ Features

  • πŸ” Evidence-Based Answers β€” Retrieves from NHS, MedlinePlus, and Mayo Clinic data before generating any response.
  • πŸ›‘οΈ Safety Agent β€” Automatically detects emergency symptoms (chest pain, severe bleeding, etc.) and returns an urgent-care disclaimer instead of a casual answer.
  • πŸ€– Telegram Bot Interface β€” Async bot using python-telegram-bot v20+ with non-blocking RAG processing.
  • 🧠 Dual LLM Backend β€” Switch instantly between Groq API (fast, high-quality) and local Qwen 2.5 0.5B (free, offline, private).
  • πŸ“š Local Vector Store β€” ChromaDB with PubMedBERT embeddings (pritamdeka/S-PubMedBert-MS-MARCO) for medically-aware semantic search.
  • ⚑ Lazy Initialization β€” Heavy models load only once and only when needed, preventing circular imports and double-loading.

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Telegram  │────▢│   main.py       │────▢│ ClinicRAGPipelineβ”‚
β”‚   User      β”‚     β”‚   (Entry Point) β”‚     β”‚   (Orchestrator) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                     β”‚
           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
           β–Ό         β–Ό          β–Ό            β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  Safety  β”‚ β”‚Routerβ”‚ β”‚Retriever β”‚ β”‚  Generator β”‚
    β”‚  Agent   β”‚ β”‚      β”‚ β”‚(ChromaDB)β”‚ β”‚(Groq/Qwen) β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
                               β”‚             β”‚
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
                    β”‚  PubMedBERT        β”‚   β”‚
                    β”‚  Vector Embeddings β”‚   β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
                                             β”‚
                              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                              β”‚  NHS / MedlinePlus / Mayo   β”‚
                              β”‚  (JSON chunks in ChromaDB)  β”‚
                              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ› οΈ Tech Stack

Component Technology
Language Python 3.12
Embeddings sentence-transformers + pritamdeka/S-PubMedBert-MS-MARCO (768-dim)
Vector DB ChromaDB (chromadb>=0.5) with cosine HNSW indexing
Local LLM Qwen 2.5 0.5B Instruct (Qwen/Qwen2.5-0.5B-Instruct) via HuggingFace Transformers
Cloud LLM Groq API (llama-3.3-70b-versatile)
Telegram python-telegram-bot>=20 (async)
Config Custom YAML/JSON config loader + python-dotenv
Logging Centralized clinic_rag_bot logger

πŸ“¦ Installation

1. Clone & Navigate

git clone <your-repo-url>
cd clinic_rag_bot

2. Create Virtual Environment (Python 3.12)

python -m venv venv

# Windows
venv\Scripts\activate

# macOS/Linux
source venv/bin/activate

3. Install Dependencies

pip install -r requirements.txt

Key packages installed:

torch>=2.0.0
sentence-transformers>=3.0.0
transformers>=4.40.0
chromadb>=0.5.0
python-telegram-bot>=20.0
groq>=0.9.0
python-dotenv>=1.0.0
numpy>=1.23.5,<2.5.0
httpx>=0.27.0

βš™οΈ Configuration

Create a .env file in the project root:

# ─── Telegram ───
TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here

# ─── LLM Provider Toggle ───
# Set to 'true' to use free local Qwen (offline, no API key needed)
# Set to 'false' to use Groq API (faster, higher quality)
USE_LOCAL_LLM=false

# ─── Groq (required if USE_LOCAL_LLM=false) ───
GROQ_API_KEY=gsk_your_groq_key_here

# ─── HuggingFace (optional, for higher download limits) ───
HF_TOKEN=your_huggingface_token

# ─── Chroma Collection Name ───
# Must match between ingestion and retriever
CHROMA_COLLECTION=clinic_data

πŸš€ Usage

1. Ingest Medical Data (One-time Setup)

Place your JSON files in:

data/raw/medlineplus/
data/raw/mayo/
data/raw/nhs/

Each JSON file should follow this schema:

{
  "title": "Headaches",
  "source": "NHS",
  "url": "https://www.nhs.uk/conditions/headaches/",
  "content": "Most headaches will go away on their own..."
}

Run ingestion:

python src/ingestion.py

You should see:

Loaded 5 documents from medlineplus
Ingested 47 chunks from 5 documents in 'medlineplus'
Total documents in collection: 120

2. Test via CLI

# Using Groq (default)
python main.py --query "i have a headache"

# Using Local Qwen (free, offline)
python main.py --query "what are diabetes symptoms"

3. Run Telegram Bot

# Correct way
python main.py --bot

# ❌ Do NOT run the bot file directly (causes circular import)
# python bot/telegram_bot.py

The bot will print:

Telegram Bot is starting...
Bot connected: @YourBotName
Bot is now polling. Send a message on Telegram!

πŸ“ Project Structure

clinic_rag_bot/
β”œβ”€β”€ .env                          # Environment variables
β”œβ”€β”€ .gitignore
β”œβ”€β”€ README.md                     # This file
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ main.py                       # Entry point (CLI + Telegram)
β”‚
β”œβ”€β”€ bot/
β”‚   └── telegram_bot.py           # Telegram adapter (async handlers)
β”‚
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ pipeline.py               # ClinicRAGPipeline orchestrator
β”‚   β”œβ”€β”€ pubmed.py                 # GeneralKnowledgeRetriever
β”‚   β”œβ”€β”€ retrievers.py             # LocalRAGRetriever wrapper
β”‚   β”œβ”€β”€ vector_store.py           # ChromaDB client (explicit embeddings)
β”‚   β”œβ”€β”€ embeddings.py             # PubMedBERT wrapper
β”‚   β”œβ”€β”€ generator.py              # AnswerGenerator (Groq / Qwen toggle)
β”‚   β”œβ”€β”€ local_llm.py              # Qwen 2.5 0.5B local inference
β”‚   β”œβ”€β”€ ingestion.py              # JSON β†’ ChromaDB indexer
β”‚   β”œβ”€β”€ safety_agent.py           # Emergency symptom detection
β”‚   β”œβ”€β”€ router.py                 # Query intent classification
β”‚   β”œβ”€β”€ config_loader.py          # YAML/JSON config parser
β”‚   └── logger.py                 # Centralized logging
β”‚
└── data/
    β”œβ”€β”€ raw/
    β”‚   β”œβ”€β”€ medlineplus/          # *.json medical articles
    β”‚   β”œβ”€β”€ mayo/
    β”‚   └── nhs/
    └── chroma_db/                # Persistent ChromaDB (auto-generated)

πŸ”‘ Key Technical Decisions

Explicit Embeddings (No Auto-Embed)

ChromaDB defaults to all-MiniLM-L6-v2 (384-dim) for auto-embedding. We override this by computing embeddings with EmbeddingModelLocal (PubMedBERT, 768-dim) and passing them explicitly via collection.add(embeddings=...). This prevents the catastrophic "distance=31" mismatch bug.

Cosine Distance β†’ Similarity

ChromaDB returns distance, not similarity:

  • distance = 0 β†’ identical
  • distance = 2 β†’ completely opposite
  • Conversion: similarity = 1 - (distance / 2)

Lazy Pipeline Loading

main.py does not instantiate ClinicRAGPipeline at import time. It uses a _get_pipeline() singleton. This prevents:

  • Double model loading when telegram_bot.py imports process_request
  • Circular import crashes

Context Truncation

Groq's free tier has a 6,000–12,000 TPM limit. Medical documents are long, so the generator limits evidence to:

  • Max 2 chunks
  • Max 800 characters per chunk
  • Max ~1,600 total context characters

This keeps prompts under ~1,500 tokens, avoiding 413 Request too large errors.


πŸ› Troubleshooting

Problem Cause Solution
ImportError: cannot import name 'process_request' Circular import Run python main.py --bot, never python bot/telegram_bot.py
413 Request too large Prompt exceeds Groq TPM limit Context is auto-truncated in generator.py. Lower top_k in retriever.
401 Invalid API Key Groq key missing or wrong Check .env file. Or set USE_LOCAL_LLM=true for free local mode.
[] empty retriever results Wrong collection name Ensure pubmed.py collection name matches ingestion (clinic_data)
Distance values like 31.45 DB embedded with wrong model Delete data/chroma_db and re-run python src/ingestion.py
Bot starts then exits immediately run_bot() not called or loop not blocked Use asyncio.Event().wait() in telegram_bot.py

⚠️ Medical Disclaimer

This bot is not a substitute for professional medical advice, diagnosis, or treatment. It is an information retrieval tool designed to surface verified medical content from public databases.

  • Always seek the advice of your physician or other qualified health provider with any questions you may have regarding a medical condition.
  • If you are experiencing a medical emergency, call your local emergency services immediately.

πŸ“ License

This project is open-source. Feel free to fork and adapt for educational or research purposes.


Built with ❀️ for responsible AI in healthcare.

About

A domain-specific medical Retrieval-Augmented Generation (RAG) system that answers health-related questions strictly based on retrieved evidence from a local vector database.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages