Use api.ai intents in botbuilder - node.js

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.

Related

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.

Should we have separate function handler for all intents if number of intents are more than 200

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.

Dialogflow Intents Follow-ups not under right intent

For example if you have IntentA and you add 2 followup intents: IntentB, IntentC, it works ok it should add a context because it doesn't have an output context yet. But here is the problem. Sometimes if you add another one, for example a FallbackIntent, it just adds another context (SOMETIMES) and if you delete it in both(IntentA and FallbackIntent), so they both have the same context, meaning they should still be connected, and the hiearchy shouldn't change,but it still does. It still works perfectly, but still this is a wierd behavior. Any ideas why this happens and how to fix it?
Intent A
Intent B
Fallback
The best way to resolve this issue and organize the structure of your dialogflow agent is to upload the intents using create_intent() function of dialogflow api.
You can give the root intent as parent_followup_intent_name, and all the intents having this root intent will fall under same intent. Note that you will need to give root intent ID not the name.
You can read more about create_intent api using python sdk.
intents_client = dialogflow.IntentsClient()
intent = dialogflow.types.Intent(
display_name=display_name,
training_phrases=training_phrases_parts,
messages=response,
input_context_names=input_contexts,
output_contexts = output_context_list,
parent_followup_intent_name=root_intent,
)
intents_client.create_intent(parent, intent)
EDIT:
As requested, here's 2nd and easier way of doing this without any prograpping knowledge.
Suppose your agent looks like below screenshot before, and you want
to group intents under how to solve intent
Go to Setting -> Export and Import -> Export as zip the agent
Once exported, unzip the files and go to intents folder. Your files will look something like below screenshot
Open how to solve.json file and copy the id of this intent
Open all the json files which you want to group under how to solve
intent (note we have to open the files which do not have
_usersays_en as they only contain user utterances
Paste the id of how to solve intent as parentId in these json
files like below screenshot (in this case intent id of how to solve intent was b2131b0e-f86d-429d-957c-65c070ddd5df)
Once all the changes have been made, then zip the directory
Again go to Setting -> Export and Import -> Restore from
zip and select the zip file you have just created
Intent will look like below screenshot once the process is complete
Hope it helps.
#sid8491 - this is absolutely ingenious :)
Thanks for that! Works like a charm and I can confirm that this is just a visual representation. No need to worry about changing your code.
Just a small addition: When you already have follow-up intents, they already carry
"id": "70a48f63-662b-48d4-9a78-dd0af3e0db87",
"parentId": "5a1b5861-fadc-480e-b03b-11bc034df8b9",
"rootParentId": "6c9cb1d6-3efb-4bac-b768-ae3265faa7b6",
Make sure to adjust rootParentId to the aforementioned id of the root-intent, leave parentId intact and you're all set. Didn't try with a follow-up/follow-up/follow-up etc. structure but I'd say it will follow the same pattern somehow.

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.

Is it possible to trigger an intent based on the response(from webhook) of another intent?

I have an intent named "intent.address" with the action name "address_action" and the training phrase "My address". When this intent is triggered, response comes from my webhook saying "Alright! your address is USER_ADDRESS".
Used app.ask() here
What I want is, when this response comes from the webhook then another intent named "intent.conversation" (event name "actions_intent_CONFIRMATION")(here also webhook enabled) gets triggered, which will ask for user confirmation to continue or not?
For example :
Alright your address is USER_ADDRESS
then next
Do you want ask address/directions again?
Intents do not reflect what your webhook says, it reflects what the user says. Intents are what the user intends to do - what they want.
So no, you can't just trigger another Intent this way. There are a few ways to do what you're asking, however.
Using the Confirmation helper with the actions-on-google v1 node.js library
If you really want to use the Confirmation helper, you need to send back JSON, since the node.js v1 library doesn't support sending this helper directly. Your response will need to look something like:
{
"data": {
"google": {
"expectUserResponse": true,
"systemIntent": {
"intent": "actions.intent.CONFIRMATION",
"data": {
"#type": "type.googleapis.com/google.actions.v2.ConfirmationValueSpec",
"dialogSpec": {
"requestConfirmationText": "Please confirm your order."
}
}
}
}
}
}
If you're not already doing JSON in your responses, then you probably don't want to go this route.
Using the Confirmation helper with the actions-on-google v2 node.js library
If you've already switched to v2 of this library, then you have the ability to send a Confirmation with something like this
app.intent('ask_for_confirmation_detail', (conv) => {
conv.ask("Here is some information.");
conv.ask(new Confirmation('Can you confirm?'));
});
Or you can just use the Dialogflow way of doing this
In this scenario - you don't use the Confirmation helper at all, since it is pretty messy.
Instead, you include your question as part of the response, and you add two Followup Intents - one to handle what to do if they say "yes", and one if they say "no".

Resources