Building a Customer Support Bot with LLMs
A useful support bot knows when to answer and when to hand over. This guide sets out the architecture of an LLM support bot, then routes 3,080 real banking queries to 77 intents with a small embedding model and shows how a confidence threshold trades the share the bot answers against how often it is right.
GloryTecks Team
Training & Placement Faculty
Last updated: 3 October 2026
Most of what customers ask a support team has been asked before: where is my card, why did my transfer fail, how do I get a refund. That repetition is why support is one of the most common uses of language models. It is also where a confident wrong answer costs most.
In 2022, Air Canada's website chatbot told a passenger he could book at full fare and claim a bereavement discount afterwards. The policy said otherwise. When he took the airline to a Canadian tribunal, the airline argued that the chatbot was responsible for its own actions. The tribunal disagreed and ordered the airline to pay (BBC: Air Canada chatbot ruling). A support bot speaks for the company.
So the design question is not only how to answer, but when to answer and when to hand over.
The architecture
A support bot that holds up in production usually has five parts:
| Part | Job |
|---|---|
| Routing | Work out what the customer wants: which intent, which product, how urgent |
| Knowledge | Retrieve the approved answer or policy text for that intent |
| Generation | Phrase a reply from that text, in the conversation's context |
| Actions | Look up orders, accounts or tickets through tools, with checks |
| Handover | Pass the conversation, with its history, to a person when needed |
The language model is essential in generation and helpful in routing. The rest is ordinary software, and that is where most of the reliability comes from.
Routing: the step that decides everything
If the bot misunderstands the question, nothing after it can help. Routing can be done by an LLM, by a trained classifier, or by comparing meanings with embeddings. The last is cheap, fast and easy to measure, so the script uses it on a real dataset.
BANKING77 contains 13,083 customer queries to a bank, each labelled with one of 77 intents, released by PolyAI for research on intent detection (Casanueva et al.: BANKING77) under the CC BY 4.0 licence (Hugging Face: PolyAI/banking77). The script embeds every training message with a small sentence model (Hugging Face: all-MiniLM-L6-v2), represents each intent by the average of its messages, and routes each test query to the closest intent.
from collections import Counter
import numpy as np
import pandas as pd
from sentence_transformers import SentenceTransformer
# Banking77 (CC BY 4.0): real customer messages to a bank, each labelled with one of 77 intents
train = pd.read_csv("banking77/train.csv")
test = pd.read_csv("banking77/test.csv")
print(f"{len(train):,} training messages, {len(test):,} test messages, {train.category.nunique()} intents")
# Route by meaning: embed every message and compare a new one with each intent's average
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
train_vecs = model.encode(train.text.tolist(), normalize_embeddings=True, batch_size=128)
test_vecs = model.encode(test.text.tolist(), normalize_embeddings=True, batch_size=128)
intents = sorted(train.category.unique())
centroids = np.stack([train_vecs[(train.category == i).to_numpy()].mean(axis=0) for i in intents])
centroids /= np.linalg.norm(centroids, axis=1, keepdims=True)
scores = test_vecs @ centroids.T
best = scores.argmax(axis=1)
ranked = np.sort(scores, axis=1)
confidence = ranked[:, -1] - ranked[:, -2] # margin between the best and second-best intent
predicted = np.array(intents)[best]
right = predicted == test.category.to_numpy()
print(f"routing every message: {right.mean():.1%} to the right intent")
# A bot should answer only when it is confident, and hand the rest to a person
print(f"\n{'margin needed':>13} {'answered by bot':>16} {'right when it answers':>22} {'sent to a person':>17}")
for threshold in (0.0, 0.02, 0.05, 0.08, 0.12):
auto = confidence >= threshold
print(f"{threshold:>13.2f} {auto.mean():>16.1%} {right[auto].mean():>22.1%} {(~auto).mean():>17.1%}")
print("\nmost frequent mix-ups (true intent -> routed to)")
mixups = Counter(zip(test.category[~right], predicted[~right]))
for (truth, guess), n in mixups.most_common(6):
print(f"{n:>3} {truth} -> {guess}")10,003 training messages, 3,080 test messages, 77 intents
routing every message: 84.9% to the right intent
margin needed answered by bot right when it answers sent to a person
0.00 100.0% 84.9% 0.0%
0.02 90.9% 89.5% 9.1%
0.05 78.9% 93.8% 21.1%
0.08 66.6% 96.2% 33.4%
0.12 48.2% 97.9% 51.8%
most frequent mix-ups (true intent -> routed to)
15 supported_cards_and_currencies -> fiat_currency_support
12 beneficiary_not_allowed -> failed_transfer
9 topping_up_by_card -> transfer_into_account
9 pending_transfer -> transfer_timing
8 declined_transfer -> declined_card_payment
6 card_delivery_estimate -> card_arrivalWhat the output shows
- Routing everything gets 84.9% right. Good for a few lines of code and no training, and not good enough to answer every customer automatically.
- Confidence buys accuracy. The script measures how far ahead the best intent is of the second best. Answering only when that margin is at least 0.05, the bot handles 78.9% of queries and gets 93.8% of those right. The other 21.1% go to a person.
- Every threshold is a business decision. Raising the bar to 0.08 lifts accuracy to 96.2% but sends a third of queries to people. The right point depends on what a wrong answer costs.
- The mix-ups are between neighbours. Supported cards and currencies against fiat currency support, a pending transfer against transfer timing. These are where a clarifying question works better than a guess, and where intents might be merged.
An LLM router would do better on many of these, at higher cost per message. The method of measuring stays the same: accuracy on a labelled test set, at the confidence level where the bot will actually answer.
Answering: grounded or not at all
Once the intent is known, the reply should come from approved content: the policy, the help article, the account data. Retrieve it, give it to the model, and require the answer to rest on it. A bot that answers from the model's general knowledge will eventually invent a policy. See how to reduce LLM hallucinations and, for the retrieval part, RAG architecture explained.
For anything that changes an account, such as refunds, cancellations or address changes, the model should prepare the action and a person or a confirmation step should approve it. See function calling and tool use.
Handover is a feature
Customers do not mind a handover. They mind repeating themselves. A good handover:
- Passes the full conversation and what the bot already looked up.
- Happens early for complaints, legal matters, vulnerable customers and anything the bot is unsure about.
- Is always available when the customer asks for a person.
- Tells the customer what happens next and how long it will take.
Measure the right things
Anthropic's guide to support bots suggests measuring how accurately the bot understands queries, how accurate its answers are, and how well it escalates, with a target of 95% or higher for correct escalation. It describes a deflection rate, the share of queries handled without a person, of 70 to 80% as a typical aim (Anthropic: customer support agent).
Deflection alone is a dangerous target, because a bot that never escalates scores perfectly on it. Track it together with accuracy on answered queries, escalation accuracy, repeat contacts and customer satisfaction. For building the test sets behind those numbers, see evaluating LLM outputs. For protecting the bot from manipulation, see guardrails for LLMs.
Common mistakes
- Optimising deflection without measuring wrong answers.
- Letting the model answer policy questions from its own knowledge.
- Hiding the route to a person.
- Handover without context, so customers start again.
- No confidence threshold, so every query gets an answer.
- Launching without a labelled test set of real customer messages.
How do I build a customer support chatbot with an LLM?
Should a support bot use an LLM or a classifier to understand questions?
What is a good deflection rate for a support bot?
Is a company responsible for what its chatbot says?
Building chatbots and assistants with LLMs is part of the Building Gen AI Applications module of the Generative AI course.
Sources
- Efficient Intent Detection with Dual Sentence Encoders (arXiv) — Casanueva et al.
- PolyAI/banking77 dataset (Hugging Face) — PolyAI
- all-MiniLM-L6-v2 model card (Hugging Face) — sentence-transformers
- Customer support agent (Claude Platform Docs) — Anthropic
- Airline held liable for its chatbot giving passenger bad advice (BBC Travel) — BBC
GloryTecks Team
Training & Placement Faculty
The GloryTecks faculty team mentors learners across Data Science, AI and Data Engineering, with a focus on real-time projects and placement readiness in Hyderabad.
Want to master Generative AI?
Join GloryTecks’ expert-led Generative AI training in Hyderabad with real-time projects and 100% placement support.
Generative AI for Images: Diffusion Models Explained
NextFunction Calling and Tool Use in LLMs Explained
Related articles
LangGraph Tutorial: Building Stateful AI Agents
Build a small support-ticket agent with LangGraph, using only plain Python and no API key: typed state and reducers, conditional edges, a retry loop, checkpoints with thread IDs, and a human approval step with interrupt. Every output shown was produced by running the code.
What Is Generative AI? A Complete Beginner's Guide
What generative AI is, in plain language: how language models produce text one token at a time (shown with a tiny runnable model), how chat models are trained, how image models differ, what the technology is reliable for, and why it confabulates.
ChatGPT Complete Guide: Features, Prompts and Use Cases
ChatGPT went from a research preview to one of the most used software products in the world in under four years. This guide maps its main features and when they arrived, shows how to write prompts that work, sorts good uses from risky ones, and explains the limits every user should check for.