LoginSignup
5
3

More than 3 years have passed since last update.

60分で組み立てたQnA MakerベースのチャットボットにLUISと連携した乗換案内機能を追加する

Posted at

今回は、前々回の記事で60分で作成したチャットボットに
乗換案内機能を追加します。

以下はやりたいことのイメージです

image.png

今あるQ&A機能をそのまま残して
Q&A回答もできるし、ユーザーの言い回しを理解して、乗換案内も可能なチャットボットを実現します。

①LUISのアカウント作成

1.以下のURLにアクセスして、LUISのアカウントを作成しましょう
https://www.luis.ai/user/tou

サブスクリプション、リージョン等の必要情報を入力していきます
最後にConfilmを選択します

image.png

2.Confilmすると簡単なチュートリアルが表示されるので確認したら、Create a LUIS app nowを選択
image.png

3.LUIS Appの一覧画面が出てくるので、Create new appを選択します
image.png

②LUIS Appの作成

1.LUISを活用してやりたいことを下記に整理します

ユーザーの発話を理解し、発話の意図が乗換案内であれば
出発地から目的地までの乗換案内を行います。

概念として
この際の意図をインテント
出発地、目的地に当てはまる単語をエンティティと呼びます

LUIS Appを作成するにあたりこのインテントとエンティティを定義していきます

image.png

2.必要情報を入力して、Appを作成します
重要なのはDescriptionにあとで何かわかるような説明を書いておくこと
image.png

3.出発地点、目的地点である駅名を理解するためのエンティティを作成します
image.png

4.List型のエンティティを作成します
image.png

5.json形式でリストを一括アップロードする場合のテンプレートを確認
json形式で一括アップロードする場合はテンプレートを把握しておく必要があります
image.png

6.本質と逸れるので詳細な手順は省きますが、csv形式の駅名一覧をWebから入手して、jsonに変換して活用します
image.png

7.作成した駅名リストをアップロードします
image.png

8.駅名にロールを設定します
ユーザーの言い回しによりエンティティが出発点になるのか、終着点になるのか役割を与えるためにインテントにエンティティを設定する際に活用します
image.png

9.意図を理解するためのインテントを作成します
image.png

10.インテント名を設定します
今回は乗換案内なのでtransfer
image.png

11.想定するユーザーの言い回しでインテントを設定していきます
image.png

12.想定する言い回しをある程度網羅して、fromとtoを設定したら完成
image.png

13.トレーニングします
image.png

14.テストします
期待通りの理解をしているかの確認をします。
インテントがtransferでエンティティのfrom/toを理解できているかどうか確認
image.png

15.Publishします
PublishすることでBot Serviceとの連携準備を行います
image.png
image.png

16.Publishが完了したら、エンドポイント情報を確認します
image.png

17. Starter_Key の Example Queryをコピーします
image.png

③BOTからLUISを呼び出せるようにする

1.Bot Serviceのビルドからオンラインコードエディタを開きます
image.png

※LUISのライブラリを使えば、もっと簡単に実装できるんですが
 今回は説明を簡略化したいがために手実装

<先頭行のほうに事前にコピーしておいたエンドポイントを設定>

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

const { ActivityHandler } = require('botbuilder');
const { QnAMaker } = require('botbuilder-ai');

// ★LUIS用の追加
const request = require(`request`);
const luisurl = "<ここにサンプルクエリ貼り付け>";

<後方行にファンクション追加>

// ★LUIS用に追加したファンクション
// LUISにメッセージを投げ、解釈の結果を取得する
function doRequest(options) {
    return new Promise(function (resolve, reject) {
      request(options, function (error, res, body) {
        if (!error && res.statusCode == 200) {
          resolve(JSON.parse(body));
        } else {
          reject(error);
        }
      });
    });
}
// ★LUIS用に追加したファンクション
// 確信度 80%以下のインテントは意図として扱わない
function getIntent(res) {
    if(res.topScoringIntent.score >= 0.8){
        return res.topScoringIntent.intent;
    } else {
        return 'None';
    }
}

// ★乗換案内用に追加したファンクション
// ルールベースで動作する処理
// 情報が不足していたら質問を返す
function createTransferMassage(res){
    if(res.entities.length == 1){
        if(res.entities[0].role == 'to' || res.entities[0].role == undefined){
            return '乗換案内ですね! 〇〇駅から'+ res.entities[0].entity +'駅に行きたい のように聞いてもらえたらご案内しますよ!!!';
        } else {
            return '乗換案内ですね! '+ res.entities[0].entity +'駅から△△駅に行きたい のように聞いてもらえたらご案内しますよ!!!';
        }
    } else if (res.entities.length == 2){

        // 乗換案内の実装に必要な情報はこの処理に入ったタイミングで揃っているはずなので
        // この処理に実際の乗換案内の為の処理を実装する
        if(res.entities[0].role == 'to'){
            return '乗換案内ですね! '+ res.entities[1].entity +'駅から'+ res.entities[0].entity +'駅に行きたいんですか?';
        } else {
            return '乗換案内ですね! '+ res.entities[0].entity +'駅から'+ res.entities[1].entity +'駅に行きたいんですか?';
        }

    } else {
        return '乗換案内ですね! 〇〇駅から△△駅に行きたい のように聞いてもらえたらご案内しますよ!!!';
    }
}


<既存のonMessage処理にLUISとの連携処理を追加>

        // When a user sends a message, perform a call to the QnA Maker service to retrieve matching Question and Answer pairs.
        this.onMessage(async (context, next) => {
            this.logger.log('Calling QnA Maker');

            // ★LUISとの連携の為に追加した処理 ここから
            const res = await doRequest({url: luisurl+encodeURIComponent(context.activity.text),method: "get"});
            switch (getIntent(res)) {
                case 'transfer':
                    // 乗換案内
            this.logger.log(res);
                    await context.sendActivity(createTransferMassage(res));
                    break;
                case 'None':
            // ★LUISとの連携の為に追加した処理 ここまで

                    const qnaResults = await this.qnaMaker.getAnswers(context);

                    // If an answer was received from QnA Maker, send the answer back to the user.
                    if (qnaResults[0]) {
                        await context.sendActivity(qnaResults[0].answer);
                    // If no answers were returned from QnA Maker, reply with help.
                    } else {
                        await context.sendActivity('本当にごめんなさい!質問が理解できませんでした');
                    }

            // ★LUISとの連携の為に追加した処理 ここから
            }
            // ★LUISとの連携の為に追加した処理 ここまで

            // By calling next() you ensure that the next BotHandler is run.
            await next();
        });

2.ソースコードの変更を保存
あとで変更内容がわかるように修正コメントをしっかり書いておきます
image.png

3.保存したらRunします
image.png

4. 一旦ここで 動作確認します
これでユーザーの意図と出発地、目的地がチャットボットで把握できていることがわかります
image.png

5.乗換案内を実装します

<ファンクションを追加します>


// 乗換案内用に追加したファンクション
// 指定のエンティティを取得する
function getEntityByRole(entities,role){
    return entities.find(item => item.role === role).entity+ '';
}

<乗換案内用のURLを構築する処理を追加します>

乗換案内用のURLはお好みのサービスを使用してください
例ではGoogle Mapの乗換案内を活用します

// 乗換案内のURL
var norikaeurl = "https://www.google.com/maps?saddr=<STATION_FROM>&daddr=<STATION_TO>&ie=UTF8&f=d&sort=def&dirflg=r&hl=ja";

既存のonMessage処理をさらに修正します


        // 乗換案内の実装に必要な情報はこの処理に入ったタイミングで揃っているはずなので
        // この処理に実際の乗換案内の為の処理を実装する

        // ユーザーが希望する出発地点と目的地の情報を取得する
        const stationFrom = getEntityByRole(res.entities,'from');
        const stationTo = getEntityByRole(res.entities,'to');

        // 乗換案内用のURL作成
        norikaeurl = norikaeurl.replace('<STATION_TO>',encodeURIComponent(stationTo ));
        norikaeurl = norikaeurl.replace('<STATION_FROM>',encodeURIComponent(stationFrom));

        // メッセージの作成
        return '乗換案内ですね! ' + stationFrom + 'から' + stationTo + 'へのご案内~\n'+norikaeurl;


6.2と3の手順の保存&Runして、動作確認します
うまく動作していることが確認できます
image.png

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3