Alexa (Amazon Echo) conversation skill - Using Session Attributes (JavaScript - AWS Lambda)

4.4k views Asked by At

this might be an easy one but I couldn't figure it out in days.

I want to make Alexa have a conversation, like;

>> Alexa, start testSkill.

A: Test Skill started. Tell me a number.

>> One.

A: Okay, tell me a color now.

>> Blue.

A: And finally, tell me an animal name.

>> Chicken.

A: You told me one, blue and chicken.

I found out that I have to handle the Session Attributes of the skill, which is a JSON holds and transfers the information between intents.

I use functions like this ;

    function testConversation(intent, session, callback) {

    var cardTitle = intent.name;
    var repromptText = "";
    var sessionAttributes = { // I don't know how to handle this
        nameOfPairOne: "",
        nameOfPairTwo: "",
    };
    var shouldEndSession = false;
    var speechOutput = "";

    var color= convertToASCII(intent.slots.color.value);
    sessionAttributes.nameOfPairOne = color;

    speechOutput = "You said "+sessionAttributes.nameOfPairOne+". Please say another thing. ";
    callback(sessionAttributes, buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}

 function testConversation2(intent, session, callback) {

    var cardTitle = intent.name;
    var repromptText = "";
    var sessionAttributes = session.attributes;
    var shouldEndSession = false;
    var speechOutput = "";

    var number = convertToASCII(intent.slots.number.value);
    sessionAttributes.nameOfPairTwo = number;

    speechOutput = "You first said "+sessionAttributes.nameOfPairOne+", and now said "+sessionAttributes.nameOfPairTwo;
    callback(sessionAttributes, buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}

//------Helpers that build all of the responses ---------//
function buildSpeechletResponse(title, output, repromptText, shouldEndSession) {
    return {
        outputSpeech: {type: "PlainText", text: output},
        card: {type: "Simple", title: "SessionSpeechlet - " + title, content: "SessionSpeechlet - " + output},
        reprompt: {outputSpeech: {type: "PlainText", text: repromptText}},
        shouldEndSession: shouldEndSession
    };
}


function buildResponse(sessionAttributes, speechletResponse) {
    return {version: "1.0", sessionAttributes: sessionAttributes, response: speechletResponse};
} 

A piece of code from onIntent() function where I call the above functions. (I know it's wrong but couldn't figure out the right way)

 else if ("getColorNum" == intentName) {
    if (session.attributes.nameOfPairOne === "") {
        testConversation(intent, session, callback);
    } else {
        testConversation2(intent, session, callback);
    }
}

And the Intent Schema JSON is like that;

 "intents": [
{
  "intent": "getColorNum",
  "slots": [
    {
      "name": "Color",
      "type": "ColorSlot"
    },
    {
      "name": "Number",
      "type": "NumberSlot"
    }
  ]
}

] }

So, am I doing all of the things wrong ? Where is the mistake ? And, how can I build a conversation like I mentioned ? Thanks from now.

2

There are 2 answers

0
Huy Le On

There are several ways to approach this, depending on the complexity of what you are trying to store.

First, using slots. If One, blue, chicken and such are in classified categories, you can store them in slots and access them the JSON way, getting the value and store in a variable in your JavaScript code. Example (sudo):

var something1 = event.request.intent.slots.assetName.value;
var something2 = event.request.intent.slots.assetName.value;
buildSpeechletResponse(`my ${something1} likes ${something2} and blah blah blah`)

Second, using attributes to store something and use it late on runtime, Alexa won't remember this after skill ended. Example:

'BreakfastIntent': function () {
    var restaurant = randomArrayElement(getRestaurantsByMeal('breakfast'));
    this.attributes['restaurant'] = restaurant.name;

    var say = 'For breakfast, try this, ' + restaurant.name + '. Would you like to hear more?';
    this.response.speak(say).listen(say);
    this.emit(':responseReady');
},

Last, storing them in a database, highly recommended one is DynamoDB and there are plenty of tutorial for storing and access Dynamo from Alexa Skill Kit. Checkout: https://github.com/alexa/alexa-cookbook/tree/master/aws/Amazon-DynamoDB/read

I hope this helps.

1
Josep Valls On

Since no one gave you any answer yet I thought I'd give you my two cents. My language of choice is Python but I can give you some high-level ideas.

  • Use a session object to keep track of everything related to the conversation. After you compute the "response", serialize your session object into the session json and deserialize it in the next intent request.
  • Maintain a conversation state and use a finite-state-machine approach to know where you are and what callback to execute given an intent and a specific conversation state.
  • Decouple your conversation logic from the ASK interface as much as you can. Try to write tests you can run locally.

If you want to see how I implemented these, check out: