Back
Conversational AI & Chatbots

A BERT-Driven Restaurant Chatbot That Classifies Guest Intent, Not Keywords

How Pfactorial Technologies built a restaurant chatbot that uses BERT sentence embeddings and cosine similarity to classify guest intent across menu, reservation, recipe, and feedback conversations.

August 21, 2026
Share
ENGAGEMENT SNAPSHOT

Pfactorial_Case_Study_CuisineWhiz image 1
Figure 1 - Key figures from this engagement, at a glance.
EXECUTIVE SUMMARY
Our client wanted a restaurant website chatbot that could actually answer varied natural-language questions - menu items, dietary options, reservations, recipes, day-based suggestions - not just match a fixed set of button-driven FAQ intents.
A naive approach, keyword matching or a rigid decision tree, fails here because guests phrase the same request many different ways; the system has to understand intent from free text, route it to the right handler among several very different capabilities, and still fall back gracefully on greetings and general conversation.
Pfactorial built a Flask chatbot that embeds user input with a pre-trained BERT model, classifies intent by cosine similarity against pre-embedded example patterns, and routes the result through a dedicated response generator for each conversational capability, backed by MongoDB for menu, feedback, and reservation data.
Why this engagement is representative This engagement demonstrates Pfactorial's ability to build a genuinely intent-driven conversational interface - semantic classification over embeddings, not keyword matching - for a use case where guests won't phrase requests the same way twice.
THE CHALLENGE
Understanding what a guest actually wants from free-text input, and routing it correctly among several very different conversational capabilities, meant going beyond keyword matching.

1. Guests don't phrase requests consistently

The same request - asking about vegan options, or booking a table - arrives worded a dozen different ways, which a fixed keyword or button-driven system can't reliably match.

2. Six different capabilities have to share one conversation

Menu inquiry, reservations, recipes, suggestions, feedback, and general conversation are handled by different logic entirely, but the user experiences them as one continuous chat.

3. Intent classification has to work without a large labeled training set

Rather than training a classifier from scratch, user input is embedded with a pre-trained BERT model and matched by cosine similarity against a smaller set of pre-embedded example patterns.

4. Structured actions still need to come out of free text

A reservation request has to yield a specific seat count and time, not just a recognized intent label, before book_table() can act on it.
The real brief Not "a scripted FAQ bot" but "a chatbot that classifies real intent from free text and routes it to the right capability, every time."
THE SOLUTION
Pfactorial built a Flask chatbot around a BERT-embedding intent classifier: user input is normalized, embedded, matched by cosine similarity against pre-embedded intent patterns, and routed to a dedicated response handler.
Pfactorial_Case_Study_CuisineWhiz image 2
Figure 1 - Every message is normalized, embedded, and classified before it ever reaches a response handler.

Architectural principles

  • Semantic matching over keyword matching - BERT embeddings and cosine similarity classify intent by meaning, so differently-worded requests for the same thing still route correctly.
  • One classifier, many downstream handlers - intent_classifier.py only determines what the user wants; response_generator.py owns a separate function per capability, keeping classification and response logic independent.
  • Preprocessing as a distinct pipeline stage - Tokenization, stopword removal, and lemmatization run before embedding, in their own module, so classification quality doesn't depend on handling raw, unnormalized text inline.
  • MongoDB as the shared data layer for menu, feedback, and reservations - Every capability that needs persistent data reads or writes through the same PyMongo-backed store, rather than each handler maintaining its own state.
CAPABILITIES DELIVERED
Each capability is a dedicated handler in response_generator.py, reached through the same intent-classification path.
CAPABILITY
WHAT IT DOES
Menu inquiry
Answers questions about specific items, vegetarian and vegan options, and special offers.
Table reservation
book_table() handles booking requests, capturing seat count and preferred time.
Recipe information
recipe_enquiry() answers questions about the recipe behind a specific dish.
Day-based suggestions
suggest() recommends dishes based on the day of the week.
Feedback collection
record_feedback() captures positive or negative feedback about the guest's experience.
General conversation
Handles greetings, farewells, and other general conversational turns outside the restaurant-specific intents.
Pfactorial_Case_Study_CuisineWhiz image 3
Figure 2 - One classification path feeds six independent, purpose-built response handlers.
Design note Matching intent by cosine similarity against a set of pre-embedded example patterns, rather than training a dedicated classifier, is what let this ship without a large labeled dataset - the tradeoff is classification quality is bounded by how well those example patterns cover real phrasing, which is exactly the limited-dataset constraint flagged as the top thing to revisit.
ENGINEERING FOR SCALE AND RELIABILITY
Four decisions kept the conversation pipeline accurate without requiring a large training investment.

Pre-trained BERT embeddings over a from-scratch classifier

Sentence embedding via a pre-trained transformer model gives semantic intent matching without training a classification model from labeled data the client didn't have.

Cosine similarity against pre-embedded patterns, not a trained decision boundary

detect_intent() compares a normalized message's embedding to a fixed set of pre-embedded example patterns, keeping the classification step simple, inspectable, and cheap to extend with new patterns.

A dedicated preprocessing module ahead of embedding

sentence_normalizer.py's tokenization, stopword removal, and lemmatization run as an isolated step, so embedding always sees cleaned input regardless of which handler eventually receives the result.

MongoDB via PyMongo for menu, feedback, and reservation data

A document store fits the varying shapes of menu items, feedback entries, and reservation requests better than a fixed relational schema would for this scope.
DELIVERY APPROACH
The pipeline was built bottom-up, from text normalization through to the response handlers guests actually see.
1. Preprocessing pipeline - building sentence_normalizer.py - tokenization, punctuation and stopword removal, and lemmatization.
2. Embedding and intent data - building data_embedder.py to embed and store the reference intent patterns used for classification.
3. Intent classification - building intent_classifier.py, matching normalized user input against pre-embedded patterns by cosine similarity.
4. Response generation and Flask app - building response_generator.py's per-intent handlers and wiring them into app.py's chat interface.
RESULTS AND IMPACT

Pfactorial_Case_Study_CuisineWhiz image 4
- Key outcomes from this engagement.
The chatbot handles six distinct conversational capabilities - menu inquiry, table reservation, recipe information, day-based suggestions, feedback collection, and general conversation - through a single BERT-embedding intent classifier rather than separate keyword-matching logic for each.
Because intent is classified semantically rather than by keyword, guests can phrase the same request in different ways and still reach the right handler, and because MongoDB backs menu, feedback, and reservation data through one PyMongo layer, the restaurant's data stays in one place rather than split across handler-specific stores.

What it enabled commercially

The restaurant now has a self-service conversational channel for menu questions, reservations, and feedback that doesn't require a large labeled training dataset to operate, with a clear path - noted directly in the delivered scope - to add conversation-context handling and deeper system integrations as usage grows.
WHY PFACTORIAL
This engagement reflects Pfactorial's practice of building intent-driven conversational systems on semantic embeddings rather than brittle keyword rules, so the interface holds up against the way people actually phrase requests.
Pfactorial_Case_Study_CuisineWhiz image 5
- Service lines this engagement draws on.
Engagement enquiries Pfactorial Technologies works with organisations that want a conversational interface guests will actually get useful answers from, not a scripted FAQ bot. If you're scoping a chatbot or conversational feature, we're happy to talk through what a semantic-classification approach would take. · pfactorial.ai
APPENDIX A - TECHNOLOGY STACK
The technology stack underpinning the system, grouped by the layer it serves.
Pfactorial_Case_Study_CuisineWhiz image 6

Result and Analysis

ENGAGEMENT SNAPSHOT

How Pfactorial Technologies built a restaurant chatbot that uses BERT sentence embeddings and cosine similarity to classify guest intent across menu, reservation, recipe, and feedback conversations.

Pfactorial_Case_Study_CuisineWhiz image 1
Pfactorial_Case_Study_CuisineWhiz image 2
Pfactorial_Case_Study_CuisineWhiz image 3
Pfactorial_Case_Study_CuisineWhiz image 4
Pfactorial_Case_Study_CuisineWhiz image 5
Pfactorial_Case_Study_CuisineWhiz image 6