Search code examples
node.jsbotframeworkazure-language-understandingazure-cognitive-searchazure-qna-maker

LUIS is not recognizing the intent when I try the same input second time


I am using LUIS for azure search and QnA recognizer for the first time together in the same code.

I am using intent.match to match the recognizers.

My problem is:

for the first time, If I ask a question which matches to the QnA intent, it returns the answer from the QnA base.

followed by the questions which are matching with the azuresearch intents. Azure search also deliver the result.

But if I repeat the questions which must be matched to the QnA, it says "no intent handler found for null"

var util = require('util');
var _ = require('lodash');
var builder = require('botbuilder');
var restify = require('restify');
var cognitiveservices = require('botbuilder-cognitiveservices');
/// <reference path="../SearchDialogLibrary/index.d.ts" />
var SearchLibrary = require('../SearchDialogLibrary');
var AzureSearch = require('../SearchProviders/azure-search');

var qintent;
// Setup Restify Server
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
    console.log('%s listening to %s', server.name, server.url);
});

// Create chat bot and listen for messages
var connector = new builder.ChatConnector({
    appId: process.env.MICROSOFT_APP_ID,
    appPassword: process.env.MICROSOFT_APP_PASSWORD

});
server.post('/api/messages', connector.listen());
// Bot Storage: Here we register the state storage for your bot. 
// Default store: volatile in-memory store - Only for prototyping!
// We provide adapters for Azure Table, CosmosDb, SQL Azure, or you can implement your own!
// For samples and documentation, see: https://github.com/Microsoft/BotBuilder-Azure
var inMemoryStorage = new builder.MemoryBotStorage();

var qnarecognizer = new cognitiveservices.QnAMakerRecognizer({
    knowledgeBaseId: '6b30ac2a-5ce9-4576-a1cb-c89abfb73889',
    authKey: 'd457c897-71cf-46bf-a3d9-9a1f51246fad',
    endpointHostName: 'https://qnadispatchwg.azurewebsites.net/qnamaker'
    });

const LuisModelUrl = 'https://westeurope.api.cognitive.microsoft.com/luis/v2.0/apps/180a9aaa-9d67-4d40-b3d3-121917f4dbb8?subscription-key=39155bb750dc4b2abd84d410d80fce21&timezoneOffset=0&q=';
var recognizer = new builder.LuisRecognizer(LuisModelUrl);
// Bot with main dialog that triggers search and display its results
var bot  = new builder.UniversalBot(connector);
bot.set('storage', inMemoryStorage);

var intents = new builder.IntentDialog({ recognizers: [recognizer, qnarecognizer] });
bot.dialog('/', intents);

intents.matches('Greeting',  (session) => {
    session.send('You reached the Greeting intent. You said \'%s\'.', session.message.text);
    session.endDialog();
});

intents.matches('qna', [
    function (session, args) {
        console.log("my args in qna:=========================================================================================== %o", args);
        var answerEntity = builder.EntityRecognizer.findEntity(args.entities, 'answer');
        session.send(answerEntity.entity);
    }
]);

intents.matches('Search.Aco', [
    function (session, args, next) {
        console.log("my args in SearchDialog:=========================================================================================== %o", args);
        var intent = args.intent;
        console.log("our intent is " + intent);
        var entities = builder.EntityRecognizer.findEntity(args.entities, 'businessterm');
        console.log("recognnized entitiy is: "+ entities.entity);
       // qintent = title;
        //session.send('You reached the Search.Aco intent. You enquire for the entitiy \'%s\'.', qintent);
        //console.log(" SearchDialog: before updating the  session.message.text------------->" + session.message.text);
        session.message.text= entities.entity;
        //console.log(" SearchDialog: after updating the  session.message.text------------->" + session.message.text);
        SearchLibrary.begin(session);
    },

    function (session, args, results) {
        // Process selected search results
        session.send(
            'Done! For future reference, you selected these properties: %s',
            args.selection.map(function (i) { return i.key; }).join(', '));
    }
]);

var azureSearchClient = AzureSearch.create('aco-intel2', '4105C6676D0CDD9B2E7891952B9E9E00', 'azureblob-index');
var jobsResultsMapper = SearchLibrary.defaultResultsMapper(jobToSearchHit);

// Register Search Dialogs Library with bot
bot.library(SearchLibrary.create({
    multipleSelection: true,
    search: function (query) { return azureSearchClient.search(query).then(jobsResultsMapper); },
    refiners: ['people', 'content', 'location']
}));

// Maps the AzureSearch Job Document into a SearchHit that the Search Library can use
function jobToSearchHit(acosearch) {
    console.log("inside jobToSearchHit");
    console.log("inside acosearch.DocUrl" + acosearch.DocUrl + "-------" + acosearch.metadata_storage_name);
    return {
        key: acosearch.id,
        title: acosearch.metadata_storage_name,
        description: acosearch.content.substring(0, 100)+"...",
        documenturl:acosearch.DocUrl,
        imageUrl: acosearch.imageurl
    };
}

module.exports = { qintent:  "qintent"};

for the second time if I am mixing the This is how it is showing:

Emulator output

can you please help me to understand what is the difference between the intent.match and dialog' matches. I tried the following way too but it does not recognize the answering entity. How can be QnA be called from the following code?

var util = require('util');
var _ = require('lodash');
var builder = require('botbuilder');
var restify = require('restify');
var cognitiveservices = require('botbuilder-cognitiveservices');
/// <reference path="../SearchDialogLibrary/index.d.ts" />
var SearchLibrary = require('../SearchDialogLibrary');
var AzureSearch = require('../SearchProviders/azure-search');

var qintent;
// Setup Restify Server
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
    console.log('%s listening to %s', server.name, server.url);
});

// Create chat bot and listen for messages
var connector = new builder.ChatConnector({
    appId: process.env.MICROSOFT_APP_ID,
    appPassword: process.env.MICROSOFT_APP_PASSWORD

});
server.post('/api/messages', connector.listen());
// Bot Storage: Here we register the state storage for your bot. 
// Default store: volatile in-memory store - Only for prototyping!
// We provide adapters for Azure Table, CosmosDb, SQL Azure, or you can implement your own!
// For samples and documentation, see: https://github.com/Microsoft/BotBuilder-Azure
var inMemoryStorage = new builder.MemoryBotStorage();

var qnarecognizer = new cognitiveservices.QnAMakerRecognizer({
    knowledgeBaseId: '6b30ac2a-5ce9-4576-a1cb-c89abfb73889',
    authKey: 'd457c897-71cf-46bf-a3d9-9a1f51246fad',
    endpointHostName: 'https://qnadispatchwg.azurewebsites.net/qnamaker'
    });

const LuisModelUrl = 'https://westeurope.api.cognitive.microsoft.com/luis/v2.0/apps/180a9aaa-9d67-4d40-b3d3-121917f4dbb8?subscription-key=39155bb750dc4b2abd84d410d80fce21&timezoneOffset=0&q=';
var recognizer = new builder.LuisRecognizer(LuisModelUrl);
// Bot with main dialog that triggers search and display its results
var bot = new builder.UniversalBot(connector, function (session, args) {
    session.send('You reached the default message handler. You said \'%s\'.', session.message.text);
}).set('storage', inMemoryStorage);

bot.recognizer(recognizer, qnarecognizer);

bot.dialog('GreetingDialog',
    (session) => {
        session.send('You reached the Greeting intent. You said \'%s\'.', session.message.text);
        session.endDialog();
    }
).triggerAction({
    matches: 'Greeting'
})

bot.dialog('HelpDialog',
    (session) => {
        session.send('You reached the Help intent. You said \'%s\'.', session.message.text);
        session.endDialog();
    }
).triggerAction({
    matches: 'Help'
})

bot.dialog('CancelDialog',
    (session) => {
        session.send('You reached the Cancel intent. You said \'%s\'.', session.message.text);
        session.endDialog();
    }
).triggerAction({
    matches: 'Cancel'
})

bot.dialog('QnADialog',[
    function (session, args, next) {
        var answerEntity = builder.EntityRecognizer.findEntity(args.entities, 'answer');
        console.log(answerEntity);
        session.send(answerEntity.entity);
    }
]).triggerAction({
    matches: 'Search.QnA'
})

bot.dialog('SearchDialog', [
    function (session, args) {
        console.log("my args in SearchDialog:=========================================================================================== %o", args);
        var intent = args.intent;
        title = builder.EntityRecognizer.findEntity(intent.entities, 'businessterm');
        console.log(title.entity);
        qintent = title.entity;
        //session.send('You reached the Search.Aco intent. You enquire for the entitiy \'%s\'.', qintent);
        //console.log(" SearchDialog: before updating the  session.message.text------------->" + session.message.text);
        session.message.text= qintent;
        //console.log(" SearchDialog: after updating the  session.message.text------------->" + session.message.text);
        SearchLibrary.begin(session);
    },

    function (session, args, results) {
        // Process selected search results
        session.send(
            'Done! For future reference, you selected these properties: %s',
            args.selection.map(function (i) { return i.key; }).join(', '));
    }
]).triggerAction({
    matches: 'Search.Aco'
});

var azureSearchClient = AzureSearch.create('aco-intel2', '4105C6676D0CDD9B2E7891952B9E9E00', 'azureblob-index');
var jobsResultsMapper = SearchLibrary.defaultResultsMapper(jobToSearchHit);

// Register Search Dialogs Library with bot
bot.library(SearchLibrary.create({
    multipleSelection: true,
    search: function (query) { return azureSearchClient.search(query).then(jobsResultsMapper); },
    refiners: ['people', 'content', 'location']
}));

// Maps the AzureSearch Job Document into a SearchHit that the Search Library can use
function jobToSearchHit(acosearch) {
    console.log("inside jobToSearchHit");
    console.log("inside acosearch.DocUrl" + acosearch.DocUrl + "-------" + acosearch.metadata_storage_name);
    return {
        key: acosearch.id,
        title: acosearch.metadata_storage_name,
        description: acosearch.content.substring(0, 100)+"...",
        documenturl:acosearch.DocUrl,
        imageUrl: acosearch.imageurl
    };
}

module.exports = { qintent:  "qintent"};

This code gives me traces like following: logs

Please help me understand whats wrong in the above codes. Would be a great help. Also, the difference between the intent.match and dialogs match. As per my understanding, bot recognizes the session.message and match it with the 'match:' argurments and call the dialogs. so it can jump to and fro between dialogs.

in the first case, its strange for me because it doesnot do it second time.

Thanks in Advance, Vivek


Solution

  • Using the IntentDialog class (e.g. var intents = new builder.IntentDialog( ... ) does not allow for intent interruption in the way that registering the dialog directly to the bot does.

    Given the following example dialogs:

    const bot = new UniversalBot(connector); // Assume an already created ChatConnector
    bot.recognizer(new LuisRecognizer(LuisModelUrl); // LUIS model with HowAreYou and Weather intents.
    
    bot.dialog('HowAreYou',[
      function (session, args, next) {
        builder.Prompts.text(session, `I\'m doing great! How are you?`);
      },
      function (session, args) {
        var result = session.message.text;
        session.send(`I\'m glad to hear you\'re doing ${result}!`);
    ]).triggerAction({
      matches: 'HowAreYou'
    });
    
    bot.dialog('Weather', [
      function (session, args) {
        builder.Prompts.text(session `Which city do you want to find the weather forecast for?`);
      },
      function (session, args) {
        // some logic to handle the response and call a weather api
      }
      ]).triggerAction({
        matches: 'Weather'
      });
    

    When a user asks "How are you?" the 'HowAreYou' intent is detected by LUIS and the bot begins the corresponding dialog. The bot then prompts the user "I'm doing great! How are you?'. If the user then says to the bot, "Show me the weather", this bot will receive that message and detect the "Weather" intent. After doing so, it will add the "Weather" dialog to the top of the dialog stack and begin the Weather dialog.

    The UniversalBot doesn't prevent you from switching dialogs when a new intent is detected. In the code for the intent dialog, it continues any dialog you were already in and doesn't support switching to a new intent/dialog.