Making a chatbot app
The goal
An app that allows users to learn bus routes and stops via text messages.
Why text?
- Don't need to download an app
- Resilient against poor cell service
- Works on any phone hardware
Basic architecture
Twilio
Chatbot
server
Google Maps
Knows about bus routes
Provides distances between points and address validation
Forwards text messages to our server
Has questions about bus routes
- "Is there a bus to Wendy's?"
- "How can I get from downtown Wheeling to Reynolds?"
- "how long get too higlnds"
- "lol ur dumb"
How do we talk to computers?
How do we talk to computers?
ChatGPT doesn't know everything yet
How do we make our own text app that is as easy to use as ChatGPT but knows what we need it to know?
Options
and of course lots of other options like
Python or Haskell NLP libraries, Microsoft Bot Framework, AWS Step Functions + Lex, etc etc
Option A
Traditional NodeJS app with NLP.js
NLP.js
- HUGE library with a ton of functionality
- Not intended for chatbot orchestration (oops)
- Integrates with Microsoft Bot Framework, among others
- Documentation is extensive but disorganized
- I DO NOT RECOMMEND THIS LIBRARY FOR THIS PURPOSE but it helped me learn the basic vocabulary of NLP so...
Utterances
What the user actually types
- "Is there a bus to Wendy's?"
- "How can I get from downtown Wheeling to Reynolds?"
- "how long get too higlnds"
- "lol ur dumb"
Intent
What the user intends to communicate
- "Is there a bus to Wendy's?"
- "How can I get from downtown Wheeling to Reynolds?"
- "how long get too higlnds"
- "lol ur dumb"
bus.findStop
bus.routeBetween
bus.timeEstimate
appraisal.stupid
Entities
Data from an utterance needed to return an answer relevant to the intent. The program may need to extrapolate on the data to be useful.
- "Is there a bus to Wendy's?"
- "How can I get from downtown Wheeling to Reynolds?"
- "how long get too higlnds"
- "lol ur dumb"
Wendy's (in Elm Grove?)
Reynolds (Memorial Hospital ?)
Reynolds (Rapid Care?)
The Highlands?
none
(Transportation Center?) in downtown Wheeling
Corpus
{
"name": "Corpus",
"data" : [
{
"intent": "agent.age",
"utterances": [
"your age",
"how old is your platform",
"how old are you",
"what's your age",
"I'd like to know your age",
"tell me your age"
],
"answers": [
"I'm very young",
"I was created recently",
"Age is just a number. You're only as old as you feel"
]
}
]
}
A collection of data used to teach the program what utterances correspond to which intents and how to respond to those intents.
Entity Recognition
What if we need to access part of the user's utterance?
{ "intent": "bus.findstop",
"utterances": [
"Where is the nearest stop to @address",
"Where is the nearest bus stop to @address",
"bus stop near @address",
"What is the stop closest to @address"
],
"slotFilling": {
"address": {
"mandatory": true,
"question": "What is your specific location or address?"
}
},
"answers": [
"Uhhhh, I dunno what the closest stop
to {{ address }} is. Sorry."
]
}
"entities": {
"address": {
"trim": [
{
"position": "afterLast",
"words": ["to"]
},
{
"position": "afterLast",
"words": ["near"]
}
]
}
}
Test-driven development
describe("findClosestStop", () => {
test('can find the stop nearest to a set of coordinates', async () => {
const stop = findClosestStop(testStops, [28.965797, 41.010086])
expect(stop.coordinates).toEqual([28.973865, 41.011122])
expect(stop.name).toEqual("A")
})
})
describe.skip("fetchPlace (live test using Google Maps API)", () => {
test('can get a canonical place from the name of a location', async () => {
const resp = await fetchPlace("Reynolds Hospital")
expect(resp.name).toBe("Reynolds Memorial Hospital")
expect(resp.address).toBe("800 Wheeling Ave, Glen Dale, WV 26038, United States")
expect(resp.coordinates).toEqual([39.9461073, -80.75260469999999])
})
Test-driven development
describe("'where is the closest stop to wendys'", () => {
test('is about finding a bus stop', async () => {
fetchPlace.mockResolvedValue({
name: "Wendy's",
address: "Elm Grove Mall",
coordinates: [28.965797, 41.010086]
})
findClosestStop.mockReturnValue({
name: "B",
location: "somewhere over the rainbow"
})
const response = await nlp.process('en', 'Where is the closest stop to wendys')
expect(response.intent).toBe('bus.findstop')
expect(response.answer).toMatch(/Wendy's \(Elm Grove Mall\)/)
expect(response.answer).toMatch(/B stop/)
expect(response.answer).toMatch(/over the rainbow/)
})
})
Adding functionality
{ "intent": "bus.findstop",
"utterances": [
"Where is the nearest bus stop to @address",
"What is the stop closest to @address"
],
"slotFilling": {
"address": {
"mandatory": true,
"question": "What is your specific location or address?"
}
},
"answers": [
"The nearest bus stop to {{ placeName }} ({{ placeAddress}})
is the {{stopName}} stop {{ stopLocation }}."
],
"actions": [
{
"name": "handleFindStop",
"parameters": []
}
]
}
export async function handleFindStop(data) {
const address = data.context.address
if (address) {
const realAddress = await fetchPlace(address)
data.context.placeName = realAddress.name
data.context.placeAddress = realAddress.address
const closestStop =
findClosestStop(elmgrove, realAddress.coordinates)
data.context.stopLocation = closestStop.location
data.context.stopName = closestStop.name
}
return data
}
What part do I need to change to get the effect I want?
Connector
NLP
Bot
NLU manager
Entity Recognition
NLU manager
Slot manager
Stemmer
Action manager
Action manager
Sentiment
QnA
onIntent callback
Pipeline
NLG manager
Corpus
Settings
Confirming the user's input
What if the program guesses incorrectly?
I tried a few options:
- use the "bot" library to create chatbot scripts
- use actions
- use slots
None of these were satisfying.
In general, NLP.js is not for a chatbot that needs to know about the state of a conversation and branch based on that.
DEMO
Option B
Low-code Chatbot SAAS
BotPress
- Software as a Service
- Very fun, fancy graphical UI
- Within a few years will probably a) be bought out by a larger firm and become really expensive or b) die
- AI-assisted coding
- Made for chatbot orchestration, unlike NLP.js
DEMO
Making a chatbot app
By emhoracek
Making a chatbot app
- 371