A 2-Hour Cashier Training Class Inspires What I Want to Build B2C AI

A 2-Hour Cashier Training Class Inspires What I Want to Build B2C AI
Amice Wong
1 day, 6 hours ago
9 min read
A 2-Hour Cashier Training Class Inspires What I Want to Build B2C AI

As a B2B AI developer, I spend my days building systems that find business signals and automate workflows. Recently, out of pure curiosity, attended a two-hour training class for cashiers at a physical retail store.

This post walks through what that class inspired me, and the architecture I'd build to fix it.


From basket data to a personalized, context-aware offer.


In this article:

The Problem

I expected to learn POS interfaces, cash handling, and inventory lookups. Instead, I found a business opportunity that neither human cashiers nor self-checkout have captured well: Upselling with Intent Signal.

Every time I pay at Walmart, I'm still asked to apply for a credit card — after almost two years in Toronto. Why not suggesting somethings based on what I'm actually buying? In convenience stores, cashiers' upselling of extra stacks ends up like robotic. In online shopping, the logic is just Simple Matching: steak always goes with BBQ sauce, for everyone, regardless of context and the target, no matter a young man or a housewife.

What we just need is a hyper-personalized digital handoff.

Picture a steak bought with champagne, by a man. Does he need flowers, or ice cream, to round out a romantic dinner, or a family celebration? And what if he got a same-day discount on that item — sent via SMS, or a QR code on the receipt?

What we need is Intent Intelligence.

Steak → BBQ Sauce

vs.

We noticed you're preparing a celebration.
Here are three offers valid until midnight.

Here's how I'd architect that.

What am I going to build?

Here's the design of Retail Intelligence System I'm building for Upselling:

Transaction
      |
      v
Signal Gathering (Demo 2)
      |
      v
Signal Enrichment
      |
      v
Intent Classification (Demo 2)
      |
      v
Contextual Retrieval (RAG) (Demo 3)
      |
      v
Recommendation Generation (Demo 3)
      |
      v
Customer Feedback
      |
      v
Continuous Learning

For a side by side comparison of the results, I will setup:

  • Demo 1: a traditional POS matching recommendation, &
  • Demo 2 & Demo 3: the simple intent intelligence I would like to build.

Setting Up the Project

python3 -m venv ragvenv
source ragvenv/bin/activate
mkdir retail-intent-demo
cd retail-intent-demo

Folder structure:

retail-intent-demo/
│
├── baskets.csv
├── traditional_pos.py
├── intent_rag.py
├── promotion_rag.py
└── requirements.txt

requirements.txt

pandas
mlxtend
sentence-transformers
scikit-learn
pip install -r requirements.txt

Basket Data

Common basket data pulled from all customers.

baskets.csv

TransactionID,Items
1,"Steak,BBQ Sauce,Beer"
2,"Steak,BBQ Sauce"
3,"Steak,Garlic Bread"
4,"Steak,Black Pepper"
5,"Champagne,Chocolate,Flowers"
6,"Champagne,Wine Glass"
7,"Cake,Candles,Balloons"
8,"Tent,Flashlight,Marshmallow"
9,"Steak,BBQ Sauce,Black Pepper"
10,"Chocolate,Flowers"

Demo 1: Traditional Recommendation (Association Rules)

What the Common "Steak → BBQ" Logic Actually Does

Apriori is commonly used to learn association rules from a basket dataset.

traditional_pos.py

import pandas as pd
from mlxtend.preprocessing import TransactionEncoder
from mlxtend.frequent_patterns import apriori, association_rules

# Load CSV
df = pd.read_csv("baskets.csv")

transactions = []

for row in df["Items"]:
    transactions.append([x.strip() for x in row.split(",")])

# One-hot encoding
te = TransactionEncoder()
te_array = te.fit(transactions).transform(transactions)

basket_df = pd.DataFrame(te_array, columns=te.columns_)

# Apriori
frequent_itemsets = apriori(
    basket_df,
    min_support=0.2,
    use_colnames=True
)

rules = association_rules(
    frequent_itemsets,
    metric="confidence",
    min_threshold=0.5
)

print("\n===== Association Rules =====\n")

for _, r in rules.iterrows():
    print(
        f"{list(r['antecedents'])} -> {list(r['consequents'])}"
    )

# -----------------------------
# Demo basket
# -----------------------------

basket = [
    "Steak",
    "Champagne",
    "Chocolate"
]

print("\n===== Traditional Recommendation =====")

recommendations = set()

for _, r in rules.iterrows():

    antecedent = set(r["antecedents"])

    if antecedent.issubset(set(basket)):
        recommendations.update(r["consequents"])

recommendations -= set(basket)

print("Basket:", basket)
print("Recommendations:", list(recommendations))

Results:

===== Association Rules =====

['Steak'] -> ['BBQ Sauce']
['BBQ Sauce'] -> ['Steak']
['Black Pepper'] -> ['Steak']
['Chocolate'] -> ['Flowers']
['Flowers'] -> ['Chocolate']

===== Traditional Recommendation =====
Basket: ['Steak', 'Champagne', 'Chocolate']
Recommendations: ['Flowers', 'BBQ Sauce']

The signal gathering step (the data pipeline feeding this) could come from the POS system itself. Not bad — at least it's not "credit card, two years running."


Traditional POS logic: steak always means BBQ sauce, no matter who's buying.

Why It Fails: Product Affinity Isn't Customer Intent

The algorithm has no understanding of why those products were purchased. It only knows what tends to sit in the same basket.

Here's what Intent Intelligence does differently: instead of relying only on basket data, much more signal can be brought in.

Signals Worth Enriching
This article focuses on the ones marked with *

  • Basket *
  • Time
  • Store
  • Weather
  • Cashier
  • Customer *
  • Payment
  • Coupon
  • Inventory
  • Location
  • Holiday *
  • Previous visits *

Complicated? No — just more context than a single basket snapshot gives you.

Demo 2: Intent Classification with Embeddings

Now we set Apriori aside entirely. Instead, we compare the basket against a set of possible intents.

all-MiniLM-L6-v2 is the embedding model used here to build the vector representation on the fly.

I previously wrote about building a RAG pipeline with LlamaIndex — Tasting RAG with LlamaIndex — which covers the classic chatbot RAG shape:

Question
    |
    v
Vector DB
    |
    v
LLM

This time, the architecture is different. Here, RAG retrieves business context, not an answer to a question:

POS Webhook
      |
      v
Basket
["Steak","Champagne","Chocolate"]
      |
      v
Intent Engine
"Romantic Dinner"
      |
      v
RAG retrieves

- today's promotions
- nearby inventory
- customer preferences
- weather
- local events
- coupon rules
- supplier campaigns
- CRM profile
      |
      v
LLM generates

"15% off flowers today"
or
"Fresh strawberries available in aisle 5"
or
"Premium wine glasses now 20% off."


From basket data to a personalized, context-aware offer.

intent_rag.py

from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity

model = SentenceTransformer("all-MiniLM-L6-v2")

basket = [
    "Steak",
    "Champagne",
    "Chocolate"
]

basket_text = ", ".join(basket)

intent_library = [
    "Romantic Dinner",
    "Family BBQ",
    "Birthday Party",
    "Camping",
    "Movie Night",
    "Weekly Grocery",
    "Office Party"
]

basket_embedding = model.encode([basket_text])

intent_embeddings = model.encode(intent_library)

scores = cosine_similarity(
    basket_embedding,
    intent_embeddings
)[0]

print("\n===== Intent Scores =====\n")

for intent, score in zip(intent_library, scores):

    print(f"{intent:20} {score:.3f}")

best = scores.argmax()

intent = intent_library[best]

print("\nDetected Intent")

print(intent)

Results:

===== Intent Scores =====

Romantic Dinner      0.438
Family BBQ           0.348
Birthday Party        0.270
Camping               0.154
Movie Night            0.204
Weekly Grocery        0.175
Office Party           0.234

Detected Intent
Romantic Dinner

Instead of "BBQ Sauce," we now have a far more meaningful marketing signal: Romantic Dinner.

Imagine you're the marketing manager. You immediately have a clearer idea of where to focus, if a significant share of customers walk in with this same intent. What you can sell and plan around is no longer just BBQ Sauce. It's an entire occasion.

This is the power of Leading with Signal: signal → what you should sell.

Why That's Still Not Enough

Knowing the intent — "Romantic Dinner" — is a big improvement. But intent alone doesn't know what's actually happening in the business today: what's on promotion, what's in stock nearby, what holiday it is, what this customer has bought before. Without that layer, "Romantic Dinner" is just a label. The next step is retrieving that context and turning it into an actual offer.

Demo 3: Contextual RAG Retrieves Business Context

Once intent is known, the next step in the pipeline is a RAG layer that recommends what to push, as an SMS, or a QR code on the customer's receipt.

promotion_rag.py

context = {
    "Romantic Dinner": {
        "promotion": "20% Roses",
        "holiday": "Valentine's Day",
        "inventory": "Flowers Available"
    },
    "Family BBQ": {
        "promotion": "10% Charcoal",
        "holiday": "Summer BBQ Weekend",
        "inventory": "BBQ Sauce Available"
    },
        "Camping": {
        "promotion": "15% Flashlights",
        "holiday": "National Park Week",
        "inventory": "Camping Gear Available"
    }
}

intent = "Romantic Dinner"

ctx = context[intent]

print("\n===== Context & Recommendation =====")

print(
    f"""
Detected Intent : {intent}

Promotion     : {ctx['promotion']}
Holiday       : {ctx['holiday']}
Inventory     : {ctx['inventory']}

Send customer a QR coupon.
"""
)

Results:

===== Context & Recommendation =====

Detected Intent : Romantic Dinner

Promotion     : 20% Roses
Holiday       : Valentine's Day
Inventory     : Flowers Available

If you noticed there's no real vector database exist in this code. I'm delighted you're still with me this deep into the article. Yes, I used plain-text JSON here, on purpose, to keep the demo simple.

My earlier article on Tasting RAG with LlamaIndex already covers embedding → vector → similarity search → retrieval in depth. This piece focuses on how those same building blocks get repurposed into an Intent Intelligence architecture for physical retail.


Detected Intent: Romantic Dinner — 20% off roses, tied to Valentine's Day, with flowers in stock nearby.

Demo 4: Feedback Loop Improves Future Recommendations

The last two stages in the design — Customer Feedback and Continuous Learning, close the loop: whether a customer redeems the QR offer or ignores it becomes a new signal, feeding back into the intent library and context rules over time. That part isn't built yet in this demo; it's the natural next step once the core pipeline above is running in production.

Key Takeaways

  • Association rules (Demo 1) show what sells together, not why. They treat every basket the same, regardless of occasion.
  • Intent classification (Demo 2) reframes the signal. The same basket becomes "Romantic Dinner" — a far more useful signal than a single product pairing.
  • Intent alone isn't enough. Contextual retrieval (Demo 3) — promotions, inventory, holidays, CRM — turns a detected intent into a specific, timely offer.
  • The whole system start at one line: SentenceTransformer("all-MiniLM-L6-v2"). Simple, but foundational.
  • all-MiniLM-L6-v2 is general-purpose. Enterprises serious about this will eventually train their own retail-specific embedding model.

Next up: how enterprises build their own Retail Embedding Models.


Great job! Take a coffee break before reading more Amice's articles :P

⁠Simplicity is prerequisite for reliability_Amice_Dev
⁠Simplicity is prerequisite for reliability. ⁠Without clarity, systems become fragile and unpredictable.

Related blogs

Full-Stack Next.js with TypeScript & Shadcn - New API with Frontend Project Checklist
Full-Stack Next.js with TypeScript & Shadcn - New API with Frontend Project Checklist

By Amice Wong

Read more
Building a Chrome Extension from Scratch — My ClosMore AI Experiment
Building a Chrome Extension from Scratch — My ClosMore AI Experiment

By Amice Wong

Read more
Building an "On-Demand AI Translator" for My Next.js Blog (Without Monthly Fees)
Building an "On-Demand AI Translator" for My Next.js Blog (Without Monthly Fees)

By Amice Wong

Read more
[8-Day Closmore SaaS Challenge] Day 5-6 — "What If...?"
[8-Day Closmore SaaS Challenge] Day 5-6 — "What If...?"

By Amice Wong

Read more
Closmore MVP: 20% Coding 80% SalesOS
Closmore MVP: 20% Coding 80% SalesOS

By Amice Wong

Read more
Can AI Build Your Website? A Full-Stack Developer’s Perspective on Vibe Coding
Can AI Build Your Website? A Full-Stack Developer’s Perspective on Vibe Coding

By Amice Wong

Read more
SEO & AEO: Is Your Blog Technically Optimized for AI and Search Engine?
SEO & AEO: Is Your Blog Technically Optimized for AI and Search Engine?

By Amice Wong

Read more
Blogging SEO & AEO: Step-by-step guide to implement MDX
Blogging SEO & AEO: Step-by-step guide to implement MDX

By Amice Wong

Read more
Tasting RAG with LlamaIndex in Python
Tasting RAG with LlamaIndex in Python

By Amice Wong

Read more
A 2-Hour Cashier Training Class Inspires What I Want to Build B2C AI
A 2-Hour Cashier Training Class Inspires What I Want to Build B2C AI

By Amice Wong

Read more