Chatbotのダイアログ

2017年10月19日(木)
樋口 勝一
連載の3回目となる今回は、Bot Builder SDKが提供するダイアログ機能を用いて、シンプルなChatbotを作成してみる。

Chatbotのダイアログとは

Bot FrameworkのBot Builder SDKは、「ダイアログ(対話)」という機能を提供しています。ダイアログの説明に入る前に、ネットでなにか調べものをする際の手順を考えてみましょう。例えば、週末にキャンプを予定しているので、天気予報のサイトで週末の奥多摩町の天気を知りたいとします。一般的にWebサイトから調べる手順としては、以下のようになるでしょう。

  1. 天気予報のサイトを検索
  2. 検索結果から天気予報サイトを選択
  3. 日本全国の地図から関東地方を選択
  4. 関東地方から東京都を選択
  5. 東京都から奥多摩町を選択
  6. 土曜日、日曜日の天気を選択

少々回りくどいですが、このような流れで最終的な目的の情報にたどり着くことができます。各ステップでの画面の表示は、直前にユーザーが選択した結果と関連したものになるため、ユーザーの選択結果ごとにWebサイトの画面内容も切り替わります。これは、様々なユーザーインターフェースを持つ一般的なアプリケーションの仕様です。

一方Chatbotは、ユーザーとボットの対話が基本的なインターフェースとなります。対話によるユーザーインターフェースは、1枚のチャット画面だけです。Chatbotを開発する場合は、1つの話題ごとにそれまでの対話内容をきちんと保持して、必要に応じて適切な回答をする必要があります。一般的なアプリケーションのように、質問のたびに画面を切り替えるわけにはいきません。Bot FrameworkのBot Builder SDKでは、ユーザーごとの内容や話題=対話の流れをきちんと保持し続けることを、基本機能の一部として提供しています。これがダイアログ(対話)と呼ばれる機能です。

対話の流れを保持するダイアログ

対話の流れを保持するダイアログ

ダイアログの作成

では実際にダイアログを作成してみましょう。名前、年齢、性別を入力する簡単なものですが、ダイアログの基本的な使い方を確認します。前回作成した「ChatBot201707」を利用します。今回の記事で使用するファイル一式は、以下のリンクからダウンロード可能です。

Chatbot03.zip

まずDialogsフォルダーに、空白のクラスファイル「HelloDialog.cs」を追加します。

空白のクラスファイルを追加

空白のクラスファイルを追加

HelloDialog.csの中身は「 RootDialog.cs」の一部をコピーして、最低限のダイアログを作成するためのテンプレートとして用意します。

リスト1:HelloDialog.cs

using System;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;

namespace Chatbot201707.Dialogs
{
    [Serializable]
    public class HelloDialog : IDialog<object>
    {
        public Task StartAsync(IDialogContext context)
        {
            context.Wait(MessageReceivedAsync);
            return Task.CompletedTask;
        }

        private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
        {
            var activity = await result as Activity;
            await context.PostAsync("こんにちは");
            context.Done<object>(null);
        }
    }
}

上記リストの内容を、一つずつ細かく見ていきましょう。

参照設定

usingで参照しているクラスは、ダイアログを作成するために最低限必要なクラスです。

Serializable属性

ダイアログはシリアル化可能でなければならないという制約があるのでSerializable属性を付けます。Chatbotでは対話の状態を保存するために必要となります。

IDialog<object>

IDialog<T>インターフェースを実装したクラスがダイアログになります。<T>はダイアログが終了した時に戻り値として返す値の型を表し、上記の例では「object」となります。

StartAsync

IDialogインターフェースはStartAsyncメソッドが開始時に呼び出されます。

context.Wait

ダイアログは基本的にcontext.Waitメソッドにメッセージを処理するメソッドを渡す形で定義します。

await context.PostAsync

ユーザーにメッセージを返します。

context.Done

ダイアログによる対話の流れが終了したらcontext.Doneメソッドでダイアログを終了します。戻り値はないので、nullを返しています。

Taskクラスとasync/await

C# 5.0から導入された、Taskクラスの構文の一つです。async Task は非同期処理のメソッドとなり、他のメソッドの終了を待つことなく独立して実行~終了されます。awaitは非同期処理の終了を待って値を取り出します。最後にTask.CompletedTaskで完了済みのTaskを取得しています。ダイアログでは非同期処理が基本となります。

ダイアログのコードを詳細に理解するために、多少難しい話が続きましたが、上記の基本となるコードをテンプレートとして利用すれば、特に難しいことは気にすることなくダイアログを作成することができます。早速このダイアログを実行してみましょう。MessagesControllerクラス(MessagesController.cs)のPostメソッドでHelloDialogを呼び出すように変更して実行します。

リスト2:MessagesController.csを一行変更

    await Conversation.SendAsync(activity, () => new Dialogs.HelloDialog());
HelloDialogを実行

HelloDialogを実行

このままでは何を入力しても「こんにちは」しか返ってきませんが、ダイアログとしてはきちんと動いていることがわかります。これを元に、少しずつカスタマイズして対話らしくしていきましょう。

ダイアログで対話のやり取り

ダイアログで対話の流れを維持しつつユーザーとやり取りをするためには、「一つの対話処理が終わったら、context.Waitメソッドで次の処理に対話を引き渡す」という作業が必要です。HelloDialogを修正して対話らしいやり取りができるようにしてみましょう。

リスト3:HelloDialog.csを修正

using System;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;

namespace Chatbot201707.Dialogs
{
    [Serializable]
    public class HelloDialog : IDialog<object>
    {
        public string userName;
        public string userPlace;
        public Task StartAsync(IDialogContext context)
        {
            context.Wait(HelloMessage);
            return Task.CompletedTask;
        }

        private async Task HelloMessage(IDialogContext context, IAwaitable<object> result)
        {
            var activity = await result as Activity;
            await context.PostAsync("こんにちは!\r\n\r\nお名前は?");
            context.Wait(NameMessage);
        }

        private async Task NameMessage(IDialogContext context, IAwaitable<object> result)
        {
            var activity = await result as Activity;
            userName = activity.Text;
            await context.PostAsync($"{userName}さん、お住まいはどちらですか?");
            context.Wait(PlaceMessage);
        }

        private async Task PlaceMessage(IDialogContext context, IAwaitable<object> result)
        {
            var activity = await result as Activity;
            userPlace = activity.Text;
            await context.PostAsync($"ようこそチャットボットへ! {userPlace}にお住いの{userName}さん");
            context.Done<object>(null);
        }
    }
}

修正を加えたHelloDialog.csの詳細を見ていきましょう。

StartAsync

最初の処理としてユーザーが何か入力したら、HelloMessageメソッドを呼び出します。

HelloMessage

「await context.PostAsync("こんにちは!\r\n\r\nお名前は?")」であいさつと名前を尋ねるメッセージを送信します。改行は「\r\n」を2回入力します。「context.Wait(NameMessage)」で、次の処理NameMessageに対話を引き渡します。

NameMessage

「userName = activity.Text」でユーザーが入力した名前を受け取ります。次の質問である住まいを尋ねるメッセージを送信し、PlaceMessageに対話を引き渡します。

PlaceMessage

「userPlace = activity.Text」でユーザーが入力した地名を受け取ります。「await context.PostAsync($"ようこそチャットボットへ! {userPlace}にお住いの{userName}さん") 」で、これまで入力された名前と地名を含めてようこそメッセージを送信します。

「context.Done<object>(null)」でダイアログを終了します。この後、ユーザーが何か入力すると初めに戻ってMessagesControllerクラス(MessagesController.cs)のPostメソッドが実行されることになります。つまり再びHelloDialogが呼び出され、対話が開始されます。

context.Waitメソッドによる連続した対話

context.Waitメソッドによる連続した対話

少しずつですがChatbotと対話ができるようになってきました。

GMOインターネット株式会社 Windowsソリューション チーフエグゼクティブ

GMOインターネットでWindowsのサービス開発運用に関わって16年、数年単位で進化し続けるMicrosoftのWindowsは新しもの好きにはたまらない製品です。自動販売機に見たことのないジュースがあれば、迷わすボタンを押します。そんなチャレンジが僕の人生を明るく、楽しくしてくれています。

お名前.com デスクトップクラウド
http://www.onamae-desktop.com/

お名前.com VPS Hyper-V
http://www.onamae-server.com/vps/hyperv/

連載バックナンバー

Web開発技術解説
第10回

Chatbotから話しかける(プロアクティブメッセージの送信)

2018/5/18
連載10回目となる今回は、Chatbotから先に話しかけるための手順を紹介します。
Web開発技術解説
第9回

Chatbotで画像コミュニケーションを実現する

2018/5/14
連載9回目となる今回は、Chatbotとの文字でのやり取りに画像を追加する方法を解説します。
Web開発技術解説
第8回

FacebookでChatbotを公開する

2018/4/17
連載8回目となる今回は、FacebookのMessengerを介してChatbotとやり取りする方法を解説します。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています