Dies ist ein Lex-basierter Chatbot, der die Kalorien berechnet, die durch Fahrten zu verschiedenen Fast-Food-Restaurants entstehen. Die Aktivierung erfolgt über einen FB Messenger-Chatbot, auf den Sie über die Facebook-Seite oder über die Messenger-App auf Ihrem Telefon zugreifen können.
Inhaltsverzeichnis
Dieser Bot nutzt AWS Lex – einen Dienst, der die Intelligenz enthält, um Benutzeranfragen zu entschlüsseln und Absichten basierend auf den in den Modellen bereitgestellten Daten auszulösen. Die Absichten rufen dann Lambda-Funktionen auf, die für die Absicht spezifische Geschäftslogik enthalten.
Derzeit gibt es viele verschiedene Absichten, in die der NLU-Prozess eingeteilt wird. Hier sind die „Kernfunktionen“ des Bots.
Es gibt auch Absichten, die die Kernfunktionen ergänzen.
Dann gibt es Absichten, die die „Persönlichkeit“ des Bots bilden. Diese wurden basierend auf der tatsächlichen Benutzernutzung erstellt und verhindern, dass die generische Fehlermeldung zur Reaktion verwendet wird.
Innerhalb jeder Absicht werden Beispieläußerungen bereitgestellt, die die potenziellen Sätze bilden, die ein Benutzer bereitstellen kann. Der Wert des Slots (z. B. Large Fry) wird als eindeutiges Attribut an die Lambda-Funktion übergeben.
Sie können die zusammenfassenden Informationen von der AWS CLI abrufen, indem Sie den folgenden Befehl ausführen.
aws lex-models get-bot --name FastFoodChecker --version-or-alias PROD
Es handelt sich um eine Kombination aus Beispieläußerungen und Slots, die bestimmen, welche Absicht die NLU-Modelle aufrufen. Diese werden in Lex gepflegt und zum Training der Modelle verwendet.
Derzeit sind hier die benutzerdefinierten Slots aufgeführt, die von den Absichten verwendet werden.
Es muss kein Element im Slot angegeben werden, damit die NLU einen Wert darin platzieren kann. Wenn die Daten jedoch spärlich sind, kann dies die Art und Weise beeinträchtigen, wie die NLU die Benutzeranfragen interpretiert.
Die Benutzerfreundlichkeit eines Chatbots erfordert eine natürliche Interaktion mit einem Benutzer. Ein Schlüsselkonzept besteht darin, wie man mehrere Slots in eine einzige Absicht integrieren kann. Ein Benutzer könnte beispielsweise fragen: „Wie viele Kalorien haben ein Big Mac, Pommes und eine Cola?“ Das sind drei verschiedene Elemente, die jeweils analysiert werden müssen. Innerhalb dieses Chatbots verfügt die Hauptverarbeitung über viele verschiedene Slots, die Absichten zugeordnet werden. Hier sind beispielsweise die Slots, die der Absicht „GetCalories“ zugeordnet sind.
Dabei sind einige Punkte zu beachten.
In der obigen Beispielanforderung würden die NLU-Modelle die Daten der Äußerung in drei verschiedene Slots (Essen, Extra und Trinken) analysieren.
Die Slot-Reihenfolge spielt beim Parsen keine Rolle, aber sie bestimmt, was die nächste Antwort wäre (Slot 1 – In welchem Restaurant sind Sie?).
Es gibt zwei Slots, die in dieser Absicht nicht erforderlich sind: Ketchup und PacketsKetchup. Diese optionalen Informationen werden abgefragt, wenn Pommes als Beilage gewünscht werden. Dies wird durch den Code in der Lambda-Funktion gesteuert, die im Validation-Code-Hook aufgerufen wird.
Die gesamte Logik bei der Formulierung von Antworten auf unterschiedliche Absichten wird in einer Reihe von Lambda-Funktionen verarbeitet. Welche Lambda-Funktion aufgerufen werden soll, wird in Lex verwaltet und auf der Absichtsebene festgelegt. Dies ermöglicht den Aufbau von Modularität innerhalb der Anwendung, wodurch die Funktionen leichtgewichtig bleiben.
Es gibt zwei verschiedene Stellen in Lex, die eine Lambda-Funktion aufrufen können. Die erste erfolgt durch eine grundlegende Validierung, und der Attributname, der sie identifiziert, heißt invocationSource. Hierfür gibt es zwei mögliche Werte: DialogCodeHook und FulfillmentCodeHook. Hier werden diese Lambda-Funktionen im Lex Bot angegeben.
Das erste Dropdown-Menü ist die Validierung und ruft bei jedem Aufruf des Bots die Lambda-Funktion auf. Das übergebene Attribut heißt DialogCodeHook. Das zweite Dropdown-Menü ist „Fulfillment“ und wird erst aufgerufen, wenn die obligatorischen Slots abgeschlossen sind und die Validierung vom ersten Aufruf abgeschlossen ist. Dadurch können die Funktionen unterschiedlich sein, was eine bessere Skalierbarkeit beim Aufbau des Bots ermöglicht.
Hier finden Sie eine Übersicht über jede derzeit geschriebene Funktion.
lambda.js – die Hauptfunktion, die die grundlegende Validierung für Abfragen übernimmt, die nur im DialogCodeHook-Modus erfolgen.
berechnen.js – Die Berechnung der Antwort für die tatsächlichen Kalorien in einer Mahlzeit wird von dieser Funktion verwaltet und von einem FulfillmentCodeHook bezogen.
pizza.js – verwaltet Absichten rund um die Berechnung der Kalorien in einer Pizza, einschließlich der Absicht – WhatPizzaTypes.
misc.js – verwaltet einfache Absichten wie Hilfe, die Einführung und weitere Details rund um eine Mahlzeit.
chinese.js – kümmert sich um Absichten rund um chinesisches Essen und verbindet die verschiedenen Slots zu einer Mahlzeit.
Die Kernfunktionalität dieses Bots besteht darin, Fragen dazu beantworten zu können, wie viele Kalorien in verschiedenen Mahlzeiten enthalten sind. Während die von Lex verwendeten Slots beim Training der NLU-Modelle hilfreich sind, können sie nicht als Suchdateien dienen. Hier kommen die JSON-Objekte ins Spiel, die im Ordner /src/data/ gespeichert sind.
Hier ist ein Beispiel des Formats.
[
{
" 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},
Die Lambda-Funktionen beziehen sich auf diese Objekte, um auf verschiedene Anfragen zu antworten und den Kalorienverbrauch für den Benutzer zu berechnen.
Jedes Lebensmittel kann für unterschiedliche Schreibweisen und Ausdrücke, die zum Abrufen verwendet werden, dupliziert werden. Zum Beispiel.
{ " 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},
Es gibt auch Nachschlagetabellen zu Saucen, Dressings und individuellen Artikelanpassungen. Zum Beispiel.
[
{
" dressingName " : " Ranch " ,
" calories " :200,
" carbs " :11,
" restaurantNames " :[ " McDonalds " ]
},
{
" dressingName " : " French " ,
" calories " :300,
" carbs " :22,
" restaurantNames " :[ " McDonalds " ]
},
Da die NLU-Modelle die vom Benutzer angegebene Rechtschreibung nicht korrigieren, liegt es an den Lambda-Funktionen, diesen Teil der Logik zu handhaben.
Die Verwaltung großer benutzerdefinierter Slots kann schwierig sein, insbesondere wenn die Daten dynamisch sind. Die Hauptsuche nach Lebensmitteln enthält mehrere hundert eindeutige Werte und wächst je nach Benutzernutzung. Der Prozess zum Erstellen dieses Slots wurde automatisiert und die Daten für den benutzerdefinierten Slot werden dem Datenobjekt „foods.json“ entnommen. Dies erfolgt über die AWS CLI, die diese direkt von der Befehlszeile laden kann. Alle Dateien sind als Referenz im Verzeichnis [slots}(https://github.com/terrenjpeterson/caloriecounter/tree/master/src/slots) enthalten. Hier sind die Schritte zum Erstellen.
Die Syntax sieht so aus.
# 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
Außerdem stammt der Prüfsummenwert aus der vorherigen Bereitstellung des benutzerdefinierten Steckplatzes. Sie können die aktuelle Prüfsumme für einen Steckplatz mit dem Befehl get-slot-type ermitteln.
# find the latest information about a custom slot
aws lex-models get-slot-type --name FoodOptions --slot-type-version ' $LATEST '
Der Schlüssel zu effektiven, langfristigen Gesprächen zwischen einem Benutzer und einem Bot liegt in der Verwaltung des Gesprächskontexts. Beispielsweise könnte ein Dialog mehrere Minuten dauern und viele Absichten hervorrufen.
Ein Teil der Erleichterung besteht darin, einen Gesprächsfluss zu gestalten. Fehlermeldungen sollten nicht zu abrupt sein und den Benutzer zu einer alternativen Abfrage führen. Die Absichten sollten auch Daten untereinander weitergeben. Dies kann erreicht werden, indem die Sitzungsdaten beim Abschluss einer Absicht gespeichert werden. Dies ermöglicht es dem nächsten Intent, die Informationen abzurufen, ohne dass der Benutzer sie bei jeder Anfrage wiederholen muss.
Im obigen Beispiel beginnt das Gespräch damit, dass der Benutzer angibt, in welchem Restaurant er isst. Dies wird durch die Absicht „FoodTypeOptions“ in der Sitzung beibehalten. Der Dialog wechselt zu den Details der Mahlzeit, der Name des Restaurants wird jedoch gespeichert. Auch die erste Antwort auf die Kalorienzahl ist kurz, bietet aber eine detailliertere Erklärung, wenn der Benutzer „mehr Details“ sagt. Auch hier werden die Daten in den Sitzungsdaten gespeichert und als Teil des Lex-Frameworks zurückgegeben. Hier ist ein Beispiel für eines der Objekte.
{
" 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 "
}
Die Lambda-Funktionen in diesem Bot sind völlig zustandslos, daher müssen alle Daten aus früheren Aufrufen über das Anforderungsobjekt kommen.
Eines der Features in den wichtigsten Chatbot-Benutzeroberflächen (Messenger, Slack usw.) sind Schaltflächen. Diese verringern den Aufwand für den Benutzer, indem sie eine Reihe solcher Optionen bereitstellen.
Jede Messaging-Plattform hat ihre eigene Implementierung dieses Musters, und hier ist, was Messenger verwendet. Lex übernimmt die Übersetzung, um die Schaltflächen in das richtige Format zu bringen, und innerhalb von Lex muss das ResponseCard-Attribut mit den Einzelheiten zu den Schaltflächendetails versehen werden.
Das Ändern von Lex erfolgt vollständig über die Konsole. Die Lambda-Funktionen, die der Geschäftslogik dienen, werden in AWS Lambda gehostet und von einem EC2-Host bereitgestellt.
Das vollständige Bereitstellungsskript ist /src/build.sh, aber eine kurze Übersicht finden Sie in den folgenden Anweisungen.
# 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
Dieser Vorgang wird für jede der von Lex aufgerufenen Lambda-Funktionen wiederholt. Dazu gehört, dass für jede Lambda-Funktion mindestens eine Testbedingung vorhanden ist, um sicherzustellen, dass die Bereitstellung korrekt durchgeführt wurde.
Eines der Themen beim Bot-Design ist die Persönlichkeit. Beim Entwerfen der Absichten ist zu berücksichtigen, welche Fragen ein Benutzer möglicherweise stellen kann. Dazu sollten themenfremde Fragen wie „Wie heißt du?“ oder emotionale Antworten wie „Oh nein“ oder „Du bist scheiße“ gehören. Diese sind einfach zu programmieren – in der Regel handelt es sich lediglich um eine einfache Anfrage-Antwort-Antwort ohne erforderliche Slots – und sorgen in der Regel dafür, dass die Dialoge natürlicher wirken.
Als Beispiel ist hier eine kurze Antwort, die in der Funktion misc.js codiert ist und auf die Frage antwortet, wenn jemand nach dem Namen des Bots fragt. In den Modellen führt eine Äußerung von „Wie heißt du?“ zu dieser Absicht.
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 })) ;
}
Als Teil der ersten Bemühungen habe ich versucht, diesen Chatbot im Slack Store zu veröffentlichen. Als Teil davon musste ich eine Website für die öffentliche Unterstützung der App erstellen. Es ist noch in Arbeit und heißt kaloriencountbot.com. Es wird von s3 gehostet und die Quelle befindet sich im Ordner /website.