We will make a simple AI chatbot in this tuturial. We will use module scripts, functions, tables, variables, if statements and some maths.
#####Tutorial Level: Intermediate
##What will you learn?
In this tutorial we will use module scripts, functions, tables, variables and mathematics. I won't teach how to use these but. I will try to explain what I did. I will try my best to teach you how can we make an AI chatbot with these.
I recommened learn 'module scripts', 'tables', 'functions', 'variables' and 'if statements' before this tutorial. Also you can reinforce what did you learn about these topics in this tutorial.
Also we will use mathematics to calculate similarity between two strings.
We will use module scripts for checking the input and creating outputs for each input category.
What did I mean by 'each input category'?. We will give a category(or type) for each input in a table for example:
local module = {}
local inputs = {
["greetings"] = {
"hello",
"hi",
"hey"
}
}
local category = nil
return module
In this module script we created a table for inputs and a category in table for each inputs. We can add more inputs and categories. Thanks to these categories, we get rid of writing outputs for each input one by one.
The 'category' variable stores our category for inputs. Let's make its value nil.
Now we need a category finder function for find category of the input. But what if we make a typing mistake such as 'helo' instead of 'hello' our AI can't answer this with a only category finder function. So we need a similarity finder function to find most similar word in inputs. Let's make a similarity algorithm to prevent little typing mistakes of inputs. We can create this function in our module script.
We don't need to acces this function from another script so we can make a default function.
function calculateSimilarity(str1, str2)
local len1 = #str1
local len2 = #str2
local matrix = {}
for i = 0, len1 do
matrix[i] = {}
matrix[i][0] = i
end
for j = 0, len2 do
matrix[0][j] = j
end
for i = 1, len1 do
for j = 1, len2 do
local cost = 0
if str1:sub(i, i) ~= str2:sub(j, j) then
cost = 1
end
matrix[i][j] = math.min(
matrix[i - 1][j] + 1,
matrix[i][j - 1] + 1,
matrix[i - 1][j - 1] + cost
)
end
end
local maxLen = math.max(len1, len2)
return (1 - matrix[len1][len2] / maxLen) * 100
end
This is a similarity calculator function to calculate similarty between our input and inputs in inputs table.
But we have a problem. We don't want most similar word always. For example what if our input is 'ahalskdasydl' we don't want a normal input for this one. So we need to add a minimum similarity condition with if statements. For example if most similar word between our input and the inputs in inputs table is >%65 then we can find a category for this input else we can return nil.
We need ignore spaces, upper letters, punctuation such as *'*ABC,?+/ :;'**. Let's create a table for make this topic more understanable:
Input | Convert
'Hello.' -> 'hello' 'What time is it?' -> 'whattimeisit' 'AI ChatBot.' -> 'aichatbot'
We can use string.gsub() for this and get the lower of all characters in the string with string.lower(). There is a example code for this.
Also we should create a function that we can acces from other scripts to get category so we should create our function as module.DecideCategory().
function module.DecideCategory(input)
local inputToCheck = string.lower(string.gsub(input, "[%p%s]", ""))
end
Now let's complete the function to get category of inputs.
function module.DecideCategory(message)
local inputToCheck = string.lower(string.gsub(input, "[%p%s]", ""))
local bestMatchPercentage = 0
local bestMatchType = "nil"
for k, v in pairs(asks) do
for i, ask in ipairs(v) do
local similarity = calculateSimilarity(inputToCheck, string.lower(ask))
if similarity == 100 then
return k
elseif similarity > bestMatchPercentage then
bestMatchPercentage = similarity
bestMatchType = k
end
end
end
if bestMatchPercentage >= 65 then
return bestMatchType -- Return category if similarity > 65
else
return "nil" -- Else return 'nil'
end
end
Let's view our full first module script
local module = {}
local inputs = {
["greetings"] = {
"hello",
"hi",
"hey"
}
}
local category = nil
function calculateSimilarity(str1, str2)
local len1 = #str1
local len2 = #str2
local matrix = {}
for i = 0, len1 do
matrix[i] = {}
matrix[i][0] = i
end
for j = 0, len2 do
matrix[0][j] = j
end
for i = 1, len1 do
for j = 1, len2 do
local cost = 0
if str1:sub(i, i) ~= str2:sub(j, j) then
cost = 1
end
matrix[i][j] = math.min(
matrix[i - 1][j] + 1,
matrix[i][j - 1] + 1,
matrix[i - 1][j - 1] + cost
)
end
end
local maxLen = math.max(len1, len2)
return (1 - matrix[len1][len2] / maxLen) * 100
end
function module.DecideCategory(message)
local inputToCheck = string.lower(string.gsub(input, "[%p%s]", ""))
local bestMatchPercentage = 0
local bestMatchType = "nil"
for k, v in pairs(asks) do
for i, ask in ipairs(v) do
local similarity = calculateSimilarity(inputToCheck, string.lower(ask))
if similarity == 100 then
return k
elseif similarity > bestMatchPercentage then
bestMatchPercentage = similarity
bestMatchType = k
end
end
end
if bestMatchPercentage >= 65 then
return bestMatchType -- Return category if similarity > 65
else
return "nil" -- Else return 'nil'
end
end
return module
I will renme our first module script to 'AI_Ask'. You can rename it what do you want.
Now we need to create a table of outputs. We can use another module script for more readable code. Let's create another module script to add outputs table.
Firstly, we don't need a output finder in our second module script. We can find outputs in scripts that we will create in future. So let's create table in our module script. This is similar to our first table but we will create this in our module table.
This is an example code:
module = {
["greetings"] = {
"Hello! How can I assist you today?",
"Hi! How can I assist you today?",
"Hi! How can I help you?",
"Hello! How can I help you?",
"Hello! How can I help you today?",
"Hello, how can I help you today?",
"Hello, how can I assist you?",
"Hello, how can I help you?"
},
}
return module
Our AI will select random output from this table. Make sure create output categories for each input category and make this two category names same.
Our AI_Ask module script can return nil. We make this for similarities that less than %65. So we need to add a nil category. Let's make a basic.
module = {
["nil"] = {
"Sorry. I did'nt understand.",
"Sorry. What do you mean?"
},
["greetings"] = {
"Hello! How can I assist you today?",
"Hi! How can I assist you today?",
"Hi! How can I help you?",
"Hello! How can I help you?",
"Hello! How can I help you today?",
"Hello, how can I help you today?",
"Hello, how can I assist you?",
"Hello, how can I help you?"
},
}
return module
Now our AI has a table for nil outputs.
This is a basic AI module scripts. You can add more input and outputs. I will rename this module scipt to 'AI_Answer'. I put these module scripts in 'ReplicatedStorage'
Now we need a script that we can send inputs to our AI and get outputs from our AI. This script will be heart of our AI. We will add more things like evaluate math sending audios & images etc.
Let's create our main script in 'ServerScriptService'. I will rename it to 'AIHandler'.
Firstly, we need declare our module scripts with 'require'. There is example code.
local AI_Ask = require(game.ReplicatedStorage.AI_Ask)
local AI_Answer = require(game.ReplicatedStorage.AI_Answer)
Now we should create an input if you want a AI chatbot with user interface (UI) you can create 'RemoteEvents'. I will create 'RemoteEvents' in 'ReplicatedStorage'. And I renamed it to 'SendMessageEvent'. You can rename it what do you want.
Let's connect our RemoteEvent to a function in our main script. There is an example code.
local AI_Ask = require(game.ReplicatedStorage.AI_Ask)
local AI_Answer = require(game.ReplicatedStorage.AI_Answer)
game.ReplicatedStorage.SendMessageEvent.OnServerEvent:Connect(function(player, message)
end
Now we need find category of our input. We can use our function in 'AI_Ask' module script. Then we can get a random answer from 'AI_Answer' module script. And we can print our answer to the output to see our answer.
local AI_Ask = require(game.ReplicatedStorage.AI_Ask)
local AI_Answer = require(game.ReplicatedStorage.AI_Answer)
game.ReplicatedStorage.SendMessageEvent.OnServerEvent:Connect(function(player, message)
--Find the category of our input
local category = AI_Ask.DecideCategory(message)
--Get random from our AI_Answer module according to our category
local answer = AI_Answer[category][math.random(1, #AI_Answer[category])]
--Show the answer in output
print(anwer)
end
This is very simple AI. You can improve this AI. You can add UI, Images, Audios that AI can send with UI. I will show you how to add a evaluate maths and how to make our AI can send images and audios from roblox library.
We can add a function to calculate Math Expressions easily without input and output categories.
Firstly we need edit our main script. We need add a function that takes the string and returns evaluated answer. But what if our input isn't a math expression? So we need return nil if this function can't calculate our input. And we can check if our answer is nil then we can set our answer with default such as we made our main script.
There is an example code:
local AI_Ask = require(game.ReplicatedStorage.AI_Ask)
local AI_Answer = require(game.ReplicatedStorage.AI_Answer)
function evaluateMathExpression(strToEdit)
--Our Evaluation code here
end
game.ReplicatedStorage.SendMessageEvent.OnServerEvent:Connect(function(player, message)
--Find the category of our input
local category = AI_Ask.DecideCategory(message)
--Set our answer to return of evaluateMathExpression function
local answer = evaluateMathExpression(message)
--If our answer is nil (if returned nil from evaluateMathExpression function)
if not answer then
--Set our answer default way
local answer = AI_Answer[category][math.random(1, #AI_Answer[category])]
end
print(anwer)
end
Sorry, I don't know how to make Evaluate Math Expression function you can fill it. I have tried this with a function that written by ChatGPT so it is working but ChatGPT's function can't calculate process piority and brackets. And that function has a lot of bug. But the other things are working so if you have evaluate math expression function you can edit it little bit and put it here.
In this script, we send our message to our math function as parameter. Our math function is empty but you can fill it. If our function can calculate it, it calculates and returns the calue else it returns nil. We can check our answer. Is our answer nil?. If it is nil we can set our answer by default way.
Let's create a input category for our audio request input in AI_Ask module script
local module = {}
local inputs = {
["greetings"] = {
"hello",
"hi",
"hey"
}
["imageRequset"] = {
"sendanimage",
"iwantimage",
"pleaseimage"
}
}
--[[
our other functions here
]]--
return module
Now let's create our category outputs in our AI_Answer module script
module = {
["nil"] = {
"Sorry. I did'nt understand.",
"Sorry. What do you mean?"
},
["greetings"] = {
"Hello! How can I assist you today?",
"Hi! How can I assist you today?",
"Hi! How can I help you?",
"Hello! How can I help you?",
"Hello! How can I help you today?",
"Hello, how can I help you today?",
"Hello, how can I assist you?",
"Hello, how can I help you?"
},
["imageRequest"] = {
"Sure, there is an image. [#=229313523]",
"Here is an image for you. [#=229313523]",
},
}
return module
What we did here?
We put a symbol and brackets for image ID. You can change this system what do you want. In our main AIHandler script we will remove this ID from our output. And we will check if there is an image ID like this our script will send an image.
Let's edit the main script.
local AI_Ask = require(game.ReplicatedStorage.AI_Ask)
local AI_Answer = require(game.ReplicatedStorage.AI_Answer)
function evaluateMathExpression(strToEdit)
--Our Evaluation code here
end
game.ReplicatedStorage.SendMessageEvent.OnServerEvent:Connect(function(player, message)
local category = AI_Ask.DecideCategory(message)
local answer = evaluateMathExpression(message)
if not answer then
--Set our answer default way
local answer = AI_Answer[category][math.random(1, #AI_Answer[category])]
end
--Check the output. Is there something like this: [#=] if there is get the ID
local startIdx, endIdx = string.find(DecidedAnswer, "%[#=(%d+)]")
if startIdx and endIdx then
local ID = string.sub(DecidedAnswer, startIdx+3, endIdx-1)
-- Get the ID
local audioId = tonumber(ID) --Set The Audio ID
--Remove ID from output
answer = string.sub(DecidedAnswer, 1, startIdx-1) .. string.sub(DecidedAnswer, endIdx+1)
print(answer) -- You can add you UI things here
else
print(answer)
end
Congrulations! You finished the tutorial. Now open the Roblox Studio and try it yourself!