Nouvelles Chroniques d'Amethyste

Penser au Sens, pas au Verbe

Créer un dialogue avec les FormFlow

Poster un commentaire

Nous avons vu une première façon de créer des dialogues:

https://amethyste16.wordpress.com/2017/01/26/un-vrai-dialogue-avec-un-bot/

Cette méthode nous a permis de poser quelques bases importantes que nous allons réutiliser. Mais elle souffre d’une certaine complexité dans certains scénarios.

On va présenter une solution qui simplifie de façon radicale le code: les FormFlow. Les FormFlow sont une collection de formulaires qui seront traités dans le cadre d’un dialogue.

Leur usage est très simple, nous n’avons besoin que de créer un objet POCO!

Premier exemple

Nous reprenons l’exemple dans lequel on demandait à l’utilisateur de se présenter, mais cette fois on va réclamer plus d’informations.

On commence par créer un objet Client:

[Serializable]
public class Client
{
   public string Nom { get; set; }
   public Genre? Genre { get; set; }
   public int? Age{ get; set; }
   public List<Animaux> Animaux { get; set; }
}
 
[Serializable]
public enum Genre
{
   Homme,
   Femme
}
 
[Serializable]
public enum Animaux
{
   Chien,
   Chat,
   Poisson,
   Oiseau
}

Cet objet regroupe la liste des informations auxquelles nous allons nous intéresser. Remarquez, c’est important, que l’objet et ses dépendances sont décorés avec SerializableAttribute.

Important: l’ordre d’apparition des propriétés dans la classe sera celui de l’apparition du dialogue qui correspond dans la conversation.

 

L’essentiel du travail est terminé. Il ne reste plus qu’à transformer notre classe en formulaires et la collection de formulaires en IDialog qui sera activé par la méthode Conversation.SendAsync() depuis le contrôleur. C’est très simple:


await Conversation.SendAsync(activity, () => FormDialog.FromType<Client>());

FormDialog est une classe statique exposant des méthodes capables de construire un IDialog depuis des formulaires ou un Type.

 

Visuellement la conversation ressemble à celle-ci:

2017-01-29_12-44-31

 

Note: Chien n’est pas affiché, je ne sais pas pourquoi. Un bug?? J’ai essayé de forcer 0, mais le bougre ne se laisse pas impressionner.

 

On peut afficher de l’aide en entrant la commande help:

2017-01-29_12-47-23

 

Qui vous fournira une liste de commandes prêtes à l’emploi. Par exemple Back permet de revenir à la question qui précède, on peut modifier l’ordre de saisie des questions…

Améliorons notre exemple

Nous avons créé dans la même commande les formulaires et le IDialog. On peut souhaiter séparer ces deux étapes pour les personnaliser un peu.

La première étape consiste à ajouter une fabrique à formulaire, typiquement on le fera dans la classe Client:

public static IForm<Client> BuildForm()
{
   return new FormBuilder<Client>()
      .Message("Salut, je suis SuperBot à votre service!")
      .OnCompletion(async (context, client) =>
      {
         await context.PostAsync("J'ai votre profil complet maintenant"); 
      })
      .Build();
}

 

Note: PostAsync est une méthode d’extension, il faudra donc compléter le code avec la déclaration:


using Microsoft.Bot.Builder.Dialogs;

 

L’architecture FormFlow est basée sur une interface fluent.

On commence par définir un message d’accueil de la conversation avec Message(), puis un message de fin de conversation avec OnCompletion(). Cette méthode reçoit en outre une instance de Client avec les valeurs de paramètres sélectionnées au cours du dialogue.

 

La méthode Build construit le dialogue qui va réclamer les différentes informations sur le client en analysant par réflexion la classe Client.

Il ne reste plus qu’à créer un IDialog. C’est le boulot de FormDialog:


await Conversation.SendAsync(activity, () => FormDialog.FromForm(Client.BuildForm));

Cette fois on utilise FromForm() qui attend en paramètre la méthode créant les formulaires, le FormFlow.

 

Visuellement le dialogue ressemble à celui-ci:

2017-01-29_12-27-26

L’utilisateur envoie un premier message pour initialiser la conversation, ce n’est jamais le bot. Ce dernier répond avec un message de salutation.

Le dialogue proprement dit commence ensuite:

2017-01-29_12-29-47

Vous remarquez qu’à la fin un récapitulatif s’affiche.

Note: Chien n’est pas affiché, je ne sais pas pourquoi. Un bug?? J’ai tenté de forcer 0, mais comme vous le voyez le bougre ne se laisse pas impressionner.

La conversation se termine ainsi:

2017-01-29_12-32-24

Je réponds Y (yes) ou O si vous êtes en français et le bot termine la conversation.

Si j’entre un autre message:

2017-01-29_12-33-59

Le bot reste dans son dernier état. C’est normal, on ne lui a pas dit quoi faire à la fin du dialogue. Nous verrons cela dans le prochain article avec le chaînage.

 

La conversation est déjà plus conviviale. On peut également intervenir sur les labels affichés avec l’attribut PromptAttribute, par exemple pour les avoir en français:


[Prompt("Quel est votre nom?")]
public string Nom { get; set; }

Ainsi:

2017-01-29_13-36-36

Mais avec les types complexes comme Genre, on perd le formulaire personnalisés. Je ne sais pas si c’est un bug ou bien s’il y a quelque chose à faire.

PromptAttribute expose plusieurs propriétés. La doc est très incomplète, je ne les vois pas toujours fonctionner comme le suggère leur nom.

 

D’autres attributs sont à notre disposition:

2017-01-29_14-28-17

Age est une valeur numérique, on peut souhaiter limiter la gamme de saisie, par exemple un âge ne peut être négatif:


[Numeric(1,99)]
[Prompt("Quel est votre âge?")]
public int? Age { get; set; }

2017-01-29_14-20-53

 

L’attribut Optional permet de rendre facultatif un champ:


[Optional]
public List<Animaux> Animaux { get; set; }

 

2017-01-29_14-22-42

 

Une lecture intéressante pour aller plus loin:

https://docs.botframework.com/en-us/csharp/builder/sdkreference/forms.html#patterns

Partager des données

Maintenant que nous avons le profil complet d’un utilisateur, on souhaite peut être les enregistrer afin de les partager avec un autre dialogue qui enchaînerait.

On a vu que la méthode OnCompletion de la factory de formulaires reçoit une instance complète de Client une fois la conversation terminée. C’est donc l’endroit idéal pour effectuer cette sauvegarde.

Le code pourrait ressembler à ceci si on décidait de sauvegarder les infos utilisateurs dans le contexte de la conversation:

public static IForm<Client> BuildForm()
{
   return new FormBuilder<Client>()
      .Message("Salut, je suis SuperBot à votre service!")
      .OnCompletion(async (context, client) =>
      {
         context.PrivateConversationData.SetValue<bool>("ProfileComplete", true);
         context.PrivateConversationData.SetValue<Client>("Client", client);
         context.PrivateConversationData.SetValue<string>("Name", client.Nom);
         context.PrivateConversationData.SetValue<string>("Age", client.Age.ToString());
         context.PrivateConversationData.SetValue<string>("Gender", client.Genre.ToString());
 
         await context.PostAsync("J'ai votre profil complet maintenant");
 
      })
     .Build();
}

 

Si on a pas besoin de personnaliser la sauvegarde, on peut enregistrer directement l’instance de Client.

Notez aussi la présence du marquer ProfileComplete. Il évitera, dans les conversations suivantes, de redemander à l’utilisateur son profil.

Je ne vais pas commenter plus en avant ce code. La gestion des états a déjà fait l’objet d’un article:

https://amethyste16.wordpress.com/2017/01/25/sauvegarde-des-etats-par-un-bot/

Bibliographie

 

Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s