chatbot: how to correctly determine intents? - python

What are the usual techniques to determine the difference between the following intents, for example?
What is the current temperature?
In this case, the straight-up response will be the current temperature.
Is current temperature 22 degrees?
In this case, the appropriate response would be yes or no.
I am building a closed-domain chatbot (eg Siri) and am wondering if there are any techniques in Python that I can read about.

I once got into programming trying to understand how bots work. What a little world. I barely remember how I did mine but let's assume you build the chat based on events that your bot can read. A very basic way to determine what to answer to each event would be a huge list of IF ELIF stuff with nested behaviours (ideally split into methods).
def handleevent (message, user, date, font, whatever):
if "current temperature" in message:
send_text_to_chat("The temperature is 22 degrees")
elif "is current temperature" in message and "?" in message:
specific_temp_asked(message)
elif "potato" in message: # you could do hundred of behaviours, and nested ones.
if user == "apple":
send_text_to_chat("Hi apple!")
else:
send_text_to_chat("I am a potato bot")
elif "who am I?" in message: # example using the event data
send_text_to_chat("you are " + user)
else:
send_text_to_chat("Be more specific")
And then you would have to code each of the specific situations like this:
def specific_temp_asked(message):
temperature = None
split_message = message.split(" ")
for i in split_message:
try:
int(i)
temperature = i
break
except:
pass
if not temperature == None:
real_temperature = check_temp(somehow)
if real_temperature == temperature:
send_text_to_chat("Yes")
else:
send_text_to_chat("Nope")
Final note: This is by no means the best way to do it, but if you are learning I'll get the work done without being too complicated, and then you improve the code slowly.

Related

Create a AI chatbot that learns how to chat without learning how to answer every question given by the user

My code is only this and it would only recognize specific questions... else it asks for the answer... but I want it to be able to learn and try to self-answer my new question.
example:
input("hello")
output("Hi, how are you?")
without teaching the ai chatbot about what to respond to "hi", I would want it to be able to respond back too
Like:
input("Hi")
output("Hello, how are you?")
It is very difficult for me and that is why I am requesting for an answer for it
My current code:
said = []
output = []
text = ""
global n
while True:
text = input("Say something: ")
if not(text == ""):
if not(text == "print(input, output)"):
input_contain = text.lower() in said
if input_contain:
n = 0
found = False
while not found:
if said[n] == str(text.lower()):
print(output[n])
found = True
else:
n = n + 1
else:
output_add = input("What should I respond to that? ")
if not output_add == "":
said.append(text.lower())
output.append(output_add)
else:
print("Error in output")
else:
for qn in said:
if not qn == int(len(said)) - 1:
print(str(qn), end=", ")
else:
print(qn)
for out in output:
if not out == int(len(output)) - 1:
print(str(out), end=", ")
else:
print(out)
else:
print("Error in input")
If you have a prebuilt set of responses, such as the "Hi, how are you", you could build a set of appropiate prompt questions for such responses, and compare the similarity (using e.g. spacy nlp) between the user input and your known prompt questions.
You could take the highest similar prompt question in order to identify the most similar question that has previoulsy been mapped to a response - then answer that questions answer, as the response.
For example, "Hello" and "Hi" have much higher similarity (0.9 or so) than "Hello" and "What is a scary animal?" do (near 0)..
So if you had
"What is a scary animal?" ==> "Tiger",
"Hi" => "Hello, how are you"?
"Hello" would map 0.9 to "Hi" and near 0 to "What is a scary animal", so pick "Hello"s answer, which is "Hello, how are you?"
"What is a fierce animal" would map closer to "What is a scary animal", so pick Tiger for that..
To allow your program to improve, you would need to save the mappings somewhere and add more.. this is roughly how GOFAI chat bots work
Machine Learning is a statistical mechanism. Here is a link somewhat related: "Estimate" the amount of training needed in advance
Personally, that is not "intelligence". Imagine having to retrain the bot on the live, when the user says something to be reused in future questions.
There is another approach to replying questions in chatbot fashion, that is called Natural Language Processing (check stackoverflow tag NLP). Specially Logic/Semantic processing can break down the user input into its constituents and from there develop the appropriate response.

Tkinter text entry validation in python

I have created a simple program which asks for a user's name and age. The program will then take the details from a textbox and work out how old they will be in 5 years time.
The interface is fine. It's the validation that I am having difficulty with. When a user enters a letter instead of a number the program shows an error message, but continues to run regardless. I have tried using a while True: loop but this seems to just crash the program.
Here's what I have written already:
def calculate():
name = (textboxName.get())
age = (textboxAge.get())
if age.isalpha():
tkinter.messagebox.showinfo("Error", "The Age is invalid")
textboxAge.delete("0","end")
newAge = int(age)+5
print("Hello",name)
print("In 5 years time you will be",newAge)
I have looked at a few other tutorials but they are a little confusing. I am going to extend this by adding another elif in and the following code
elif age >= 100:
tkinter.messagebox.showinfo("Error", "You have entered a number greater than 100")
textboxAge.delete("0","end")
but this doesn't like the fact it is a string not an integer.
What would be the best way to check to see if a number has been entered into a textbox?
def calculate():
name = (textboxName.get())
age = (textboxAge.get())
try:
newAge = int(age)+5
except ValueError:
tkinter.messagebox.showinfo("Error", "The Intended Reading Age is invalid")
textboxAge.delete("0","end")
return
print("Hello",name)
print("In 5 years time you will be ",newAge)
# ...
If an error occurs somewhere in the try-section, python will not crash, but rather jump to the except part. The critical step is converting age into integer. This throws a ValueError if it is a string. In this case, the message box is shown and the text in your textbox is deleted. return then will stop the function, so the rest of it won't be processed. If nothing happens in the try-section, then except will be skipped.

Creating Decision Tree for Simple Game

I am currently a new student learning python. This is my first real experience doing much computer coding. For my project I must create a fill in the blank quiz with three different levels of difficulty. Once the user chooses a difficulty the game should print a different paragraph based on the difficulty. Each section of the game works fine but I am having trouble creating the "difficulty chooser." No matter the difficulty I choose, the game rolls through the easy, medium, and the hard level in order and then crashes.
Below I have included the introductory text and the difficulty chooser. I would love some help. I am sure there are really obvious things I don't see. Thank you!
def introduction():
print '''Welcome to Kevin's European Geography Quizzes.
Test your knowledge of European geography. \n'''
difficulty = raw_input('''Do you want to play an easy, medium, or hard game?
Please type the number 1 for easy, 2 for medium, or 3 for hard.\n''' )
game_chooser(difficulty)
def game_chooser(difficulty):
cursor = 0
difficulty_choice = [easy_game(), medium_game(), hard_game()]
#each element of the above list links to a procedure and starts one of the
#mini-games.
while cursor < len(difficulty_choice):
if difficulty != cursor:
cursor += 1
else:
difficulty_choice[cursor]
break
You can do with if else if you only want to print something but if you have separate code block for each level then define a function for each level and use this pattern :
You can define the function blocks and call them basis on user input something like:
# define the function blocks
def hard():
print ("Hard mode code goes here.\n")
def medium():
print ("medium mode code goes here\n")
def easy():
print ("easy mode code goes here\n")
def lazy():
print ("i don't want to play\n")
# Now map the function to user input
difficulty_choice = {0 : hard,
1 : medium,
4 : lazy,
9 : easy,
}
user_input=int(input("which mode do you want to choose : \n press 0 for hard \n press 1 for medium \n press 4 for lazy \n press 9 for easy "))
difficulty_choice[user_input]()
Then invocation of function block will be:
difficulty_choice[num]()
Add a conditional for the input.
if difficulty == 'easy':
print("here is an easy game")
elif difficulty == 'medium':
print('here is a medium game')
elif difficulty == 'hard':
print('here is hard')
else:
print('Please enter valid input (easy, medium, hard)')
Under each if statement put your game code.
The reason your code goes through all the difficulties is because of this line:
difficulty_choice = [easy_game(), medium_game(), hard_game()]
When Python sees something like easy_game(), it calls the easy_game function and replaces it with the result. You don't want to call the function yet though, so you can take off the parenthesis to store just the function instead:
difficulty_choice = [easy_game, medium_game, hard_game]
This will mean you have to call the function after you take it out of the array.
As for the crash, when you use raw_input() you get a string back. That means when you type in the 1 to decide for an easy game, you get the character 1, which is represented by the number 49. That's why your code goes through everything and crashes: Your 1 is really a 49. In fact, if you type 1 < '1' into the interpreter, you'll get True back.
To fix that, you can pass the result of raw_input() to the int() function, which will parse it and give you the proper integer (or throw an exception if it can't be parsed). The last line of introduction would then look like game_chooser(int(difficulty)).
You could also skip most of the code of game_chooser by just indexing into the array (that's what they're for, after all):
def game_chooser(difficulty):
# the lack of parens here means you get the function itself, not what it returns
difficulty_choice = [easy_game, medium_game, hard_game]
#each element of the above list links to a procedure and starts one of the
#mini-games.
# note the parens to actually call the retrieved function now
difficulty_choice[difficulty]()

Issues with lists? Error checking not working

I am relatively new to python, and I just started learning how to use classes. This is the first program I've made where I've tried to integrate them, but I'm coming up with a small issue I can't seem to fix, and I think it has to do with lists. The code is as follows:
(The topic is getting the user to choose what type of seat to purchase).
class SeatBooking:
def __init__(self, seat):
self.seat = seat
possible_types = []
possible_types.extend(["Low_Economy", "Standard_Economy", "High_Economy",
"Business", "First", "Residence"])
possible_types = " ".join(possible_types)
while True:
if self.seat not in possible_types:
print("Sorry, but this is not a valid answer. Please try again!")
self.seat = str(input("What type of ticket would you like? The possible types are: {} "
.format(possible_types)))
else:
print("You have chosen to book a {} ticket.".format(self.seat))
confirmation = str(input("Please confirm with 'Yes' or 'No': ")).lower()
if confirmation == "yes":
print("Excellent decision! Ready to continue")
print("=" * 170)
break
elif confirmation == "no":
self.seat = str(input("What type of ticket would you like? The possible types are: {} "
.format(possible_types)))
else:
print("That doesn't seem to be a valid answer.")
Here is the main file (to execute the different classes I'll make):
import type_seat
# Choose the seat to book
print("=" * 170)
print("Welcome to Etihad! This program can help you organize your flight, payments and usage of miles!")
possible_types = []
possible_types.extend(["Low_Economy", "Standard_Economy", "High_Economy",
"Business", "First", "Residence"])
possible_types = " ".join(possible_types)
seat_type = str(input("What type of ticket would you like? The possible types are: {}. "
.format(possible_types)))
type_seat.SeatBooking(seat_type)
The problem I have is that I seem to be able to enter certain letters and it doesn't count them as an error even though they're not one of the available seats. For example, when I enter the letters "h" or "s", my error checking part of the code doesn't respond to it, but when I enter the letter "b" or random words like "try" it does. It doesn't seem to be completely random though, and it seems to only happen with letters or parts of the first 3 'items' in the possible_types[] list. However, I haven't tested this fully. This is why I thought it had something to do with lists, so if anyone knows what's causing this, I'd really appreciate it if they could help me resolve this and perhaps help me from repeating this mistake in the future!
Note, for the lists I am using .join, but I also tried str().
You don't have a list, you are testing characters against one long string:
possible_types = " ".join(possible_types)
The letters h and s are in that string (in the words High_Economy and Business, respectively), but the sequence try doesn't appear anywhere in the string.
If you only wanted to allow whole words to match, you'd need to leave possbile_types a list, or ideally convert it to a set (as sets allow for fast membership testing). You can define the list, no need for list.extend() here:
possible_types = ["Low_Economy", "Standard_Economy", "High_Economy",
"Business", "First", "Residence"]
or make it a set by using {...}:
possible_types = {"Low_Economy", "Standard_Economy", "High_Economy",
"Business", "First", "Residence"}
Do not join this into a string, just test directly against the object:
if self.seat not in possible_types:
If you still need to show the values to a user in an error message, join the values then, or store the str.join() result in a different variable for that purpose.
Note that you shouldn't deal with user input validation in the class __init__ method. Leave user interaction to a separate piece of code, and create instances of your class after you validated. That way you can easily swap out user interfaces without having to adjust all your data objects too.
possible_types = " ".join(possible_types)
Above statement will create one string as "Low_Economy Standard_Economy High_Economy Business First Residence".
Now you are doing
if self.seat not in possible_types:
This will check for a particular character in the string present or not. In your case you are finding 'h' which is present and 'try' which isn't.
Your program will work if you remove this statement
possible_types = " ".join(possible_types)

Python beginner, text-based game

I'm fairly new to the programming game; I'm 3/4 of the way through Learn Python the Hard Way and I had a question about a little text-based game I made... So in this game my guy is stranded on a desert island and you have the option(raw input) of going left right or into the jungle. After choosing a direction, you're given the option to choose how many miles to walk. Each direction is supposed to have a different end result (and mile distance).
If you enter a number that is less than the number of miles to the destination, you're prompted with a choice to either "turn around or "keep going". If you enter turn around, you're taken back to the beginning, where you're again asked to choose a direction. If you enter keep going, the program returns to miles(), where you can choose a new amount of miles to walk.
def miles():
print "How many miles do you walk?"
miles_choice = raw_input("> ")
how_much = int(miles_choice)
if how_much >= 10:
right_dest()
elif how_much < 10:
turn()
else:
print "You can't just stand here..."
miles()
Ok so here's two questions:
How would I make it so that if the user originally enters a number of miles less than the destination distance, and the second mile input + the first mile input == the amount of miles to the destination, it will add the inputs and run my destination function, not just repeat miles().
Since all three final destinations will have different distances, should I write three separate mile functions? Is there a way to make it so that depending on the original direction chosen, miles() will run the different endpoints?
I'm sorry if this doesn't make a lick of sense... I'm still learning and I'm not sure how to fully explain what I'm trying to get across.
You could store the amount of miles to walk in each direction in a dict, and then check the dict to see if the user has walked far enough:
distances = {
'right': 7,
'left': 17,
'forward': 4
}
direction_choice = raw_input("> ")
miles_choice = raw_input("> ")
if how_much >= distances['direction_choice']:
right_dest()
elif how_much < distances['direction_choice']:
turn()
else:
print "You can't just stand here..."
miles()
Be sure to properly validate and cast the user input, which I have not addressed. Good luck!
I don't fully understand the requirements (the intended behavior and constraints). However, you might consider passing a parameter to your function (through and argument) to convey the maximum number of miles which the play could go in that direction).
For example:
#!/usr/bin/env python
# ...
def miles(max_miles=10):
print "How many miles do you walk?"
while True:
miles_choice = raw_input("> ")
try:
how_much = int(miles_choice)
except ValueError, e:
print >> sys.stderr, "That wasn't a valid entry: %s" % e
continue
if max_miles > how_much > 0:
break
else:
print "That's either too far or makes no sense"
return how_much
... in this case you pass maximum valid number of miles into the function through the "max_miles" argument and you return a valid integer (between 1 and max_miles) back.
It would be the responsibility of this function's caller to then call right_dest() or turn() as appropriate.
Note that I've removed your recursive call to miles() and replace it with a while True: loop, around a try: ... except ValueError: ... validation loop. That's more appropriate than recursion in this case. The code does a break out of the loop when the value of how_much is valid.
(By the way, if you call miles() with no parameter then the argument will be set to 10 as per the "defaulted argument" feature. That's unusual to Python (and Ruby) ... but basically makes the argument optional for cases where there's a sensible default value).
#Question #1: I used Class intern variables. You will maybe need them for further programming parts and should take it to zero when you are done on one direction, to start with zero for next step/lvl.
#Question #2: Dictionaries are the best way to do so,self.dest. Parameter pos used as key to get the value from the dictionary.
class MyGame:
def __init__(self):
self.current_miles = 0
self.dest = {'Left' : 10, 'Into the jungle' : 7, 'Right' : 22}
def miles(self,pos):
print "How many miles do you walk?"
miles_choice = raw_input("> ")
self.current_miles += int(miles_choice)
if self.current_miles >= self.dest.get(pos):
self.miles("Right")
elif self.current_miles < self.dest.get(pos):
print "you went "+ str(self.current_miles) + " miles"
else:
print "You can't just stand here..."
self.miles(pos)
mg = MyGame()
mg.miles('Into the jungle')

Categories