How to Make a Chatbot Using JS
Imagine yourself in a world where all you have to do to interact with a company is drop a line to an intelligent humanoid who can resolve all your problems, have a chat with you, and crack a couple of jokes while handling any of your requests. Sounds cool, right?
Well, we're not in that world. But we are in a similar one thanks to chatbots (who aren't so great at cracking jokes, alas). They are actually useful in various tasks from making an appointment with a doctor to persuading your robot vacuum not to chase your cat.
According to this research, businesses can save up to 30% on serving customer requests with a chatbot. That's because you no longer have to hire humans to do repetitive tasks like answering basic questions or handling monotonous requests. A chatbot will do it way quicker and cheaper.
But for your chatbot to be truly responsive and human-like, it needs a couple of things prehistoric people (<2010s) didn't have — AI and Machine Learning (ML). They allow the chatbot to perform operations that cannot be described as a simple algorithm or a sequence of actions, therefore providing a greater degree of personalization.
So, in case you're willing to integrate a chatbot into your business processes — you're welcome!
Feel free to jump to sections 4 and 5 if you're interested in the technical side of the issue to see how we implement a chatbot using Node.js.
🤖 Rule-based Chatbot VS AI Chatbot Development
When talking about chatbots, it's important to understand the difference between a rule-based and AI-powered chatbot. A rule-based chatbot has pre-written options for a user to choose from, meaning that you can't type anything that's not on the list.
You can think of it as a bunch of buttons on a player, where you can’t actually press anything but the buttons themselves.
Why don't we take a look at the pros and cons of a rule-based chatbot:
- Relatively easy to implement
- No need for a large amount of data (no question-answer datasets
- Safe and reliable as the user cannot go out of the pre-defined options
- Sometimes it's easier for people to see all the options at once instead of writing text
- Inflexible (the bot can't handle anything it's not been designed for
- Doesn't generate personalized answers
- Conversation with them doesn't feel natural
An AI-powered chatbot functions in a completely opposite way. As the name suggests, they use AI, Machine Learning and Natural Language Processing to perform their tasks. With them, you can write any text and they, ideally, will be able to recognize your “intent”, or rather what kind of response you want to hear from them. The output (answer) of AI-powered chatbots may be generated from scratch, or be created from a template — just as with the rule-based bots.
The pros and cons of an AI-powered chatbot:
- Allows higher flexibility because they can handle any textual request
- Can become better and learn with every request they get
- Can be multilingual
- Can handle voice requests
- Conversations feel more natural compared to rule-based ones
- Can try to detect if the user is angry, happy or somewhere in-between (this is called “sentiment analysis”)
- Need to be trained, which may even require you to rent a cloud engine
- Are much “heavier”, both in terms of computational resources and memory, and mostly require to be run in the backend, which generates additional cost
But in case you really like some features of both an AI and a rule-based chatbot, you can get the best of both worlds by building a hybrid chatbot. It will generally use rule-based patterns but also rely on Machine Learning for complex tasks such as sentiment analysis or handling textual requests.
❓ Why Companies Build a Chatbot: Use Cases
Companies use chatbots for a wide range of purposes like automating appointments booking or making personalized offers. In this article, we'll talk about the most common use cases of AI chatbots.
Making Appointments & Booking
Let's say you have a fitness or healthcare business where your users have to make some kinds of appointments. In that case, AI chatbots may come in handy as they would be much cheaper than a real person answering the calls, but also way more flexible compared to rule-based chatbots, being able to handle new situations, like answering a question it has never seen before.
If you're particularly interested in Booking Apps, we have a special article dedicated to developing an online booking system for applications or websites:
Making Personalized Recommendations
In case you offer users a variety of products, you could integrate a Recommendation System into the AI chatbot. Such a system can provide users with a personalized approach and give recommendations based on their previous searching & buying history.
Receiving Voice Inputs & Giving Voice Outputs
If you'd like to improve the accessibility of your chatbot, adding voice Inputs & Outputs might turn out to be a great idea. For example, this could be useful for interacting with an IoT device, which may not actually have a keyboard or a display. The most popular example can be Alexa from Amazon.
Managing a Few Apps & Services
Let's imagine that your business uses a whole suite of different apps and/or services to deal with different tasks. So that your users don't have to constantly switch between them, you can make one point of access to all of them in your chatbot. For example, if your company has different apps for task management, attendance tracking, and planning, you can set up one chatbot and connect to all those services through their APIs.
⚙️ How to Create a Chatbot: Tech Insights
To better understand the logic behind chatbots development, let's take a look at how they function.
(Pre-) Processing stage
The first step in the workflow of any text-based ML application is preprocessing. We need it because computers aren't able to understand the logic behind sequences of characters (such as this article) or sounds (such as a recorded message) — they only perceive numbers. For that reason, we need to somehow turn texts or voice requests into a bunch of numbers.
In case we have voice input, we first have to convert it into text. For this task we can use open-source solutions such as Kaldi or PocketSphinx for mobile devices. Alternatively, if we don't want to run the speech recognition process on our own server, we can use third-party APIs such as Google's Speech-to-Text or Amazon Transcribe.
The first step in preprocessing is tokenization, which is defining the boundaries between tokens (i.e. words in ML lingo). Then, we have to extract entities — it's usually called Named Entity Recognition (NER). This will allow the chatbot to understand that words like “New York” or “the day after tomorrow” are entities, thus, they function as single units in a sentence and should not be separated.
The last important step in preprocessing is Word Embedding. This will actually allow us to substitute separate words with huge numeric vectors. The way word embeddings (vectors that correspond to words) are calculated isn't that important to understand this issue, but the most popular algorithm is Word2Vec. All these steps can be done in a few lines of code using such libraries as Python's SpaCy or Node's Natural/compromise.
By this point, we should already have all the text converted into a matrix of size (embedding dimension x number of words), which means we are ready to crunch these numbers to understand what the user wants from the chatbot.
Understanding the Intent of a User
An intent in chatbot architecture describes what the user wants the chatbot to do with their message. For example, when you ask your friend “How've you been?”, you expect them to share their recent news. In a similar way, when you ask a chatbot “Can I have a meeting with a doctor tomorrow?”, you expect it to either book an appointment for you, or reject the query in case there's no time available and offer another option.
This idea of having discrete intents combines well with the ML task of classification. The point of it is to classify the input data into two or more discrete categories, e.g. tell whether an email is spam or not. In our case with chatbots, we wish to classify a user message into its intent. This is nicely represented by the following graph:
Here, you can see how the embeddings are input in the first layer and then are forward-propagated, or fed in, through the many connections that are between the layers of the network. When the embeddings reach the final, or output layer, they choose the most likely intent. This intent is passed on to generate the response of the model.
Creating Fully Connected Neural Networks like the one above is pretty easy, and almost any ML library should provide ways to build them. Some of the most popular ML frameworks for this and other tasks are PyTorch, Tensorflow for JS / Python, and mlpack for C++.
Generating a Response
There are a few approaches to generate a response for a given user message.
The first one is to use a generative language model, such as GPT-3 or a simpler Recurrent Neural Network (RNN). But the problem with this approach when integrating chatbots is that it is either unnecessarily complicated (say hello to GPT-3 with 175 billion parameters) or not good enough (RNN's quality of generated text leaves much to be desired). This approach won't use the intents, and will just write the answer based on the message itself. Generating a message from scratch is a very complex task which requires models trained on loads of textual data, which most people don't have.
Another approach is to have generic pre-written messages that correspond to every intent. This kind of model will be much more lightweight and fit well into chatbots' tasks. But it also enables a higher level of customization. For example, the pre-written messages may have placeholders for the user's name, named entities, or any variables at all. Thus, you achieve relatively customizable answers while keeping the model rather simple.
After getting the response in a textual form, you might optionally want to convert it into speech. For this task, there's a number of speech recognition APIs, such as Google's Text-to-Speech or Amazon Polly.
✅ AI Chatbot Implementation
After we talked about how chatbots function on the inside, let's take a look at the process of chatbot implementation.
The easy way: No-Code Platforms
If you don't want to get your hands dirty with writing a chatbot from scratch, you can choose one out of many available chatbot platforms. The most popular ones are Dialogflow (offers a $600 trial) and Wit.ai (free). They both provide a clear and intuitive UI for creating intents, adding named entities, setting up logical flow between different intents, etc. Such chatbots use ML.
They are also a great option if you don't want to run your bot on your server. You can use their APIs to get answers to users' requests. For example, connecting to Wit.ai in Node.js to get a response is as easy as this:
However, there might be reasons for deciding against any of the platforms:
- Security. If you use a platform, you can't make sure that no one else has access to the data your users pass to the chatbot.
- Prices. Most of the platforms utilize a subscription-based model. It means that in a long-term run it may not be the most cost-efficient solution.
- Lack of flexibility. If you build a chatbot from scratch, you can use literally any tool the programming world has, while platforms usually limit the choice.
If any of these reasons resonate with you, or you just feel like building a chatbot from scratch (which is certainly fun), check out the next section.
The harder way: Node + NLP.js
In order to implement a chatbot from scratch, we first have to choose an NLP/ML framework to process the text and create a neural network. Since this article focuses on Node implementation of chatbots, NLP.js is a good choice for this task.
It has the following features:
- Custom entities in addition to the built-in ones like dates, names, etc.
- Built-in fully connected neural networks for classification.
- Language recognition.
- Tokenization, stemming, and other basic NLP tasks.
Our chatbot will have the following features:
- Saying hello, goodbye, giving info about the pricing according to corresponding intents.
- Booking a meeting with a doctor by means of extracting the date & time, and the name of the doctor.
- Recognizing custom entities, such as doctors’ names.
- Parsing dates in natural language format, i.e. not only in a standardized way like 20.05.2021, but also with queries like “the day after tomorrow” or “next Monday”.
Here are versions of the packages we're going to use:
First of all, we have to create a corpus, i.e. a dataset of question-answer entries, as well as of all the entities required. We decided not to add too many intents to keep it simple:
Here you can see the intents with their corresponding expressions that the bot will use to train itself. Once the bot recognizes an intent, it will randomly choose one of the given answers. Some of the intents don't have their corresponding answers because those cases are more complex and we have to use a callback function to process them.
Also, you can see our custom entity type — “doctor”. There are three doctors available and their alternative names (so that the bot understands that “Christine” and “Christie” refer to the same person).
Next, we have to set up our index.js file where we'll run trainings and establish the connection with Telegram's API. For the second task, we will use TeleBot, which is a very lightweight package, just what we need.
First, we import the required packages, set up the environment variables with the token of the bot in .env and create two instances of two major classes: NlpManager and TeleBot. The first will be used to create neural networks, train them based on our defined corpus, extract named entities and give answers. The second one will connect the chatbot to Telegram's API to exchange messages between the user and the bot.
The rest of the code defines the unnamed function, which is our main loop where all the training and message exchange is happening.
If you run this code, you'll first see a training log of our NN, and then the message of the bot being started. By this time, you should be able to text the bot whose token you've provided in .env, and it will respond to you.
But there's one feature that we haven't implemented yet: the actual booking itself. You can remember that we haven't provided any answers for “user.book” intent, so even if the bot does recognize it, there will be no answer. To fix this, we will create a whole custom class called User. Its main task will be to enable the booking functionality, i.e. asking for and recognizing the name of the doctor, as well as date & time of the reservation.
Also, we will define an object “availableDoctors”, which is meant to imitate a system with dynamic time slots. As this article doesn't focus on creating such a system, a demo JS object with doctors will do just fine.
The main structure of our User class looks like this:
The most important method here is onIntent, which will give additional behavior to some intents. For example, the intent “user.book” will not only respond to the user, but also send a request to the API. onIntent will also be used to create a logical flow in a conversation. The instance variable nextIntent will be responsible for that. For instance, if our user inputs something like “I want to see a doctor”, the bot should ask the user to specify the doctor and time, and expect the next message to be interpreted only as the same “user.book” intent. That's why we'll explicitly save and later use this info about the next intent in the variable.
Here're the inner workings of our booking process:
Let's go through it step-by-step:
Lines 6-30 are extracting the doctor's name, the date & the time of the reservation (if they are present) and saving them in the class instance.
Lines 33-61 explain what to do in case all the necessary data (i.e. doctor's name, date, and time) are present. If the date and time the user wants are past, the user will be asked to make another choice. In case everything’s fine, the user gets a success message.
The lines 66-90 define what to do in case not all information is available. Firstly, the user will be given a list of doctors to choose from. Then they will be asked to provide the date, and lastly — the time of the reservation.
The last thing we haven't spoken about is the processDate method, which will just parse a natural language string and extract either the date or the time offset from the beginning of the day:
Also, don't forget to export the object with doctors and the class itself at the end of the file:
Now, let's rewrite our index.js using our freshly-made class:
Here, you can see that we set up an object called userDatabase. While in a real-world application you might want to store the data about your users, in this demo project that's overkill, so we just use a JS object instead.
Also, since we've already trained and saved our NN, we just load it from our automatically created “model.nlp” file.
Lastly, after our NlpManager has processed the user input, we pass the result to our User's onIntent, which further modifies the answer and sends it to the chat.
Phew, seems like this is it. Let's try it out now.
If you need to look at the code for building a chatbot once again, feel free to take a couple of steps back.
You can create chatbots with help of such multiple services like work with chatbot development companies, chatbot platforms to build it yourself, use pre-written codes for chatbot development, etc.
To sum up, we'd like to say that building & integrating a chatbot is hard but definitely worth it — it can help you optimize customer support, communicate with your clients, and target your audience better.
You can access all of the source code along with detailed documentation in our GitHub repo.
If you need any help with the development or have any questions left, we'd be happy to help you!