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.
Related
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.
What if I have more than 100 intents including the followup intents. Should we write separate handler for each 100 intent and call a common function from the handler function. Is it correct?
Here we want to have common function with intent name as parameter, because all we do is fetch the response from database.
Shall we have parameterized function in intentmap set or have separate handler function for all these intents and call common parameterized function from inside. Please suggest.
Yes using paramerized functions or classes is a good practice. With this setup you can easily re-use any required logic if two intents perform similair actions in the webhook.
If you require some different behaviour you can enter values into the parameters, an example of this would be a function that ends the conversation.
app.intent("Stop Conversation"), (conv) => {
const message = "Okay, have a nice day";
endConversation(conv, message);
});
app.intent("Cancel Reservation"), (conv) => {
const message = "Okay, I will cancel your reservation. Have a nice day."
endConversation(conv, message)
});
endConversation(conv, message) {
conv.close(message);
}
You could choose to go for one single handler that looks up the intent name and then fetches the response, but this can cause some issues when working with Helper intents. These Helper intents require extra parameters that normal intents do no use, so you will have to account for them in your common handler or write seperate handlers for them. If you do not need these intents, then there isn't any harm in using a single handler.
One extra thing to note, having 100 intents is quite alot. Remember that intents should be used to indicate what you user says and not as a step in your flow. Usually this means that you only have one intent to handle yes input from your users and you will use context to detirmine which step of the conversation you are in.
If you are using the actions-on-google or dialogflow-fulfillment libraries, then yes, having an Intent Handler for each Intent and having those handlers call other functions with the parameters you want is the best approach.
However... if you're not using these libraries, you certainly have other options available.
For example, using multivocal you can set builder functions that extract parameters into the request environment and make the database call. If you set the "Action" field in Dialogflow you can (but don't have to) use this as the basis for an Action Handler.
If you just want to stick to your own libraries, you can parse the JSON yourself and make whatever function calls based on whatever values you wish.
I'm setting up a reset password intent using Dialogflow, where I'm performing some validation via webhooks. Unfortunately, I'm not able to figure out how to reprompt the user in case of failed validation.
I've tried to trigger the intent again using an event, but it doesn't seem to be working. I've also tried setting the same input contexts to trigger the intent again, but neither seem to work.
So I've created 2 parameters within the intent, which are being filled via prompts, following which I am performing the validation. Here's the code:
function getPasscode(agent) {
console.log(agent.parameters);
if(/^\d{6}$/.test(agent.parameters.code1) && agent.parameters.code1 == agent.parameters.code2) {
// Reset passcode call
} else {
return new Promise((resolve, reject) => {
agent.add("Your codes don't match. Please try again.");
var output = JSON.stringify({"followupEvent": {"name": "GetPasscode", "data": {}}})
resolve(output);
});
}
}
The bot outputs the text properly, but isn't triggering the event, as intended.
Am I missing something?
Remember that Intents represent what the user does and not what your action is trying to do. In general, you don't "trigger" an Intent - the user does.
So if you're "reprompting" the user - send that prompt in your reply to them. Then make sure the Intent is setup to capture their reply. This may involve in your setting an Output Context to narrow which Intents are evaluated to consider the reply.
You can't both send back a response and trigger an Intent with an event. Sending an event from your fulfillment is almost never needed and, when done, discards anything you may already have set for a response. All it does is cause the Intent with the event registered to it to be triggered. (Your code has two problems in this respect - you both try to send a response, and you're trying to send the followup event incorrectly.)
In your use-case, you do not need to call the event as per my understanding. Better way to do this is :
Set-up intent where you ask and confirm the password and store it
Validate this in your webhook
Here is the pseudo code:
if validationPassed {
call your api to reset password
send reset password confirmation output to user
}
if validationFailed {
setup output context to ask-password intent again
send output to user to re-enter the password
}
As #Prisoner says, you do not trigger an intent, the user does. We do the processing and send the response once the intent is triggered.
Hope it helps.
If a user invokes my action, she is asked by google whether her account should be linked or not (something like: "If you want to use xxx, I got to link your account at xxx with google. Is this okay?"). Now, if she chooses "no", and my action returns an answer with expectUserResponse set to false, google assistant seems to jump into a very awkward endless loop of my returned response, even emitting the assistant's "conversation finished" sound after each response:
("In order to use xxx, I got to link your account at xxx with google. Is this okay?" - "No" - "Okay, this means you are not able to use your account at xxx. Get back to us if you change your mind.")
"Schönen Tag" (in the second speech bubble) is the response I am sending from my fulfillment.
So what we are getting here is an endless "Schönen Tag" - GoogleSound - "Schönen Tag" - GoogleSound - "Schönen Tag" - GoogleSound - Schönen Tag"- GoogleSound and so on. With no additional user input between each message. Imho, this definitely shouldn't happen, no matter if I configured sth wrongly or not.
I don't even need my fulfillment server to reproduce this. If I create a dialogflow intent, attach the actions_intent_SIGN_IN event to it and let this intent return a static response with "set this intent as end of conversation" set to true, I am able to fully reproduce this strange behaviour:
(this actually was the setup for all screenshots above)
If I recreate this intent, but change the setting to not end the conversation after sending the response, I do not get an endless loop anymore. But this isn't what I intended to do.
It also doesn't seem to matter if I require the sign in for explicit invocations or not (in the integrations-tab).
It looks like this was a bug, with a fix released this morning.
Are you still running into this issue?
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.