Ask questions and get answers in custom_actions

Hey :wave:

Is it possible with a custom action to ask a question to the user and retrieve his answer dynamically?

Like :

    * answer_question
    - action_get_categories
    * select_categoty
    - action_get_subject
    * select_subject
    - action_get_question
    - action_get_question
    - action_get_question
    *change_subject
    - action_get_subject
    * select_subject
    - action_get_question
    - action_get_question
    - action_get_question

Sample conversation:

Bot: Which category?
Options : Reading / Movies & Series / Cooking
User: Films & Series 
Bot: What theme? 
Options: Netflix / TV / Cinema
User: Cinema
Bot: Did you go to see the last Harry Potter movie? 
Options: Yes / No / Not yet
User: Yes
Bot: What did you think of Cedric's death? 
Options: Too bad / He had to die...
User: Too bad

Thanks in advance for advice.

Thanks, Valentin

Looking at your sample conversation, it seems like using a form you could pretty easily make this.

Justina from Rasa has written a blog post about forms and she talks about them in the Rasa Masterclass. Masterclass is a series of videos that will cover all the basics about building bots using Rasa.

Blog post about forms:

Rasa Masterclass:

Hey

Thanks for suggestions.

I don’t see how to dynamically create the question to the user with FormAction. I go through an action that sends a random question to the user.

Config

language: "fr" 

pipeline:
  - name: WhitespaceTokenizer
    case_sensitive: False
  - name: RegexFeaturizer
  - name: LexicalSyntacticFeaturizer
  - name: CountVectorsFeaturizer
    analyzer: "char_wb"
    min_ngram: 1
    max_ngram: 4
  - name: EntitySynonymMapper
  - name: ResponseSelector
    epochs: 100
  - name: CRFEntityExtractor
    BILOU_flag: true
  - name: "DucklingHTTPExtractor"
    url: "http://localhost:8000"
    dimensions: ["time", "number", "amount-of-money", "distance"]
    locale: "fr_FR"
    timezone: "Europe/Paris"
    timeout : 3
  - name: DIETClassifier
    entity_recognition: false
    epochs: 100
  
policies:
  - name: AugmentedMemoizationPolicy
    max_history: 15
  - name: TEDPolicy
    max_history: 5
    epochs: 100
  - name: FormPolicy
  - name: MappingPolicy
  - name: FallbackPolicy
    nlu_threshold: 0.1
    ambiguity_threshold: 0.04
    core_threshold: 0.3
    fallback_action_name: utter_oupsomethingfailed

NLU

# intent:choice_category
[aleatoire](category_questions)
[caractere](category_questions)
[couple](category_questions)
[sexe](category_questions)
[loisirs](category_questions)
[cinema](category_questions)
[humour](category_questions)
[quotidien](category_questions)

## intent:answer_question
[AC13_1](answer_id)

## regex:answer_id
- [A-Z]{1,3}[0-9]{1,4}_[0-9]{1,4}

Domain

actions:
- action_menu
- action_ask_question
- utter_ask_criteries_genre
- utter_ask_genre
- utter_ask_mood
- utter_goodbye
- utter_greet
- utter_happy
- utter_iamabot
- utter_menu
- utter_oupsomethingfailed
- utter_thanks
- utter_unhappy
- utter_validate_criteries_genre
entities:
- criteries_sexe
- sexe
- module
- category_questions
- question_id
- answer_id
- answer_question
intents:
- deny
- thanks
- greet
- sexe
- mood_great
- affirm
- mood_unhappy
- goodbye
- bot_challenge
- module
- choice_category
- menu
slots:
  module:
    type: unfeaturized
  category_questions:
    type: unfeaturized
  question_id:
    type: unfeaturized
  answer_id:
    type: unfeaturized
  criteries_sexe:
    type: categorical
    values:
    - homme
    - femme
    - bi
    - __other__
  sexe:
    type: categorical
    values:
    - homme
    - femme
    - autre
    - __other__
responses:
  utter_ask_criteries_genre:
  - buttons:
    - payload: /sexe{"criteries_sexe":"homme"}
      title: πŸ‘¨β€πŸ¦± Homme
    - payload: '/sexe{"criteries_sexe": "femme"}'
      title: πŸ‘© Femme
    - payload: '/sexe{"criteries_sexe": "bi"}'
      title: Bi
    text: Et que recherches-tu ?
  utter_ask_genre:
  - buttons:
    - payload: /sexe{"sexe":"homme"}
      title: πŸ‘¨β€πŸ¦± Homme
    - payload: '/sexe{"sexe": "femme"}'
      title: πŸ‘© Femme
    - payload: '/sexe{"sexe": "autre"}'
      title: Non genrΓ©
    text: De quel genre te dΓ©signes-tu ?
  utter_ask_mood:
  - text: Comment vas-tu aujourd'hui ?
  utter_goodbye:
  - text: Bye
  - text: Prends soin de toi, Γ  bientΓ΄t πŸ‘‹
  - text: Bye πŸ‘‹
  - text: A plus tard πŸ‘‹
  - text: Au plaisir de te reparler πŸ‘‹
  utter_greet:
  - text: Salut, moi c'est Archelot πŸ€–
  - text: Bonjour! πŸ˜‡ Archelot! Ravi de te rencontrer.
  utter_happy:
  - text: Great, carry on!
  utter_iamabot:
  - text: Je suis Archelot, une IA imaginΓ© par mes dΓ©veloppeurs Valentin et Coralie.
  utter_menu:
  - buttons:
    - payload: /menu{"module":"notifications"}
      title: News 
    - payload: /menu{"module":"questions"}
      title: Parler
    - payload: /menu{"module":"help"}
      title: FAQ
    text: Que souhaites-tu faire?
  utter_oupsomethingfailed:
  - text: Oups, je n'ai pas compris ta réponse... Après tout je ne suis qu'un bot
      πŸ€–
  utter_thanks:
  - text: Au plaisir πŸ˜‡
  utter_unhappy:
  - text: Ouais c'est la vie !
  utter_validate_criteries_genre:
  - text: 'Merci πŸ˜‡ Genre : {sexe} et recherche : {criteries_sexe}'
session_config:
  carry_over_slots_to_new_session: false
  session_expiration_time: 60

Story

## répondre à des questions caractère
* menu{"module": "questions"}
    - action_menu
* choice_category{"category_questions": "caractere"}
    - slot{"category_questions": "caractere"}
    - action_ask_question

## rΓ©pondre Γ  des couple
* menu{"module": "questions"}
    - action_menu
* choice_category{"category_questions": "couple"}
    - slot{"category_questions": "couple"}
    - action_ask_question

actions.py

from datetime import datetime
from typing import Any, Dict, List, Text, Union, Optional
import json
import logging
from rasa_sdk import ActionExecutionRejection
from rasa_sdk import Action, Tracker
from rasa_sdk.events import EventType, SlotSet, FollowupAction, UserUtteranceReverted
from rasa_sdk.executor import CollectingDispatcher
from rasa_sdk.forms import FormAction, REQUESTED_SLOT, Action
from rasa.core.slots import Slot
from typing import Dict, Text, Any, List, Union

import random
import re
import sqlite3
import requests

logging.basicConfig(filename='example.log',level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')

cateogory =[
    {"category" : "SantΓ©", "slug" : '/choice_category{"category_questions": "sante"}'},
    {"category" : "Public", "slug" : '/choice_category{"category_questions": "public"}'},
]

db_questions = [
    {"a" : "public",
        "questions" : [
            {
                "id" : 1,   
                "message" : "Tu vas Γ  la plage cet Γ©tΓ©",
                "buttons" : [
                    {"answer" : "Je pense oui", "slug" : '/answer_question{"answer_id": "AC01_1"}'},
                    {"answer" : "Pas les moyens", "slug" : '/answer_question{"answer_id": "AC01_2"}'},
                ]
            }
]

class menu_action(Action):
    def name(self):
            return "action_menu"
    
    def run(self, dispatcher, tracker, domain) -> List[EventType]:
        buttons = []

        if tracker.get_slot('module') == 'questions':
            message_title = "De quoi veux-tu discuter?"
            for i in range(len(cateogory[0])) :
                buttons.append(
                    {
                        "title": cateogory[i]["category"],
                        "payload": cateogory[i]["slug"],
                    }
                )

            dispatcher.utter_message(message_title, buttons=buttons)
            return []

class action_ask_question(Action):
    def name(self) -> Text:
         return "action_ask_question"

    def run(self, dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any]) -> List[Dict[Text, Any]]:

        id_question = 0
        message_title = "?"
        if tracker.get_slot('category_questions') :
            slot_category = tracker.get_slot('category_questions')
        
            buttons = []   
            for categories in db_questions:
                if categories['a'] == slot_category :
                    choice = random.choice(categories['questions'])
                    id_question = choice['id']
                    message_title = choice['message']
                    for answer in choice['buttons']:
                        buttons.append(
                            {
                                "title": answer['answer'],
                                "payload": answer['slug'],
                            }
                        )
            
            #Ici je renvoi la question et les rΓ©ponses possible Γ  afficher
            dispatcher.utter_message(message_title, buttons=buttons, tracker=tracker)
            SlotSet("question_id", id_question)
            return []

Theoretically, I have to go through an action for the answer but this intent cannot contain all possible answers. Is it possible with a FormAction to retrieve them automatically?

rasa interactive

5    slot{"category_questions": "public"}                                                                        
      action_ask_question 1.00                                                                                       
      Question to ask ?                                                                                               
      Buttons:                                                                                                       
      1: answer (/answer_question{"answer_id": "AC02_1"})                                                             
      2:                                                                                                             
     answer 2 (/answer_question{"answer_id": "AC02_2"})                                                           
      Type out your own message...                                                                                   
      action_listen 1.00                                                                                             
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 6                                                                           /answer_question{"answer_id": "AC02_2"} 
                                                                                        intent: answer_question 1.00 
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 7    slot{"answer_id": "AC02_2"}                                                                                    
      action_ask_question                                                                                            
      Do you prefer ?                                                                                    
      Buttons:                                                                                                       
      1: Answer 1 (/answer_question{"answer_id": "AC03_1"})                                                              
      2:                                                                                                             
      Answer 2 (/answer_question{"answer_id": "AC03_2"})                                                           
      Type out your own message...                                                                                   
      action_listen 0.99                                                                                             
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 8                                                                           /answer_question{"answer_id": "AC03_1"} 
                                                                                        intent: answer_question 1.00 
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 9    slot{"answer_id": "AC03_1"}                                                                                    
      utter_menu

Do I have to restart an action when the user has replied in order to retrieve his answer? What do you think about doing that?