Data | Versão | Descrição | Autor |
---|---|---|---|
29/05/2019 | 0.0.1 | Criação do documento | Iuri |
31/05/2019 | 0.0.2 | Inserção de dos sobre a implementação do FormAction | André, Iuri |
FormActions
Uma das ações mais comuns em conversas é receber uma lista de dados, como por exemplo, o nome, telefone e cpf de um usuário. Os FormActions foram feitos para isso, coletar múltiplos dados do usuário durante a conversa. No site do Rasa na parte de Forms eles usam de exemplo uma conversação na qual o usuário deseja pesquisar por um restaurante, esse exemplo pode ser encontrado aqui.
Utilizando FormActions
A implementação de um FormAction no bot não é muito diferente dos Slots em si, apesar dele utilizar dos slots para funcionar. Para criar um Form é necessário seguir os seguintes passos:
1. Adicionar o “FormPolicy” nas políticas (policies) do Bot
policies:
- name: "FormPolicy"
2. Declarar o Form no arquivo domain.yml
forms:
- restaurant_form
3. Criar a intent e stories de acesso ao Form
## happy path
* request_restaurant
- restaurant_form
- form{"name": "restaurant_form"}
- form{"name": null}
De acordo com o Rasa, é necessário definir o form usado, como mostrado no exemplo, e após o uso declarar form{"name": null}
para finalizar o uso
4. Implementar a classe do FormAction nas custonActions do bot
- A classe deverá herdar os dados da classe
FormAction
, que deverá ser importada no início do código; - Ela deve possuir as funções
name
,required_slots
esubmit
;
As funções serão explicadas no código de exemplo
É recomendado que os slots utilizados sejam do tipo unfeaturized
O FormAction só fará requisição de slots ainda não preenchidos.
class UserForm(FormAction):
# Define o nome do form para que o mesmo seja
# utilizado dentro do arquivo domain.yml
def name(self):
return "user_form"
# Define quais slots fazem parte do formulário
# ou seja, quais dados você quer do usuário
def required_slots(self, tracker):
return ['username', 'password']
# Ação realizada pelo bot após receber
# todos os slots requisitados
def submit(self, dispatcher, tracker, domain):
dispatcher.utter_message('Coletei todos os slots!')
# O submit deve retornar uma lista
# Caso não haja retorno na função,
# basta colocar uma lista vazia
return []
5. É necessário criar uma utter utter_ask_{slot_name}
para cada slot requisitado pelo Form;
6. É necessário criar uma intent com nome inform
onde devem ser listados os exemplos de frases com os slots requisitados;
Após alguns testes, foi validado que é possível usar mais de um Form na intent inform
7. Caso nada seja extraido do slot, será acionada a action ActionExecutionRejection
Para mais informações sobre FormActions, o Rasa disponibiliza sua documentação na página de Form deles.
Implementação dos FormActions
A implementação dos Forms pode ser encontrada no github do Rasa, tendo a base do Form e o FormValidation no arquivo events.py
e a implementação no arquivo forms.py
.
Dentro do arquivo forms.py
tem a classe FormAction
, que possui herança da classe Action
, e que é a base desse estudo. Logo no início do arquivo também ser visto a variável REQUESTED_SLOT
que possui o valor "requested_slot"
, sendo ela uma constate para o nome do slot que guarda a lista de slots que o formulário irá utilizar.
A classe possui as seguintes funções:
name(self)
: Por padrão do Rasa, é uma função obrigatória em todas Actions é define o nome da mesma.required_slots(tracker)
: Retorna a lista de slots que o formulário deve preencher, sendo essa uma função obrigatória em todos os FormActions.from_entity(self, entity, intent=None, not_intent=None)
: Retorna um dicionário para mapear o modo de extração dos slots.from_trigger_intent(self, value, intent=None, not_intent=None)
: Retorna um dicionário para mapear o modo de extração dos slots.from_intent(self, value, intent=None, not_intent=None)
: Retorna um dicionário para mapear o modo de extração dos slots.from_text(self, intent=None, not_intent=None)
: Retorna um dicionário para mapear o modo de extração dos slots.slot_mappings(self)
: Retorna um dicionário que mapeia os slots requeridos pelo forms.get_mappings_for_slot(self, slot_to_fill)
: Retorna uma lista de dicionários que mapeia os slots requeridos com o modo como devem ser extraídos.intent_is_desired(requested_slot_mapping, tracker)
: Valida se a intent do usuário combina com as condições de intent do formulário.get_entity_value(name, tracker)
: Verfica se uma entidade específica já está preenchida e, caso esteja, extrai seu valor.extract_other_slots(self, dispatcher, tracker, domain)
: Extrai o valor dos slots definidos pelo usuário como “subslots” de algum slot listado entre os requeridos.extract_requested_slot(self, dispatcher, tracker, domain)
: Extrai o valor dos slots necessário para o formulário. Utiliza o mapa gerado pelas outras funções para válidar os slots desejados e como extraí-los. Retorna um dicionário com os valores.validate_slots(self, slot_dict, dispatcher, tracker, domain)
: Valida os slots utilizando as funções de ajuda de validação. Chama a funçãovalidade_{slot}
para cada um dos slots e, caso a função não esteja implementada, define o slot com o valor mapeado.validate(self, dispatcher, tracker, domain)
: Extrai e valida o valor de cada slot. Caso nenhum valor seja extraído, retorna a execução de rejeição da FormAction.- Pode ser sobrescrita para customizar a validação e o retorno.
request_next_slot(self, dispatcher, tracker, domain)
: Solicita o próximo slot listado e o template da utter, se necessário. Caso não haja mais slots, retornaNone
`.deactivate(self)
: Retorna um eventoForm
com nomeNone
para desativar o formulário e resetar os slots.submit(self, dispatcher, tracker, domain)
: Define quais ações serão tomadas após a extração dos valores dos slots._to_list(x)
: Converte um objeto para uma lista, caso seja algo do tipoNone
, retorna uma lista vazia._list_intents(self, intent=None, not_intent=None)
: Verifica se uma intent pode ou não ser acessada durante o preenchimento do formulário._log_form_slots(self, tracker)
: Registra os valores de todos os slots necessários antes de enviar o formulário._activate_if_required(self, dispatcher, tracker, domain)
: Ativa o formulário caso ele tenha sido chamado pela primeira vez. Se ativado, valida os slots necessários que foram preenchidos antes da ativação do formulário e retorna o eventoForm
com o nome do formulário, bem como qualquer eventoSlotSet
da validação de slots pré-preenchidos._validate_if_required(self, dispatcher, tracker, domain)
: Retorna uma lista de eventos do tipoself.validate(...)
e se a validação é requerida:- o formulário é ativado;
- o formulário é chamado após a
action_listen
; - a validação do formulário não é cancelada.
_should_request_slot(tracker, slot_name)
: Verifica se a ação do formulário deve solicitar o slot fornecidorun(self, dispatcher, tracker, domain)
: Executa os efeitos colaterais do formulário. Passos:- Ativa o
Form
, se necessário; - Valida a entrada do usuário, se necessário;
- Define os slots validados;
- Template utter_ask_{slot} ativada para o próximo slot;
- Entra na função
submit
caso os slots estejam preenchidos; - Desativa o formulário.
- Ativa o
__str__(self)
: Define o modo como o formulário é printado.