To us humans, conversation is second-nature; it comes naturally to us, but the same can’t be said for bots. Even a simple question like, “How was your day?”, could be rephrased several ways (for example, “How’s it going?”, “How are you?”), none of which a bot can ordinarily understand.
We can interpret the intent behind the question, but a lot goes into building the logic to facilitate a smarter conversation with a bot, and for most developers, coding it from scratch isn’t feasible.
Well, fortunately, there’s something called NLU (natural-language understanding), which enables better human-computer conversation – in other words, a smart chatbot that utilizes machine learning and other technologies in order to better understand human interactions.
An NLU algorithm doesn’t just recognize text, but also interprets the intent behind it. Hence, it’s an ideal algorithm for chatbots. Read more about NLU here.
And this is where the purpose of this article comes in. We are going to build our chatbot using Google’s NLU platform, DialogFlow. Read on to learn more about DialogFlow, and how to integrate it into a React application with this follow-along tutorial.
In simple terms, DialogFlow is an end-to-end tool powered by NLU to design and integrate chatbots into our interfaces. It translates natural language into machine-readable data using machine learning (ML) models trained by the language we provide. How does it work? Let’s learn while we build our chatbot.
Open the DialogFlow console and log in with your Google account. Upon successful login, we see the following interface:
The first thing that may catch your attention is the Create Agent option.
Nothing fancy! The chatbot itself is an agent. Collecting the user’s query, acting on it, and finally sending a response are all handled by our agent.
Let’s create our chatbot; how about a bot for a coffee shop, for our example? No prizes for guessing my coffee shop inspiration.
This is how the console should look now:
Now, more jargon has appeared on the screen – Intents.
The console says that “Intents are mappings between a user’s queries and actions fulfilled by your software”. So, what does this mean?
Let me explain: in the case of our bot, we expect to receive queries like, “I would like a cappuccino” and, “When does the shop open?”, etc. We can categorize these queries into user intentions such as “Take Order”, “Timings”, etc. To handle them, we define these categories as intents in our agent.
We also see that our agent comes with two default intents; Default Welcome Intent and Default Fallback Intent. Let’s explore them in a little more detail:
Quite a bit of jargon in here; let’s go through them one by one:
In a human conversation, to understand phrases, we usually need some context. Likewise, with the bot, an intent needs to know the context of the query. To make this possible, we connect one or more intents via contexts. We will learn more about this later in this article.
These are example phrases to train and help our agent match queries with the right intent. More phrases and variations will improve the accuracy of intent matching.
By seeing the default list of phrases, it’s clear that this intent is used to greet our users.
We just learned the agent looks for training phrases to trigger intents. However, intents can also be triggered by events. There are two types of events:
Once the query is matched with the right intent, next comes action on it. To act, sometimes we need to extract some data from the query. To extract, we define parameters with entity types. Take this for example: “Is the coffee shop open today?”; the parameter to extract here is today, which is critical info needed to perform some logic and respond accordingly. We will learn more about this later in this tutorial.
Response to the user. Here, we see static phrases, but they can be dynamic, too, if we make use of parameters or fulfillment.
When we enable fulfillment, the agent gives a dynamic response by making an API call defined by us (e.g., if a user wants to book a table, we can check the database and respond based on availability). Again, more on this a little later.
Let’s try this default intent first before going any further. On the right side, we have a Dialogflow Simulator to test our agent. We say something similar to the training phrases, and the agent responds from the list of responses:
And, if we say something completely different, the agent triggers a Default Fallback Intent:
So, if the agent fails to find the right intent, it triggers the fallback intent. Pretty smooth, right? But, let’s try to avoid it and add intents to carry out the whole conversation.
Let’s try to create this conversation shown in the diagram below:
Modify the response of Default Welcome Intent to greet and ask for the order. Something like this; “Greetings! What would you like to order?”.
Create an intent to take the order, and name the intent to something simple that makes sense, like Take Order.
Now, we add training phrases. The user might say, “I would like to have 2 lattes”. Adding this to the list of training phrases is enough to get it matched with this intent. But, we also need the qualifying information; ‘2’ and ‘lattes’.
To extract them, we will add a couple of parameters within the phrase – quantity (@sys.number entity type) and item (@sys.any entity type):
Add more variations to make it better. Here’s some I added:
Once the parameter-rich phrases are added, this table is automatically filled. We can mark them as required/optional and set a default value if needed. In addition, we need to define a question as a prompt, to force the user to enter the required parameter, if not entered the first time.
So, this means that if we get the first phrase from the list, “I would like to order”, and the item (required) is missing, the prompt will get triggered to ask for it. If we get the second phrase from the list; “I would like to order a mocha”, and the quantity (optional) isn’t there, then, instead of prompting the user to enter the quantity, we will just set the default value as “1” and use that.
We can return a dynamic response to make use of parameters to repeat the order and ask if they would like an add-on:
To do this, create an intent to handle the user’s response to the add-on question.
The response will be either Yes or No. This is the ideal scenario to introduce follow-up intents. As the name suggests, these are used to follow up with the conversation in the parent intent.
DialogFlow provides many predefined follow-up intents for common replies like “Yes”, “No” or “Cancel”. Also, If you need to handle custom replies, you can create your own custom follow-ups. We’re going to use the predefined ones for this tutorial, but it’s up to you if you wish to put your own spin on it.
From the intent list, hover the mouse over the TakeOrder intent and click Add follow-up intent. Select Yes and the follow-up intent is created, named Take Order – yes.
You may have noticed there’s an input context added (TakeOrder-followup). For follow-ups to work, they need to be linked to the parent intent, and that’s where the context comes in handy.
When we create a follow-up intent, an output context is automatically added to the parent intent and an input context of the same name is added to the follow-up intent.
We don’t need to worry about the training phrases, as they are already in place. Now, add a dynamic response for this intent. As contexts are in place, we can use the parameters from the parent intent, like this:
Similarly, create a predefined follow-up intent to handle No in response. This one will be named Take Order – no. Just add a dynamic response here, as well:
The agent is ready to carry out the conversation. We can always test it in the simulator, but let’s try something different. Click “Integrations” in the sidebar and enable Web Demo. Open the URL provided there and start the conversation:
The agent is working fine as expected, but wouldn’t it be nice to show the total billing amount of the order? We can fetch the prices from a server and return the calculated amount to the user.
While discussing intents, we came across Fulfillment, which helps to return dynamic responses by making API calls defined by us. That’s what we need here to interact with our server.
Find Fulfillment in the sidebar and open it. DialogFlow provides two ways to use fulfillment:
I’m going to build a webhook service and make it public. Before proceeding, make sure the service meets the requirements mentioned here.
Create a node app in a new directory and install the necessary dependencies:
mkdir centralperk-server cd centralperk-server npm init -y npm i express dialogflow-fulfillment
Start with a basic server in index.js:
const express = require("express"); const app = express(); app.get("/", (req, res) => { res.send("Hi from server!"); }); app.listen(8080, () => { console.log("server running..."); });
This simply runs the server at port 8080. Now, let’s write some code to handle webhook requests from DialogFlow:
const express = require("express"); const app = express(); const { WebhookClient } = require("dialogflow-fulfillment"); const getPrice = require("./helpers"); app.get("/", (req, res) => { res.send("Hi from server!"); }); app.post("/", express.json(), (req, res) => { const agent = new WebhookClient({ request: req, response: res }); function handleIntent(agent) { const intent = agent.intent; const item = agent.contexts[0].parameters.item; const quantity = agent.contexts[0].parameters.quantity; const billingAmount = getPrice(intent, item, quantity); const response = intent === "Take Order - yes" ? `Great! Your ${quantity} ${item} and cookies will be ready in no time. Please pay ${billingAmount}$.` : `Okay! Your ${quantity} ${item} will be ready in no time. Please pay ${billingAmount}$.`; agent.add(response); } const intentMap = new Map(); intentMap.set("Take Order - yes", handleIntent); intentMap.set("Take Order - no", handleIntent); agent.handleRequest(intentMap); }); app.listen(8080, () => { console.log("server running..."); });
Helpers to calculate the price:
const priceList = { mocha: 5, latte: 7, cookies: 2, }; module.exports = function (intent, item, quantity) { const total = intent === "Take Order - yes" ? priceList[`${item}`] * quantity + priceList["cookies"] : priceList[`${item}`] * quantity; return total; };
Let’s walk through the above implementation.
The imported WebhookClient will handle the communication with DialogFlow’s webhook fulfillment API. When a fulfillment-enabled intent is matched, an HTTPS POST webhook request is sent to our server. This request is handled by agent.handleRequest(intentMap). It takes in a map of handlers and each handler is a function callback. The one defined here extracts all the needed information from the passed instance, calculates the billing amount, and then finally returns the dynamic response.
Using ngrok is the fastest and easiest way to put the server on the Internet. Follow the steps here for quick setup and installation. Once done with the steps, run the following command:
ngrok http 8080
And the secured public URL is ready in no time:
ngrok (Ctrl+C to quit) Visit http://localhost:4040/ to inspect, replay, and modify your requests Session Status online Account Piyush (Plan: Free) Version 3.0.6 Region India (in) Latency 55ms Web Interface http://127.0.0.1:4040 Forwarding https://2c73-182-64-199-236.in.ngrok.io -> http://localhost:8080 Connections ttl opn rt1 rt5 p50 p90 5 0 0.00 0.01 2.37 5.21
(Note: Remember to keep the local server up and running)
Go ahead and enable the webhook in the Fulfillment window and enter the secured public URL. In addition, bear in mind that webhook calls need to be enabled for both follow-up intents.
All looks good. Let’s test it now:
Our chatbot is all set up!
There are many ways to integrate the chatbot into a React app:
In this blog tutorial, we’re going with the Kommunicate option.
Follow these steps:
Go for Free Trial and sign up with your Google account.
Click Bot Integrations and select DialogFlow ES.
Get the JSON key from the DialogFlow cloud account using the instructions mentioned in the following image:
Next, we get to choose a customized avatar:
And that’s all we need to do for DialogFlow ES and Kommunicate integration!
Create a chatbot component and paste the following code in useEffect():
(function (d, m) { var kommunicateSettings = { appId: "<YOUR APP_ID>", popupWidget: true, automaticChatOpenOnNavigation: true, }; var s = document.createElement("script"); s.type = "text/javascript"; s.async = true; s.src = "https://widget.kommunicate.io/v2/kommunicate.app"; var h = document.getElementsByTagName("head")[0]; h.appendChild(s); window.kommunicate = m; m._globals = kommunicateSettings; })(document, window.kommunicate || {});
import React, { useEffect } from "react"; function Chatbot() { useEffect(() => { (function (d, m) { var kommunicateSettings = { appId: "<YOUR APP_ID>", popupWidget: true, automaticChatOpenOnNavigation: true, }; var s = document.createElement("script"); s.type = "text/javascript"; s.async = true; s.src = "https://widget.kommunicate.io/v2/kommunicate.app"; var h = document.getElementsByTagName("head")[0]; h.appendChild(s); window.kommunicate = m; m._globals = kommunicateSettings; })(document, window.kommunicate || {}); }, []); return <div></div>; } export default Chatbot;
Remember to replace the placeholder with your appId. Finally, import it into the App component:
import "./App.css"; import Chatbot from "./Chatbot"; function App() { return ( <div className="App"> <Chatbot /> </div> ); } export default App;
Run the app locally to test the integration:
And just like that, we have added our chatbot to our React app. Visit this dashboard to add more customizations to things like color, icons, and notification sounds, etc.
That’s all for this blog! We learned several aspects of chatbot development; from building it with DialogFlow, connecting with the Node.js server, to finally Integrating it into React app. I hope it made sense to you and you were able to follow along with this tutorial with ease.
If you have any questions, you can leave them in the comments and I’ll be happy to answer them. Feel free to reach out to me on LinkedIn or Twitter.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.