
RAG application · IBM watsonx.ai · 2026
NutriMind AI
Multi-user RAG nutrition assistant on IBM watsonx.ai Granite. It answers from each user's own PDFs with page-level citations, routes requests rule-first to four specialist agents, and computes calories, BMI, macro targets and the health score in Python, not in the model.
Origin: Began as my IBM SkillsBuild internship project, then extended through five engineering milestones into the multi-user, deployed system described here.
Evidence
- 472 automated tests at 91% line coverage
- CI on every push: lint and tests, Alembic migrations on a real PostgreSQL server, and a boot check of the production Docker image
- Rule-first routing to four specialist agents, with Granite classification only when no rule matches
- Citations built from stored chunk metadata; nutrition calculations done deterministically in Python
- Per-user isolation enforced down to vector search; asynchronous document ingestion
- Deployed on Render in IBM Live mode
Public demo: do not upload personal health documents; treat stored demo data as disposable.
- Python
- Flask
- IBM watsonx.ai
- ChromaDB
- PostgreSQL
- Docker
- pytest
- GitHub Actions
How it works, key decision and limitations
How it works
- Routing: six ordered regex rules route clear requests without an LLM call (small talk and BMI checks are answered directly). Granite classifies the rest at temperature 0 against an allow-list, and any failure falls back to the Knowledge agent. The agents are role-specific handlers, not an autonomous planning loop.
- Retrieval: PDFs are split into page-bounded chunks (800 characters, 120 overlap), embedded, and searched in ChromaDB (top 5). Each embedding provider has its own collection and similarity threshold, and answers below it are labelled general knowledge.
- Calculations: BMR and TDEE (Mifflin-St Jeor), macro targets, WHO BMI categories, meal totals from an 85-food table and the five-component health score are plain Python.
- Engineering: Flask, SQLAlchemy and Alembic on SQLite or PostgreSQL. Uploads return 202 and are indexed on a bounded background pool. The multi-stage Docker image runs as a non-root user.
- Security: CSRF protection, a nonce-based Content-Security-Policy, rate limits on authentication, chat, upload and account endpoints, and account export and deletion.
Key decision
One process with eight threads, deliberately. ChromaDB caches its vector-index reader per process, so a second worker would report the right chunk count and then find nothing when it searched. The app takes an advisory lock on the vector-store directory at startup and logs an explanation if a second process appears.
Limitations
- Password-reset links are written to the application log, not emailed.
- Single process only; scaling out needs ChromaDB moved out of the application.
- No error tracking, alerting or uptime monitoring.
- Live watsonx.ai calls are not exercised in CI; the test suite runs in demo mode.
- No OCR for scanned PDFs. General nutrition information, not medical advice.
