Forecasting for slow-moving retail SKUs, where most days have zero sales and ordinary time-series models fall apart. The project pairs an intermittent-demand forecasting pipeline with an agent and a domain knowledge base, served from a live AWS deployment and driven from a React dashboard.
At the item-store-day level, most retail SKUs sell nothing on a given day. In the M5 (Walmart) dataset, 51% of days are zero and 87% of series are intermittent or lumpy. MAPE is undefined when the actual is zero, so accuracy is measured with WAPE and MASE, and the models that actually work are the intermittent-demand family (Croston, SBA, TSB, ADIDA) alongside gradient-boosted trees.
Each series is classified by how often it sells (ADI) and how variable the quantity is (CV²), which gives four regimes: smooth, intermittent, erratic, and lumpy. Different regimes are won by different models, which is the idea behind the router below.
- Forecaster. A ladder from naive baselines through the Croston family to a global LightGBM model, backtested per regime. A router picks the model that wins each regime, and forecasts are reconciled with MinT so they stay consistent across the item, department, store, and state hierarchy.
- Copilot. A Bedrock agent over the forecast API. It sends simple lookups to a cheaper model (Haiku) and reasoning to a stronger one (Sonnet), and calls tools for forecasts, recent history, and driver explanations.
- Knowledge base. A retail demand-planning corpus covering product categories, SNAP and holiday effects, weekly seasonality, and replenishment. The copilot can search it, so one assistant answers both "what is the forecast?" and "how do SNAP days move demand, and how much should I stock?"
- Dashboard. A React UI over a FastAPI backend that ties the pieces together.
Accuracy comes from a rolling-origin backtest: train before a cutoff, then score against the real sales after it. On the full M5 Foods set of 14,370 series, the regime-aware router lands ahead of every individual model on the held-out window.
In practice the gains over a well-tuned moving average are small, which is expected for intermittent demand. The lasting wins are per-regime routing, where LightGBM is about 6% better than the baseline on smooth SKUs, and hierarchical reconciliation, which drops the cross-level coherence error from 16.1 to 0 so the store, department, and total forecasts add up.
The same Forecast Explorer routes a smooth SKU to LightGBM and an intermittent one to TSB:
| Smooth demand (LightGBM) | Intermittent demand (TSB) |
|---|---|
![]() |
![]() |
The copilot sends a simple lookup to a cheap model and a reasoning question to a stronger one:
| Dashboard | Knowledge base |
|---|---|
![]() |
![]() |
| System overview and headline numbers. | Grounded answers with cited sources. |
React dashboard -> FastAPI backend (local)
/forecast, /history, /info -> local model or the AWS Lambda
/chat -> Bedrock agent + tools
/rag -> Qdrant + Bedrock
/results -> reports/
train -> MLflow registry -> exported model -> Docker -> ECR -> Lambda + Function URL
drift monitor: EventBridge (daily) -> Lambda -> CloudWatch metric + alarm (reads from S3)
The forecast API and its monitoring run on AWS. The dashboard and backend run locally and call Bedrock and the deployed Lambda. The endpoint is never hard-coded. It comes from an environment variable or is read from the CloudFormation stack output at runtime.
uv sync # Python environment
uv run poe api # FastAPI backend at http://localhost:8000
cd frontend && npm install && npm run dev # React UI at http://localhost:5173Common tasks:
uv run poe <task> |
What it does |
|---|---|
check |
lint, format check, and tests |
data, eda |
fetch M5, write the intermittency report |
benchmark |
backtest the model ladder |
reconcile |
hierarchical reconciliation |
train |
train and register the production router |
serve, api |
the lean forecast API, or the full backend for the UI |
copilot, rag |
the agent and knowledge-base demos |
Deploy with uv run poe export-model, then sam build and sam deploy (see infra/). Tear down
with sam delete --stack-name spf --region us-east-1.
| Area | Tools |
|---|---|
| Language and tooling | Python 3.12, uv, poethepoet, ruff |
| Forecasting | Nixtla (statsforecast, mlforecast, hierarchicalforecast), LightGBM |
| MLOps | MLflow, Docker |
| Cloud | AWS Lambda, Function URL, ECR, S3, EventBridge, CloudWatch, SAM |
| Agent and RAG | Bedrock (Haiku and Sonnet, Titan embeddings), Qdrant, Mangum |
| Frontend | React, TypeScript, Vite, Recharts |
| CI | pytest, pre-commit with gitleaks, GitHub Actions |
src/spf/ data, intermittency, models, reconcile, evaluate, registry, serving, agent, rag
frontend/ React + TypeScript + Vite dashboard
kb/ retail knowledge base (RAG corpus)
infra/ Dockerfiles, SAM template, drift monitor
notebooks/ EDA, model benchmark, reconciliation
config/, tests/, reports/






