Bot Framework Composer でボット開発 : その 2 ダイアログとトリガー

[前回](https://qiita.com/kenakamu/items/caad3629629513646c9b) は Bot Framework Composer のインストールを行いまいした。今回から実際のボットを作っていきます。
この記事ではボットの中心機能となるダイアログとトリガーを紹介します。

# ダイアログ

QnA のような一問一答形式の場合、ボットはユーザーとの会話を記憶する必要はありませんが、複数回のやり取りを行う場合、どこまでやり取りしたか、またユーザーの回答が何であったかなどをコンテキストとして記憶しておく必要があります。

ダイアログはこのような会話の単位であり、簡単なボットであれば数個のダイアログ、複雑なボットであれば数十のダイアログから構成されます。

ダイアログの実行には以下の要素が必要です。

- ユーザー入力の理解
- トリガー
- アクションの実行
- ユーザー入力の保存
- 応答の作成

## ユーザー入力の理解

ユーザーがボットに話しかけた際、どのダイアログを実行するべきか判断する必要があります。Bot Framework Composer では数種類の方法をサポートしていますが、今回は単純なキーワードを使います。より高度な方法は以降の記事で紹介します。

## トリガー

ダイアログを実行するきっかけとなるイベントをトリガーと呼び、ユーザー入力以外にも、ユーザーがボットを会話に追加した時や会話から退出した時、他のダイアログから呼び出しされた時などのシステムイベントがあります。

- インテントトリガー: 自然言語解析を設定した場合に、ユーザーの意図解析の結果でトリガーする
- ダイアログイベント: ダイアログの開始やキャンセル、エラーなどのイベントでトリガーする
- アクティビティ: 会話の開始や終了、タイピング中などのシステムイベントでトリガーする
- メッセージ: メッセージが届いた時や更新された時などのイベントでトリガーする
- カスタムイベント: 独自に生成したイベントでトリガーする


## アクションの実行

ユーザーに質問をしたり、HTTP リクエストを実行したりと、ダイアログの中で実行するアクションを設定します。Bot Framework Composer では非常に多くのアクションをサポートしますが、詳細は以降の記事で紹介し、今回はシンプルなアクションのみ使います。

## ユーザー入力の保存

ユーザーに名前を聞いたり、年齢を聞いた場合、その結果を保存しておく必要があります。また状況に応じて保存した値を削除するなどの操作もサポートされます。

## 応答の作成

言語生成の機能を使い、動的な回答を作成したり、複数パターンから応答をランダムに返すことができる他、テキスト以外のリッチなカードを返すこともできます。今回はシンプルにテキスト応答のみ使います。

# ボットのメモリー

ボットが会話を記憶する際、以下 2 つのオブジェクトにデータを格納します。

- turn: ユーザーからのメッセージが来て、ボットが応答するまでの一度の会話のデータ
- dialog: アクティブなダイアログとその親や子ダイアログのデータ

また以下 2 つのスコープがあります。

- user: ユーザー単位の情報
- conversation: 特定の会話単位の情報で会話が終了すると削除される

# 既存のボットから確認する
[前回](https://qiita.com/kenakamu/items/caad3629629513646c9b)作成したオウム返しボットを見ていきます。

## メインダイアログ

ボットを作成すると、指定した名前でメインのダイアログが作成されます。また既定で以下 2 つのトリガーが作成されます。

- Unknown intent: インテントトリガーの一種で、意図が不明な場合にトリガーする
- Greeting: アクティビティトリガーの一種で、ユーザーが会話に参加したなどのタイミングでトリガーする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/e2f0ca07-0466-17fc-6779-8d99bdcfdf19.png)

**Unknown intent**
前回の記事で内容を確認した通り、ユーザーにオウム返しを行っています。

**Greeting**
Conversation Update はユーザーかボットの追加または会話からの退出でトリガーされます。ボットからの情報は **turn** というオブジェクトに格納されており、ここでは ```turn.Activity.members.Added``` で参加したメンバーのリストを取得しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/6a5d3736-0d73-4a0a-8661-3b664922d4df.png)

foreach ループを使った場合、それぞれの値は ```dialog.foreach.value``` に含まれるため、MemberAdded のユーザーと、メッセージの宛先 (Recepient) が同じか if/else で確認しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/96ec8c46-f6c1-0619-ce40-e0376fd24a0c.png)

では実際のデータの中身を見てみましょう。

1\. Greeting の直後の「＋」をクリックして、「Send a resposne」を追加。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/04e4c7e5-395e-2092-090a-5b8739dc805a.png)

2\. 中身に ```- '${turn}'``` を指定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/8bbb155f-2a23-e992-d480-0612fd3ac1c2.png)

3\.「Test in Emulator」をクリックしてエミュレーターで検証。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/6819eea6-adeb-92e5-ae96-a874eebbd6c5.png)

4\. 内容が確認できる。以下に turn.Activity.membersAdded と主なプロパティだけを抜粋。2 つのアカウント追加されたことが分かる。一方は "Bot" でもう一方は "User" だがこれらはあくまで表示名。

```json
{
    "activity": {
        "type": "conversationUpdate",
        ...
        "recipient": {
            "id": "1ee85b90-6df9-11ea-8a1e-3be055026012",
            "name": "Bot",
            "role": "bot"
        },
        "membersAdded": [
            {
                "id": "1ee85b90-6df9-11ea-8a1e-3be055026012",
                "name": "Bot"
            },
            {
                "id": "e013a91c-c374-4232-bf64-fbb46de88cd7",
                "name": "User"
            }
        ],
        "membersRemoved": [],
        ...
        }
    }
}
```
## Greeting の変更

現状 Greeting ではなにも処理されないため、内容を変更しましょう。

1\. 追加した「Send a response」を削除。True ブランチに「Send a response」を追加。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/e3851a6f-3844-7ee3-821d-ca16af984040.png)

2\. 内容を ```- '${dialog.foreach.value.name}' さん、こんにちは。``` に変更。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/fc6cace2-40ce-3e63-aa04-83c9afe909fe.png)

3\.「Restart Bot」実行後、エミュレーターで動作を確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/fb686ae5-6440-0390-6706-3e99625a2d72.png)

# ダイアログを追加

最後にダイアログを追加してみましょう。

1\.「New Dialog」をクリックして、ダイアログを追加。名前を「Time」にして次へ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/a0960355-bf61-e22e-abc7-ac9600a5ca03.png)

2\. 作成した「Time」ダイアログを選択。「Begin Dialog」トリガーが追加されていることを確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/4ffcbeb4-e594-1761-fe14-10dd427eed9f.png)

3\.　Begin Dialog トリガーを選択し、「Send a response」を追加。内容に ```- 今の時間は '${addHours(utcNow() ,9)}'です。``` と入力。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/20e7a1b8-de35-07b3-9d25-d43e6cbbbdca.png)

4\. メインダイアログより「Unknown intent」を選択。アクションの追加より「Begin a new dialog」を選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/442dc7c1-8d00-c59d-9443-6d2805be5eba.png)

5\. Time ダイアログを選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/962ecb04-aee1-498a-7964-d9e80e03b4fb.png)

6\.「Restart Bot」クリック後、エミュレーターで検証。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/92fb0c60-246f-1553-6d09-c703af3a03a6.png)

# まとめ

今回はダイアログとトリガーについて、簡単に見ていきました。次回はユーザーインプットの理解について見ていきます。

[次の記事へ](https://qiita.com/kenakamu/items/11bae087ca93f5a6d997)
[目次へ戻る](https://qiita.com/kenakamu/items/6dc043cfc1f199032883#bot-framework-composer)