이것은 다양한 패스트푸드 레스토랑을 방문하여 얻은 칼로리를 계산하는 Lex 기반 챗봇입니다. 이는 Facebook 페이지나 휴대폰의 메신저 앱을 통해 액세스할 수 있는 FB 메신저 챗봇을 통해 활성화됩니다.
목차
이 봇은 모델에 제공된 데이터를 기반으로 사용자 요청을 해독하고 의도를 트리거할 수 있는 인텔리전스가 포함된 서비스인 AWS Lex를 사용합니다. 그런 다음 의도는 해당 의도와 관련된 비즈니스 논리를 포함하는 람다 함수를 호출합니다.
현재 NLU 프로세스가 분류하는 다양한 의도가 있습니다. 봇의 "핵심 기능"은 다음과 같습니다.
핵심 기능을 보완하는 인텐트도 있습니다.
그런 다음 봇의 '개성'을 형성하는 의도가 있습니다. 이는 실제 사용자 사용을 기반으로 생성되었으며 일반 오류 메시지가 응답하는 데 사용되는 것을 방지합니다.
각 의도 내에는 사용자가 제공할 수 있는 잠재적 문장을 구성하는 샘플 발언이 제공됩니다. 슬롯(예: Large Fry)의 값은 고유한 속성으로 람다 함수에 전달됩니다.
다음 명령을 실행하여 AWS CLI에서 요약 정보를 가져올 수 있습니다.
aws lex-models get-bot --name FastFoodChecker --version-or-alias PROD
이는 NLU 모델이 호출할 의도를 결정하는 샘플 발언과 슬롯의 조합입니다. 이는 Lex에서 유지 관리되며 모델 교육에 사용됩니다.
현재 인텐트에서 사용되는 사용자 지정 슬롯은 다음과 같습니다.
NLU가 값을 배치하기 위해 슬롯에 항목을 지정할 필요는 없습니다. 그러나 데이터가 희박하면 NLU가 사용자 요청을 해석하는 방식이 저하될 수 있습니다.
챗봇을 사용하려면 사용자와의 자연스러운 상호 작용이 필요합니다. 한 가지 핵심 개념은 여러 슬롯을 단일 의도에 통합하는 방법에 관한 것입니다. 예를 들어, 사용자는 "빅맥, 감자튀김, 콜라의 칼로리는 얼마인가요?"라고 질문할 수 있습니다. 이는 각각 파싱해야 하는 세 가지 항목입니다. 이 챗봇 내에서 기본 처리에는 의도에 매핑되는 다양한 슬롯이 있습니다. 예를 들어 GetCalories 의도에 매핑되는 슬롯은 다음과 같습니다.
여기에는 주목해야 할 몇 가지 항목이 있습니다.
위의 요청 예에서 NLU 모델은 발화의 데이터를 세 가지 다른 슬롯(Food, Extra 및 Drink)으로 구문 분석합니다.
슬롯 순서는 구문 분석에 중요하지 않지만 다음 응답이 무엇인지 결정합니다(슬롯 1 - 어느 레스토랑에 있습니까?).
이 의도에는 필요하지 않은 두 개의 슬롯(Ketchup 및 PacketsKetchup)이 있습니다. 사이드 메뉴로 감자튀김을 요청할 경우 선택사항입니다. 이는 검증 코드 후크에서 호출되는 Lambda 함수의 코드에 의해 구동됩니다.
다양한 의도에 대한 응답을 공식화하는 모든 논리는 일련의 람다 함수에서 처리됩니다. 호출할 람다 함수는 Lex 내에서 관리되며 의도 수준에서 설정됩니다. 이를 통해 애플리케이션 내에 모듈성을 구축하여 기능을 가볍게 유지할 수 있습니다.
Lex에는 람다 함수를 호출할 수 있는 두 가지 다른 지점이 있습니다. 첫 번째는 기본 검증을 통한 것이며 이를 식별하는 속성 이름을 invocationSource라고 합니다. 이에 대한 잠재적인 값은 DialogCodeHook 및 FulfillmentCodeHook 두 가지가 있습니다. Lex Bot에서 이러한 Lambda 함수가 지정되는 위치는 다음과 같습니다.
첫 번째 드롭다운은 유효성 검사이며, 봇이 호출될 때마다 람다 함수를 호출합니다. 전달되는 속성을 DialogCodeHook이라고 합니다. 두 번째 드롭다운은 Fulfillment이며 필수 슬롯이 완료되고 초기 호출의 유효성 검사가 완료된 후에만 호출됩니다. 이를 통해 기능이 달라질 수 있어 봇 구축 시 확장성이 향상됩니다.
다음은 현재 작성된 각 함수의 개요입니다.
Lambda.js - DialogCodeHook 모드에서만 제공되는 쿼리에 대한 기본 유효성 검사를 처리하는 기본 함수입니다.
계산.js - 식사의 실제 칼로리에 대한 응답 계산은 이 기능에 의해 처리되며 FulfillmentCodeHook에 의해 제공됩니다.
pizza.js - WhatPizzaTypes 인텐트를 포함하여 피자의 칼로리 계산과 관련된 인텐트를 처리합니다.
misc.js - 도움말, 소개, 식사에 대한 자세한 내용과 같은 간단한 인텐트를 처리합니다.
Chinese.js - 중국 음식에 대한 인텐트를 처리하고 다양한 슬롯을 결합하여 식사를 구성합니다.
이 봇의 핵심 기능은 다양한 식사에 얼마나 많은 칼로리가 있는지에 대한 질문에 답할 수 있는 것입니다. Lex가 사용하는 슬롯은 NLU 모델을 훈련하는 데 도움이 되지만 조회 파일 역할을 하는 기능은 없습니다. 이것이 /src/data/ 폴더에 저장되는 json 객체가 들어오는 곳입니다.
다음은 형식의 샘플입니다.
[
{
" 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 함수에 달려 있습니다.
대규모 사용자 지정 슬롯을 관리하는 것은 어려울 수 있으며, 특히 데이터가 동적인 경우 더욱 그렇습니다. 기본 음식 조회에는 수백 개의 고유한 값이 있으며 사용자 사용량에 따라 증가합니다. 이 슬롯을 생성하는 프로세스는 자동화되었으며 사용자 정의 슬롯의 데이터는 food.json 데이터 개체에서 가져옵니다. 이는 명령줄에서 직접 로드할 수 있는 AWS CLI를 통해 수행됩니다. 모든 파일은 참조용으로 [slots}(https://github.com/terrenjpeterson/calciumcounter/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 "
}
이 봇의 람다 함수는 완전히 상태 비저장이므로 이전 호출의 모든 데이터는 요청 개체를 통해 와야 합니다.
주요 챗봇 사용자 인터페이스(메신저, 슬랙 등)의 기능 중 하나는 버튼입니다. 이는 이와 같은 일련의 옵션을 제공함으로써 사용자의 노력을 줄여줍니다.
각 메시징 플랫폼에는 이 패턴의 자체 구현이 있으며 여기에는 Messenger가 사용하는 것이 있습니다. Lex는 버튼을 올바른 형식으로 변환하기 위해 번역을 처리하며, Lex 내에서 responseCard 속성은 버튼 세부 정보와 함께 제공되어야 합니다.
Lex 수정은 콘솔을 통해 완전히 수행됩니다. 비즈니스 로직을 제공하는 람다 함수는 AWS 람다에서 호스팅되며 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 })) ;
}
초기 노력의 일환으로 저는 이 챗봇을 슬랙 스토어에 게시하려고 했습니다. 그 일환으로 앱의 공개 지원을 위한 웹사이트를 구축해야 했습니다. 현재 진행 중인 작업이며, 칼로리카운트봇.com이라고 합니다. s3에서 호스팅되며 소스는 /website 폴더에 있습니다.