ChatbotにLUISを実装する
LUISアプリケーションが完成したところで、ChatbotからLUISを利用する方法を紹介しましょう。
LUISアプリケーションの発行
まずは、Azureの「Cognitive Services」にLUISを利用するためのAPIを作成します。Azureにサインインし、「新規」-「Cognitive Services」からAPIを作成します。作成するAPIのタイプは「Language Understanding(LUIS)」を選択します。
Azure Cognitive ServicesにAPIを作成(1)
Keysを選択して、2つあるキーのうち、どちらか一方を取得しておきます。
Azure Cognitive ServicesにAPIを作成(2)
次に、LUISの管理画面に戻り、LUISアプリケーションを外部から呼び出して利用できるように発行を行います。画面上のメニューから「PUBLISH」を選択します。
- Publish to : Production
- Timezone : Tokyo
- North America Regions
この設定で「Add Key」をクリックします。表示されたダイアログで、「Tenant name」はLUISアプリケーションのApp Idを、「Key」はAzureで作成した「Cognitive Services API」の名前をそれぞれ選択します。その後「Add Key」をクリックして確定します。
キーの割り当て
管理画面を確認すると、Azureで作成したCognitive Services APIのKeyが自動的に読み込まれています。
最後に忘れずに「Publish to production slot」をクリックして、アプリケーションの発行を行います。
APIとの接続が成功しているか確認してみましょう。画面下部の「Chatbot201707_LUIS」の「Endpoint」のURLをクリックします。
Endpointの確認
URLの最後に「ビーフカレーをください」と日本語で追加して参照します。成功していれば、json形式のデータが表示されます。queryが「ビーフカレーをください」の場合に、LUISによって「intent=Order」「entity=ビーフカレー」のように言語解析されています。LUISアプリケーションとCognitive Services APIが、適切に接続できていることが確認できました。
Endpointのテスト結果
Chatbot201707への実装
AIが利用できる環境が整ったところで、開発中の「Chatbot201707」にLUISの機能を実装してみましょう。C#からLUISを呼び出すときに必要となるのが、LUISアプリケーションの「Application ID」とAzure Cognitive Services APIの「Key」となります。「Application ID」は管理画面の「SETTINGS」から確認できます。
Application IDの確認
Azure Cognitive Services APIの「Key」は管理画面の「PUBLISH」にある、「Chatbot201707_LUIS」の「Key string」から2つのKeyが確認できます。いずれか一方だけで構いません。
Keyの確認
Visual Studioで「Chatbot201707」を開き、Dialogsフォルダーに、空白のクラスファイル「ChatDialogs.cs」を追加します。ダイアログの中からLUISを呼び出します。
リスト1:ChatDialog.cs
02 | using System.Threading.Tasks; |
03 | using Microsoft.Bot.Builder.Dialogs; |
04 | using Microsoft.Bot.Builder.Luis; |
05 | using Microsoft.Bot.Builder.Luis.Models; |
07 | namespace Chatbot201707.Dialogs |
09 | [LuisModel("取得した Application ID", "取得した Key")] |
11 | public class ChatDialog : LuisDialog<string> |
14 | public async Task None(IDialogContext context, LuisResult result) |
16 | await context.PostAsync($"申し訳ありません注文内容がわかりません。"); |
17 | context.Wait(MessageReceived); |
21 | public async Task GetOrder(IDialogContext context, LuisResult result) |
23 | EntityRecommendation entity = result.Entities[0]; |
24 | string order = entity.Entity; |
個別に注意すべきポイントを見ていきましょう。
参照設定
ダイアログを利用するので「Microsoft.Bot.Builder.Dialogs」を追加し、LUIS呼び出しに必要な「Microsoft.Bot.Builder.Luis」と「Microsoft.Bot.Builder.Luis.Models」を追加しておきます。
[LuisModel("取得した Application ID", "取得した Key")]
LUIS呼び出しに必要な「Application ID」と「Key」を設定します。
[Serializable]
Chatbotではお約束なので忘れずに。
public class ChatDialog : LuisDialog<string>
普通のダイアログと異なり、「LuisDialog」インターフェースを実装します。ダイアログの戻り値はユーザーが入力した文字とするのでstring型を指定しておきます。
[LuisIntent("")]
LUISアプリケーションの実行結果として、Intentが空の場合は、該当するIntentがなかったということで、エラー処理としてメッセージを表示します。
[LuisIntent("Order")]
「Order」Intentに該当した場合の処理を記述します。
EntityRecommendation entity = result.Entities[0]
今回のサンプルではEntityは一つだけですが、設定内容によっては複数返ってくることがあります。そこで、配列の一つとしてEntityを受け取ります。
string order = entity.Entity
注文内容からEntityにマッチした文字列(ビーフカレー)を取得します。
context.Done(order)
ダイアログを終了して、Rootダイアログに戻ります。戻り値として注文内容を返します。
続いて、ChatDialogsを呼び出すために、RootDialogを修正します。
リスト2:RootDialog.cs
002 | using System.Threading; |
003 | using System.Threading.Tasks; |
004 | using Microsoft.Bot.Builder.Dialogs; |
005 | using Microsoft.Bot.Connector; |
006 | using System.Collections.Generic; |
008 | namespace Chatbot201707.Dialogs |
011 | public class RootDialog : IDialog<object> |
013 | private List<string> menuList = new List<string>() { "ランチコース", "カレー", "ドリンク", "デザート", "終了" }; |
015 | public Task StartAsync(IDialogContext context) |
017 | context.Wait(HelloMessage); |
018 | return Task.CompletedTask; |
021 | private async Task HelloMessage(IDialogContext context, IAwaitable<object> result) |
023 | await context.PostAsync($@"いらっしゃいませ!ご注文を伺います。 |
025 | (メニューをご覧になる場合は「メニュー」と入力してください。)"); |
027 | context.Wait(MessageReceivedAsync); |
030 | private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result) |
032 | var activity = await result; |
033 | switch (activity.Type) |
035 | case ActivityTypes.Message: |
036 | var message = activity as IMessageActivity; |
037 | switch (activity.Text) { |
039 | MenuMessage(context); |
042 | await context.Forward(new ChatDialog(), ChatResumeAfterDialog, message, CancellationToken.None); |
049 | private void MenuMessage(IDialogContext context) |
051 | PromptDialog.Choice(context, SelectDialog, menuList, "ランチメニューをお選びください。"); |
054 | private async Task SelectDialog(IDialogContext context, IAwaitable<object> result) |
056 | var selectedMenu = await result; |
057 | switch (selectedMenu) |
060 | context.Call(new LunchDialog(), LunchResumeAfterDialog); |
063 | context.Call(new CurryDialog(), CurryResumeAfterDialog); |
066 | context.Call(new DrinkDialog(), DrinkResumeAfterDialog); |
069 | context.Call(new DessertDialog(), DessertResumeAfterDialog); |
072 | await context.PostAsync("ご注文を承りました。"); |
073 | context.Wait(HelloMessage); |
078 | private async Task LunchResumeAfterDialog(IDialogContext context, IAwaitable<string> result) |
080 | var lunch = await result; |
081 | await context.PostAsync($"ランチコースは {lunch} ですね。"); |
082 | MenuMessage(context); |
085 | private async Task CurryResumeAfterDialog(IDialogContext context, IAwaitable<CurryFormQuery> result) |
087 | var curry = await result; |
090 | for (int i = 0; i < curry.Topping.Count; i++) |
092 | topping += " " + curry.Topping[i]; |
095 | await context.PostAsync($@"カレーは {curry.Curry.ToString()} ですね。 |
097 | ライスは {curry.Rice.ToString()} ですね。 |
099 | サイズは {curry.Size.ToString()} ですね。 |
104 | MenuMessage(context); |
107 | private async Task DrinkResumeAfterDialog(IDialogContext context, IAwaitable<string> result) |
109 | var drink = await result; |
110 | await context.PostAsync($"お飲み物は {drink} ですね。"); |
111 | MenuMessage(context); |
114 | private async Task DessertResumeAfterDialog(IDialogContext context, IAwaitable<string> result) |
116 | var dessert = await result; |
117 | await context.PostAsync($"デザートは {dessert} ですね。"); |
118 | MenuMessage(context); |
121 | private async Task ChatResumeAfterDialog(IDialogContext context, IAwaitable<string> result) |
123 | var chat = await result; |
124 | await context.PostAsync($"ご注文は {chat} ですね。"); |
125 | MenuMessage(context); |
修正箇所だけピックアップしています。ここでも注意すべきポイントをチェックしていきましょう。
HelloMessage
これまではユーザーから入力があった場合、最初にPromptDialogでメニューを表示していましたが、LUISを利用するために、直接注文を入力するか、メニューを表示するかを選択できるようにしています。
MessageReceivedAsync
ユーザーから入力された文字列が「メニュー」の場合は、これまで通りMenuMessage メソッドを呼び出してPromptDialogでメニューを表示します。
await context.Forward(new ChatDialog(), ...)
その他の文字列の場合はChatDialogを呼び出します。ChatDialogが終了した時には、ChatResumeAfterDialogメソッドを実行します。ChatDialogにはmessageでユーザーからの入力文字列を渡しています。
ChatResumeAfterDialog
ChatDialogからの戻り値=カレーの注文を受け取り、確認メッセージを表示します。
エミュレーターで動作を確認してみましょう。
エミュレーター上で動作を確認する
ユーザーから直接入力された文字列に対して、LUISの言語解析によって注文内容を識別しているのが確認できました。
以上、Chatbotに言語解析のAIを搭載することで、Chatbotをさらに成長させることができました。今回のサンプルは、LUISの使い方としてはとてもシンプルなものですが、ここから様々な機能追加をすることで、より使いやすいChatbotとして育てることができます。AzureのCognitive Services にはこの他にも、画像認識や、音声認識など様々なAIが提供されています。引き続きこれらのAIを実装して、Chatbotを成長させていきましょう。