Il s'agit d'un chatbot basé sur Lex qui calculera les calories générées par les déplacements dans différents restaurants de restauration rapide. Il est activé à partir d'un chatbot FB Messenger accessible depuis la page Facebook ou via l'application Messenger sur votre téléphone.
Table des matières
Ce bot utilise AWS Lex, un service qui contient l'intelligence nécessaire pour déchiffrer les demandes des utilisateurs et déclencher des intentions en fonction des données fournies dans les modèles. Les intentions appellent ensuite des fonctions lambda qui contiennent une logique métier spécifique à l'intention.
Il existe actuellement de nombreuses intentions différentes dans lesquelles le processus NLU est classé. Voici les « fonctions principales » du bot.
Il existe également des intentions qui complètent les fonctionnalités de base.
Ensuite, il y a les intentions qui forment la « personnalité » du bot. Ceux-ci ont été créés sur la base d’une utilisation réelle par l’utilisateur et empêchent l’utilisation du message d’erreur générique pour répondre.
Dans chacune des intentions, des exemples d'énoncés sont fournis qui construisent les phrases potentielles qu'un utilisateur peut fournir. La valeur de l'emplacement (c'est-à-dire Large Fry) est transmise à la fonction lambda en tant qu'attribut unique.
Vous pouvez obtenir les informations récapitulatives à partir de l'AWS CLI en exécutant la commande suivante.
aws lex-models get-bot --name FastFoodChecker --version-or-alias PROD
Il s'agit d'une combinaison d'exemples d'énoncés et d'emplacements qui déterminent quelle intention les modèles NLU invoqueront. Ceux-ci sont conservés dans Lex et sont utilisés pour former les modèles.
Actuellement, voici les emplacements personnalisés utilisés par les intentions.
Il n'est pas nécessaire qu'un élément soit spécifié dans l'emplacement pour que la NLU y place une valeur. Cependant, si les données sont rares, cela peut dégrader la façon dont la NLU interprète les demandes des utilisateurs.
La convivialité d’un chatbot nécessite une interaction naturelle avec un utilisateur. Un concept clé concerne la manière d’incorporer plusieurs emplacements dans une seule intention. Par exemple, un utilisateur pourrait demander « Combien de calories dans un Big Mac, des frites et un Coca ? » Il s’agit de trois éléments différents qui doivent chacun être analysés. Au sein de ce chatbot, le traitement principal comporte de nombreux emplacements différents qui correspondent aux intentions. Par exemple, voici les emplacements qui correspondent à l'intention GetCalories.
Il y a quelques éléments à noter à cet égard.
Dans l'exemple de requête ci-dessus, les modèles NLU analyseraient les données de l'énoncé dans trois emplacements différents (Food, Extra et Drink).
L'ordre des emplacements n'a pas d'importance pour l'analyse, mais il détermine quelle serait la prochaine réponse (emplacement 1 - Dans quel restaurant êtes-vous ?)
Deux emplacements ne sont pas requis dans cette intention : Ketchup et PacketsKetchup. Cette information facultative est demandée si des frites sont demandées comme accompagnement. Ceci est piloté par le code de la fonction Lambda qui est invoquée dans le hook de code de validation.
Toute la logique de formulation des réponses à différentes intentions est traitée dans une série de fonctions lambda. La fonction lambda à appeler est gérée dans Lex et définie au niveau de l'intention. Cela permet de construire une modularité au sein de l’application, tout en gardant les fonctions légères.
Il existe deux emplacements différents dans Lex qui peuvent invoquer une fonction lambda. La première s'effectue par validation de base, et le nom d'attribut qui l'identifie est appelé invocationSource. Il existe deux valeurs potentielles pour cela : DialogCodeHook et FulfilmentCodeHook. C'est ici que ces fonctions Lambda sont spécifiées dans Lex Bot.
La première liste déroulante est la validation et appelle la fonction lambda à chaque fois que le bot est appelé. L'attribut qu'il transmet s'appelle DialogCodeHook. Le deuxième menu déroulant est l'exécution, et n'est appelé qu'une fois que les créneaux obligatoires ont été remplis et que la validation de l'appel initial est terminée. Cela permet aux fonctions d'être différentes, permettant une meilleure évolutivité dans la création du bot.
Voici un aperçu de chaque fonction actuellement écrite.
lambda.js - la fonction principale qui gère la validation de base des requêtes, provenant uniquement du mode DialogCodeHook.
calculate.js - le calcul de la réponse pour les calories réelles d'un repas est géré par cette fonction et provient d'un FulfilmentCodeHook.
pizza.js - gère les intentions relatives au calcul des calories dans une pizza, y compris l'intention - WhatPizzaTypes.
misc.js - gère des intentions simples comme l'aide, l'introduction et plus de détails autour d'un repas.
Chinese.js - gère les intentions autour de la nourriture chinoise et relie les différents emplacements pour former un repas.
La fonctionnalité principale de ce robot est de pouvoir répondre aux questions sur le nombre de calories contenues dans différents repas. Bien que les emplacements utilisés par Lex soient utiles pour entraîner les modèles NLU, ils n'ont pas la capacité de servir de fichiers de recherche. C'est là qu'interviennent les objets json stockés dans le dossier /src/data/.
Voici un exemple du format.
[
{
" restaurant " : " Chipotle " ,
" foodItems " :[
{ " foodName " : " Chicken Burrito " , " foodType " : " Burrito " , " protein " : " chicken " , " calories " :975},
{ " foodName " : " Steak Burrito " , " foodType " : " Burrito " , " protein " : " steak " , " calories " :945},
{ " foodName " : " Carnitas Burrito " , " foodType " : " Burrito " , " protein " : " carnitas " , " calories " :1005},
Les fonctions lambda font référence à ces objets pour répondre à différentes requêtes, et calculer la consommation calorique de l'utilisateur.
Chaque aliment peut être dupliqué pour différentes orthographes et expressions utilisées pour le récupérer. Par exemple.
{ " foodName " : " Fries " , " calories " :340},
{ " foodName " : " Fry " , " calories " :340},
{ " foodName " : " Frys " , " calories " :340},
{ " foodName " : " French Fries " , " calories " :340},
{ " foodName " : " French Fry " , " calories " :340},
{ " foodName " : " Medium Fries " , " calories " :340},
{ " foodName " : " Medium Fry " , " calories " :340},
Il existe également des tables de recherche concernant les sauces, les vinaigrettes et les ajustements d'articles individuels. Par exemple.
[
{
" dressingName " : " Ranch " ,
" calories " :200,
" carbs " :11,
" restaurantNames " :[ " McDonalds " ]
},
{
" dressingName " : " French " ,
" calories " :300,
" carbs " :22,
" restaurantNames " :[ " McDonalds " ]
},
Étant donné que les modèles NLU ne corrigent pas l'orthographe fournie par l'utilisateur, c'est aux fonctions Lambda de gérer cette partie de la logique.
La gestion de grands emplacements personnalisés peut être difficile, en particulier si les données sont dynamiques. La recherche d'aliments principale contient plusieurs centaines de valeurs uniques et augmente en fonction de l'utilisation des utilisateurs. Le processus de création de cet emplacement a été automatisé et les données de l'emplacement personnalisé sont extraites de l'objet de données foods.json. Cela se fait via l'AWS CLI qui peut les charger directement à partir de la ligne de commande. Tous les fichiers sont contenus dans le répertoire [slots}(https://github.com/terrenjpeterson/caloriecounter/tree/master/src/slots) pour référence. Voici les étapes utilisées pour créer.
La syntaxe ressemble à ceci.
# foods.json is the data object that will be passed to the lambda function
request= $( < foods.json )
# invoke the lambda function from the command line and write the output to output.json
aws lambda invoke --function-name convertFoodsObjForSlot --payload " $request " output.json
data= $( < output.json )
# invoke lex to create a new version of the FoodEntreeNames custom slot using the data from output.json
aws lex-models put-slot-type --name FoodEntreeNames --checksum < enter latest checksum here > --enumeration-values " $data " >> sysout.txt
De plus, la valeur de la somme de contrôle provient du déploiement antérieur de l'emplacement personnalisé. Vous pouvez trouver la somme de contrôle actuelle d'un emplacement à l'aide de la commande get-slot-type.
# find the latest information about a custom slot
aws lex-models get-slot-type --name FoodOptions --slot-type-version ' $LATEST '
La clé d’une conversation efficace à long terme entre un utilisateur et un robot réside dans la gestion du contexte de la conversation. Par exemple, une boîte de dialogue peut durer plusieurs minutes et invoquer de nombreuses intentions.
Une partie de la facilitation de cela consiste à concevoir un flux de conversation. Les messages d'erreur ne doivent pas être trop brusques et doivent conduire l'utilisateur vers une requête alternative. Les intentions doivent également transmettre des données entre elles. Cela peut être accompli en enregistrant les données de session lors de la réalisation d'une intention. Cela permet à l'intention suivante de récupérer les informations et de ne pas obliger l'utilisateur à les répéter à chaque demande.
Dans l'exemple ci-dessus, la conversation commence lorsque l'utilisateur indique dans quel restaurant il mange. Ceci est conservé dans la session par l'intention FoodTypeOptions. La boîte de dialogue passe aux détails du repas, mais le nom du restaurant est enregistré. De plus, la réponse initiale sur le nombre de calories est brève, mais offre une explication plus détaillée si l'utilisateur dit « plus de détails ». Une fois de plus, les données sont stockées dans les données de session et sont transmises dans le cadre du framework Lex. Voici un exemple d'un des objets.
{
" messageVersion " : " 1.0 " ,
" invocationSource " : " FulfillmentCodeHook " ,
" userId " : " 1712299768809980 " ,
" sessionAttributes " : {
" restaurantName " : " Burger King " ,
" foodName " : " Whopper " ,
" foodCalories " : " 660 " ,
" extraName " : " Onion Rings " ,
" extraCalories " : " 410 " ,
" drinkCalories " : " 310 " ,
" drinkName " : " 32 oz. Large Coke " ,
" totalCalories " : " 1380 "
},
" bot " : {
" name " : " FastFoodChecker " ,
" alias " : " PROD " ,
" version " : " 42 "
},
" outputDialogMode " : " Text " ,
" currentIntent " : {
" name " : " DailyIntakeAnalysis " ,
" slots " : {},
" slotDetails " : {},
" confirmationStatus " : " None "
},
" inputTranscript " : " Analyze my meal "
}
Les fonctions lambda de ce bot sont complètement apatrides, donc toutes les données des invocations précédentes doivent passer par l'objet de requête.
L'une des fonctionnalités des principales interfaces utilisateur des chatbots (Messenger, Slack, etc.) concerne les boutons. Ceux-ci réduisent l'effort de l'utilisateur en fournissant une série d'options comme celle-ci.
Chaque plateforme de messagerie a sa propre implémentation de ce modèle, et voici ce que Messenger utilise. Lex gère la traduction pour mettre les boutons dans le format correct, et dans Lex, l'attribut ResponseCard doit être fourni avec les détails des détails du bouton.
La modification de Lex se fait entièrement via la console. Les fonctions lambda qui servent la logique métier sont hébergées dans AWS lambda et sont déployées à partir d'un hôte EC2.
Le script de déploiement complet est /src/build.sh mais un aperçu rapide peut être trouvé dans les instructions suivantes.
# this creates the build package as a zip file containing the code and relevant data objects
zip -r foodbot.zip lambda.js data/restaurants.json data/foods.json data/drinks.json
# this CLI command copies the build package to an s3 bucket for staging
aws s3 cp foodbot.zip s3://fastfoodchatbot/binaries/
# this CLI command takes the package from the s3 bucket, and overlays the lambda function 'myCalorieCounterGreen'
aws lambda update-function-code --function-name myCalorieCounterGreen --s3-bucket fastfoodchatbot --s3-key binaries/foodbot.zip
# this CLI command invokes the lambda function with the data object read into request, and writes out a response to the testOutput data object.
aws lambda invoke --function-name myCalorieCalculatorGreen --payload " $request " testOutput.json
Ce processus est répété pour chacune des fonctions lambda appelées par Lex. Cela inclut d'avoir au moins une condition de test pour chaque fonction lambda afin de garantir que le déploiement a été effectué correctement.
L’un des sujets de la conception de robots concerne la personnalité. Lors de la conception des intentions, il convient de prendre en compte toutes les questions possibles qu'un utilisateur peut poser. Cela devrait inclure des questions hors sujet, telles que « quel est votre nom » ou des réponses émotionnelles comme « oh-non » ou « tu es nul ». Ceux-ci sont faciles à coder - généralement juste une simple requête-réponse sans aucun emplacement impliqué, et ont tendance à rendre les dialogues plus naturels.
Pour un exemple, voici une brève réponse codée dans la fonction misc.js qui répond si quelqu'un demande quel est le nom du robot. Dans les modèles, un énoncé de « quel est votre nom » répond à cette intention.
if (intentName === ' MyName ' ) {
console.log( " user requested bot name " ) ;
return getBotName(intentRequest, callback) ;
}
...
function getBotName(intentRequest, callback) {
const sessionAttributes = intentRequest.sessionAttributes || {} ;
var botResponse = " My name is Chuck. I'm a chatbot that helps people sort out " +
" fast food options. Talking about food all day makes me hungry!!! " ;
callback(close(sessionAttributes, ' Fulfilled ' ,
{ contentType: ' PlainText ' , content: botResponse })) ;
}
Dans le cadre de l'effort initial, j'essayais de publier ce chatbot sur la boutique Slack. Dans ce cadre, j'avais besoin de créer un site Web pour le soutien public de l'application. C'est un travail en cours appelé caloriecountbot.com. Il est hébergé par s3 et la source se trouve dans le dossier /website.