هذا هو برنامج chatbot يعتمد على Lex والذي سيقوم بحساب السعرات الحرارية الناتجة عن الرحلات إلى مطاعم الوجبات السريعة المختلفة. يتم تمكينه من خلال FB Messenger Chatbot الذي يمكن الوصول إليه من صفحة Facebook، أو من خلال تطبيق Messenger على هاتفك.
جدول المحتويات
يستخدم هذا الروبوت AWS Lex - وهي خدمة تحتوي على معلومات ذكية لتتمكن من فك تشفير طلبات المستخدم وتشغيل الأغراض بناءً على البيانات المقدمة في النماذج. تقوم النوايا بعد ذلك باستدعاء وظائف لامدا التي تحتوي على منطق عمل خاص بالنية.
يوجد حاليًا العديد من الأغراض المختلفة التي تقوم عملية NLU بفرزها. فيما يلي "الوظائف الأساسية" للروبوت.
هناك أيضًا نوايا تكمل الميزات الأساسية.
ثم هناك النوايا التي تشكل "شخصية" الروبوت. تم إنشاؤها بناءً على الاستخدام الحقيقي للمستخدم، ومنع استخدام رسالة الخطأ العامة للرد.
داخل كل غرض من الأغراض، يتم توفير نماذج من الكلام التي تبني الجمل المحتملة التي قد يقدمها المستخدم. يتم تمرير قيمة الفتحة (أي Large Fry) إلى وظيفة lambda كسمة فريدة.
يمكنك الحصول على المعلومات التلخيصية من AWS CLI عن طريق تنفيذ الأمر التالي.
aws lex-models get-bot --name FastFoodChecker --version-or-alias PROD
إنه مزيج من نماذج العبارات والفواصل التي تحدد الهدف الذي ستستدعيه نماذج NLU. يتم الاحتفاظ بها في Lex، ويتم استخدامها لتدريب النماذج.
حاليًا، إليك الفتحات المخصصة التي تستخدمها النوايا.
لا يلزم تحديد عنصر في الفتحة حتى تتمكن وحدة NLU من وضع قيمة فيه. ومع ذلك، إذا كانت البيانات متفرقة، فقد يؤدي ذلك إلى انخفاض كيفية تفسير NLU لطلبات المستخدم.
تتطلب سهولة استخدام chatbot حدوث تفاعل طبيعي مع المستخدم. أحد المفاهيم الرئيسية يدور حول كيفية دمج فتحات متعددة في هدف واحد. على سبيل المثال، يمكن للمستخدم أن يسأل "كم عدد السعرات الحرارية الموجودة في بيج ماك، والبطاطا المقلية، والكولا؟" هذه ثلاثة عناصر مختلفة يحتاج كل منها إلى التحليل. ضمن برنامج الدردشة الآلي هذا، تحتوي المعالجة الرئيسية على العديد من الفتحات المختلفة التي تحدد المقاصد. على سبيل المثال، فيما يلي الفتحات التي يتم تعيينها إلى هدف GetCalories.
هناك بعض العناصر التي يجب ملاحظتها في هذا.
في طلب المثال أعلاه، ستقوم نماذج NLU بتحليل البيانات من الكلام إلى ثلاث فتحات مختلفة (الطعام، والإضافية، والشراب).
لا يهم ترتيب الفتحة عند التحليل، ولكنه يحدد الاستجابة التالية (الشريحة 1 - في أي مطعم أنت؟)
هناك فتحتان غير مطلوبتين في هذا الغرض - Ketchup وPacketsKetchup. يتم طلب هذه المعلومات الاختيارية إذا تم طلب البطاطس المقلية كعنصر جانبي. يتم ذلك بواسطة التعليمات البرمجية الموجودة في وظيفة Lambda التي يتم استدعاؤها في ربط رمز التحقق من الصحة.
تتم معالجة كل المنطق في صياغة الاستجابات للنوايا المختلفة في سلسلة من وظائف لامدا. تتم إدارة وظيفة lambda المطلوب استدعاؤها داخل Lex، ويتم تعيينها على مستوى الهدف. يتيح ذلك إنشاء نمطية داخل التطبيق، مما يحافظ على خفة الوظائف.
هناك نقطتان مختلفتان داخل Lex يمكنها استدعاء دالة lambda. الأول هو من خلال التحقق الأساسي، واسم السمة الذي يحددها يسمى invocationSource. هناك قيمتان محتملتان لهذا - DialogCodeHook وFulfillmentCodeHook. هنا يتم تحديد وظائف Lambda هذه في Lex Bot.
القائمة المنسدلة الأولى هي التحقق من الصحة، وتستدعي وظيفة لامدا في كل مرة يتم فيها استدعاء الروبوت. تسمى السمة التي يمررها DialogCodeHook. القائمة المنسدلة الثانية هي الاستيفاء، ويتم استدعاؤها فقط بمجرد اكتمال الخانات الإلزامية، واكتمال التحقق من الاستدعاء الأولي. وهذا يسمح للوظائف بأن تكون مختلفة، مما يتيح إمكانية التوسع بشكل أفضل في بناء الروبوت.
فيما يلي نظرة عامة على كل وظيفة مكتوبة حاليًا.
lambda.js - الوظيفة الرئيسية التي تتعامل مع التحقق الأساسي من الاستعلامات، والتي يتم الحصول عليها فقط في وضع DialogCodeHook.
Calculator.js - تتم معالجة حساب الاستجابة للسعرات الحرارية الفعلية في الوجبة بواسطة هذه الوظيفة، ويتم الحصول عليها من خلال FulfillmentCodeHook.
Pizza.js - يتعامل مع المقاصد المتعلقة بحساب السعرات الحرارية في البيتزا، بما في ذلك المقاصد - WhatPizzaTypes.
Misc.js - يتعامل مع الأغراض البسيطة مثل المساعدة والمقدمة والمزيد من التفاصيل حول الوجبة.
chinese.js - يتعامل مع المقاصد المتعلقة بالطعام الصيني، ويربط الفتحات المختلفة معًا لتكوين وجبة.
تتمثل الوظيفة الأساسية لهذا الروبوت في القدرة على الإجابة على الاستفسارات المتعلقة بعدد السعرات الحرارية الموجودة في الوجبات المختلفة. على الرغم من أن الفتحات التي يستخدمها Lex مفيدة في تدريب نماذج NLU، إلا أنها لا تتمتع بالقدرة على العمل كملفات بحث. هذا هو المكان الذي تأتي فيه كائنات json المخزنة في المجلد /src/data/.
هنا عينة من التنسيق.
[
{
" 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},
تشير وظائف لامدا إلى هذه الكائنات للرد على استعلامات مختلفة، وحساب استهلاك السعرات الحرارية للمستخدم.
قد يتم تكرار كل عنصر غذائي لتهجئة وعبارات مختلفة تستخدم لاستردادها. على سبيل المثال.
{ " 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},
توجد أيضًا جداول بحث حول الصلصات والضمادات وتعديلات العناصر الفردية. على سبيل المثال.
[
{
" dressingName " : " Ranch " ,
" calories " :200,
" carbs " :11,
" restaurantNames " :[ " McDonalds " ]
},
{
" dressingName " : " French " ,
" calories " :300,
" carbs " :22,
" restaurantNames " :[ " McDonalds " ]
},
نظرًا لأن نماذج NLU لا تصحح التهجئة التي يقدمها المستخدم، فإن الأمر متروك لوظائف Lambda للتعامل مع هذا الجزء من المنطق.
قد تكون إدارة الفواصل المخصصة الكبيرة أمرًا صعبًا، خاصة إذا كانت البيانات ديناميكية. يحتوي البحث الرئيسي عن الطعام على عدة مئات من القيم الفريدة، وينمو بناءً على استخدام المستخدم. تمت أتمتة عملية إنشاء هذه الفتحة، ويتم أخذ بيانات الفتحة المخصصة من كائن بيانات Foods.json. يتم ذلك من خلال AWS CLI الذي يمكنه تحميلها مباشرةً من سطر الأوامر. جميع الملفات موجودة في دليل [slots}(https://github.com/terrenjpeterson/calorycounter/tree/master/src/slots) كمرجع. فيما يلي الخطوات المستخدمة في الإنشاء.
بناء الجملة يبدو مثل هذا.
# 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
كما أن قيمة المجموع الاختباري هي من النشر المسبق للفتحة المخصصة. يمكنك العثور على المجموع الاختباري الحالي للفتحة من خلال أمر get-slot-type.
# find the latest information about a custom slot
aws lex-models get-slot-type --name FoodOptions --slot-type-version ' $LATEST '
إن مفتاح المحادثات الفعالة طويلة الأمد بين المستخدم والروبوت يدور حول إدارة سياق المحادثة. على سبيل المثال، يمكن أن يستمر الحوار لعدة دقائق، ويستدعي العديد من المقاصد.
جزء من تسهيل ذلك هو تصميم تدفق المحادثة. يجب ألا تكون رسائل الخطأ مفاجئة جدًا، ويجب أن تقود المستخدم إلى استعلام بديل. يجب أن تقوم النوايا أيضًا بتمرير البيانات بين بعضها البعض. يمكن تحقيق ذلك عن طريق حفظ بيانات الجلسة عند إكمال الهدف. يسمح هذا للهدف التالي باسترداد المعلومات وعدم مطالبة المستخدم بتكرارها مع كل طلب.
في المثال أعلاه، تبدأ المحادثة بإشارة المستخدم إلى المطعم الذي يتناول الطعام فيه. يستمر هذا في الجلسة من خلال غرض FoodTypeOptions. يتحول مربع الحوار إلى تفاصيل الوجبة، ولكن يتم حفظ اسم المطعم. كما أن الاستجابة الأولية بشأن عدد السعرات الحرارية مختصرة، ولكنها تقدم شرحًا أكثر تفصيلاً إذا قال المستخدم "مزيد من التفاصيل". مرة أخرى، يتم تخزين البيانات في بيانات الجلسة، ويتم تمريرها مرة أخرى كجزء من إطار عمل Lex. هنا مثال لأحد الكائنات.
{
" 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 "
}
وظائف lambda في هذا الروبوت عديمة الحالة تمامًا، لذا يجب أن تأتي أي بيانات من الاستدعاءات السابقة من خلال كائن الطلب.
إحدى الميزات الموجودة في واجهات مستخدم chatbot الرئيسية (Messenger، Slack، وما إلى ذلك) هي الأزرار. تعمل هذه على تقليل الجهد الذي يبذله المستخدم من خلال توفير سلسلة من الخيارات مثل ذلك.
كل منصة مراسلة لديها تطبيقها الخاص لهذا النمط، وإليك ما يستخدمه Messenger. يتعامل Lex مع الترجمة للحصول على الأزرار بالتنسيق الصحيح، وداخل Lex، يجب تزويد سمة ResponseCard بالتفاصيل الخاصة بتفاصيل الزر.
يتم تعديل Lex بالكامل من خلال وحدة التحكم. تتم استضافة وظائف lambda التي تخدم منطق الأعمال في AWS lambda، ويتم نشرها من مضيف EC2.
البرنامج النصي للنشر الكامل هو /src/build.sh ولكن يمكن العثور على نظرة عامة سريعة في الإرشادات التالية.
# 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
يتم تكرار هذه العملية لكل من وظائف لامدا التي يستدعيها Lex. يتضمن ذلك وجود شرط اختبار واحد على الأقل لكل وظيفة لامدا للتأكد من أن النشر قد تم بشكل صحيح.
أحد المواضيع في تصميم الروبوت يدور حول امتلاك شخصية. الشيء الذي يجب مراعاته عند تصميم المقاصد هو ما هي جميع الأسئلة المحتملة التي قد يطرحها المستخدم. يجب أن يتضمن ذلك أسئلة خارجة عن الموضوع، مثل "ما اسمك" أو ردود عاطفية مثل "أوه-لا" أو "أنت مقرف". من السهل ترميزها - عادةً ما تكون مجرد استجابة طلب بسيطة بدون أي خانات، وتميل إلى جعل مربعات الحوار أكثر طبيعية.
على سبيل المثال، إليك استجابة مختصرة مشفرة في وظيفة Misc.js والتي تستجيب إذا سأل شخص ما عن اسم الروبوتات. في النماذج، فإن نطق "ما اسمك" يفي بهذا القصد.
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 })) ;
}
كجزء من الجهد الأولي، كنت أحاول نشر برنامج الدردشة الآلي هذا في متجر Slack. وكجزء من ذلك، كنت بحاجة إلى إنشاء موقع ويب للحصول على الدعم العام للتطبيق. إنه عمل قيد التقدم، ويسمى calorycountbot.com. تتم استضافته بواسطة s3، والمصدر موجود في المجلد /website.