I've started learning DialogFlow, I want to know that how can I create an Intent in Fulfillment by code, instead of creating it by GUI.
here is my existing Fulfillment code:
'use strict';
const functions = require('firebase-functions');
const { WebhookClient } = require('dialogflow-fulfillment');
const { Card, Suggestion } = require('dialogflow-fulfillment');
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
function welcome(agent) {
agent.add(`Welcome to my firstagent!`);
}
function fallback(agent) {
agent.add(`I didn't understand!`);
agent.add(`I'm sorry, can you try again??`);
agent.add(`Can you please tell me again??`);
agent.add(`Sorry, I don't understand`);
}
let intentMap = new Map();
//these are the intents that I've already created in dialog flow, But I want to create a new intent in code!
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Fallback Intent', fallback);
agent.handleRequest(intentMap);
});
You cannot create an Intent "in Fulfillment" as the fulfillment webhook is intended to answer to whatever intents are currently set up in your agent, not manipulate them.
You can however manipulate the agent programmatically with the Dialogflow API, which is a normal Google Cloud API that has client libraries in many different programming languages. To create an intent you would want to have a look at the projects.agent.intents.create method.
Dialogflow has a create intent API, so you can create an intent from your Dialogflow fulfillment with the create intent API
User request --> Dialogflow --> fulfillment
| |
-<-<- create intent API
While the other answer correctly points out that the newly created intent cannot be used to respond to the fulfillment request (the agent must be trained before the new intent can be matched by Dialogflow). It can be useful to create a learning or dynamically changing agent over time. For instance if you have a lot of user queries for a particular subject like soccer that are matched to you fallback intent you can programmatically create a new intent with those queries as training phrases and note that soccer queries will be supported soon when you build that feature.
Related
I have one intent "Buy-Car" where I ask some basic information like color, size, region. These are saved in parameter. Then I trigger another intent called "TriggerBot" which will call my backend process to fulfill the order via fulfillment. How do I pass the parameter information from one intent to the other one?
Here I define the parameters in Buy-Car intent:
Here I define the TriggerBot intent:
Here is my code for fulfillment:
function TriggerBot(agent) {
console.log("Parameters: "+JSON.stringify(agent.parameters));
const color = agent.parameters.color;
const size = agent.parameters.size;
const region = agent.parameters.region;
}
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
let intentMap = new Map();
intentMap.set('Default Welcome Intent', Welcome);
intentMap.set('Default Fallback Intent', Fallback);
intentMap.set('Trigger Bot', TriggerBot);
agent.handleRequest(intentMap);
}
Everything is undefined in this case, however if I use just one intent, it works fine. So the value is lost when transferring from one intent to another.
There are 3 main ways to pass parameters between intents in Dialogflow ES
Followup Intents
You can use follow-up intents to automatically set contexts for pairs of intents. A follow-up intent is a child of its associated parent intent.
Followup intents are helpful for keeping track of what happened in the conversation. They also help you pass parameters between intents.
Use session variables
Declare something called a session variable in each of your Dialogflow entry intents.
This has the added benefit of being able to pass parameters between intents even if the conversation takes an unexpected turn.
Use Context
It was described in another stackoverflow thread - How to save data between conversations in Dialogflow?.
In addition I think you could also find some additional information in the below links
Sending parameter value from one intent to another and assign it to parameter
3 ways to pass parameters between intents in Dialogflow ES
DialogFlow- Invoke intent without training phrases and saving response
I was able to get it to work using Context based on the answer from PjoterS:
I first set the output context in the "Buy-Car" intent:
Then I get it in the "TriggerBot" input intent:
Then I access the values in the handler:
function TriggerBot(agent) {
const color = agent.contexts[0].parameters.color;
const size = agent.contexts[0].parameters.size;
const region = agent.contexts[0].parameters.region;
}
I am trying to make my VUI repeat its last sentence when prompted for this (e.g. when user says 'I'm sorry, I did not hear you). I have tried to do this using the libraries multivocal and VoiceRepeater but this is not working for me so I want to implement it according to this guide: https://developers.google.com/assistant/conversational/tips
I have already taken the following steps:
Created an intent called 'Repeat'.
Added training phrases for the intent
Enabled webhook call for this intent
In Node.js added the following code:
// See https://github.com/dialogflow/dialogflow-fulfillment-nodejs
// for Dialogflow fulfillment library docs, samples, and to report issues
'use strict';
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
function repeat(agent) {
const REPEAT_PREFIX = [
'Sorry, Ik zei ',
'Laat me het herhalen: ',
'Wat ik zei is'
];
const reply = (agent, inputPrompt, noInputPrompts) => {
agent.data.lastPrompt = inputPrompt;
agent.data.lastNoInputPrompts = noInputPrompts;
agent.ask(inputPrompt, noInputPrompts);
};
// Intent handlers
const normalIntent = (agent) => {
reply(agent, 'Hey this is a question', 'Ik zie niks');
};
let repeatPrefix = promptFetch.getRepeatPrefix(); // randomly chooses from REPEAT_PREFIX
// Move SSML start tags over
if (agent.data.lastPrompt.startsWith(promptFetch.getSSMLPrefix())) {
agent.data.lastPrompt =
agent.data.lastPrompt.slice(promptFetch.getSSMLPrefix().length);
repeatPrefix = promptFetch.getSSMLPrefix() + repeatPrefix;
}
agent.add(repeatPrefix + agent.data.lastPrompt,
agent.data.lastNoInputPrompts);
}
// Run the proper function handler based on the matched Dialogflow intent name
let intentMap = new Map();
intentMap.set('Repeat', repeat);
agent.handleRequest(intentMap);
});
Unfortunately, this does not work for me, one error I get it that it says 'FetchPrompt is not defined', which I don't understand. I know the setup is okay because this code does return: 'this is a response from the webhook' if I prompt the VUI for repeating its sentence:
// See https://github.com/dialogflow/dialogflow-fulfillment-nodejs
// for Dialogflow fulfillment library docs, samples, and to report issues
'use strict';
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
function repeat(agent) {
agent.add('this is a response from the webhook');
}
// Run the proper function handler based on the matched Dialogflow intent name
let intentMap = new Map();
intentMap.set('Repeat', repeat);
agent.handleRequest(intentMap);
});
As #Prisoner points out, your code is intended to work only with dialogflow-fulfillment but you based it on an actions-on-google example. That's the cause of the code errors.
Some things you want to do are only available for Actions on Google, for example, the object conv contains functionality that only works when executed within Google Assistant, and this is the core of the problem.
Even if you fix the code, the example you're following uses conv.data as a temporary storage for the last reply of the agent during the conversation; however conv.data functionality is not availabe outside Google Assistant platforms, and, at the moment, Dialogflow doesn't have a straightforward approach to get the agent last response.
If you don't want to integrate with Google Assistant, then you'd need to find a suitable workaround to store your agent last reply. Here's a question where this issue with the temporary storage was discussed, maybe you can use it as a reference.
For your use case, I think you could get away with it by using contexts to store the last replies.
I'm developing a chat bot for Telegram using DialogFlow, but I can't go through two topics, and I can't find the documentation for them.
The flow of the conversation, is the user answer some closed questions and send an image.
How do I get this image?
And to save her along with the other answers?
The answers need to be saved as a form/survey and not as a conversation history.
I have a similar setup in my chatbot. I store the answers in a Firebase database.
In order to interact with the Firestore Database you should implement a Fulfillment
You can see a guide on how to implement Firebase for DialogFlow here
Here you can see a sample of my code. In general lines after setting up the connection to the Firebase database you just want to map your intents to your functions using intentMap.set.
As you said you are using closed answers you can set intets to handle the responses and each "final" intent will trigger a different function that will write a different message to the db.
To write the response to the Firesbase database you just only need to implement admin.database().ref().push().set({}) with the information and the desired structure.
In my example I also store the conversation Id from the chat payload and the date.
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
//const DialogflowApp = require('actions-on-google').DialogflowApp;
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
admin.initializeApp({
credential : admin.credential.applicationDefault(),
databaseURL: 'ws://YOURDATABASE.firebaseio.com/'
});
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
var userId;
let conv = agent.conv();
const ROOTREF = admin.database().ref();
const actions = new Map();
let intentMap = new Map();
intentMap.set('Default Fallback Intent', fallback);
intentMap.set('NoTunel', handleWriteToDbNoTunnel(agent));
agent.handleRequest(intentMap);
function assignConv(agent){
userId = agent.parameters.UserId;
return admin.database().ref('Users/'+ userId).set({
Apellido:"XXXX",
Nombre:"XXXX",
chatId:333,
});}
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
var token = "YOUR TOKEN HERE";
var url = "https://api.telegram.org/bot"+ token;
function handleWriteToDbNoTunnel(agent){
const Dia = new Date();
if(matricula !== "")
return admin.database().ref('Limpieza/').push().set({
chatId: request.body.queryResult.outputContexts[3].parameters.telegram_chat_id+'"',
Field1: answer1,
Field2: answer2,
day: day.getTime()
});
}
});
Also if you want to store images with the user responses you can implement the getfile method from the telegram api and store the image code or the image itself
I am adding this answer to slightly improve on Chris32's answer.
There is a better way to get the value of the Telegram Chat ID as I am using it in a personal project.
I will go end to end to explain my approach.
I have mapped some files to some specific intents. In my intent-mapper.js file, I have mapped Default Welcome Intent to welcome.js file as prescribed in the documentation for the Dialogflow Fufillment library for NodeJS (Please note that the library is deprecated and not being updated, personally I am using a fork of the repo that I have worked on personally).
const intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
.
.
Then, in welcome.js,
const globalParameters = {
'name': 'global-parameters',
'lifespan': 9999,
'parameters': {}
};
globalParameters.parameters.telegramChatId = agent.originalRequest?.payload?.data?.chat?.id || -1;
.
.
agent.setContext(globalParameters);
The telegramChatId variable in the global parameters context will save the value for the chat ID which can be passed to a helper function to send a message. In order to to retrieve the value from the global parameters, the code snippet is this.
const globalParameters = agent.getContext('global-parameters');
const telegramChatId = globalParameters.parameters.telegramChatId;
Then the Telegram message helper function is largely the same as in Chris32's answer. The message can be any string and chatId can be passed as an argument to the following helper function.
const TelegramBot = require('node-telegram-bot-api');
const { telegramBotToken } = process.env.TELEGRAM_BOT_TOKEN;
const bot = new TelegramBot(telegramBotToken, { polling: false });
const sendTelegramTextMessage = (message, chatId) => {
try {
bot.sendMessage(chatId, message, {parse_mode: 'html'});
} catch (err) {
console.log('Something went wrong when trying to send a Telegram notification', err);//remove console.log()
}
};
The reason I have put this all in a context since in my use case I am sending push notifications via Telegram once the user asks for it (this happens later in the conversation flow), so I have implemented it this way. The main point to note is that the agent object already has the detectIntentRequest variable saved inside it which in turn has the value we need as a part of its payload. Here's a snippet of the same.
Please note I have removed many lines from my code for brevity, but in a nutshell, the chat ID can be accessed from
agent.originalRequest?.payload?.data?.chat?.id
And the value for the telegram bot token is a secret value which can be saved in an environment variable or Secrets Manager. Please note my answer explains a better way to retrieve the chat ID without needing to refer directly to the request object since Dialogflow Fulfillment library already caches the value in the body for us. The other stuff for receiving and sending images is explained in the main answer.
I have a dialogflow agent with 3 intents:
Default Welcome Intent
Default Fallback Intent
My Intent
My intent has a firebase web hook for fulfillment. When I enter a conversation with my chatbot, I get Default Welcome Intent. But as soon, as I ask a question that is handled by My Intent I seem stuck in My Intent even if i say something like hello after that. How do I exit out of my fulfillment and clear the state so that the Default Fallback and Default Welcome start working again? A lot of the fulfillment samples I've seen, handle these other intents within the custom fulfillment which seems wrong when you have these defaults already configured. Another example, is I enabled SmallTalk and after asking a question that's filled by My Intent, I then ask 'how old are you', this intent fires the agent.smalltalk.age against My Intent which makes no sense really.
Fulfillment code:
var https = require ('https');
const functions = require('firebase-functions');
const DialogFlowApp = require('actions-on-google').DialogFlowApp;
const WELCOME_INTENT= 'Default Welcome Intent';
const FALLBACK_INTENT = 'Default Fallback Intent';
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
let action = request.body.queryResult.action;
response.setHeader('Content-Type','applicaiton/json');
const parameters = request.body.queryResult.parameters;
console.log("action: " + action)
if (action === 'input.wifi'){
getClientCount(parameters['geo-city'], response);
}
});
function getClientCount(location, CloudFnResponse) {
var chat = "sample text";
CloudFnResponse.send(buildChatResponse(chat));
return;
});
});
}
function buildChatResponse(chat) {
return JSON.stringify({"fulfillmentText": chat});
}
Default Fallback Intent:
Default Welcome Intent:
My Intent:
In the last screenshot please ignore the 'end of conversation' checkbox I was trying it out to see how it changed the behavior when I was taking the screenshot.
Turning off smalltalk solved the issue and exporting all the Smalltalk intents from the prebuilt agent and importing them into my Agent got smalltalk working.
I know this is in Beta, but I've set up a Knowledge base for my agent and the intent doesn't seem to get recognized.
When setting up the Knowledge base, the "try it" test works and retrieves successfully, but when trying the same request from the simple chat bot, the intent is not recognized. What else is necessary to hook the Knowledge feature to the agent?
What is the medium of that simple chat bot you are using?Is it android/web?
Assuming are using dialogflow v2 node.js library,We have to pass the full path of the knowledgeBase in the queryParams inside the detectIntent function request object.Then only dialogflow will look into knowledgeBase for matching the user input to the Knowledge base Intents.
Sample of request object-
// const projectId = 'ID of GCP project associated with your Dialogflow agent';
// const sessionId = `user specific ID of session, e.g. 12345`;
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
let request = {
session: sessionPath,
queryInput: {
text: {
text: 'hi,how are you?',
languageCode: 'en-US',
},
},
queryParams: {
knowledgeBaseNames:['projects/stockmarket-XXXX/knowledgeBases/XXXXXXXXXXXXXXx'] //Paste your knowledge base path,Check this out from the diagnostic info
}
};
checkout https://github.com/googleapis/nodejs-dialogflow/blob/master/samples/detect.v2beta1.js#L438
Let me know if you have any questions :)