Rule based chatbots
Embora seja possível utilizar ferramentas prontas como o Chatterbot
ou o
NLTK
, elas são indicadas para sistemas com muitas variações de respostas. Na
verdade, um chatbot simples pode ser feito com um programa bem mais enxuto. A
seguir, vamos ver a criação de um chatbot simples que se comunica com o
usuário.
1. Criação de um chatbot simples
1.1. Escopo do chatbot
Vamos começar definindo o escopo do chatbot. Para o nosso exemplo, vamos criar uma aplicação extremamente simples para apenas detectar quando o usuário cumprimenta o chatbot e responde adequadamente.
Exemplo de interação:
Usuário: Bom dia, tudo bem?
R: Bom dia! Comigo está tudo ótimo, e com você?
1.2. Criando expressões regulares
A primeira coisa que vamos desenvolver é a expressão regular para detectar que
o usuário teve a intenção de cumprimentar o chatbot. Embora seja possível
apenas detectar a frase Bom dia, tudo bem?
, essa solução não é nada robusta.
Afinal, se o usuário disser Boa tarde
ou mesmo deixar de usar pontuação ou
letras maiúsculas o sistema já não conseguiria detectar a intenção
corretamente.
Sempre que houver aqui uma expressão regular, recomenda-se fortemente testá-la no regex101
Vamos partir da premissa de que não temos intimidade com expressões regulares, portanto faz mais sentido irmos construindo nossa expressão aos poucos e considerando cada caso possível, um de cada vez. Vamos começar detectando o cumprimento do usuário. Vamos considerar que o usuário pode dar bom dia, boa tarde ou boa noite. A expressão regular fica:
(Bom dia|Boa tarde|Boa noite)
Explicação do chat gepeto
( ... )
: Os parênteses são usados para agrupar partes da expressão regular. Eles não apenas agrupam a expressão, mas também criam o que é conhecido como um grupo de captura. Isso significa que o texto que corresponde a esta parte da expressão pode ser "capturado" para uso posterior.Bom dia
: Esta é uma sequência literal que corresponderá exatamente ao texto "Bom dia".Boa tarde
: Assim como "Bom dia", esta é outra sequência literal que corresponderá exatamente ao texto "Boa tarde".Boa noite
: E esta é mais uma sequência literal, correspondendo exatamente ao texto "Boa noite".|
: Este é o operador de alternância, e funciona como um "ou" lógico. Isso significa que a expressão regular corresponderá a "Bom dia" OU "Boa tarde" OU "Boa noite".
Portanto, essa expressão regular procura por uma de três diferentes saudações. Se qualquer uma das saudações especificadas aparecer no texto de destino, a expressão regular considerará isso uma correspondência.
O ChatGPT é muito bom para criar ou explicar expressões regulares. Use-o em conjunto com o regex101.
Temos alguns problemas aqui. O primeiro é que não estamos capturando a próxima parte da expressão, em que o usuário pode perguntar se está tudo bem. O segundo problema é que se o usuário não usar letras maiúsculas no começo das palavras, o padrão não é reconhecido. É possível resolver isso direto na aplicação em Python, mas vamos explorar um pouco mais as opções de regex. Vejamos como pode ficar:
\b(?:(?:[Bb]o[am])\s(tarde|dia|noite))
Explicação do chat gepeto
A expressão regular \b(?:(?:[Bb]o[am])\s(tarde|dia|noite))
é um pouco mais
complexa do que a anterior e possui diversas partes:
\b
: É um metacaractere que representa um limite de palavra (word boundary). Isso significa que o padrão seguinte deve começar no início de uma palavra.(?: ... )
: É um grupo de não captura. Grupos de não captura agrupam múltiplas partes de uma expressão regular juntas sem capturar o texto que corresponde a essa parte para uso posterior. Eles são usados para aplicar quantificadores a partes da expressão, mas sem a sobrecarga de armazenar a captura.[Bb]
: É um conjunto de caracteres que corresponderá a 'B' maiúsculo ou 'b' minúsculo.o[am]
: Segue a mesma lógica do conjunto anterior, correspondendo a "oa" ou "om", o que permite a formação das palavras "Boa" ou "Bom".\s
: Corresponde a qualquer espaço em branco, como espaços, tabs, etc.(tarde|dia|noite)
: É um grupo de captura que vai corresponder a uma das palavras "tarde", "dia" ou "noite".
Portanto, essa expressão regular vai corresponder a "Bom dia", "Bom tarde",
"Bom noite", "Boa dia", "Boa tarde", "Boa noite", com qualquer combinação de
maiúsculas e minúsculas para "Bom/Boa", devido ao [Bb]
e [oa]
, e apenas se
essas combinações aparecerem como palavras isoladas no texto, por causa do
\b
.
Aqui estão alguns exemplos de como esta expressão regular funcionaria:
- "Bom dia" → Corresponde (e captura "dia")
- "boa tarde" → Corresponde (e captura "tarde")
- "BoM Noite" → Corresponde (e captura "noite")
- "abomdia" → Não corresponde (porque "abomdia" não começa no limite de uma palavra devido à ausência de espaço ou separador de palavras antes de "bom")
Acho que agora conseguimos uma expressão legal para capturar nossa primeira intenção! Vamos para a segunda? Agora vamos ver se o usuário perguntou se o chatbot está bem? Para isso, vamos direto para uma expressão regular mais robusta:
\b(?:[Tt]udo)?\s?(?:(?:[bB]em)|(?:[bB]ão)|(?:[fF]irme)|(?:em\sriba))\?
Explicação do chat gepeto
\b
: Indica um limite de palavra, o que significa que o padrão deve ocorrer no início de uma palavra.(?:[Tt]udo)?
: É um grupo de não captura que procura pela palavra "Tudo" ou "tudo". O?
depois do grupo de não captura indica que a presença da palavra "Tudo" ou "tudo" é opcional.\s?
: Corresponde a zero ou um espaço em branco (espaço, tabulação, etc.). A questão?
significa que o espaço pode ou não estar presente.(?: ... )
: São grupos de não captura usados para agrupar expressões alternativas sem capturar o que foi correspondido.(?:[bB]em)
: Este grupo de não captura vai corresponder à palavra "bem" ou "Bem".(?:[bB]ão)
: Este grupo de não captura vai corresponder à palavra "bão" (uma forma coloquial de "bom" em português) ou "Bão".(?:[fF]irme)
: Este grupo de não captura vai corresponder à palavra "firme" ou "Firme".(?:em\sriba)
: Este grupo de não captura vai corresponder à expressão "em riba" (provavelmente uma forma coloquial de "em cima").\?
: Corresponde a um ponto de interrogação literal, indicando que a expressão que está sendo buscada provavelmente é uma pergunta.
Juntando tudo isso, a expressão regular pode corresponder a várias formas de perguntar informalmente como alguém está, incluindo a presença ou ausência da palavra "Tudo" no começo e também considerando variações comuns de escrita e coloquialismos. Por exemplo, ela pode corresponder a:
- "Tudo bem?"
- "tudo bão?"
- "Bem?"
- "bão?"
- "Firme?"
- "em riba?"
A expressão é flexível o suficiente para pegar diferentes variações de espaçamento e capitalização.
1.3. Criando um dicionário de intenções
Agora que já temos nossas expressões regulares, vamos criar um dicionário para mapear as intenções do usuário. Para isso, crie um arquivo python:
touch test-chatbot.py
Nesse arquivo, vamos adicionar o seguinte:
#! /bin/env python3
import re
intent_dict = {
r"\b(?:(?:[Bb]o[am])\s(tarde|dia|noite))": "greetings",
r"\b(?:[Tt]udo)?\s?(?:(?:[bB]em)|(?:[bB]ão)|(?:[fF]irme)|(?:em\sriba))\?": "genki"
}
command = input("Digite o seu comando: ")
for key, value in intent_dict.items():
pattern = re.compile(key)
groups = pattern.findall(command)
if groups:
print(f"Encontrei a seguinte intenção: {value}")
Deixe o script executável com:
chmod +x test-chatbot.py
E execute-o com:
./test-chatbot.py
Note que as expressões regulares ainda não estão perfeitas. Brinque um pouco com elas, talvez até criando novas expressões com intenções redundantes, para que o chatbot fique um pouco mais inteligente.
1.4. Atrelando a intenção a uma ação
Apenas identificar a intenção não é o suficiente para criar um chatbot capaz de executar ações mais complexas. Vamos agora criar um dicionário que linka intenções à ações. Edite o código anterior e deixe-o assim:
#! /bin/env python3
import re
def greet_back(time_of_day):
if time_of_day == "dia":
return f"Bom {time_of_day}!"
else:
return f"Boa {time_of_day}!"
def genki_back(_):
return "Comigo está tudo bem, e com você?"
intent_dict = {
r"\b(?:(?:[Bb]o[am])\s(tarde|dia|noite))": "greetings",
r"\b(?:[Tt]udo)?\s?(?:(?:[bB]em)|(?:[bB]ão)|(?:[fF]irme)|(?:em\sriba))\?": "genki"
}
action_dict = {
"greetings": greet_back,
"genki": genki_back
}
command = input("Digite o seu comando: ")
for key, value in intent_dict.items():
pattern = re.compile(key)
groups = pattern.findall(command)
if groups:
print(f"{action_dict[value](groups[0])}", end=" ")
print()
Pronto! Temos o nosso pequeno chatbot configurado e funcional!
2. Exercícios propostos
Criando...