December 2016

Volume 31 Number 13

Microsoft Bot Framework - アプリケーション データに時間や場所を問わずアクセスするために Bot Framework を使用する

Srikantan Sankaran

前回 (msdn.com/magazine/mt788623) は Microsoft Bot Framework を紹介しました。そこでは、このフレームワークを使用して、顧客が音声とテキストを使ってサービスにインタラクティブにアクセスするという課題に対処しました。これは、昨今多くの企業が直面している課題です。前回は Microsoft Flow や Azure Logic Apps のようなテクノロジを使用して、保険会社に配置されているさまざまな基幹業務アプリケーションから収集した情報を必要とするビジネス シナリオを取り上げました。Azure Search を使用して、構造化データと非構造化データの両方を含むコンテンツにインデックスを付け、クライアント アプリケーションから利用できるようにしました。

前回も示した図 1 は、このシナリオを実装するソリューションのアーキテクチャを示しています。

ソリューションのアーキテクチャ
図 1 ソリューションのアーキテクチャ

今回は、Azure Search を使って収集した情報を利用するボット アプリケーションをビルドしてデプロイする予定です。このボット アプリケーションを Azure Web アプリとしてデプロイし、Microsoft Bot Framework Connector Service を使って発行します。このボット アプリケーションには、Connector Service を使って Skype チャネルからアクセスします。これにより、顧客は自身の保険契約依頼の状態へのアクセス、発行済みの保険契約書類のダウンロード、立入検査予定の立案などが可能になります。顧客は保険契約申請時に登録した Microsoft アカウントを使用してボットに対して自身の ID を確認し、Skype クライアントでのメッセージングや、音声コマンドを使って、ボットとやり取りします。ボット アプリケーションは LUIS サービスを統合します。このサービスは、Azure Cognitive Services で利用できるサービスの 1 つで、Azure Search でクエリを実行するために顧客の会話を解釈します。

このソリューションをビルドするのに必要な手順を以下に示します。コードのスニペットは、Visual Studio 2015 ソリューションから流用したもので、このコラムからダウンロードできます。以下の手順に従って必要な操作を行います。

  • LUIS モデルを作成して発行します。このモデルは、LUIS サービスが提供する UI を使用して、ボットでサポートすべきさまざまな会話をキャプチャします。
  • Visual Studio 2015 のテンプレートを使用して新しいボット アプリケーションを作成します。本稿記載のソリューションからコード ブロックを追加しても、本稿付属のソリューション ファイルをダウンロードして、この後の説明の参考にしてもかまいません。MessageController.cs ファイルがボット アプリケーションへのエントリ ポイントです。ボット アプリケーションは、特殊な種類の .NET MVC アプリケーションです。
  • Bot Framework SDK で利用できる LUIS ダイアログを使用して、LUIS の機能をボットに埋め込みます。これは、ソリューションの PolicyInfoDialog.cs ファイルに実装しています。
  • Azure Search SDK を使用して、Azure Search Service REST API を呼び出すコードを実装し、LUIS サービスによって解釈された検索要求を実行します。これは、ソリューションの PolicyInfoDialog.cs ファイルに実装しています。
  • ユーザーからの初回要求時に、Skype チャネルでユーザーの Microsoft アカウントをインタラクティブな方法で取得するために、FormFlow ダイアログを実装します。これは、付属ソリューションの SigninForm.cs ファイルに実装しています。
  • Azure Search SDK を使用して、ユーザーが初めてボットにアクセスしたときに取得した Microsoft アカウントを基に、ユーザーのプロファイル情報を取得します。これは、SearchService.cs に実装しています。
  • Bot State Service を使用して、ユーザーのプロファイル情報を保持します。ボット ユーザーの住所と連絡先情報を Bot State Service に格納し、立入検査の予定を設定するときにこの情報を使用します。これも、PolicyInfoDialog.cs ファイルに実装しています。
  • 開発用コンピューターでサービスとしてローカルに実行して、ボット アプリケーションをテストし、Bot Framework Channel Emulator を使用して、ユーザーとサービスのやり取りを評価します。
  • ボット アプリケーションをローカルにテストした後、Visual Studio から直接 Azure の Web アプリにこのアプリケーションをデプロイします。
  • ボット アプリケーションを Microsoft Bot Framework Connector Service に登録し、Azure にデプロイしたボット アプリケーションの URL を構成します。
  • ボット アプリケーション名、アプリ ID、ボットの登録中に取得したシークレット情報を Visual Studio ソリューションに追加し、Azure に再発行します。
  • ボットの開発者ポータルで、Skype や Slack のようなチャネルからボット アプリケーションへのアクセスを有効にします。

ボット アプリケーション用の Visual Studio 2015、およびボット アプリケーションで使用される LUIS モデルのファイル エクスポートは GitHub リポジトリ (bit.ly/2cOfANh) からダウンロードできます。

ユースケースのさまざまなシナリオ向けの LUIS モデルの作成とトレーニング

図 2 では、ユーザーが (LUIS 発話により) どのような会話をボットと交わす可能性があるか、その会話が特定の LUIS のインテント (意図) またはアクション (動作) として Azure Search によりどのように解釈されるべきか、および検索を実行する際に、LUIS の発話からキーワード (LUIS エンティティ) をどのように抽出してクエリ パラメーターとして使用するかを対応付けて示しています。

図 2 発話とインテントのマッピング

発話または会話 マップされるインテントまたはアクション 修飾子またはパラメーター
LUIS が解釈できない内容 (またはトレーニングされていない内容)。 なし (既定値) 該当なし
Get (or show) all my policy requests. (すべての保険契約依頼を取得 (または表示) する)。 GetPolicyRequests ログイン中のボット ユーザーについての情報
I have a policy request application, or is my policy request approved? (保険契約依頼の申請中、または保険契約要求の承認待ち)。 GetPendingPolicyRequests 状態が承認以外になっている保険契約依頼についての情報
Get me the details of policy request number VehPolicy001. (保険契約依頼番号 VehPolicy001 の詳細を取得する)。 GetPolicyRequestDetails 保険契約依頼 Id – VehPolicy001
Get the status of my life insurance policy request. (生命保険契約依頼の状態を取得する)。 GetPolicyRequestStatusByType 保険契約要求の種類 – 生命保険
Get policy document number Policy0101 issued this year. (今年発行された保険契約証券類番号 Policy0101 を取得する)。 GetPolicyDocument ファイル名 – Policy0101、最終更新日 – 今年
Schedule a site-inspection visit tomorrow. (立入検査の予定日を明日に設定する)。 ScheduleSiteVisit 立入検査日 – 明日

LUIS サービスは、luis.ai で Web アプリケーションを提供しています。luis.ai では、LUIS アプリケーション モデルを視覚的に作成できるため、問題をすぐに認識できます。図 3 は、今回のシナリオ用に構成した LUIS モデルを示しています。

今回のシナリオの LUIS モデル
図 3 今回のシナリオの LUIS モデル

 前の手順で特定したインテント、エンティティ、発話は、すべて LUIS アプリケーションで作成できます。[Intents] (インテント) メニューを使用してインテントを追加し、[Entities] (エンティティ) メニューを使用してエンティティを作成します。次の特別な 2 つの種類のエンティティがあることに注意してください。

  • 階層型エンティティ: 今回の場合は、「Insurance Policy」(保険契約) を 階層型エンティティ (親) としてとして作成し、「Life」(生命保険)、「Vehicle」(車両保険)、「Home」(住宅保険) といったさまざまな種類を子として作成します。
  • 事前構築済みのエンティティ: 今回は、LUIS が提供する事前構築済みエンティティ Datetime を使用しています。保険契約書類が作成された年や立入検査が予定されている日付などの入力パラメーターは、LUIS エンジンによって、これらの事前構成済みのエンティティ型にマップされます。

「Keyword」(キーワード) というエンティティは、Azure Search で保険契約書類のインデックス内をフルテキスト検索するために使用します。

ここで、[New utterances] (新しい発話) タブを選択して、先ほど特定した会話ごとに発話を作成し、それらにラベルを付けます。

発話ごとに、インテントと関連エンティティを特定するか、ラベルを付けます。たとえば、図 3 では、「get me the status of my life insurance policy request」(生命保険契約依頼の状態を取得する) をインテント「GetPolicyRequestStatusByType」にマップし、「Life Insurance」(生命保険) というテキストを階層型エンティティ「Insurance:life」(保険:生命) にマップしていす。

このような手順を繰り返し、保険の種類 Vehicle (車両)、Home (住宅)、General (全般) などに応じて他のバリエーションを追加します。また、同じ依頼が、異なる発話で行われる可能性を考慮します。ただし、同じメッセージを意味する可能性を考え、言葉の組み合わせや語順をすべて取り込む必要はありません。たとえば、図 4 に示すように、「show me the status ...」(...状態を表示する) というフレーズを新しい発話として追加すると、LUIS は、以前の発話「get me the status ….」(….状態を取得する) のバリエーションとして適切に解釈できます。 別のフレーズを使用して質問しても、LUIS はインテントと関連エンティティを適切に推定します。

LUIS 発話のラベル付けとバリエーションの処理
図 4 LUIS 発話のラベル付けとバリエーションの処理

それぞれの発話を取り込むと、LUIS アプリケーションはモデルをトレーニングするオプションを提示します。その結果、LUIS のAI エンジンでは、クライアント アプリケーションが使用するモデルを最適化して生成するようになります。新しい発話にラベルを付けたとき、または既存の発話に変更を加えたときは、必ずこの手順を繰り返します。

LUIS モデルを発行することで、クライアント側のアプリケーションは、REST API を使用してこのモデルを呼び出せるようになります。アプリケーションの発行ダイアログでも、モデルをテストして想定どおりに動作するかどうかを確認できます。図 5 は、会話のフレーズを渡してブラウザーで出力を表示することで、LUIS モデルを簡単にテストする方法を示しています (分かりやすくするため、図 5 では発話の 3 つの異なる呼び出しと対応する結果のスナップショットを並べて表示しています)。

LUIS モデルのテスト
図 5 LUIS モデルのテスト

LUIS が推奨するインテントがリストの一番上に表示され、そのインテントに割り当てられる確率の最大値が添えられます。応答には、フレーズから抽出されたエンティティも返されます。発話から複数のエンティティが特定される場合は、それらのエンティティも返されます。ここでは LUIS モデルが発話フレーズに含まれる「this year」(今年) のような単語を解釈し、事前構築済みのエンティティ「Datetime」にマップして、クライアント アプリケーションが直接使用する値「2016」を返すしくみを示しています。

ボット アプリケーションの作成

これで LUIS モデルの準備が整ったので、ボット アプリケーションで使用できます。 aka.ms/bf-bc-vstemplate から Visual Studio 2015 のテンプレートをダウンロードして、ボット アプリケーションを作成します (ボット アプリケーションのビルドの基礎と、ボット エミュレーターを使用してボットをローカルにビルドおよびテストする方法については bit.ly/2dYrwyW (英語) を参照してください)。

このテンプレートを使用して作成したプロジェクトは、MVC プロジェクトのバリアントです。このプロジェクトへのエントリ ポイントは、MessageController.cs です。ここでは、Skype や Slack のようなチャネルからの着信メッセージを処理して、応答を送り返します。

Bot Framework 用の LUISDialog を使用して、ボットからのメッセージを暗黙のうちに LUIS モデルに渡します。LUIS モデルに実装したインテントごとに対応するメソッドを実装します。ボット アプリケーションは実行中にこのメソッドを直接呼び出すことができます。これにより、会話を解釈し、インテントとエンティティを特定するために LUIS REST API を呼び出すカスタム コードを用意する必要がなくなります。

LUIS アプリケーション ID とシークレット値を使用して、クラスレベルの属性 LuisModel を装飾します。属性 LuisIntent は、ユーザーの要求にそのインテントが見つかったときの実装を提供するメソッドで使用します。ボットで LUIS 統合を行うために必要なことはこれだけです。LUIS 統合をコードに実装する方法を図 6 に示します。

図 6 LUIS 統合の実装

[LuisModel("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 
  "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")]
[Serializable]
  public class PolicyInfoDialogs : LuisDialog<object>
  {
    [LuisIntent("")]
    public async Task None(IDialogContext context, LuisResult result)
    {
      // Called when the incoming utterance is not recognized (default bin)
    }
    [LuisIntent("GetPolicyRequests")]
    public async Task GetPolicyRequests(IDialogContext context, LuisResult result)
    {
      //
    }
        ----------------------
      }

この時点で、各メソッドの実装を簡素化することができます。たとえば、単純にどの LuisIntent 実装が呼び出されたかを特定するメッセージを返すことができます。

MessageController.cs では、以下のスニペットを追加して、ボット ユーザーからの着信メッセージをキャプチャし、それを LuisDialog に渡します。

if (activity.Type == ActivityTypes.Message)
  {
    await Conversation.SendAsync(activity, () => new PolicyInfoDialog());
  }

ボット アプリケーションを実行し、Bot Framework Channel Emulator を起動します。このエミュレーターは aka.ms/bf-bc-emulator (英語) からダウンロードできます。以前に LUIS モデルをトレーニングした発話を入力し、応答メッセージが、LUIS インテントにマップされた適切なメソッドが呼び出されたことを示しているのを確認します。

検索統合

ここで、NuGet パッケージ マネージャーを使用して .NET用 Azure Search SDK を Visual Studio ソリューションに追加します。ソリューションの SearchService.cs ファイルは、この SDK を使用して Azure Search Service との統合のすべてをカプセル化しています。このファイルには、Azure Search に対してクエリを実行するメソッド、顧客のUserProfile を返すメソッド、および PolicyRequests と Policy­Requests を取得するメソッドを実装します

さまざまなシナリオ用の検索コマンドは、Lucene クエリ構文を使用して構築します。この処理は、PolicyInfoDialog.cs ファイルに実装しています。次にいくつか例を示します。 

ボット ユーザーの保険契約依頼をすべて会話内から取得するコマンドは以下のようになります。

command += "CustomerId:(" + msftid + ")";
Retrieve all policy requests for the bot user that haven’t yet been approved:
command += "CustomerId:(" + msftid + ") -PolicyStatus:(Approved)";
Query policy requests for the bot user based on the policy request Id:
public async Task GetPolicyRequestDetails(IDialogContext context, LuisResult result)
  {
    ----------------------
      string  command += "CustomerId:(" + msftid + ") AND PolicyRequestId:(" +
        result.Entities[0].Entity + ")";
    ----------------------
  }

このスニペットは、LUIS エンジンによって、PolicyRequestId エンティティ (検索クエリを実行するための入力パラメーター) が LuisResult (結果) 経由で渡されるしくみを示しています。

図 7 に、プロパティベースで文書を検索する方法と、フルテキスト検索で文書を検索する方法を両方示します。

図 7 のコードのプロパティ名と値の関連付けについては、前回のコラムを参考にしてください。また、後ほど紹介するリンクを使用して、本稿付属のデータベースで使用されているテーブルのスキーマをすべてダウンロードできます。 

図 7 文書のプロパティベースの検索とフルテキスト検索の実装

foreach (EntityRecommendation curEntity in request.Entities)
  {
    switch (curEntity.Type)
      {
        case "PolicyNumber": // Look for documents with this filename
          {
            command += " filename:(" + request.Entities[0].Entity + ") ";
            break;
          }
        case "keyword": // Search within documents for this keyword expression
          {
            command += " " + request.Entities[0].Entity + " ";
            break;
          }
        case "builtin.datetime.date":
        // Retrieve based on the year a document was generated
          {
            char[] delimiterChars = { '.', ':' };
        List<string> allvalues = curEntity.Resolution.Values.ToList<string>();
             string val = allvalues[0];
             string[] vals = val.Split(delimiterChars);
             command += " lastmoddate:(" + vals[0] + ") ";
             break;
          }
        default:
          {
            break;
          }
        }
    }

ユーザーへの応答の書式設定

ボットから返された応答メッセージ内のテキストの書式を設定して、そのスタイルにハイパーリングやメディア コンテンツなどを追加することができます。これにより、このボット アプリケーションがより使いやすくなります。以下に例をいくつか示します。

Azure Search から返された動的データのみが太字になるようにフォントのスタイルを設定するには、動的データを連続する 2 つのアスタリスク (**) で囲みます。

foreach (SearchResult<PolicyRequest> result in eachGroup)
  {
    searchResponse += (counter) + ". **" + result.Document.PolicyRequestId + "**" +
      " on the insured :**" + result.Document.InsuredIdentifier + "**
      for an amount of :**" +
    result.Document.Currency + " " + result.Document.AssessedValue + "**
      has status: **" +
    result.Document.PolicyStatus + "**\n\n";
    counter++;
  }

Azure Search から返される保険契約書類にハイパーリンクを追加するには、その書類の URL にLink 属性を追加します。

foreach (SearchResult<PolicyDocument> result in results)
 {
   searchResponse += counter + ". Link: [" + result.Document.filename + "](" +
     result.Document.fileurl + ") \n";
   counter++;
 }

ボット ユーザーの特定

Bot Framework は、ユーザーのチャネル ID と、会話コンテキストの表示名を提供しますが、Skype などのチャネルへのログインに使用したユーザー名は提供しません。したがって、この情報は、ユーザーがボットとの会話を開始するときに明示的に取得します。この情報の提供をユーザーに求めるため、先ほど説明した FormFlow ダイアログを使用します。ユーザー プロファイル情報は、アカウント名を基に Azure Search から取得します。

ボットと会話しているユーザーが FormDialog で共有される Microsoft アカウント名の所有者であることを保証するには、Microsoft アカウントのサインイン ページへの対話型ログインを統合し、認証および検証の後にのみボットへのアクセスを提供するようにします。この処理は本稿の範囲外とし、ここでは取り上げません。または、SigninForm ダイアログを使用して、単純にアカウント名の提供をユーザーに求め、ユーザーの身元を次のように仮定します。

public static IForm<SigninForm> BuildForm()
  {
    return new FormBuilder<SigninForm>()
      .Message("I would need some information before we get started.")
      .Field(nameof(MicrosoftAccount))
      .Build();
  }

Bot State へのユーザー プロファイルの格納

ボットでの会話の開始時にユーザーを特定したら、Azure Search からユーザー プロファイル オブジェクトを取得します。この情報を Bot State に保存し、たとえば、ユーザーが立入検査の予定を設定する要求を行う際に使用します。ユーザーの住所と連絡先番号は、立入検査の確認中に検証します。

Bot Framework は、ユーザーおよび会話ごとに privateConversationData コンテキストにこの情報を保存する API を提供します (図 8 参照)。

図 8 ユーザー プロファイルの格納

private async Task SignUpComplete(IDialogContext context, IAwaitable<SigninForm> result)
  {
----------------------------------------------
----------------------------------------------
  UserProfile profile =
    await LoadBotUserState(profileIdentityForm.MicrosoftAccount);
  if (profile == null)
  {
  message = $"Sorry, I could not locate your profile in our system.
    Please retry with the right Microsoft Account";
  }
  else
  {
    context.PrivateConversationData.SetValue<bool>("ProfileComplete", true);
    context.PrivateConversationData.SetValue<string>(
      "FirstName", profile.FirstName);
    context.PrivateConversationData.SetValue<string>("LastName", profile.LastName);
    context.PrivateConversationData.SetValue<string>("Address", profile.Address);
    context.PrivateConversationData.SetValue<string>("Phone", profile.Phone);
    context.PrivateConversationData.SetValue<string>(
      "CustomerId", profile.CustomerId);
    message = $"Thanks {profile.LastName},{profile.FirstName}
      for identifying yourself! \n\n
      Let me know how I could assist you.";
  }
    await context.PostAsync(message);
  }
    context.Wait(MessageReceived);
  }

要求のたびに、ボット アプリケーションは、Bot State をチェックして、要求を発行したユーザーの ID が検証済みで、ユーザーのプロファイル情報を利用できることを確認します。

Bot Channel Framework Emulator をローカルに使用

ボット アプリケーションは、ユーザーからの要求を実行するために、Azure Search 名前空間、アクセス キーおよびインデックス名を使用する必要があります。これらのパラメーターは SearchParameters.cs ファイルに収容しています。ボット アプリケーションの実行時に web.config ファイルから、これらのパラメータ値を設定します。これで、Visual Studio ソリューションを実行できるようになります。

Bot Channel Framework Emulator を起動して LUIS モデルをトレーニングした会話を入力します。代表的なユーザー対話シナリオをキャプチャしたものを図 9 に示します。ローカル環境でボット アプリケーションが正常に実行されたら、これを Azure Web アプリに公開できます。

エミュレーターからのボットの実行
図 9 エミュレーターからのボットの実行

Azure へのボットのデプロイ

Azure Portal から Azure の Web アプリを作成してから、アプリケーションの設定で、Visual Studio ソリューションの web.config ファイルから appSetting のキーと値を追加します。

Azure Web アプリにボット アプリケーションを発行する前に、dev.botframework.com/bots/new (英語) でそのボットを登録する必要があります。このプロセスにより、ボット ID、Microsoft アプリ ID、およびシークレットが生成されます。これらの値を、Visual Studio 2015 に含まれるボット アプリケーションの web.config に追加して、アプリを Azure に再発行します。

登録プロセス中、リダイレクト URL パラメーターには、Azure でのボット アプリケーションの SSL 対応 URL に “/api/messages” をサフィックスとして付けて使用します。

複数のチャネルでのボットの有効化

dev.botframework.com/bots (英語) のボット レジストリで、開発したボットを他のチャネルでも有効にすることができます。図 10 は、ボット アプリケーションを Skype チャネルと Slack チャネルで有効にしたようすを示しています。Slack でボット アプリケーションを有効にするには、いくつかの追加の手順が必要です。Slack チャネルへのボットの追加を選択するとウィザードが起動するので、表示される手順に従います。

複数のチャネルでのボットの登録
図 10 複数のチャネルでのボットの登録

図 11 は、Skype からアクセスした場合のボット アプリケーションを示しています。

Skype からのボットへのアクセス
図 11 Skype からのボットへのアクセス

まとめ

Microsoft Bot Framework と、LUIS や Azure Search などのサポート テクノロジは、ユーザーと企業の基幹業務アプリケーションとのやり取りを可能にする直感的なボット アプリケーションをビルドするための適切な基盤を提供します。それぞれのサービスが、ソリューションの全体的なアーキテクチャに適合しているため、コンポーネントを個別に成熟させ、コンポーネントどうしの独立性を向上できます。たとえば、LUIS モデルを調整して、ユーザーがボットとの会話に使用する発話をサポートする数を増やすことができます。そのとき、中核となるボット アプリケーションや Azure Search のコンテンツに変更を加える必要はありません。ボット アプリケーションをデプロイすると、追加のコード変更を行わなくても、さまざまなチャネルからアクセスできるようになります。さらに多くのチャンネルを段階的に追加でき、ボット アプリケーションへのアクセス数や利用率を向上することができます。


Srikantan Sankaran は、インドのバンガロールを拠点に各地で活動する DX チームのテクニカル エバンジェリストです。彼は、インドのさまざまな独立系ソフトウェア ベンダー (ISV) と共に活動しており、Microsoft Azure にソリューションをデプロイしています。連絡先は sansri@microsoft.com (英語のみ) です。

この記事のレビューに協力してくれたマイクロソフト技術スタッフの Sandeep Alur と Hemanth Kathuria に心より感謝いたします。
Sandeep Alur は、インドのバンガロールを拠点に各地で活動する DX チームのリード エバンジェリストです。

Hemanth Kathuria は、インドの Services チームでシニア コンサルタントを務めています。