BEACHSIDE BLOG

MicrosoftとかC#を好むレンジャーの個人的メモ

Bot Builder V4 (4.0.1-preview) 開発入門 : C# : State の保持

2019-10追記: 2018年9月末に GA したバージョン V4.0.7 で、破壊的変更が多かったためこのバージョンと互換の無い部分があります。バージョンにご注意くださいね。

State は、外見だけなら V3 からあまり変更はないですが...さらっと整理しておきます♪

手順だけ整理しようと思ったけどそれじゃー自分メモの意味がないので、ダラダラと余計な解説があります。

Bot Builder V4 で State を使う際に気にすることは、以下2点を今回のアジェンダとしてます。

Overview

State ? 日本語だと「状態」っていう?個人的には「ステート」って言ってますが...(←日本語でも単語を英語の読むことが多いので、別業界の人からみたら「ルー大柴かよ」って感じなんだろうなぁ...)

3-1. State を使うための設定

前置き

Bot Builder V4 は ASP.NET Core なので、こーゆーのは Middleware で設定してしまうやつです。プロジェクトテンプレートで生成された EchoBot のコード(このブログ内では「デフォルトのコード」と呼ぶことにします)をみると、Startup.csConfigureServices で設定されています。

デフォルトではインメモリーで保持する設定になっているので、サクッとデバッグができるようになっています。
あくまでローカルデバッグ専用のサクッと動かせるためのものです。デバッグを再起動したら消えちゃうやつです。

ここでは、簡易サンプルとして Azure Storage の Table に 保存する設定してみます。

State を保持する場所を指定(Azure Storage の Table の例)

まず、Azure ポータルでポチポチしてStorage を作成し接続文字列を取得しておきましょう。
(Storage の作成やキーの取得方法が不明な場合、別の内容について記載した以前のブログの「Table Storage の作成」部分と「接続文字列は、Azure Portal のStorage アカウントの」という文のあたりが参考に...)

Storage の接続文字列と Bot の State を保存するテーブル名は、appsettings.json の中に設定してみます。
VS 2017 でソリューションエクスプローラーから appsettings.json を開き、例えば以下のようにします(4~7行目が変更したとこ)。
接続文字列の値とテーブル名を自身の値にします。
(私の場合、テーブル名を botState としました)


次に、準備として Nuget で Microsoft.Bot.Builder.Azure をインストールします。
VS 2017 の上部のメニューで ツール > Nuget パッケージ マネージャー > ソリューションの Nuget パッケージの管理 を開きましょう(って操作はめんどいのでいつも クイック起動 から起動しますが...)

Nuget の管理画面の 参照 をクリックし以下をしましょう。

  • 検索に「Microsoft.Bot.Builder.Azure」と入力
  • プレリリースを含める にチェック
  • 現時点の最新(v4.0.1-preview)インストール

f:id:beachside:20180729183158p:plain


VS2017 のソリューションエクスプローラーから Startup.cs を開き ConfigureServices をみましょう。みるべき場所は2行だけです。
(以下の ConfigureServices メソッドだけを切り抜いて貼ったコードだと 24行目32行目 の部分)

dataStore をインメモリーと指定している部分をコメントアウトしましょう(以下コードだと24行目)。

次は、Tableに保存する部分をアンコメントします(以下コードだと32行目)。ついでに、先ほど appsetting.json に設定した情報を取得するよう変更しています。

※ ローカル開発用に、例えば appsetting.debug.json とか環境ごとにつくったりユーザーシークレットを設定したりってのが普通ですが、今回は Bot の話中心なのでやってません。

これで Bot の State の保存先が設定できました。次はなんのステートを保存するかの設定(上記コードの 35行目のあたり)の話をします。

と、その前にざっくりと デフォルトで用意されている ConversationStateUserState の概要を書いておきます。

ConversationState と UserState の概要

BotBuilder V4 での State は、まず、2つ用意されています。

BotState を継承したクラスが2だけっぽいのでそう思っています。つまりは自作したい場合、 BotState とか BotState<T> を継承したクラス作って、必要な設定....PropertyName とGetStorageKey の設定あたりですね....をしてあげれば、作れそうですね。)

二つの特徴をざっくりまとめると以下の感じです。

Stateの種類 特徴
ConversationState 各チャンネルの会話の接続(ConversationId毎)ごとに保持。
保存のキーは $"conversation/{context.Activity.ChannelId}/{context.Activity.Conversation.Id}"
UserState チャンネルのユーザーごとに保持。
保存のキーは $"user/{context.Activity.ChannelId}/{context.Activity.From.Id}"

2つと分ける意味は特にないのですが、もうひとつ、ダイアログを管理するための Dictionary<string, object> 型のストアもあります。これはダイアログについて書くタイミングがあれば触れます。

State の初期化設定

"初期化"というよりシリアライズ - デシリアライズの設定といった方が適切かもです。

ConversationState は、デフォルトで EchoState クラスのスキーマになるよう上記のコード35行目で実装がされています。

デフォルトのコードだと、UserState の保存はありませんので設定してみましょう。

どのようなスキーマで保存するかは、自身で自由に決めれます。ジェネリクスで指定してあげればシリアライズ - デシリアライズしてくれます。
ConversationState だとサンプルコードでは EchoState クラスですね。UserState はサンプルの実装がないので、以下のようにてきとーなクラスを作ってみました。

UserState を利用するなら、先ほど書いた Startup.csConfigureServices メソッドに以下のコード(6行目)を追加してあげれば利用できます。

これで、会話をユーザーに送った際に、良しなに State に保存してくれる設定ができました。


3-2. State の Read/Write

デフォルトのコードで EchoBot クラスを見ると、ConversationState の読み込む方法や更新のコードはありますので、察して独自の実装もできそうですね。

// ConversationState を読み込む
 var state = context.GetConversationState<EchoState>();

UserSate は以下のように取得できます。

// UserSate を読み込む
 var user = context.GetUserState<User>();

別の呼び出し方もありますが、結局内部でこのコードが呼ばれるのでここでは書かないです....

書き込む際は、普通に(上記のコードだと、stateuser に)代入してあげればよいです。会話をユーザーに返すときに、SDK が保存してくれます。


Botデバッグして、Emulator でアクセスしてみましょう。Azure Storage Explorer などを使って、設定した Storage の Table を見てみると保存できていることを確認できます。Emulator で会話を再接続(Start Over をクリック)すると、ConversationState は新しいレコードが作られるけど、UserState は単一であることがなども確認できますね。

終わりに

設定を保存するだけなら、この文章の1/3で終わったところですが、実装を見て "最小限" のメモをしたらダラダラなブログになってもーた.....。

次回は、QnA や LUIS との連携あたりでしょうか....Prompt も簡単だけど V3 から結構変わったの整理しとこうか....RichCard や Bot Builder tools も整理したいところです。