Skip to main content
Glama
yash-desai-naik

Nutrition Research Assistant

Nutrition Research Assistant

Production-grade multi-agent Indian nutrition research assistant: answers questions about Indian food and nutrition by combining a curated internal knowledge base, hybrid RAG, specialized agents, MCP tools, conversational memory, optional live web search, graceful failure handling, and end-to-end OpenTelemetry-compatible tracing.

graph TD
    Client[CLI / Swagger / Demo script] --> API[FastAPI :8000]
    API --> Sup[Agno Supervisor]
    Sup --> KA[Knowledge Agent]
    Sup --> CA[Calculator Agent]
    Sup --> DA[Document Agent]
    Sup --> WA[Health/Web Agent]
    KA --> MCP[FastMCP Server :8001/mcp]
    CA --> MCP
    DA --> MCP
    WA --> MCP
    MCP --> Chroma[ChromaDB + BM25 + RRF + Reranker]
    MCP --> Calc[Safe calculator]
    MCP --> Web[Agno DDGSTools]
    MCP --> Docs[documents.json]
    Sup --> Mem[(SQLite memory)]
    API --> OTel[OpenTelemetry + structlog]

Core principle (PRD §89): the LLM decides what should happen; MCP tools perform what must happen deterministically. Agents never touch Chroma directly — everything flows through MCP.

Tech Stack

Layer

Choice

Python / packages

3.12+ · uv (pyproject.toml + lock)

LLM

DeepSeek deepseek-v4-flash (OpenAI-compatible)

Agents

Agno (Supervisor + 4 specialized agents)

MCP

FastMCP server, streamable-http on :8001/mcp

Vector DB

ChromaDB (local, cosine)

Embeddings

BAAI/bge-small-en-v1.5 (local)

Reranker

BAAI/bge-reranker-base (local cross-encoder)

BM25

rank_bm25

Memory

Agno sessions + SQLite (data/app.db)

API

FastAPI + uvicorn

Observability

OpenTelemetry (console exporter; OTLP-swappable) + structlog

Web search

Agno built-in DDGSTools (optional)

Repository Layout

corpus/         8 curated nutrition documents (frontmatter + per-food tables)
scripts/        ingest.py · rebuild_index.py · test_retrieval.py · eval_questions.json
                test_mcp_wiring.py · test_agents.py · test_memory.py · demo.py
src/
  api/          FastAPI app, routes, schemas          (chat / health / ready)
  agents/       models.py (DeepSeek) · mcp_client.py · agents.py (specialists + supervisor)
  mcp_server/   server.py + tools/{knowledge,calculator,documents,web}.py
  rag/          embeddings · chroma · bm25 · fusion (RRF) · reranker · pipeline
  memory/       SQLite storage
  services/     circuit breaker · retry
  observability tracing (OTel) · logging (structlog)
  config/       settings.py          core/ errors.py · models.py
  cli.py        REPL client
tests/          unit + API tests

Quick Start

# 1. Environment
uv venv --python 3.12
uv sync

# 2. Secrets
cp .env.example .env        # set DEEPSEEK_API_KEY

# 3. Ingest the corpus (downloads bge-small-en-v1.5 on first run)
uv run python scripts/ingest.py

# 4. Start the MCP tool server  (terminal 1)
uv run python -m src.mcp_server.server

# 5. Start the API                 (terminal 2)
uv run uvicorn src.api.app:app --reload --port 8000

Swagger UI: http://127.0.0.1:8000/docs (API port is configurable via API_PORT in .env — this workspace uses 8002 because 8000 is taken)

Try it:

uv run python -m src.cli                # interactive REPL
uv run python scripts/demo.py           # scripted 5-question demo
curl -X POST http://127.0.0.1:8000/api/v1/chat \
  -H "Content-Type: application/json" \
  -d '{"session_id":"demo","message":"How much protein is in 100g cooked chickpeas?"}'

API

Endpoint

Purpose

POST /api/v1/chat

{session_id, message}{status, session_id, response, sources[], trace_id}

GET /health

{status, mcp, chroma, llm} — liveness, no secrets

GET /ready

{status, checks} — alive vs ready distinction

Degraded responses carry status: "degraded", a human-readable message, and the trace_id — never a 500 traceback.

MCP Tools (port 8001)

Tool

Backend

Notes

search_knowledge(query, top_k, filters)

hybrid RAG (dense + BM25 + RRF + rerank)

returns chunks with document_id / score / method

calculate(expression)

AST-whitelisted safe evaluator

no eval, no code execution

get_document(document_id)

ingested documents.json

never fabricates ids

search_web(query)

Agno DDGSTools

optional; WEB_SEARCH_ENABLED

search_health_information(query)

DDGSTools + domain ranking

prioritizes WHO/ICMR/NIH/CDC

Agents

Agent

Tools

Handles

Supervisor

team of 4

intent, routing, composition, memory

Knowledge

search_knowledge

nutrition facts, comparisons, raw vs cooked

Calculator

calculate

serving scaling, totals

Document

get_document

document retrieval

Health/Web

web + health search

current info, general health (educational only)

Failure Handling

  • MCP down → controlled degraded response with trace_id (no 500); circuit breaker fails fast, probes after cooldown (PRD §70/§85)

  • MCP timeout — 10s transport read timeout; agent run bounded by AGENT_TIMEOUT_SECONDS

  • Chroma down → "Knowledge retrieval temporarily unavailable" (optionally falls back to web)

  • LLM down / bad keyLLM_UNAVAILABLE-style degraded message

  • Web disabled → "Live search is currently unavailable."

  • Retry policy: 2 attempts with backoff, then degrade — never endless

Observability

Every request produces one trace: api.request → supervisor.run → mcp.<tool> → rag.dense_search / rag.bm25 / rag.rrf / rag.reranker, with trace_id echoed in the API response, logs, and spans. All logs are JSON (structlog) with timestamp, level, logger, trace_id, span_id. Set OTEL_EXPORTER_OTLP_ENDPOINT to export to Jaeger/Tempo/Collector (uv sync --extra otel).

Tests & Retrieval Eval

uv run pytest -q
uv run python scripts/test_retrieval.py --rerank   # Recall@5/10, MRR, Hit@1/3 over 24 questions

The eval dataset (scripts/eval_questions.json, 24 questions across 7 categories) measures retrieval quality; the reranker lifts Hit@1/3 over raw fusion. Results land in data/eval/results.json.

Definition of Done (PRD §87)

  • Core — FastAPI runs; DeepSeek via OpenAI-compatible client; Agno agents operational; FastMCP operational; Chroma populated; 8 documents; memory works

  • MCP — all 5 tools work and agents use MCP as the real execution path

  • RAG — dense + BM25 + RRF + cross-encoder rerank; metadata preserved; eval exists

  • Agents — Supervisor + 4 specialists; multi-agent composition works

  • Memory — session ids; follow-ups resolve; persisted in SQLite

  • Observability — trace_id per request; nested spans; MCP/RAG/LLM/errors traced

  • Failure handling — MCP unavailable/timeout, Chroma, LLM, web all controlled; no unhandled exception reaches the user

Troubleshooting

  • Hugging Face symlink warning — Windows-only; set HF_HUB_DISABLE_SYMLINKS_WARNING=1 (cosmetic)

  • Retrieval returns nothing — Chroma empty: run python scripts/ingest.py

  • "tool service unavailable" — MCP server not running (start it) or breaker cooling down (wait ~30s, or restart API)

  • Health says llm not_configuredDEEPSEEK_API_KEY missing in .env

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/yash-desai-naik/interview-task-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server