A prototype e-commerce storefront where the user navigates and shops by typing natural-language commands instead of clicking. Built in 2023 as an exploration of using a zero-shot language model as the primary UI control surface.
Status: archived prototype (2023). Kept as-is for reference.
You type things like:
- "show me the cart"
- "sort products from cheap to expensive"
- "add the red running shoes to my cart"
- "tell me more about the leather wallet"
…and the app routes you to the right page or performs the right action. No buttons required.
user text
│
▼
┌──────────────────────┐
│ BART-large-MNLI │ zero-shot classification against
│ (zero-shot) │ 9 candidate intent labels
└──────────┬───────────┘
│ intent (e.g. "add to cart")
▼
┌──────────────────────┐
│ intent → handler │ FastAPI route mapping
└──────────┬───────────┘
│
├── for product-targeted intents:
│ fuzzy-match (Levenshtein, fuzzywuzzy)
│ instruction text against product names
│ → resolve product_id
│
▼
redirect / DB action / rendered page
Two pieces of NLP, both running locally:
- Intent classification.
facebook/bart-large-mnlivia Hugging Facetransformerszero-shot-classificationpipeline. The candidate labels are the 9 actions the storefront supports (home,products,cart,add to cart,remove from cart,product's price high to low, etc.). No training data, no fine-tuning — the model picks the most entailed label. - Entity extraction. For intents that target a specific product (
add to cart,remove from cart,product's details information), the instruction is fuzzy-matched against every product name in the database usingfuzz.partial_ratio. If the best match clears a similarity threshold (70), that product's ID is used.
- Zero-shot over fine-tuning — there was no labeled dataset and the action set was small and well-defined. NLI-based zero-shot classification handles this regime cleanly, and changing the action set means editing a Python list, not retraining.
- Fuzzy match over NER — product catalogs change constantly and a NER model would need re-training each time. Levenshtein against the live
productstable always reflects current inventory. - Local inference — kept the whole stack runnable on a laptop with no API keys or per-request cost.
- Backend: FastAPI, Uvicorn
- NLP: Hugging Face Transformers,
facebook/bart-large-mnli, fuzzywuzzy - Database: MySQL (products, cart)
- Frontend: Jinja2 templates, vanilla HTML/CSS
app.py FastAPI app — routes, DB access, intent handling
templates/ Jinja2 templates (home, products, product_detail, cart, account, popup)
static/images/ Product images
schema.rar MySQL schema (products, cart tables)
requirements.txt Pinned Python dependencies
test_intent.py Scratch script for trying out the classification pipeline
# 1. MySQL
# create database `webgpt` and load the schema from schema.rar
# populate the `products` table with rows (id, name, price, description)
# 2. Set the DB password
export DB_PASSWORD=your_mysql_password
# 3. Install + run
pip install -r requirements.txt
python app.py
# → http://127.0.0.1:8000The first request triggers Hugging Face to download the BART-MNLI weights (~1.6 GB), so cold start is slow. After warmup, classification takes ~300 ms on CPU; faster with CUDA.
Things that were good-enough for a prototype but not what I'd ship today:
- No accuracy measurement. Intent quality was eyeballed during development, not measured on a labeled holdout. A real version starts with ~50 hand-labeled instructions per intent and tracks top-1 accuracy as the candidate label set evolves.
- Fuzzy-match threshold of 70 is arbitrary. Picked because it worked on full product names ("Apple iPhone 13") and rejected unrelated text. It also rejects partial names ("iphone"), which we caught in testing. A real version tunes this on a labeled instruction set, or replaces it with an embedding-based product lookup.
- Label-design overlap. "products" and "product's price low to high" share most of their semantic space; zero-shot NLI confuses them. Better label design (or two-stage classification: action category → modifier) would help.
- Cold start is ~1.6 GB. First request triggers the BART-MNLI download. For a real deployment the model would be baked into the container image or pre-loaded.
This was a prototype, not a product. Things it doesn't do:
- Auth (the
/accountpage is a stub). - Quantity-aware add/remove (always ±1).
- Multi-product instructions ("add the wallet and the shoes").
- Anything resembling input validation or rate limiting on the LLM endpoint.
Walkthrough video: https://www.youtube.com/watch?v=ab88SojP580