GhostWording / gw-config-apis

this repo contains static json that can live through apis with github management only
0 stars 7 forks source link

[bot] sequence evolution with steps #4

Closed rhwy closed 7 years ago

rhwy commented 7 years ago

We want to introduce the notion of steps with some enhancements of the actual format. the proposal reference file is here: https://github.com/GhostWording/gw-config-apis/blob/master/data/bot/sequences/experiments/CatsOrDogsv4-1.json

Important Changes:

Label vs Steps

The Label should disappear. Within questions, the label is just a special form of steps with only one step. in order to simplify the format of the construct we should keep only the steps and remove the Label. this means that a label in question (with no other steps) will become:

 "Steps": [{
   "Type": "Text",
   "Id": "1",
   "Label": {
     "en": "Are you more",
     "fr": "Est-ce que tu préfères",
     "es": "Te gustan mas"
   }
}]

Within commands, the Label signification is different, as it refers to the label shown inside a button (not a text with a question). That's why it makes sense to keep it appart the Steps. But in order to clarify things, we'll call it CommandLabel :

"CommandLabel": {
    "en": "a cat person 🐱",
    "fr": "les chats 🐱",
    "es": "los gatos 🐱"
  },

Types

actually, the main type is question and the sub-types are defining the action of a command which make the action implicit and complex if we want to do multiple commands. Basically what it's important is to note if something in the sequence tree is a node (something followed by a next step) or a leaf (a terminal action). That's why we'll try now to use type=node and type=leaf.

Just to be clear, in a sequence we'll have a type=node when there's another element after, which mean that it has a Commands property or a LinksTo property, otherwise it'll be a type=leaf

Steps

Steps are a series of things to do, before showing the command buttons in a question/node and after the selection of a command within a command/leaf. The principle of the steps is to simplify actual logic by describing a series of things to do after and before an interaction with a user.

Steps don't have a unique Id like a node or a leaf, they are part of it and executed sequentially, that's why we'll use the id as something do identify the step within the steps and to order them. It is recommended to use the ten by ten notation to be able to add a new step in between without renumbering everything (10,20,30...)

Actions

We talked about the property action (like : "Actions": ["DoVote","ShowSurveyResults"]. But We miss 2 points here: 1/ we don't have an easy way to add to action some specific parameters and 2/ we don't know exactly when these actions are executed along with the steps defined in the command.

That's why the simplest thing to do, is to add the actions to do as a step. doing that we can ensure the coherence of the order of actions, be able to show something before the action (showing a gif before showing the survey results for exemple) and because it's not just a string but an object we can add extra parameters:

...
 "Steps": [
    {
        "Type":"Action",
        "Id":"10",
        "Name":"DoVote",
        "Params": {
          "additionalCounter" : "surveyCatsAndDogsCounterWhatever"
        }
    },
    {
      "Type": "Image",
      "Id": "20",
      "Source": {
        "Type": "AnimatedGif",
        "Source": "Giphy",
        "Path": "LKRtMj7xvviUg"
      }
    }, ...
AgnesGsd commented 7 years ago

I'm trying to see if this new format works for all the sequences we have already developed (be it for Messenger or Android), and I have several questions/remarks/concerns to share for the quiz sequences and the stories.

Quiz

The reference file is this one: https://github.com/GhostWording/gw-config-apis/blob/master/data/bot/sequences/experiments/FastestDolphinOrWhaleV4.json

To show it was a quiz question (with a right and a wrong answer), we used to use a boolean like this: "Quiz": false. What I chose to do is to replace it with an Action as defined previously by @rhwy , using a true/false parameter, like this:

{
      "Type": "Action",
      "Id": "10",
      "Name": "Quiz",
      "Params": {
          "additionalCounter": "False"
          }
    }

But I have no idea if such an information (true/false) can be considered as a counter....?

AgnesGsd commented 7 years ago

Stories

The reference file is this one: https://github.com/GhostWording/gw-config-apis/blob/master/data/bot/sequences/experiments/SherlockGoesCampingV4.json

I thought that we could keep the "Type": "Story" to show that what follows isn't a question but just a row of steps...? What do you think about it @rhwy?

Then I wanted to use the new "Type": "Break" with a "Mode": "Stop", and that's when I wondered: where should we define the type of stop we want? In the previous format, we used to define a question like this at the beginning of the sequence:

"ContinueQuestion": {},
  "Commands": [
    {
      "Type": "ContinueStory",
      "Id": "ContinueYes",
      "Label": {
        "en": "And...",
        "fr": "Et...",
        "es": "Y..."
      }
    },
    {
      "Type": "DoNothing",
      "Id": "ContinueNo",
      "Label": {
        "en": "Stop!",
        "fr": "Stop!",
        "es": "Stop!"
      }
    }
  ]

Would it be the same with the Break? What should I write, and where?

Finally, what about the very end of the sequence, with the SetUserProperty?

"LinksTo": {
    "Type": "SetUserProperty",
    "Id": "StorySherlockGoesCamping",
    "Label": {
      "en": "Thanks for reading my story!",
      "fr": "Merci d'avoir lu mon histoire !",
      "es": "¡Gracias por leer mi historia!"
    },
    "Parameters": {
      "property": "StorySherlockGoesCampingDone",
      "value": "true"
    }
  }

Is it a problem to keep it this way, since we decided that the "Label" should disappear, replaced by "Type": "Text" in the steps or "CommandLabel" in the commands?

rhwy commented 7 years ago

Quiz

There is 2 things about quizzes: 1/ know that a response is the right one and 2/ notify the user about the result.

We can have 2 options here:

1.add a property + action

We add a generic property to the sequence element command. It can be called "IsPreferedChoice" with boolean values "true|false". I prefer this generic name because quiz is too specific, we may want to use this feature outside the "Quiz" app.

then we can add a step to notify the user about the correctness of his answer.

 {
   "Type": "Action",
   "Id": "10",
   "Name": "ShowCorrectResponseFeedback",
   "Params": {
      "imageUrl": "url_to_success_gif"
    }
}

(and we can avoid the Params property and decide that the client app has a default mechanism to show correct/not correct response)

2. just add an action

if we decide that the information "IsPreferedChoice" is not relevant appart the fact to show the user the result, we can avoid the property and just a step "ShowCorrectResponseFeedback" (which will be different depending on the choice):

 {
   "Type": "Action",
   "Id": "10",
   "Name": "ShowCorrectResponseFeedback",
   "Params": {
      "isCorrect": "true/false"
    }
}
rhwy commented 7 years ago

Stories

yes, I think that we can keep the type="story" for now, at least for better reading even if it should be used in apps. A story is just a special sequence with only one leaf after all, so we can execute it correctly without any other information.

ok for the step break in stop mode

{
  "Type": "Break",
  "Id": "20",
  "Mode": "Stop"
}

without no other parameters, it's on client responsibility to show the right toolbar (question "would you like to continue?" + buttons yes/no). this can be defined in bot resources file for exemple.

if we want a specific question/buttons, we should be able to add them within step's parameters:

{
  "Type": "Break",
  "Id": "20",
  "Mode": "Stop",
  "Label" : {
     "en" : "go on?", "fr": "on continue?", "es":"continuar?"
  },
 "Options": {
    "yes" : {  "en" : "yes", "fr": "oui", "es":"si" },
    "no" : {  "en" : "no", "fr": "non", "es":"no" },
 }
}

It's obviously longer but we have no choice if we want specific question/buttons

rhwy commented 7 years ago

LinksTo

In fact the linksto can have the new format too and be simplied in it's structure:

"LinksTo": {
    "Id": "StorySherlockGoesCamping",
    "Steps" : [
       { 
           "Type" : "Text",
           "Id" : 10,
           "Label": {
                 "en": "Thanks for reading my story!",
                 "fr": "Merci d'avoir lu mon histoire !",
                 "es": "¡Gracias por leer mi historia!"
            }
      },
      { 
           "Type" : "Action",
           "Id" : 20,
           "Name":"SetUserProperty",
          "Parameters": {
               "property": "StorySherlockGoesCampingDone",
               "value": "true"
          }
 }
andreasdieryck commented 7 years ago

Quiz

Of the two choices offered above by Rui, a combination of both seems to make sense.

Reminder:

Until now, a typical quiz sequence commands and leaves looks like this:

The question:

 "Commands": [
   {
     "Type": "Leaf",
     "Id": "FastestDolphin",
     "StepValue": "1",
     "CommandLabel": {
       "en": "dolphin"
     },
     "Steps": [
       {
         "Type": "Text",
         "Id": "10",
         "Label": {
           "en": "Wrong answer! A blue whale can swim as fast as 50km/h, whereas most dolphins don't reach 40km/h"
         }
       }
     ]
   },
   {
     "Type": "Leaf",
     "Id": "FastestWhale",
     "StepValue": "1",
     "Label": {
       "en": "whale"
     },
     "Steps": [
       {
         "Type": "Text",
         "Id": "10",
         "Label": {
           "en": "Right answer! A blue whale can swim as fast as 50km/h, whereas most dolphins don't reach 40km/h. "
         }
       }
     ]
   }
 ]

Two things we should note here:

To adapt this format to this specific UI, we just need indicate to the client if a leaf is true or false. One clear way to do it is at the leaf level, without interfering in the steps. Using Rui's "isCorrect": "true/false", we could have

 "Type": "Leaf",
  "Id": "FastestDolphin",
  "StepValue": "1",
  "isCorrect": "false"
  "CommandLabel": {
    "en": "dolphin"
  },
  "Steps": [
    {
      "Type": "Text",
      "Id": "10",
      "Label": {
        "en": "Wrong answer! A blue whale can swim as fast as 50km/h, whereas most dolphins don't reach 40km/h"
      }
    }
  ]

And that's it.

AgnesGsd commented 7 years ago

Quiz

Based on Andreas' quiz sequence example, I got an idea that I wanted to share with you.

At first, what we wanted to do with the "isCorrect": "false" was to create a special animation or design at the app level that would be different for a correct and a wrong answer, but it wouldn't have had any consequence at the bot level (i.e. if used in a chatbot, the sequence would have been a classic one, with a question, some commands and a feedback).

So my question is: what if we used this boolean to introduce an automatic response from the chatbot (be it on Messenger or in our apps)? For example, if it is an "isCorrect": "true" answer, the bot could send an automatic message "Exactly!", "Great!", "Wouah, how could you know that?" or whatever, and then the traditional feedback (picture, gif or message) would be sent in another message. Same if it is an "isCorrect": "false" answer, with messages such as "Oups...", "Nice try!", "Sorry but... nope" etc...

We could create a small list of such messages that could be relevant in any context as long as there is a right and a wrong answer (or several ones), just as the list of emojis per intention we already have: https://github.com/GhostWording/gw-config-apis/blob/master/bot/SmileysForIntentions.json

Why is this really really cool? 1/ it makes the conversation much more spontaneous and less repetitive 2/ it saves time (no need to add those messages as feedback in the .json files) 3/ the same file could be used in our bots and in a specific tab of our apps without making any change 4/ can be useful for Quiz sequences but also for all our redirection sequences such as "Which picture do you think is the most popular to say I love you?" etc.

If I take the same example as above (in Andreas' comment):

"Type": "Leaf",
  "Id": "FastestDolphin",
  "StepValue": "1",
  "isCorrect": "false",
  "CommandLabel": {
    "en": "dolphin"
  },
  "Steps": [
    {
      "Type": "Text",
      "Id": "10",
      "Label": {
        "en": "A blue whale can swim as fast as 50km/h, whereas most dolphins don't reach 40km/h"
      }
    }
  ]

(no need to add the "Wrong answer!" in the feedback anymore)

Tell me what you think about it please :)

rhwy commented 7 years ago

The problem with that @AgnesGsd with the steps is that the client don't know when to do this interaction with the user, is it before starting the steps? after? and if we want to show a gif, then show the correctness, then continue with other text ?

I agree with @andreasdieryck on the fact that the property should be at the command level and not at the step level, but if we want to notify the user about the correctness we must introduce a special step for that.

it could be something like that:

"Type": "Leaf",
  "Id": "FastestDolphin",
  "StepValue": "1",
  "isCorrect": "false",
  "CommandLabel": {
    "en": "dolphin"
  },
  "Steps": [
   {
      "type":"action",
      "id": "1",
      "name": "notifyCorrectness"
   },
    {
      "Type": "Text",
      "Id": "10",
      "Label": {
        "en": "A blue whale can swim as fast as 50km/h, whereas most dolphins don't reach 40km/h"
      }
    }
  ]

then this notifyCorrectness action can take it's data to a resource file as proposed by @AgnesGsd to introduce some salt in the responses. if we want a custom thing, we just need to add an image step or text to tell user he's right

rhwy commented 7 years ago

one additional thing we could introduce in steps, is that if the id (used for ordering) is the same, we'll randomise the steps with the same order. that means in the previous exemple, that if id=10 in the notifyCorrectness action and text, we'll randomly show one before the other. that could be a solution to introduce more natural conversation in steps (if order is not important obviously )

AgnesGsd commented 7 years ago

ShowCards

The reference file is this one: https://github.com/GhostWording/gw-config-apis/blob/master/data/bot/sequences/experiments/AreYouOftenLateV4.json

So far the "Type":"ShowCards" displays a card (text+image) from a specific intention or theme of our database. The current format is this one:

{
              "Type":"ShowCards",
              "Id": "OftenLateMessageYes",
              "StepValue" : "1",
              "Label": {
                  "en": "Yes",
                  "fr": "Oui",
                  "es": "Si"
              },
              "LinksTo": {
                "Type": "Intention",
                "Id": "9B2C8B"
              }
            }

And I tried to adapt it like this:

            "Steps": [
              {
                "Type": "Action",
                "Id": "10",
                "Name": "ShowCards",
                "Params": {
                  "Type": "Intention",
                  "Id": "9B2C8B"
                }
              }
            ]

Does it seem fine?

AgnesGsd commented 7 years ago

Carousel

Just a quick recap of what @rhwy said about this last week. The reference file is this one: https://github.com/GhostWording/gw-config-apis/blob/master/data/bot/sequences/experiments/CarouselV4-2.json

The carousel is a special command composed of a picture and a short text (we usually display several cards like this in order for the user to choose one, thus the name Carousel).

The former way of coding it was:

{
            "Type": "Carousel",
            "Id": "MostPopularTextImage1",
            "StepValue": "1",
            "Carousel": {
                "Image": {
                    "Source": "Internal",
                    "Path": "/specialoccasions/I-love-you/default/small/530773_10150757735645255_150787610254_11952370_462269272_n.jpg"
                },
                "Text": {
                    "en": "I want all of you, forever, you and me, everyday.",
                    "fr": "Je veux tout de toi, pour toujours, toi et moi, tous les jours.",
                    "es": "Quiero todo de ti, para siempre, tu y yo, todos los días."
                }
            }
        }

Now, we prefer to use a combination of CommandPicture and CommandLabel:

"Type": "Leaf",
      "Id": "MostPopularTextImage1",
      "StepValue": "1",
      "isCorrect": "true",
      "CommandPicture": {
        "Type": "Image",
        "Source": "Internal",
        "Path": "/specialoccasions/I-love-you/default/small/530773_10150757735645255_150787610254_11952370_462269272_n.jpg"
      },
      "CommandLabel": {
        "en": "I want all of you, forever, you and me, everyday.",
        "fr": "Je veux tout de toi, pour toujours, toi et moi, tous les jours.",
        "es": "Quiero todo de ti, para siempre, tu y yo, todos los días."
      }

When CommandPicture and CommandLabel come together, it means we want to display a card, but both of them can be used independently to display a picture or a text as a command.

andreasdieryck commented 7 years ago

New carousel proposal

Following the recent document V2, there is some concern on @Olivier's part about the carousel:

Should we decide to switch to another way of writing carousels, we could have something like:

Commands: [
{
            "Type": "Leaf",
            "Id": "MostPopularTextImage1",
            "Carousel": {
                "Picture": {
                    "Source": "Internal",
                    "Path": "/specialoccasions/I-love-you/default/small/530773_10150757735645255_150787610254_11952370_462269272_n.jpg"
                },
                "Label": {
                    "en": "I want all of you, forever, you and me, everyday.",
                    "fr": "Je veux tout de toi, pour toujours, toi et moi, tous les jours.",
                    "es": "Quiero todo de ti, para siempre, tu y yo, todos los días."
                }
            }
        }

I think it's easiest way to do it: having the Label and Picture gathered under one Property Carousel.

What do you think about it?

rhwy commented 7 years ago

my personal point of view, is that this is not logical at all even if it "seems" easier to write.

we are here within a command that is within a list of commands (a menu then). what does this "carousel" thing means here? is that the role of the command to tell the menu how it should be displayed? This is an information that should be at the command level (something like displayHint: carousel) what happened if some commands have the carousel and others not? We simplified the sources for labels as commandLabel for the label within the button and moved all other sources of text within a special step type=text, if we add now again a new place to put a label we'll complexity things again.

andreasdieryck commented 7 years ago

As suggested by @AgnesGsd, one way to solve this issue is to define the carousel at a higher level, e.g. potentially replacing commands by carousel. Example:

Carousel: [
{
            "Type": "Leaf",
            "Id": "MostPopularTextImage1",
            "CommandPicture": {
                    "Source": "Internal",
                    "Path": "/specialoccasions/I-love-you/default/small/530773_10150757735645255_150787610254_11952370_462269272_n.jpg"
                },
            "CommandLabel": {
                    "en": "I want all of you, forever, you and me, everyday.",
                    "fr": "Je veux tout de toi, pour toujours, toi et moi, tous les jours.",
                    "es": "Quiero todo de ti, para siempre, tu y yo, todos los días."
                }
        }

This actually makes more sense because CommandLabel and CommandPicture can then be defined on their own, without actually needing one another.

What do you think @rhwy ?

andreasdieryck commented 7 years ago

Pause and Break

Having reviewed the Story and other sequences with Olivier, we have concluded that:

Concretely, that means that whilst we have for instance:

       "Type": "Break",
       "Id": "25",
       "Mode": "Wait",
       "Parameters": {
           "ms": 4000
        }

We now have:

       "Type": "Pause",
       "Id": "25",
       "Parameters": {
           "Mode": "Wait",
           "ms": 4000
        }

Similarly:

       "Type": "Break",
       "Id": "25",
       "Mode": "Stop",

becomes...

       "Type": "Break",
       "Id": "25",
       "Parameters": {
           "Mode": "ConfirmContinuation"
        }

In this, ConfirmContinuation will be defined in the client (we'll come back to that in a later ticket.

@rhwy : what do you think about these changes?

rhwy commented 7 years ago

as we clarified our domain and kept things logically structured, it sounds like very nice updates to me ;-)

rhwy commented 7 years ago

about the carousel, this makes more sense like that, but I still prefer to keep "Commands" as a generic identifier of a list of commands. there is no structural difference between "commands" and "carousel" : it's a list of actionable choices in both cases. the fact that in some cases we would like to display choices with texts+images buttons instead of just text buttons doesn't change the logic behind and shouldn't be two separate things. That's why my first choice would be to keep the "commands" with eventually a property "displayHint":"carousel" (or other name) that will guide the client to the right implementation of display.

AgnesGsd commented 7 years ago

@andreasdieryck I know it's just a careless mistake, but just to be sure: it's a "Type":"Pause"and not a "Type":"Break" in your second example :)

       "Type": "Pause",
       "Id": "25",
       "Parameters": {
           "Mode": "ConfirmContinuation"
        }
andreasdieryck commented 7 years ago

@rhwy Sergey pointed out, as I did before, that the repetition of the "Type" and "Source" in the Steps when displaying a media can be misleading. @Frederikos does not parse them twice.

Example:

          "Type": "Image",
          "Id": "10",
          "Source": {
            "Type": "AnimatedGif",
            "Source": "Giphy",
            "Path": "6Umkh0GwRYhfG"
          }

-> Here, we will only parse the Id, the 2d Type, the 2d Source and the Path

We could do two things:

  1. Merge both types. In this case, we would have a "Type": "Image", a "Type": "AnimatedGif" and a "Type: Video" rather "Type": "Image" and then some detail with the second "Type"
  2. Replace the first "Source" by "Parameters" to reduce confusion

That would mean, this sequence would transform into:

          "Type": "AnimatedGif",
          "Id": "10",
          "Parameters": {
            "Source": "Giphy",
            "Path": "6Umkh0GwRYhfG"
          }

What do you think about it @rhwy ?

rhwy commented 7 years ago

@Frederikos Just to understand, the problem of having 2 "type" properties is that because you flattened the json properties in your binding/model ?

@Frederikos , @andreasdieryck in all the cases, I think it's important to keep the top level type information : here we define the type of the step, it defines the kind of generic thing to do (show text, image, or do an action). the fact that's an animated gif, is a detail of a more generic Image block.

I'm ok for the source -> parameters, but, if there is no technical constraint, i would like to keep a top level generic type and a subtype, that in fact should be a media type (we need to provide that media type information when creating an image for facebook).

so in our exemple case, it would be:

{
    "Type" : "Image",
    "Id" : 10,
    "Parameters" : {
          "MediaType" : "gif",
          "Source" : "Giphy",
          "Path" : "6Umkh0GwRYhfG"
    }
}

sounds good like that?

Frederikos commented 7 years ago

@rhwy Currently i have 5 types of content "Type" : "Text", "Image", "Gif", "Sound", "Video" For me it's better if we have it as root type not like 2 values types image-picture, image-gif. i store it in database as one field.

rhwy commented 7 years ago

yep, understand @Frederikos

my main point, is that I have to send the media type of the image (image/gif, image/jpg) and that it should make sense to specify it.

We can "guess" the type but while reading the sequences we don't know the type of the media.

btw, your/mine internal representation of the data (the one that is easy to manipulate for us) is not the same as the json. If you need only one field, couldn't you concatenate the info type+mediaType if it makes more sense for you?

because if we do it the other way (I mean having only one property with 2 informations inside) it's more difficult to extract info and be clear on the intention

andreasdieryck commented 7 years ago

Follow-up of this conversation in the new main documentation here: https://github.com/GhostWording/gw-config-apis/blob/master/data/bot/readme.md