Alexa Skill - Keep Intent alive for dialogue - node.js

For demonstrations purposes I should devolop an Alexa Skill on a dialogue basis.
All the alexa responses are hardcoded.
The template of the skill is like:
Part 1:
User: Alexa, ask MySkill {Question1}.
Alexa: Hardcoded answer.
Part 2:
User: Alexa, ask MySkill {Question2.1}
Alexa: Hardcoded answer for Question2.1.
User: Alexa, ask MySkill {Qeustion2.2}
Alexa: Hardcoded answer.
I was able to create part 1. But in Part 2 i have some problems.
Do I need seperate Intents for questions 2.1 and 2.2. Or is there a possiblity to keep the skill alive?

I'm going to first assume that you're using the alexa-sdk during your development. If you don't know that that is, please check out this link:
https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs
There are multiple ways you can break up questions to your skill in your intent schema. They can either be individual intents, such as "QuestionOneIntent" and "QuestionTwoIntent", or a single intent "QuestionIntent" where the slot values in those intents correspond to individual questions. As the original post hasn't given much information, I can't say which structure would be the best setup.
There are two general types of responses in the alexa-sdk. ":tell" will make Alexa say a response and immediately go back to her idle state (not listening to you). ":ask" will say a response, wait 8 seconds, and follow up with a reprompt message all while waiting for you to give another command.
As for keeping the session alive in a conversation, you could simply emit your response by using
var speechOutput = "This is the answer to Question"
var speechOutputReprompt = "Do you have any more questions?"
this.emit(":ask", speechOutput, speechOutputReprompt)
This will allow for your session to stay open and the user can continue to ask more questions. You will have to make another intent that will close the session if you answer "No" to the reprompt, thus making the shouldEndSession variable true. Here is an example of how I might structure the code:
"QuestionIntent": function(){
var responseName = ""
var slots = this.event.request.intent.slots
for (var slot in slots){
if(slots[slot].value != undefined){
responseName = slots[slot].name;
switch(responseName){
case "QuestionOneIntent":
var QuestionOneAnswer = "Answer to question one";
this.emit(":tell", QuestionOneAnswer);
break;
case "QuestionTwoIntent":
var QuestionTwoAnswer = "Answer to question two";
this.emit(":ask", QuestionTwoAnswer, QuestionTwoAnswerReprompt);
break;
default:
console.log("error");
break;
}
}
}
}

Looks like you are using single-turn interactions (i.e. you don't keep the session open, check shouldEndSession https://www.npmjs.com/package/alexa-app). Regardless, you need to either save the current state in the session object or store it somewhere else (tied to the unique request.userId).
Using different intents may be another solution but its prone to fail if you have similar utterances that may be incorrectly mis-mapped to one another.

Related

Amazon alexa skill game, managing program flow

I am creating a simple amazon alexa game.
I have all the intents I need for the game, but my question is how do I make sure you can only choose valid options?
Example: If alexa ask me a yes or no answer how to a promt for this?
Currently: If you answer with 10 example, it will say "You just triggered NumberIntent", I want it to say "Please choose a valid option" and then repromt until it gets a yes or no.
I am currently using the canhandle on the intents but it doesn't help for the program crashing.
Would be nice if anyone can help me!
You shouldn't try to force the user to respond to a specific question.
Vocal design is different than visual design. On a website, I can click wherever I want.
Alexa's interaction and vocal design in general is like a deck of card. Each card is an intent.
And as a user, I can choose to pick any card whenever I want.
As a user, I can say :
repeat the question
ask for help
launch another specific intent
or maybe I just want to quit the skill.
The card picked won't be the one you expect. It will go to either an intent you've defined or AMAZON.FallbackIntent
So be careful with that.
Maybe If I say something different, it's because I want to do something different.
If you still want to implement it on specific intent, you need to adapt the logic based on the user's progression.
After you ask the user a question, you save the progression in the sessionAttribute
const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
sessionAttributes.progression = "EXPECT_YES_OR_NO";
Within your intent, you add a logic to detect if the user should first response to yes or no
canHandle(handlerInput) {
return (
Alexa.getRequestType(handlerInput.requestEnvelope) === "IntentRequest" &&
Alexa.getIntentName(handlerInput.requestEnvelope) ===
"MySpecificIntent"
);
},
handle(handlerInput) {
const { responseBuilder } = handlerInput;
const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
if(sessionAttributes.progression === "EXPECT_YES_OR_NO") {
const utt = "I didn't catch that, do you like StackOverflow? You can say yes or no."
return responseBuilder.speak(utt).reprompt(utt).getResponse();
}
},
I recommend you to follow alexa skill sample tutorial zero to hero it summarise everything you need to know about developing a skill on Alexa with examples & videos.

How can I make Alexa Intent handler respond with different things each time it is called?

I am trying to develop an Alexa Skill with ASK node.js SDK. I am trying to build a game where the Alexa and the user take turns counting (not a great game, but useful educationally for me). Alexa starts with one, then the user two, then Alexa says three, and so on until the user says an incorrect number. In this case, I hope to implement logic to end the game.I am struggling to figure out how to get Alexa to respond differently after each time the user says a number. Is this a situation where I need multiple intent handlers? It seems like that would be silly, as the general logic does not change. I'm struggling to find up to date example code of game logic generally, so any resources that I can learn from would be greatly appreciated. The code I have as of yet is as follows--
const MyGameIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'MyGameIntent';
},
handle(handlerInput) {
const speechText = 'One';
return handlerInput.responseBuilder
.speak(speechText).listen()
.getResponse();
}
};
Obviously, I have not gotten very far. I have successfully created an intent and tested that Alexa will respond with 'One' when I ask to start a game. Where I am stuck is how to get Alexa to say 'One', then wait for a user to say 'Two', and depending on if they said the correct number, Alexa would say 'Three' or 'Game over' and end the game. The Codecademy course for ASK uses a different and outdated syntax, but it is the closest I have come yet to an answer. It suggests to chain a .listen() after speak, but does not provide information about whether this .listen() will re-prompt the same intent handler
To make it work as you wish, you need to keep the state of the game in Session Attributes between utterances. Please read it to better understand how it works.
If about your game, I would suggest to follow those steps:
You speak "start the game", the skill responses with "One" (you have already implemented this part) AND stores the game state (ie. by saving next expected answer)
When it's your turn, the skill should check if received answer is equal to expected and react - continue and store next expected answer or finish the game. For this step you'll need another handler for intent which expects just a number.
There is an example from Alexa team that shows how to create a game and store the state between utterances - Trivia Game.

Dialogflow: Is it possible to bypass the default reprompts if needed?

I am developing an app with dialogflow and actions-on-google, using webhook and node.js to program intents.
My problem is that dialogflow gives default reprompts on intents as seen in the code below.
app.intent('Reprompt', (conv) => {
const repromptCount = parseInt(conv.arguments.get('REPROMPT_COUNT'));
if (repromptCount === 0) {
conv.ask(`What was that?`);
} else if (repromptCount === 1) {
conv.ask(`Sorry I didn't catch that. Could you repeat yourself?`);
} else if (conv.arguments.get('IS_FINAL_REPROMPT')) {
conv.close(`Okay let's try this again later.`);
}
});
The context is that I am programming a conversational agent that asks users questions such as "What made you smile today?" and I expect the users to talk about this question with their partner. The best case scenario is that the app asks the question and then only listens for "Next question" or "End conversation", but does not interrupt the users.
As for now, the default reprompt interrupts the users, saying "What was that?" after a bit of time.
Is it possible to fiddle with the reprompt so that it stops doing that?
I know that the reprompts is part of the 'Best practice' for developing conversational agents, but I this case it seems counter intuitive.
I had a similar situation where I ask users a few questions (survey), and whatever the answer is, I go to the next question. From my understanding, you are looking for something similar. Let's suppose you want to ask the user 'What's your name?' and after they answer you want to ask them 'Where are you from?'.Here's how you do it (maybe not the optimal way but it works):
1. Create an intent 'name' and add a required parameter with prompt 'What is your name?'
2. Add a custom event 'userName'
3. Enable webhook call for the fulfillment
4. (Optional) You can add training phrases to invoke this intent
Follow the same steps as above to create another intent for the country
1. Name intent 'country' and add a required parameter with prompt 'Where are you from?'
2. Add a custom event 'userCountry'
3. same as above
Now we will write a fulfillment function for 'name' intent that will trigger country question
function Name(agent){
agent.add('This will invoke as respond to name question');
agent.setFollowupEvent('userCountry');
}
So, what is happening is that when the user invokes intent name he has to tell his name and your bot doesn't respond but instead trigger country intent, where answering 'Where are you from?' is also required. So the final conversation will look as follow:
Bot: What is your name?
User: Blah Blah
Bot: Where you from?
User: Blah Blah

How do I incorporate await sleep(milliseconds) between two set of messages in MS Bot Framework?

I am developing a Bot using NodeJs which should ask the user a set of questions and then after a break, ask the same another of questions again.
I am using await sleep(milliseconds) in between.
While testing using the Emulator, I noticed that the questions from the first set are sent one by one, saving the user's response. The second set is sent all at once without allowing the user to respond to each question of the second set individually.
await turnContext.sendActivity(askFirstSetOfQuestions(question));
await sleep(60000);
await turnContext.sendActivity(askSecondSetOfQuestions(question));
await next();
image - screen capture from emulator
Quite lost here..
any help would be appreciated.
There are two things to consider here. First, if you truly need a delay, it is better to await a promise than use sleep. You can do this via await new Promise (resolve => setTimeout(resolve, DELAY_LENGTH);. I use this frequently in conjunction with typing indicator to give the bot a more natural feeling conversation flow when it sends two or more discrete messages without waiting for user input.
However, as Eric mentioned, it seems like you might want a waterfall dialog instead for your use case. This sample from botbuilder-samples is a good example. Each step would be a prompt, and it will wait for user input before proceeding. I won't try to write an entire bot here, but a single question-answer step would look something like:
async firstQuestion(stepContext) {
return await stepContext.prompt(TEXT_PROMPT, 'Question 1');
}
async secondQuestion(stepContext) {
stepContext.values.firstAnswer = stepContext.result;
return await stepContext.prompt(TEXT_PROMPT, 'Question 2');
}
and so forth. Not sure what you are doing with the responses, but in the example above I'm saving it to stepContext.values so that the answers are all available in later steps as part of the context object.
If you can detail more of what your use case/expected behavior and share what askFirstSetOfQuestions is, we could provide further assistance.

DialogFlow follow up triggers empty response

I have a DialogFlow intent follow up that I'm having a hard time with. It's the only follow up to my main intent, and the issue I'm having is that when
the incidents.data array is empty it doesn't trigger the conv.ask statement in the else case and causes DialogFlow to throw an empty speech response error. The code looks something like this:
app.intent('metro_timetable - yes', async (conv: any) => {
const incidents = await serviceIncidents.getIncidents();
if (incidents.data.length > 0) {
conv.ask('I have incidents')
} else {
conv.ask(
`I wasn't able to understand your request, could you please say that again?`
);
}
});
incidents.data gets stored in the global scope, and is set deep within
the metro_timetable intent. It stores an incident for the follow up. Because all yes responses trigger the follow up I setup an else case so it catches it if someone says yes when metro_timetable doesn't understand their original request and asks them to repeat it. If incidents.data actually has information to share the dialog triggers correctly and I have incidents is correctly read to the user.
In DialogFlow it looks something like this. Where am I going wrong here?
Your description is a little convoluted how incidents.data actually gets set, but it sounds possible that instead of it being set to an empty array, it isn't set at all. In this case, I suspect that the following happened:
incidents.data would be undefined
Trying to evaluate incidents.data.length would cause an error
Since the program crashes, your webhook doesn't return a result. Since you probably didn't set a result in the UI for the intent, an empty result was returned.
You can probably solve this by doing a test such as (for example)
incidents && incidents.data && incidents.data.length > 0
Your other issue, however, seems to be that you have a Followup Intent set for a scenario where you don't actually want that as the followup. This is one of the reasons you probably shouldn't use Followup Intents but, instead, only set a context when you send a response where that context would make sense, and look for the "Yes" response in the context you define. Then, when metro_timetable doesn't understand the request, you don't set the context and you give an error.
To do this, you would remove the automatically generated metro_timetable-followup context from the two Intents. You'll create your own context, which I'll name timetable for purposes of this example.
In the fulfillment for the metro_timetable Intent, if you respond with something that needs confirmation (ie - when "yes" will be something the user says), you would set the timetable context with something like
conv.contexts.set('timetable',2);
conv.ask('Are you sure?');
You can then create an Intent that checks for timetable as the Incoming Context and has training phrases that are equivalent to "yes". In that Intent, you'd do what you need to and respond.

Resources