Quantcast
Channel: かずきのBlog@hatena
Viewing all articles
Browse latest Browse all 1387

Bot Framework SDK 4.4.3 時点でのボット作成の最小手順からダイアログまで

$
0
0

Bot Framework SDK (Bot Builder SDK?) v4 で v3 から大きく実装方法が変わったわけですが、結構 v4.1, 4.2, 4.3, 4.4... と進んでいくうちに、意外と v4 当初の作り方が非推奨になったりしてしょんぼりすることがあったので v4.4.3 時点での Bot Framework SDK の推奨っぽい土台作りを見てみようと思います。ついでに個人的な興味で ASP.NET Core 3.0 Preview 6 で使ってみようと思います。

ASP.NET Core Web アプリケーションから始めてみる

Bot Framework のプロジェクトテンプレートは最初から設定されてるので便利なのですが、何が設定されてるかは何処かで一度確認しないといけないので ASP.NET Core Web アプリケーションテンプレートから始めていこうと思います。ということで Empty から始めます。 あと HTTPS 今回はいらないのでオフっと。

f:id:okazuki:20190704223319p:plain

Microsoft.Bot.Builder.Integration.AspNet.Coreパッケージを NuGet から追加します。

v4 初期の頃はエンドポイントは自動で Bot Framework SDK が追加してくれてたのですが、それがなくなったので自前で ASP.NET Core のコントローラーを定義するようになっています。それに対応するために MVC まわりの設定を Startup.csConfigureメソッドを変更します。

publicvoid Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseMvc();
}

この後 ConfigureServicesメソッドでサービスを登録してまわるのですがボットがないと登録するボットが無いのでボット用のクラスを作成します。v4 当初は IBotインターフェースを実装するという形でしたが最近 ActivityHandlerクラスを継承する形が推奨っぽいです。

using Microsoft.Bot.Builder;

namespace BotStepByStep
{
    publicclass MyBot : ActivityHandler
    {
    }
}

ActivityHandlerクラスは IBotインターフェースを実装してるので同じっちゃぁ同じですけどね。

自分のボットクラスが出来たので Startupクラスの ConfigureServicesメソッドに必要なものを追加していきます。

publicvoid ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();
    services.AddSingleton<IBotFrameworkHttpAdapter, BotFrameworkHttpAdapter>();
    services.AddTransient<IBot, MyBot>();
}

あとは api/messagesで POST のリクエストを受け取るコントローラーを定義して、そこで IBotFrameworkAdapterIBotを使って Bot Framework に処理を流して完了です。

using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using System.Threading.Tasks;

namespace BotStepByStep.Controllers
{
    [Route("api/messages")]
    [ApiController]
    publicclass BotController : ControllerBase
    {
        privatereadonly IBotFrameworkHttpAdapter _adapter;
        privatereadonly IBot _bot;

        public BotController(IBotFrameworkHttpAdapter adapter, IBot bot)
        {
            _adapter = adapter;
            _bot = bot;
        }

        [HttpPost]
        public async Task PostAsync()
        {
            await _adapter.ProcessAsync(Request, Response, _bot);
        }
    }
}

v4 であった .botファイルが無くなった(非推奨になった)ので appsettings.jsonや Azure App Service の構成や Azure App Configuration で設定情報を管理できるようになるのでいいですね。

Echo ボットの実装

動かしたいところですが、このままだとボットに何も実装してないので以下のようにオウム返しする実装を追加します。

using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Schema;

namespace BotStepByStep
{
    publicclass MyBot : ActivityHandler
    {
        protectedoverride async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
        {
            await turnContext.SendActivityAsync(turnContext.Activity.Text);
        }
    }
}

実行したら Bot Framework Emulatorで繋いでみます。

繋いで適当に話しかけてみると…

f:id:okazuki:20190704223642p:plain

動いた!!やったね!!

ユーザーとの対話状態を覚えてほしい

そんなときはダイアログですね!!というわけで以下のダイアログのライブラリをインストールします。

  • Microsoft.Bot.Builder.Dialogs

Bot Framework には色々なダイアログの種類があるのですが…今回は WaterfallDialogを使ってみようと思います。 名前の通り順番に流れるようにステップをこなしていくダイアログです。

ダイアログ自体の詳細はこちらのドキュメントで。

docs.microsoft.com

まずはダイアログが裏で状態を覚えるために使うステート管理機能を ConfigureServicesメソッドで登録します。

publicvoid ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();
    services.AddSingleton<IBotFrameworkHttpAdapter, BotFrameworkHttpAdapter>();
    services.AddTransient<IBot, MyBot>();

    // for state
    services.AddSingleton<IStorage, MemoryStorage>();
    services.AddSingleton<UserState>();
    services.AddSingleton<ConversationState>();
}

Bot にダイアログを追加します。ComponentDialogを継承した MainDialogクラスを作ります。

using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;

namespace BotStepByStep
{
    publicclass MainDialog : ComponentDialog
    {
        public MainDialog() : base("MainDialog")
        {
            AddDialog(new TextPrompt("TextPrompt"));
            AddDialog(new WaterfallDialog("Steps", new WaterfallStep[]
            {
                async (context, cancellationToken) =>
                {
                    await context.Context.SendActivityAsync("最初のステップです。");
                    return await context.PromptAsync("TextPrompt", new PromptOptions
                    {
                        Prompt = MessageFactory.Text("名前を入力してください"),
                    });
                },
                async (context, cancellationToken) =>
                {
                    var name = context.Result asstring;
                    context.Values["name"] = name;
                    return await context.PromptAsync("TextPrompt", new PromptOptions
                    {
                        Prompt = MessageFactory.Text("何か気の利いたコメントを入力してください"),
                    });
                },
                async (context, cancellationToken) =>
                {
                    var comment = context.Result asstring;
                    var name = context.Values["name"] asstring;
                    await context.Context.SendActivityAsync($"{name}さんの好きな言葉は「{comment}」ですね。");
                    return await context.EndDialogAsync();
                },
            }));

            InitialDialogId = "Steps";
        }
    }
}

プロンプトが入力をいい感じにしてくれるやつです。複雑なダイアログのサンプルは以下のリポジトリーにあります。

github.com

そして ConfigureServicesでダイアログを登録して

publicvoid ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();
    services.AddSingleton<IBotFrameworkHttpAdapter, BotFrameworkHttpAdapter>();
    services.AddTransient<IBot, MyBot>();

    // for state
    services.AddSingleton<IStorage, MemoryStorage>();
    services.AddSingleton<ConversationState>();

    // dialog
    services.AddSingleton<Dialog, MainDialog>();
}

Bot ではダイアログを起動して、ダイアログの処理が終わったらステートを保存するようにします。

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

namespace BotStepByStep
{
    publicclass MyBot : ActivityHandler
    {
        public MyBot(ConversationState conversationState, Dialog dialog)
        {
            ConversationState = conversationState;
            Dialog = dialog;
        }

        public ConversationState ConversationState { get; }
        public Dialog Dialog { get; }

        protectedoverride async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
        {
            await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>("DialogState"), cancellationToken);
            await ConversationState.SaveChangesAsync(turnContext, cancellationToken: cancellationToken);
        }
    }
}

動かしてみると…

f:id:okazuki:20190704234617p:plain

ちゃんと定義したダイアログに従って会話が今どこにいるのかという情報を保持しつつ処理が進んでいくのがわかると思います。

まとめ

そろそろ、Bot Framework 落ち着いたかな??


Viewing all articles
Browse latest Browse all 1387

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>