A conversational analytics assistant for an FMCG Beverages category team. Business users ask natural-language questions about sales, promotions, regional performance, inventory, and forecasts; the app answers using a synthetic dataset.
User question
│
▼
query_engine.parse_query() ── rule-based intent + entity extraction
│ (regex/fuzzy matching on product/region/
│ category/promo/time), assigns a confidence
│ level: high / medium / low
│
├── confidence = low ──► llm_intent.classify_intent_with_llm()
│ OPTIONAL: re-classifies intent via Groq
│ (llama-3.1-8b-instant). If no API key, or
│ classification fails, the original
│ rule-based intent is kept.
│
▼
query_engine.run_parsed_query() ── computes EXACT numbers from pandas
│ (source of truth, fully auditable)
▼
llm_layer.rephrase_with_llm() ── OPTIONAL: rephrases the factual summary
│ conversationally via Groq. Numbers are
│ NEVER recalculated by the LLM.
▼
Streamlit chat UI (app.py)
- Reliability / no hallucinated numbers: all figures come from deterministic pandas computations, auditable via the "show underlying data summary" expander in the UI.
- Cost-aware LLM usage: the rule-based engine handles the large majority of expected questions with zero API calls. The LLM intent classifier only fires as a fallback for genuinely ambiguous questions (confidence = "low") — most traffic never triggers it. The phrasing layer is a second, independent optional call.
- Deployability: Streamlit Community Cloud has no GPU / local model access, so a local LLM (e.g. Ollama) isn't viable there. Groq's free API is a drop-in, low-latency alternative — and both LLM features are fully optional.
- Graceful degradation: if no API key is set, or any API call fails, the app falls back to the rule-based result instead of breaking. The app is 100% functional with zero API keys configured.
- Auditable forecasting: the forecast feature uses a simple, explainable linear-trend projection (no external ML library, no LLM) rather than a black-box model — appropriate for a "Risk Awareness" lens, since a forecast you can't explain is hard to defend to a business stakeholder.
Synthetically generated (generate_data.py, seeded for reproducibility)
following the brief's schema:
| File | Rows | Description |
|---|---|---|
data/product_master.csv |
15 | Beverage products across 4 categories (Water, Juice, Energy, Dairy, Carbonated) |
data/store_master.csv |
28 | Stores across North/South/East/West, 4 store formats |
data/sales_promotions.csv |
6,720 | 16 weeks × product × store, with promo flags/types and visible uplift |
data/inventory.csv |
6,720 | Weekly stock movement, derived consistently from sales, with realistic stockouts |
Promo weeks show ~70%+ higher average units sold than non-promo weeks. Inventory has a small but realistic stockout rate (~0.4%) driven by occasional delivery shortfalls.
pip install -r requirements.txt
python generate_data.py # regenerate the synthetic dataset (optional, already included)
streamlit run app.py- Get a free API key at https://console.groq.com/keys
- Local: create
.streamlit/secrets.tomlwith:GROQ_API_KEY = "your-key-here"
- Streamlit Cloud: add
GROQ_API_KEYunder app Settings → Secrets.
With a key set, two things turn on:
- Conversational phrasing of every answer
- LLM-based intent re-classification for low-confidence (ambiguous) questions
Without a key, the app runs fine — answers are the rule-based summaries directly, and ambiguous questions fall back to a general sales summary.
- Push this folder to a public GitHub repo (e.g.
shelfwise). - Go to https://share.streamlit.io → "New app" → select the repo, branch,
and
app.pyas the entry point. - (Optional) Add
GROQ_API_KEYin app settings → Secrets. - Deploy. You'll get a public URL like
https://<app-name>.streamlit.app.
- "How did Spark Lemon Sparkling Water perform during BOGO promotions?"
- "Compare sales across regions for the Carbonated category"
- "What are the top performing products in the South region?"
- "Show me stockouts for juice products in the South region"
- "What's the inventory situation for ColaCo Classic Cola?"
- "Which products are overstocked?"
- "Show me sales for the last 8 weeks"
- "Forecast demand for ColaCo Classic Cola for the next 4 weeks"
- "Predict next 6 weeks sales for VoltMax Energy Drink in the West region"
- Entity matching is keyword/fuzzy-based — ambiguous or very informally phrased product names may not resolve correctly.
- Only one product/region/category filter is applied at a time per dimension (no complex boolean queries like "Region A or Region B").
- "Overstock" and "stockout" thresholds are heuristic definitions chosen for this dataset, not official inventory-policy definitions.
- Forecasts use a simple linear trend on historical units sold; they do not account for planned promotions, seasonality beyond the existing trend, or supply constraints — explicitly stated in every forecast response.
- Both LLM layers (phrasing and intent fallback) only enhance the experience; if Groq's free tier rate-limits or is down, the app falls back to plain rule-based text and rule-based intents (by design).
- Evaluation set of Q&A pairs with expected numeric answers, for regression-testing the query engine
- Multi-dimensional filters (e.g. two regions at once)
- Seasonal decomposition in the forecast model
- Confidence-aware UI (visually flag when an LLM fallback was used)