Node.js
BotFramework
chatbot

MS BotFramework(Azure Bot Service) for Node.js で使えるTips

はじめに

  • MS BotFrameworkはMicrosoftが提供する多チャネル対応のChatBot開発Frameworkです。
  • Node.js / C# に対応しています。
  • ユーザーの状態、セッション管理やデータ管理(永続化)などが簡単に行なえます。
  • 同MicrosoftのLUISと連携させたりすれば、イケてるChatBotが作れる気がします。
  • サーバーは自前で用意する必要があります。

BotFramework for Node.jsの基本

  • npm packageのBotBuilderによって実装されている。
  • 基本はwaterflowモデルにて実行される。

waterflowモデル

  • 基本的に上のfunctionから実行されていく
  • next(result) メソッドを呼び出すと、強制的に次のfunctionへ制御が移行する。
    resultはoptionalで指定すれば次のfunctionのargsで取得可能
    nextを使用する際はfunction自体の引数にnextを含める必要がある。(2つめのfunction)
function(session, args, next) {
    session.send('次のfunctionに遷移します');
    next({'status': 'ok'});
},
function(session, result) {
    session.send('遷移しました');
    session.send('引数は' + result.status + 'です。');
}

Prompt

  • ユーザーからの入力値(選択肢)を取得するメソッド
  • promptを出すと、BOTの制御は一旦そこで止まる
  • ユーザーが何らかのアクション(文字入力、選択肢を選ぶなど)をすると、自動的に次Functionが開始する
  • ユーザーの入力値は次functionの引数に設定されている
  • テキスト入力を受け付けるには

    • builder.Prompts.text(session, messageObject) について
      • ユーザーの自由入力を待つ
      • 次functionの引数の['response']のキーに値が格納される
      • messageObjectにattachment等を設定するとリッチなUI選択肢が表示可能
  • 選択肢を提示してユーザーの選択を受け付けるには

    • builder.Prompts.choice(session, text, 'choice1|choice2|choice3', {retryPrompt: '正しい選択肢を選んで下さい'})
      • choiceはユーザーに選択肢を与える
      • 次functionの引数の['response']['entity']に選択した値が格納される
      • 選択肢はArrayも設定可能
      • retryPromptを設定すると、ユーザーが選択肢を選ばなかった場合(直接文字入力など)に表示する文言を設定可能

session

  • session内にユーザー固有の情報や、BOTの接続チャネル、今までのメッセージ履歴など多くの情報を保持している
  • 頻繁に使用するものは以下
    • session.endConversation()
      • 会話を明示的に終了する。
    • session.dialogData
      • そのDialog(後述)内でのみ有効な一時的なデータ保存領域。
      • functionを跨いでデータを保持したい場合などに使用する。
    • session.conversationData
      • その会話内で有効なデータ領域(?)
      • session.endConversation() を呼ぶとクリアされる
    • session.userData
      • ユーザー毎にデータを永続化できる保存領域。
      • どのようにしてユーザーを特定しているかは分からないが、各ユーザー毎にデータを保存しておける。
      • session.userData.hogehoge = 'hogehoge' のようにして値を格納する。
      • delete session.userData.hogehoge 削除できる。
    • session.send(message)
      • Botにmessageを発言させる。
    • session.sendTyping()
      • Botにタイピングインジケータを表示させる
      • Botが次のアクションをしたタイミングで消える
      • プラットフォームに依る(と思う)が、一定時間経過でも自動的に消える
    • session.message.address
      • このaddressをbotの発言時に付加すると、確実に対象の相手へsendできる
      • 例えば遅延処理などで自動でreplyする際などはaddress指定しないと別のユーザーと混線する

Facebook Messenger連携

  • Facebook Messenger上で表現できるUI(jsonで記述)は以下のようにして実装可能
// FacebookAPIのQuickReplyボタン(画像カスタム)を表示
var reply = new builder.Message(session).text('カスタムボタンを出します。');

reply.sourceEvent({
    facebook: {
        quick_replies: [
            {
                content_type:"text",
                title:"はい",
                payload:"ANY STRINGS",
                image_url:"https://hogehoge.com/images/yes.png"
            },
            {
                content_type:"text",
                title:"いいえ",
                payload:"ANY_STRINGS",
                image_url:"https://hogehoge.com/images/no.png"
            }
        ]
    }
});

session.send(reply);
// FacebookAPIのSendLocationボタンを表示
var reply = new builder.Message(session).text('現在地を送信するボタンを出します。');

reply.sourceEvent({
    facebook: {
        quick_replies: [{
            content_type:"location"
        }]
    }
});

session.send(reply);

dialog

  • Botの今処理している場所(状態)をdialogという表現で管理する
  • 別のDialogを開始する
    • session.beginDialog('hogehoge/dialog_name', args)
      • hogehoge/dialog_nameのdialogを開始する
      • argsは任意だが、指定すれば次dialogへ値を渡すことができる
      • dialogを開始しても後続のコードは実行されるので、以下のようなコードは注意
function(session, args) {
    session.send('次のDialogに遷移します');
    session.beginDialog('/next/start', {'status': 'ok'});

    // beginDialogの後にreturnしていないので、
    // '/next/start'のDialogが開始するのとほぼ同時に以下のコードも実行される
    session.send('次のDialogが終わりました。');
}
  • 別のDialogに置きかえる

    • session.replaceDialog('hogehoge/dialog_name', args)
      • hogehoge/dialog_nameのdialogを開始する
      • beginDialogとは違い、replaceDialogは今までのdialogStackがすべて消える
      • つまり、hogehoge/dialog_nameが終了しても元のdialogへは戻らない
  • Dialogを終了する

    • session.endDialog()
      • 今現在のdialogを終了する
      • 引数は渡せない
      • endDialogにより、一つ前のdialogの次functionから始まる
  • Dialogを終了し、戻り先に引数を渡す

    • session.endDialogWithResult(args)
      • endDialogの引数あり版
      • 一つ前のdialogの次functionから始まり、そのfunctionの引数としてargsが渡される

終わりに