【Azure】QnA MakerからBot Serviceを作る


# 前のステップ
[QnA Makerを作る](https://qiita.com/vicugna-pacos/items/ae23905d02e48bb48c26)

# Bot Serviceを作る
作成したナレッジベースを元にBot Serviceを作ると、TeamsやWebサイトでチャットボットを利用できるようになる。

[QnA ボット - Azure Bot Service - QnA Maker - Azure Cognitive Services | Microsoft Docs](https://docs.microsoft.com/ja-jp/azure/cognitive-services/qnamaker/tutorials/create-qna-bot)

# チャットボットの挙動をカスタマイズする
チャットボットの挙動、例えば初めの挨拶や回答の返し方など、をカスタマイズする場合は、Bot Service作成時に一緒に出来上がったApp Serviceのソースコードをカスタマイズする。

前提条件：

*  SDKではNode.jsを選択した。botbuilder v4
*  PCにNode.js、Visual Studio Codeがインストールされている。

## Gitでソースコードを取得する
QnA MakerからBot Serviceを作成した場合、Bot ServiceのApp Serviceは既にボットとして利用できる状態になっている。
カスタマイズしたいときは、配置済みのApp Serviceから一旦ソースを取得する。
本稿には、ローカルGitと連携する方法を載せる。

[ローカル Git リポジトリからデプロイする - Azure App Service | Microsoft Docs](https://docs.microsoft.com/ja-jp/azure/app-service/deploy-local-git)

上記手順の、クイックスタートでソースとしてローカルGitを指定した後、デプロイセンターから資格情報の設定を行う。
![49457824-f808-a199-c7cf-642a2b3dc0b3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/a668abad-ae3f-16df-57a9-9b00eccef785.png)

「FTP/資格情報」を押す。
![cf64e796-f70b-9bee-5527-9cbd7be6963e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/c9c0656c-7498-7046-f48f-22aaeb4d3131.png)

「ユーザーの資格情報」タブを選び、IDとパスワードを入力する。IDは既定値が設定されている。
![e0595148-4b37-ae93-35c1-db9e2f965873.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/b494b450-2969-6b0e-f772-15689d44ca45.png)

パスワード設定後、App Service 概要ページでGitリポジトリのURLを確認できるので、これをコピーしてリポジトリをクローンする。
![4222fea2-7d49-a0f5-db39-47e28709fb71.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/4d86defe-294b-f160-9ae6-d9107604ada1.png)

## Visual Studio Codeで編集する
クローンしたリポジトリをVisual Studio Codeで開く。  

まずターミナルを開き、必要なモジュール群をダウンロードする。  
ターミナルで「`npm install`」と入力し、実行。
![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/fc41d522-d8c9-1e2c-b9fe-635cb5857856.png)

しばらく待てば、モジュールがダウンロードされる。

ボットの機能は、`bots/qnaBot.js`に書かれている。主にここを編集する。
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/603edb01-f0d1-3ef3-59df-73848bcae1b5.png)

生成されたソースでは、あいさつ文などが英語なので、ひとまず日本語に直す。
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/8517bcb3-c39d-0cfd-a8a0-9183f7d01f33.png)

編集したソースをコミット＆プッシュすれば、Azure上のソースが置き換わる。

Bot Serviceのサンプル集  
[microsoft/BotBuilder-Samples (GitHubにあるMS提供のBot Serviceサンプル集)](https://github.com/microsoft/BotBuilder-Samples)

## マルチターン(follow-up prompt)に対応する

自動的に作られたボット用スクリプトがマルチターンの会話に対応していない場合、スクリプトを改修して対応させる必要がある。
私がBot Serviceを作成した時点ではまだfollow-up promptがPREVIEWの段階だったため、スクリプトも対応していなかったが、2019年11月現在、PREVIEWでは無くなったようなので、Bot Serviceのスクリプトもfollow-up promptに対応しているかもしれない。

### 実装サンプル

まず、新たにjsファイルを作成し、QnA Makerから返ってきた回答にfollow-up promptが付いているか確認し、付いている場合は返答にボタンを追加する処理を作る。

```javascript:qnaResultHelper.js
const { MessageFactory } = require('botbuilder');

/**
 * QnA Maker から返ってきた答えを適切な形にしてクライアントへ返す
 * 
 * @param {string} qnaResult QnA Maker から返ってきたanswer
 * @returns Activity
 */
module.exports.createAnswer = function(context, qnaResult) {

    if (qnaResult.context && qnaResult.context.prompts && qnaResult.context.prompts.length > 0) {
        // answerにfollow-up promptが付いている
        return createPrompt(qnaResult);
    } else {
        return qnaResult.answer;
    }
}

/**
 * follow-up prompt をクライアントへ返す
 * 
 * @param {QnAMakerResult} qnaResult 
 * @returns Activity
 */
function createPrompt(qnaResult) {
    let title = qnaResult.answer;
    let prompts = qnaResult.context.prompts;
    let buttons = [];

    // DisplayOrderで並べ替え
    prompts.sort(function(a, b) {
        if (a.displayOrder == b.displayOrder) {
            return 0;
        } else if (a.displayOrder < b.displayOrder) {
            return -1;
        }
        return 1;
    });

    // follow-up promptのボタンを作る
    for (let prompt of prompts) {
        buttons.push(prompt.displayText);
    }

    return MessageFactory.suggestedActions(buttons, title);
}
```

次に、既存のqnaBot.jsの`onMessage`の処理を修正し、回答を返す部分で、先ほど作ったHelperクラスのメソッドを呼び出すようにする。

```javascript:qnaBot.js
this.onMessage(async (context, next) => {
    this.logger.log('Calling QnA Maker');

    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]) {
        // 自作したHelperのメソッドを呼び出す
        let answer = QnAResultHelper.createAnswer(context, qnaResults[0]);
        await context.sendActivity(answer);

    // If no answers were returned from QnA Maker, reply with help.
    } else {
        await context.sendActivity('お答えできません。他の言い方で質問してみてください。');
    }

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

# ローカルでテストする

## .envファイルの作成
Azureからクローンしたリポジトリには.envファイルが含まれていないため、ローカルでテストする際には追加する必要がある。
置く場所はリポジトリフォルダ直下。

```:.envサンプル
MicrosoftAppId=""
MicrosoftAppPassword=""

QnAKnowledgebaseId="<knowledge-base-id>"
QnAAuthKey="<your-endpoint-key>"
QnAEndpointHostName="<your-hostname>"
```

上記2つは空白で良い。下3つは、Bot ServiceのWeb Appの構成を見ると分かる。
![22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/84c0b48b-b078-86ac-d599-25bafa1a1d45.png)

しかし、テキストを選択してコピーするのが難しい作りになっているので、見ながら打ち込むか、HTMLソースを開いてコピーするとかするしかない。

## Bot Framework Emulatorのインストール
公式サイト
https://github.com/Microsoft/BotFramework-Emulator/blob/master/README.md

ローカルで起動したBot Serviceに対してテストできるツール。テスト先はローカル以外にも、Azureにデプロイ済みのBotも可能。
上記サイトへ行き、最新のインストーラをダウンロード、実行する。

## Visual Studio Codeでデバッグを実行する

Visual Studio Code でBot Serviceのソースを開いた状態の画面で、メニュー→「デバッグ」→「デバッグの開始」を押す。
![14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/7ee041eb-9201-b05f-3886-dc6acfd8d2be.png)

初めてデバッグ実行するときは言語を選ぶダイアログ(？)が表示されるので、「Node.js」を選ぶ。

しばらく待つと、Bot Serviceが起動する。
![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/d9cdcbb5-e580-19b6-4ad6-cd2256930a35.png)

デバッグコンソールに、Bot Serviceのポート番号が表示される。これをBot Emulatorで使用する(画像だと`3978`がポート番号)。

## Bot Service Emulatorから接続する

Bot Service Emulator を起動する。
![16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/3ca3f041-5b0e-c356-de8c-f42c35af4a0d.png)

「Open Bot」を押す。
![17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/9b2d4fef-ed0d-9934-938e-45792fa30aba.png)

「Bot URL」に`http://localhost:3978/api/messages`と入力し、他は空白のまま Connect を押す。このURLのポート番号は、先ほどのデバッグ実行したときに表示されたものに合わせる。
![18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/1da27767-a2cf-3ecf-5b85-516e03393781.png)

デバッグ実行中のボットに接続され、会話のテストができる。
![19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/cac24cfb-0237-0db5-e46e-08f1748a6434.png)

# サンプルコード
GitHubに実際に作ったものを置いた。
[vicugna-pacos/js-azure-qnamaker-bot](https://github.com/vicugna-pacos/js-azure-qnamaker-bot)

# 次のステップ

[Webページにチャットボットを設置する](https://qiita.com/vicugna-pacos/items/f300c532cdd55b90c937)