6

I am building a custom Alexa skill and want to make REST API calls. Currently, I am able to make GET request to fetch data from my web service which is then used by Alexa. However, I have the following requirements and I am not sure how to go about developing this.

  1. Invoke the Skill (Complete)
  2. User will invoke "Get List of Topics" intent, Alexa makes the GET REST API call and provides the "list of topics"(Complete)
  3. Make Alexa prompt the user to select a topic from the list (Pending)
  4. Receive the response made by the user in the lambda function and use the response to make a POST/PUT call. (Pending)
  5. Reprompt the user if the selected topic is invalid (Pending).

How do I implement 3, 4, and 5? I am currently using Python 3.6 to write the lambda function at AWS developer console. Are there any Amazon Alexa APIs guide for Python 3.6.

How do I do this in Java which is my preferred way?

I followed the instructions here to develop what I currently have: https://github.com/simonprickett/alexabart

Is there any detailed documentation available on how to write Alexa specific lambda function and its associated API guide for Python3 or Java.

Thanks

samdset
  • 233
  • 1
  • 5
  • 19
  • 1
    you wish to know the code for Python or Java? – Nikhil Wagh Jun 07 '18 at 19:18
  • @NikhilWagh I would like to know the functions/API's to use...or how to structure the code for Alexa....which I believe will make the code. Currently Python, however, I am also looking into developing the Alexa Skill in Java too, but not sure how to go about it. Like what classes, functions and APIs to implement, – samdset Jun 07 '18 at 19:26
  • 1
    I had my code in Python, and I did something very similar. Will it be okay if I give you my code and tell you what you should do? – Nikhil Wagh Jun 07 '18 at 19:29
  • @NikhilWagh Sure, I can make do with the code for now and learn from it. Thanks! – samdset Jun 07 '18 at 19:35

2 Answers2

1

You can use slots and Dialog.ElicitSlot directives, to get the information from the user. More specifically, you'll need a slot for which you will return Dialog.ElicitSlot response, and in the speechOutput of the response you'll provide the list of options, and when user provides the information, it'll be collected in the slot. see this : https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#elicitslot

If you're looking for code, this is what I did in Python 2.7

def dialog_elicit_slot(output, slotToElicit, city_name, movie_name, attributes, multiplex = None, confirmationStatus = "CONFIRMED"):
    return {
        "version": "1.0",
        "sessionAttributes": attributes,
        "response": {
            "outputSpeech": {
                "type": "PlainText",
                "text": output
            },
            "shouldEndSession": False,
            "directives": [
                {
                    "type": "Dialog.ElicitSlot",
                    "slotToElicit": slotToElicit,
                    "updatedIntent": {
                        "name": "GetMovieDetails",
                        "confirmationStatus": confirmationStatus,
                        "slots": {
                            "CITY": {
                                "name": "CITY",
                                "confirmationStatus": "CONFIRMED" if city_name != None else "NONE",
                                "value": city_name
                            },
                            "NAME": {
                                "name": "NAME",
                                "confirmationStatus": "CONFIRMED" if movie_name != None else "NONE",
                                "value": movie_name
                            },
                            "MULTIPLEX": {
                                "name": "MULTIPLEX",
                                "confirmationStatus": "CONFIRMED" if multiplex != None else "NONE",
                                "value" : multiplex
                            }
                        }
                    }
                }
            ]
        }
    }

Here, you can see I had 3 slots, out of which 2 (CITY and NAME) were made required in the skill builder.

This is what my skill does. It asks for the city and name of the movie in the beginning (invokation of the skill), then my skill would make a GET request to remote site to get the list of mulitplexes. And when I had the list of multiplexes which show that movie(which the user told and is collected in NAME slot) in his particular city, I give them the list of Multiplexes (which is just a string, output variable in the above code). And Dialog.ElicitSlot directive collects the slot information for the slotToElicit slot(which in this case is MULTIPLEX).

If this looks overwhelming, you can just contact me directly.

Nikhil Wagh
  • 1,376
  • 1
  • 24
  • 44
  • 1
    If you wish to see my complete code (which generally helps) see this : https://github.com/Nikhil-Wagh/Alexa-Flick/blob/master/lambda_function.py – Nikhil Wagh Jun 07 '18 at 19:43
  • I am still working on it. Th.anks for the insight, will contact you if I need more clarification – samdset Jun 08 '18 at 16:07
  • One of my requirement is to fetch a question from the database making a REST call and ask the user that question and then fetch a list of predefined responses ( again by making a REST call ) and prompt user to choose from that response. I am not sure, how do I tie up this mechanism using slots. Since slots need to have predefined values. – samdset Jun 10 '18 at 01:16
  • No, it's not necessary. They are made such that even if the utterance said by the user matches with the one in your intent schema, the slot will be able to identify the input given by the user. You can try adding some values, and then test it with a new value. It should match, and the `value` key in the intent should be able to hold this new slot value you just came up with. – Nikhil Wagh Jun 10 '18 at 06:52
  • How do I nake Alexa ask the user a list of questions which will be fetched using REST call and for each question I will also have to fetch a predetermined list of responses (using REST) and make Alexa ask the questions one at a time and then prompt the user to select one of the responses and capture the choice made by user. I am not sure how to implement this using slots. – samdset Jun 11 '18 at 01:13
  • You'll have to use the `sessionAttributes` for this. You can save the question in your `sessionAttributes` and when user answers that question you save the response. – Nikhil Wagh Jun 11 '18 at 06:41
  • Do you have a lot of questions? Or are they a predefined set of questions? – Nikhil Wagh Jun 11 '18 at 06:41
1

If you have a lot of questions and you want each of them answered, you can use sessionAttributes variable. The session variable can retain it's value through out the course of the session. You can make a dictionary to be saved in your sessionAttributes(It has to be a dictionary).

You can save something like this in your session variable.

sessionAttributes: {
    "AllQuestions" : ["string", "string"],
    "LastQuestionIndex" : integer
}

I'm assuming you're able to get the list of questions (using a GET request).

Step 1 : Make a slot

Answer would be a slot which is going to store your answer.

Step 2 : Get your questions ready

When your intent has just started and you don't have anything in your sessionAttributes (use a simple if-else) you'll have to make the GET request and gather all of your questions (maybe in a list or something). You make your GET request and store all the questions in your sessionAttributes['AllQuestions']. And set LastQuestionIndex = -1.

Now the tricky part comes in. (I'm also assuming you're able to use Dialog.ElicitSlot Directive).

Step 3 : Ask questions one-by-one.

Now you have a list of all the questions and you also have the index of last question that was asked. And now you just have to increment the index, get the Next Question, and use Dialog.ElicitSlot Directive to ask this new question. And update the LastQuestionIndex in your sessionAttributes.

Step 4 : Getting the answer

Before proceeding to next question, you'll also have to check if the slot Answer has any value or not? If it does have a value (it is not "None"), then you can use the LastQuestionIndex variable and store the answer for that particular question.

If you're looking for code, here you go:

# Line 1 - 22 should be in your intent function
sessionAttributes =  dict()
if 'sessionAttributes' in event['session']: 
    sessionAttributes = event['session']['sessionAttributes']

if not sessionAttributes.has_key('AllQuestions') : 
    # Make the GET REQUEST
    Questions = ["all", "of", "your", "Questions", "stored", "in", "a", "list"]
    sessionAttributes['AllQuestions'] = Questions
    sessionAttributes['LastQuestionIndex'] = -1


Answer = getSlotValue('Answer')
if Answer != None:
    # AllAnswers is a dictionary which has key as the question number and value is the answer
    # AllAnswers = {
    #   0 : "Answer to your first question",
    #   1 : "Answer to your second question"
    # }
    AllAnswers[sessionAttributes['LastQuestionIndex']] = Answer

return ansNextQuestion(sessionAttributes)


def askNextQuestion(sessionAttributes) :
    Last = sessionAttributes['LastQuestionIndex']
    sessionAttributes['LastQuestionIndex'] = Last + 1

    if Last < len(sessionAttributes['AllQuestions']): 
        outputSpeech = "Your next question is " + sessionAttributes['AllQuestions'][Last + 1]
        return {
            "version": "1.0",
            "sessionAttributes": sessionAttributes,
            "response": {
                "outputSpeech": {
                    "type": "PlainText",
                    "text": outputSpeech
                },
                "shouldEndSession": False,
                "directives": [
                    {
                        "type": "Dialog.ElicitSlot",
                        "slotToElicit": "Question",
                        "updatedIntent": {
                            "name": "GetMovieDetails",
                            "confirmationStatus": "NONE",
                            "slots": {
                                "Answer": {
                                    "name": "Answer",
                                    "value": "NONE" # You can change the value of Answer to get new answer, this is allowed.
                                }
                            }
                        }
                    }
                ]
            }
        }
    else : 
        # You are out of questions, now proceed to what you should do after getting all the answers.
Nikhil Wagh
  • 1,376
  • 1
  • 24
  • 44
  • 1
    Take a look at this : https://developer.amazon.com/blogs/alexa/post/2279543b-ed7b-48b4-a3aa-d273f7aab609/alexa-skill-recipe-using-session-attributes-to-enable-repeat-responses – Nikhil Wagh Jun 11 '18 at 07:30
  • Thanks a lot, Nikhil. I was missing the **sessionAttributes **. Quick clarification, I believe I need to use **sessionAttributes** for responses too. Since each question has its own set of "valid responses" which the user need to choose from. e.g Question 1 has a list of 5 valid responses which the user can select and Question 2 has a unique list of 4 valid responses. The mapping between the question and its valid set of responses are available via GET (e.g _/questions/{id}/responses_ will get me the set of responses that are valid for the question with the given "id" ) – samdset Jun 11 '18 at 16:40
  • 1
    You know what to do. If you need me mail me or anything. I'm free these days. :P – Nikhil Wagh Jun 11 '18 at 16:48