A production-style AI system that helps parents understand situations, make safe decisions, and discover relevant products. Not just a chatbot — a parenting decision engine with hard safety guarantees.
Voice Input (optional)
│ AssemblyAI STT (EN + AR)
▼
User Input (text or transcribed)
│
▼ Step 0
Arabic Detection + Translation → English (Ollama)
│
▼ Step 1 [parallel]
NLP Extractor Intent Classifier Risk Classifier (ML)
(age, symptoms) (rules → ML fallback) (TF-IDF + LR)
│
▼ Step 2
Rule-Based Risk Engine ←── AUTHORITATIVE (overrides ML for critical/medium)
│
▼ Step 3 [emergency path]
Critical + Emergency? ──YES──► Emergency Template (no LLM, instant, safe)
│ NO
▼ Step 4
RAG Pipeline → FAISS knowledge + product retrieval
│
▼ Step 5
Decision Engine → Ollama LLM (llama3.2) structured response
│ Vague query? Cap confidence ≤ 0.60
▼ Step 6
Safety Layer → Rule-based post-LLM validation
Medium risk? Force doctor_flag=True
│
▼ Step 7
Arabic requested? → Translate summary via Ollama
│
▼
Structured JSON Response
│
▼
React Frontend → RTL Arabic support
| Layer | Technology |
|---|---|
| LLM | Ollama (llama3.2) |
| Embeddings | sentence-transformers (all-MiniLM-L6-v2) |
| Vector DB | FAISS (IndexFlatIP) |
| ML classifiers | scikit-learn (TF-IDF + Logistic Regression) |
| Rule engine | Pure Python regex (authoritative, pre-LLM) |
| Speech-to-text | AssemblyAI (English + Arabic, auto-detect) |
| Backend | FastAPI + Python |
| Frontend | React + Vite + TailwindCSS |
| Logging | Loguru |
Deterministic risk classification that overrides ML for safety-critical decisions:
- 30+
_CRITICALregex patterns: not breathing, seizure, blue lips, battery ingestion, fever ≥104°F, choking, unresponsive, newborn (<3 months) with any fever - 15+
_MEDIUMpatterns: persistent rash, 101–103°F fever, ear pain, signs of dehydration, pink eye evaluate(query, ctx)→(risk_level, reason)— runs before LLM, cannot be overriddenis_vague(query)→bool— True if ≤3 words; used to cap LLM confidence
Rules first, ML fallback:
_EMERGENCY_RE: explicit emergency signals only (not breathing, gasping, seizure, battery)_PRODUCT_RE: recommendation patterns (best X for, recommend, which product)_guard_ml_intent(): if ML predicts "emergency" without an explicit signal → downgrade to "advice"- Returns intent with confidence=0.95 for rule matches
Lightweight TF-IDF + Logistic Regression, used as signal only (rule engine takes precedence for critical/medium).
Extracts structured context from queries:
- Child age (months, weeks, years, special terms like "newborn")
- Symptom categories (fever, sleep, teething, rash, etc.)
- Urgency keywords, concern type
- Emergency bypass:
emergency_response(ctx)— instant hardcoded template, no LLM, confidence=0.97, always sets doctor_flag=True generate()— builds RAG-augmented prompt, calls Ollama, parses JSON_translate_to_english()/translate_to_arabic()— bilingual support via Ollama- Reduced
num_predictto 700 for lower latency
- Accepts audio bytes, writes to temp file, calls AssemblyAI transcriber
language_detection=True,speech_model=best- Runs in
asyncio.to_threadto avoid blocking - Graceful fallback when API key not set
- Enriches query with extracted age/symptom context
- Retrieves top-K knowledge items + products via FAISS cosine similarity
- Filters products by scenario keywords
Post-LLM rule-based validator (runs after LLM, cannot be bypassed):
- Emergency patterns →
doctor_flag=True,risk=critical - Newborn + fever → escalate to critical
- Medium risk →
doctor_flag=True(always) - Confidence <0.5 → disclaimer prepended
Synthetic training data via Ollama: knowledge entries, product catalog, realistic queries.
{
"situation": "Warm 1-2 sentence summary of what is happening",
"child_stage": "Developmental stage (e.g., Young Infant 0-3 months)",
"advice": ["Actionable tip 1", "Tip 2", "Tip 3"],
"products": [
{
"product_name": "Sophie la Girafe Teether",
"category": "Teething",
"use_case": "Teething pain and gum soreness",
"age_range": "3-12 months",
"description": "Natural rubber teether...",
"reasoning": "Why this helps specifically for your situation"
}
],
"confidence": 0.85,
"doctor_flag": false,
"risk_level": "low",
"intent": "advice",
"arabic_summary": null,
"session_id": "uuid-here"
}| Trigger | Action |
|---|---|
| Emergency patterns (not breathing, seizure, etc.) | Bypass LLM entirely → instant template, doctor_flag=true, risk=critical |
| Newborn (<3 months) + any fever | doctor_flag=true, risk=critical, ER advice |
| High fever (≥104°F) | doctor_flag=true, risk=critical |
| Dangerous ingestion (battery, chemicals) | doctor_flag=true, risk=critical |
| Medium risk (101–103°F, rash, ear pain) | doctor_flag=true, risk=medium |
| Vague query (≤3 words) | Confidence capped at ≤0.60 |
| Confidence <0.5 | Disclaimer prepended to advice |
| Method | Endpoint | Description |
|---|---|---|
GET |
/health |
System health + AssemblyAI status |
POST |
/chat |
Main chat endpoint |
POST |
/transcribe |
Audio → text (AssemblyAI) |
POST |
/generate-data |
Generate synthetic training data |
Request:
{
"message": "My 6 month old is teething, what can I do?",
"language": "en",
"session_id": null
}Response: See structured output format above.
Request: multipart/form-data with field audio (webm/ogg/mp4 file)
Response:
{
"text": "My baby has a fever of 101 degrees",
"language": "en",
"confidence": 0.98
}Browser mic (MediaRecorder API)
│ audio/webm;codecs=opus
▼
POST /api/transcribe (FormData)
│
▼
AssemblyAI (language_detection=True, speech_model=best)
│
▼
{text, language, confidence}
│
▼
VoiceInput component → sets textarea value → user reviews → sends
- Arabic input detected (Unicode range
–ۿ): translated to English before processing language=aror Arabic input:arabic_summaryfield always populated- Arabic summary: translated via Ollama from the first 3 advice points + situation
- Emergency Arabic: hardcoded safe phrase (no LLM dependency for emergencies)
- Frontend: RTL layout for Arabic messages
15 test cases covering: normal advice, product recommendations, medium-risk concerns, emergencies, edge cases (vague queries, exhausted parent, multilingual/Arabic).
Run evaluation:
cd tests
python evaluate.py --url http://localhost:8000 --output results.jsonRun single test:
python evaluate.py --url http://localhost:8000 --id TC007| Decision | Rationale |
|---|---|
| Rule engine runs pre-LLM and is authoritative | LLM cannot hallucinate its way around safety-critical risk levels |
| Emergency bypass skips LLM entirely | Eliminates hallucination risk for most dangerous cases; response is instant |
| Hybrid intent classifier (rules → ML) | ML alone mis-fires "emergency" for non-emergency queries; rules are explicit |
| AssemblyAI for STT | Best-in-class accuracy with Arabic support and language auto-detection |
| Pure Python, no LangChain | Full control, easier debugging, no abstraction overhead |
| Lightweight sklearn classifiers | Fast startup, no GPU needed |
| FAISS over ChromaDB/Pinecone | Simple, local, no external dependencies |
| asyncio.gather for classification | NLP + intent + risk run in parallel, saving ~100ms per request |
NurtureAI/
├── backend/
│ ├── app/
│ │ ├── api.py # FastAPI app, startup, all routes
│ │ ├── config.py # Environment configuration
│ │ ├── models.py # Pydantic request/response models
│ │ ├── data_generation.py # Synthetic data via Ollama
│ │ ├── embedding.py # sentence-transformers + FAISS
│ │ ├── rag_pipeline.py # RAG retrieval orchestration
│ │ ├── decision_engine.py # Ollama LLM + translation + emergency
│ │ ├── safety_layer.py # Post-LLM safety rules
│ │ └── voice_handler.py # AssemblyAI STT integration
│ ├── ml/
│ │ ├── nlp_extractor.py # Context extraction (age, symptoms)
│ │ ├── intent_classifier.py # Hybrid rules + ML (advice/product/emergency)
│ │ ├── risk_classifier.py # ML risk (low/medium/critical)
│ │ └── risk_engine.py # Rule-based risk engine (authoritative)
│ ├── data/
│ │ ├── seed_knowledge.json # 21 parenting knowledge entries
│ │ ├── seed_products.json # 25 product catalog entries
│ │ └── seed_queries.json # Realistic parent queries
│ ├── indexes/ # FAISS indexes (auto-built on startup)
│ ├── logs/ # Rotating application logs
│ ├── ml_models/ # Trained sklearn models (auto-saved)
│ ├── .env.example
│ └── requirements.txt
├── frontend/
│ ├── src/
│ │ ├── App.jsx
│ │ ├── main.jsx
│ │ ├── index.css
│ │ ├── components/
│ │ │ ├── Header.jsx
│ │ │ ├── ChatInterface.jsx # Main chat + voice input wiring
│ │ │ ├── MessageBubble.jsx
│ │ │ ├── ResponseCard.jsx
│ │ │ ├── ProductCard.jsx
│ │ │ ├── SafetyAlert.jsx
│ │ │ ├── TypingIndicator.jsx
│ │ │ ├── VoiceInput.jsx # MediaRecorder → /transcribe
│ │ │ └── WelcomeScreen.jsx
│ │ └── utils/
│ │ └── api.js # sendMessage + transcribeAudio + health
│ ├── package.json
│ ├── vite.config.js
│ ├── tailwind.config.js
│ └── index.html
├── tests/
│ ├── test_cases.json # 15 structured test cases
│ └── evaluate.py # Evaluation runner + metrics
├── README.md
└── SETUP.md
ChatGPT – Used for documentation, prompt engineering, ideation, brainstorming, and research support Claude – Used for code generation and implementation assistance AssemblyAI – Used for speech-to-text (STT) processing in voice input features Ollama (running LLaMA 3) – Used as the primary local LLM for inference and response generation