Hi I have made an entity called answers in dialogflow, this entity contains all the answers to my questions for my quiz game.
I get the questions from my database and then check to see if the given answer is correct.
app.intent('answer-question', (conv, {answer})=> {
if(answer == ((conv.data.answers)[0])){
//stuff}
else{
conv.close('you lose');
}
});
However, this function only works when the user gets the answer correct. If the user answers the question incorrectly, then I get the following error:
"Question Master isn't responding right now. Try again soon."
MalformedResponse
'final_response' must be set.
So my question is, how can i cater for the infinite selection of wrong answers a user might give?
Cheers!
You should handle that in a fallback intent. A new Dialogflow agent comes with a default: https://dialogflow.com/docs/intents/default-intents#default_fallback_intent
You should also consider using contexts, so the fallback intent knows that you are expecting an answer and provide a different response when an answer isn't expected.
Related
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
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.
API.ai's prebuild packages allow you to easily get long lists of intents. Currently I'm trying to make use of their smalltalk package, which has at about 100 intents, and response to each.
I am making use of the api-ai-recognizer package to listen for intents. That works well, but now I have to match those intents, so that I can define the dialog (which is nothing more than using the fulfillment). And this is where I am having trouble.
intents = IntentDialog({recognizers: [apiairecognizer(CLIENT_TOKEN)]})
intents.matches('smalltalk', smalltalk_handler) // No luck
intents.matches(/smalltalk/, smalltalk_handler) // No luck
intents.onDefault(default_handler)
In the default_handler I capture the args:
{"score":1,
"intent":"smalltalk.greetings.how_are_you",
"entities": [
{
"entity":"Lovely, thanks.",
"type":"fulfillment",
"startIndex":-1,
"endIndex":-1,
"score":1
},
{
"entity":false,
"type":"actionIncomplete",
"startIndex":-1,
"endIndex":-1,
"score":1
}
]}
This makes sense according to the documentation of how matches works.
But that does mean that I don't know how to actually use the full list of intents, without explicitly copying every single intent in.
Just to clarify, if I use the exact intent:
intents.matches('smalltalk.greetings.how_are_you', smalltalk_handler)
I receive the nice response: Lovely, thanks.
Any suggestions?
So far, the only thing I have come up with is to modify the api-ai-recognizer such that it will return only smalltalk as intent, whenever it encounters a version of it. This way the intent dialog only needs to recognize one intent. Because they are handled in the same way, it doesn't matter at this point.
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.
In a given moment, my bot asks for a number.
builder.Prompts.number(session, "Cool. What's the number?", {
retryPrompt: "I couldn't understand $%##. Could you type it again?"
});
I need the message to contain the user response instead of $%##. Is there a way to do it? I tried to use session.message.text and results.response, but these are still from the outer function because of closure.
For anyone looking for this answer: currently, there is no way to change the message inside the Prompt dialog.
I looked up the code for BotFramework for nodeThe message you send as retryPrompt is as is, without replacements.