An AI-powered pediatric health assistant with RAG-grounded medical responses, animated doctor interface, and voice synthesis.
Parents with sick children often turn to the internet at 2am, finding conflicting information from unreliable sources. PediatricAI provides evidence-based pediatric guidance grounded in medical literature, with clear citations so parents can verify every recommendation.
A parent types "my child has a 102Β°F fever and won't eat." PediatricAI retrieves relevant medical guidelines from its corpus, generates a structured response (what it likely is β what to do now β when to call the doctor), speaks the answer aloud, and shows which medical sources it used β all in under 5 seconds.
β οΈ Medical Disclaimer: PediatricAI is a portfolio project for educational purposes. It is not a substitute for professional medical advice, diagnosis, or treatment. Always consult a qualified healthcare provider for medical concerns.
Parent asks: "My child has a 102Β°F fever"
β
CloudFront β serves React frontend over HTTPS from S3
β
ALB β routes API requests to the backend container
β
ECS Fargate β runs FastAPI in Docker (serverless)
β 1. Check if the question is too vague
β 2. Convert question to 384-dim vector
β 3. Search pgvector for matching medical chunks
β 4. Score confidence: refuse / fallback / answer
β 5. Send chunks + question to GPT-4o-mini
β 6. Stream response tokens back via SSE
β
RDS PostgreSQL β stores 274 medical chunks with pgvector
β
OpenAI GPT-4o-mini β generates the medical response
β
Doctor answers with citations, animated face, and voice
flowchart TD
Q(["Parent asks a question"]) --> AMB{"Is the question too vague?"}
AMB -->|yes| CLARIFY(["Ask a targeted follow up question, then stop"])
AMB -->|no| EMBED["Embed the question into a 384 dimensional vector"]
EMBED --> RETR["Search for the top matching medical chunks"]
RETR -. reads .-> DB[("RDS PostgreSQL + pgvector: 274 chunks")]
RETR --> CONF{"Confidence score: 0.6 best + 0.4 average"}
CONF -->|below 0.45| REFUSE["Refuse politely and refer to a pediatrician"]
CONF -->|0.45 to 0.55| FALL["Add a general knowledge fallback"]
CONF -->|0.55 or higher| GROUND["Ground the answer in the retrieved chunks"]
FALL --> LLM["GPT-4o-mini generates the response"]
GROUND --> LLM
REFUSE --> STREAM(["Stream tokens to the UI via SSE"])
LLM --> STREAM
STREAM --> DOC(["Doctor animates and reads the answer aloud"])
- 12 medical PDFs ingested into 274 chunks with 100-token overlap
- Sentence embeddings using all-MiniLM-L6-v2 (384 dimensions)
- pgvector cosine similarity search with configurable thresholds
- Confidence bands: 0.55+ = high confidence, 0.45-0.55 = general knowledge fallback, <0.45 = refusal
- Citations showing which medical source supported each answer
- Ambiguity detection β catches vague queries ("my child is sick") before wasting compute on bad embeddings, asks targeted follow-up questions
- Emergency detection β "Call 911 right now" appears first for choking, seizures, poisoning
- Rate limiting β 15 queries/hour per IP (slowapi) to protect the OpenAI budget
- Input validation β 5,000 character limit with live counter
- Error boundary β React error boundary prevents white-screen crashes
- 11 expression PNGs β idle (with blink variants), thinking, talking (4 mouth positions), concerned, reassuring
- Context-aware expressions β thinking while waiting, talking during streaming, concerned for urgent symptoms
- Browser speech synthesis β male voice reads responses aloud at 1.35x speed
- Stop/replay controls β pause the doctor mid-sentence, replay the last response
- AWS ECS Fargate β serverless container running the FastAPI backend
- RDS PostgreSQL 16 with pgvector extension
- CloudFront + S3 β frontend served globally over HTTPS
- Secrets Manager β API keys encrypted at rest, injected at runtime
- CI/CD β GitHub Actions runs 59 tests β deploys to ECR + S3 on every push
- Start/stop scripts β
start.shbrings everything up in 5 minutes,stop.shshuts it down in 30 seconds
| Layer | Technology | Why |
|---|---|---|
| Frontend | React 18, Vite, Tailwind CSS | Fast builds, utility-first styling, modern React hooks |
| Backend | FastAPI, Python 3.11 | Async support for streaming, automatic OpenAPI docs |
| Database | PostgreSQL 16 + pgvector | Vector similarity search natively in SQL |
| Embeddings | all-MiniLM-L6-v2 (384d) | Fast, accurate sentence embeddings without GPU |
| LLM | GPT-4o-mini (OpenAI) | Best quality/cost ratio, follows system prompts reliably |
| Streaming | Server-Sent Events (SSE) | Real-time token delivery, simpler than WebSockets |
| TTS | Browser SpeechSynthesis API | Free, instant, no API calls needed |
| Infrastructure | AWS ECS, ALB, RDS, S3, CloudFront | Production-grade, scalable, cost-controlled |
| CI/CD | GitHub Actions | Auto-deploy on push, test gating |
| Testing | pytest (59 tests) | Unit tests for evaluation, clarification, symptoms, generation |
When a parent asks a question, here's what happens in ~3 seconds:
1. Ambiguity check β Before doing any work, the system checks if the query is too vague. "My child is sick" gets a follow-up question asking for specifics. "My child has a 102Β°F fever" passes through immediately.
2. Symptom extraction β Keywords are extracted for analytics: "fever", "loss_of_appetite", "cough". Severity is estimated from urgency words.
3. Embedding β The question is converted into a 384-dimensional vector using SentenceTransformers (all-MiniLM-L6-v2). This captures semantic meaning β "burning up" maps close to "fever" in vector space.
4. Retrieval β pgvector performs cosine similarity search across 274 medical chunks. Returns the top 5 most relevant chunks with similarity scores.
5. Confidence scoring β A weighted formula (0.6 Γ best_score + 0.4 Γ average_score) determines response confidence:
- β₯ 0.55: High confidence β answer grounded in medical corpus
- 0.45β0.55: Moderate β supplement with general knowledge
- < 0.45: Low β refuse gracefully with referral to pediatrician 6. Prompt assembly β System prompt + patient info (name, age, conditions) + retrieved chunks + conversation history (last 6 messages) + user question β sent to GPT-4o-mini.
7. Streaming generation β GPT-4o-mini streams tokens via SSE. Each token appears in the chat immediately. The doctor face animates its mouth during streaming.
8. Post-processing β A dictionary-based word fixer (130,000+ English words + medical terms) catches any broken words from PDF extraction artifacts.
59 tests across 4 modules, all passing:
| Module | Tests | What it covers |
|---|---|---|
test_evaluation.py |
14 | Refusal thresholds, confidence formula, boundary cases at 0.45 |
test_clarification.py |
16 | Specific queries pass through, vague queries caught, greetings handled |
test_symptoms.py |
13 | Keyword extraction accuracy, severity classification |
test_generation.py |
12 | Prompt assembly, patient info inclusion, history limits, urgency detection |
python -m pytest tests/ -v
# ========================= 59 passed =========================- macOS or Linux
- Python 3.11 (via conda or pyenv)
- Node.js 20+
- Docker Desktop
- An OpenAI API key ($5 credit lasts months)
# 1. Clone and enter
git clone https://github.com/Akarsh-Doki/pediatric-ai.git
cd pediatric-ai
# 2. Start the database
docker compose up db -d
# 3. Backend setup
conda create -n pediatricai python=3.11 -y
conda activate pediatricai
pip install -r backend/requirements.txt
# 4. Configure environment
cp .env.example .env
# Edit .env: add your OpenAI API key
# 5. Ingest medical corpus
python -m backend.scripts.ingest_corpus
# 6. Start backend
python -m uvicorn backend.main:app --reload --port 8000
# 7. Frontend (new terminal)
cd frontend
npm install
npm run dev
# 8. Open http://localhost:5173python -m pytest tests/ -vThe project is deployed on production AWS infrastructure:
| Service | Purpose |
|---|---|
| ECS Fargate | Runs the backend Docker container (0.5 vCPU, 1GB RAM) |
| ALB | Routes traffic, health checks, stable endpoint |
| RDS | PostgreSQL 16 with pgvector, stores 274 medical chunks |
| S3 + CloudFront | Serves React frontend globally over HTTPS |
| Secrets Manager | Encrypted storage for API keys |
| ECR | Docker image repository |
Start/stop scripts control costs:
./aws-scripts/start.sh # Start everything (~5 min, ~$1/day while on)
./aws-scripts/stop.sh # Stop everything (~30 sec, ~$0.50/month while off)
./aws-scripts/hibernate.sh # Deep stop ($0.00/month)
./aws-scripts/status.sh # Check what's runningpediatric-ai/
βββ backend/
β βββ config.py # Settings (reads .env)
β βββ main.py # FastAPI app, CORS, rate limiting
β βββ models/
β β βββ database.py # 7 SQLAlchemy tables
β β βββ schemas.py # Pydantic request/response models
β βββ routers/
β β βββ chat.py # /chat/query + /chat/stream (SSE)
β β βββ patients.py # CRUD for patient records
β β βββ documents.py # PDF upload + live ingestion
β β βββ tts.py # Text-to-speech endpoint
β βββ services/
β β βββ generation.py # LLM prompt building + streaming + word fixer
β β βββ retrieval.py # pgvector cosine similarity search
β β βββ evaluation.py # Confidence scoring + refusal logic
β β βββ clarification.py # Ambiguity detection
β β βββ ingestion.py # PDF β chunks β embeddings
β β βββ tts_service.py # Google TTS (backend fallback)
β βββ utils/
β β βββ symptoms.py # Keyword-based symptom extraction
β β βββ embeddings.py # SentenceTransformer wrapper
β β βββ chunking.py # 600-token chunks, 100 overlap
β βββ scripts/
β βββ ingest_corpus.py # Bulk PDF ingestion
βββ frontend/
β βββ src/
β βββ App.jsx # Main app with doctor animation
β βββ hooks/
β β βββ useChat.js # SSE streaming + state management
β β βββ useAudioSync.js # Browser speech synthesis
β β βββ useTheme.jsx # Dark/light mode
β βββ components/
β βββ ChatInterface.jsx # Message bubbles, skeleton loading
β βββ DoctorFace.jsx # 11-PNG expression animation
β βββ ErrorBoundary.jsx # Crash recovery
β βββ CitationPanel.jsx # Source references
β βββ Sidebar.jsx # Patient selection, settings
βββ tests/
β βββ test_evaluation.py # 14 tests: thresholds, confidence
β βββ test_clarification.py # 16 tests: ambiguity detection
β βββ test_symptoms.py # 13 tests: keyword extraction
β βββ test_generation.py # 12 tests: prompt assembly
βββ aws-scripts/ # Start/stop/hibernate/teardown
βββ data/pdfs/ # 12 medical PDFs (corpus)
βββ .github/workflows/deploy.yml # CI/CD pipeline
βββ docker-compose.yml # Local full-stack
βββ .env.example # Environment template
| Decision | Choice | Why |
|---|---|---|
| RAG vs fine-tuning | RAG | Update medical guidelines by swapping PDFs, not retraining. Provides citations. |
| GPT-4o-mini vs larger models | GPT-4o-mini | $0.002/query. Follows system prompts perfectly. Best quality/cost ratio. |
| pgvector vs Pinecone/Weaviate | pgvector | No additional service to manage. Lives in the same PostgreSQL as patient data. |
| Browser TTS vs cloud TTS | Browser | Free, instant, no API latency. Works offline. |
| ECS Fargate vs EC2 | Fargate | No servers to patch. Scales to zero when not in use. |
| Confidence bands vs binary | Bands | Gradual degradation instead of hard refuse. 0.45-0.55 uses general knowledge as fallback. |
- Replace keyword symptom extraction with a medical NER model (SciSpacy/BioBERT)
- Add HTTPS on ALB with ACM certificates
- Multi-language support (Spanish is the most-requested)
- Image upload for rash identification (GPT-4o vision)
- Conversation export to PDF for pediatrician visits
Akarsh Doki
- GitHub: @Akarsh-Doki
- LinkedIn: linkedin.com/in/akarsh-doki Built from scratch β full-stack AI engineering from RAG pipeline design to production AWS deployment.
