Edited at

bot-expressを使ってピザ注文受付Botを60分で作ってみる


概要

この記事ではBotの利用例でよく引き合いに出されるピザ注文受付Botを開発する方法をチュートリアル形式で解説します。Bot本体はNode.jsベースで開発し、コンシェルジュ型Botの開発フレームワーク:bot-expressを使ってどのくらい開発が単純化されるかを体験することを目的としています。bot-expressはNPMでインストールできます。

今回作成するBotはLINEとFacebook Messengerの両方で利用することができます。


所要時間


  • 60分


必要なスキル


  • Node.jsの基本的な知識

  • LINEまたはFacebook MessengerでBotを作成した経験


手順


LINEまたはFacebookで最低限必要な設定をおこなう

利用するメッセージプラットフォームに応じて、必要な設定をおこないます。

LINEの場合はまずLINE Business Centerにアクセスし、今回のBot用に新しいMessaging APIのビジネスアカウントを作成し、Wehookの有効化、Channel Access Tokenの生成などをおこないます。

参考:AIが入ったBotの作り方を学ぼう - Part2 LINEでビジネスアカウントを作成する

Facebook Messengerの場合はまずFacebook for Developersにアクセスし、Facebookアプリの作成・設定をおこないます。公式サイトに良いGetting Startedドキュメントがあるのでありますのでそちらを参照ください。

いずれに場合もWebhookのURLは後述のBot本体を開発した後に設定しますので現時点では設定不要です。


dialogflowでピザ注文を認識できるagentを作成する

dialogflowは自然言語処理のサービスです。ユーザーが発話した文章が何を意図しているのかを特定するために利用します。また、その文章の中からパラメーターを抽出する機能も備えています。2018年5月現在、基本的な機能は無償で利用できますのでまずは下記dialogflowのサイトにアクセスしてアカウントを作成してください。

dialogflow

アカウントが作成できたらdialogflowにログインし、Agentを新規に作成します。

最初にEntityを登録します。これはユーザーが発話する文章の中でBotが抽出したいパラメータになります。今回はピザの種類(pizza)とサイズ(size)という2つのパラメーターをEntityとして登録します。



次にIntentを登録します。Intentはユーザーが何を求めているのかを表すものです。今回は「ピザの宅配をお願いします」というIntentを作成します。このIntentに複数の例文を登録することでdialogflowはそのIntentについて学習をおこない、次第に少々の表記揺れがあってもユーザーの意図を認識できるようになります。

例文には全くパラメーターが入っていないパターン(ex.ピザを注文したいのですが)と、パラメーターが入っているパターン(マリナーラのLサイズをお願いたいのですが)をいくつか登録します。パラメーターが入っているパターンの例文を登録すると、自動的にパラメーター部分が黄色くマークされるはずです。これによってdialogflowは文章からパラメーターを抽出してくれるようになります。

そしてこのIntentのActionを handle-pizza-order と設定してください。

このActionで指定した値をもとにbot-expressはどのスキルを利用するかを決定します。後ほどスキルスクリプトを handle-pizza-order.js というファイル名で作成しますが、そのファイル名とこのActionが一致している必要があります。


Bot本体の開発準備をおこなう

ソースコードを入れるディレクトリを作成し、gitレポジトリを初期化しておきます。

$ mkdir pizza-bot

$ cd pizza-bot/
$ git init

.gitignoreファイルを作成してgitの管理対象外とするファイルを記述しておきます。


/.gitignore

.DS_Store

.gitignore
node_modules
npm-debug.log

npm initコマンドでpackage.jsonファイルを作成し、bot-expressをインストール

します。

$ npm init --yes

$ npm install --save express bot-express

インストール中にskillディレクトリとindex.jsスクリプトを自動生成するか聞いてきますので「y」と入力して自動生成させます。

May I create skill directory and index.js for you? (y/n): y

これでカレントディレクトリ配下には下記の4つのファイル・ディレクトリが存在しているはずです。

index.js    node_modules    package.json    skill

自動生成されたindex.jsスクリプトにはbot-expressの初期設定が含まれています。今回はこれ以上変更する必要はありませんが、詳細な設定オプションについてはbot-expressのドキュメントを参照してみてください。

また、skillディレクトリにはBotのスキルを1スキル1ファイルという形で作成していくことになります。


Bot本体で「スキル」を作成する

早速skillディレクトリ直下にピザの注文を受け付けるスキル「handle-pizza-order.js」を下記コードの通り作成します。このファイル名はdialogflowで設定したIntentのactionと同一である必要があります。


/skill/handle-pizza-order.js

'use strict';

module.exports = class HandlePizzaOrder {

// コンストラクター。このスキルで必要とする、または指定することができるパラメータを設定します。
constructor() {
this.required_parameter = {
pizza: {
message_to_confirm: {
type: "template",
altText: "ご注文のピザはお決まりでしょうか? マルゲリータ、マリナーラからお選びください。",
template: {
type: "buttons",
text: "ご注文のピザはお決まりでしょうか?",
actions: [
{type:"postback",label:"マルゲリータ",data:"マルゲリータ"},
{type:"postback",label:"マリナーラ",data:"マリナーラ"}
]
}
}
},
size: {
message_to_confirm: {
type: "template",
altText: "サイズはいかがいたしましょうか? S、M、Lからお選びください。",
template: {
type: "buttons",
text: "サイズはいかがいたしましょうか?",
actions: [
{type:"postback",label:"S",data:"S"},
{type:"postback",label:"M",data:"M"},
{type:"postback",label:"L",data:"L"}
]
}
}
},
address: {
message_to_confirm: {
type: "text",
text: "お届け先の住所を教えていただけますか?"
}
},
name: {
message_to_confirm: {
type: "text",
text: "最後に、お客様のお名前を教えていただけますか?"
}
}
};
}

// パラメーターが全部揃ったら実行する処理を記述します。
async finish(bot, event, context){
let message = {
text: `${context.confirmed.name} 様、ご注文ありがとうございました!${context.confirmed.pizza}${context.confirmed.size}サイズを30分以内にご指定の${context.confirmed.address}までお届けに上がります。`
};

await bot.reply(message);
}
};


まずコンストラクターにこのスキルで必要となるパラメーターをthis.required_parameterのプロパティとして設定しています。ここに設定したパラメーターはすべて集まるまでbot-expressがユーザーにメッセージを送信して確認(質問)します。

そのときに送信するメッセージを各パラメーターごとにmessage_to_confirmプロパティで設定できます。このプロパティはサポートされているメッセージプラットフォーム(LINEとFacebook Messenger)のいずれかのフォーマットで設定します。今回はLINEのメッセージフォーマットで記述しています。

それぞれのメッセージフォーマットについては下記の公式APIリファレンスを参照ください。

次にfinishメソッドを実装しています。このメソッドは必要なパラメーターが全部揃ったら実行されます。このメソッドには5つの引数が与えられており、中でも特に重要なのが第三引数のcontextです。このオブジェクトには確定したパラメーターの値が収められています。今回の例だとpizza, size, address, nameの情報がcontext.confirmedに入っています。開発者はこの情報を利用して最終処理を記述することができます。

また、第一引数のbotにはreply()メソッドが用意されておりLINEやFacebook Messengerといったメッセージプラットフォームの違いを意識することなく返信ができるようになっています。


Bot本体をデプロイする

作成したBot本体のコードをクラウドへデプロイします。この記事ではクラウドにherokuを利用します。まずheroku上に新しいアプリケーションを作成します。

$ heroku apps:create

Creating app... done, ⬢ powerful-reaches-12345
https://powerful-reaches-12345.herokuapp.com/ | https://git.heroku.com/powerful-reaches-12345.git

今回、私の環境で作成されたアプリケーション名は powerful-reaches-12345 ですが、このアプリケーション名はherokuの全アプリケーションに渡りユニークな値になります。以降はこのアプリケーション名をご自身の値に置き換えて進めてください。

このアプリのgitレポジトリを登録します。

$ heroku git:remote -a powerful-reaches-12345

set git remote heroku to https://git.heroku.com/powerful-reaches-12345.git

herokuでBot本体を起動するための設定ファイルである Procfileを作成し、書きの通りに記述します。


/Procfile

web: node index.js


次にBot本体の環境変数に必要なパラメーターを設定します。

LINEやFacebook Messenger、そしてdialogflowと連携するにあたり、それぞれのサービスのアプリケーションIDやトークンをBot本体に設定する必要があるためです。これらはメインスクリプトの中で環境変数を通じて設定できるようになっています。

$ heroku config:set GOOGLE_PROJECT_ID=あなたの値

$ heroku config:set GOOGLE_CLIENT_EMAIL=あなたの値
$ heroku config:set GOOGLE_PRIVATE_KEY=あなたの値
$ heroku config:set LINE_CHANNEL_ID=あなたの値
$ heroku config:set LINE_CHANNEL_SECRET=あなたの値

GOOGLE_PROJECT_IDとGOOGLE_CLIENT_EMAILの値はDialogflowのAgent設定画面のGeneralタブで確認できます。下図のProject IDがGOOGLE_PROJECT_ID、Service AccountがGOOGLE_CLIENT_EMAILです。

GOOGLE_PRIVATE_KEYは、Dialogflowの同設定画面からService Accountのリンクをクリックし、Google Cloud Platformのサービスアカウントの設定画面から生成します。

Dialogflow Integrationsの右端になるメニューからキー生成を選択します。

JSONを選択して作成をクリックします。

JSON形式の鍵ファイルがダウンロードされます。このJSONファイルを開くと、中にprivate_keyの項目がありますのでその値をGOOGLE_PRIVATE_KEYに設定してください。

gitコマンドでコードをherokuにデプロイします。

$ git add .

$ git commit -m "first commit"
$ git push heroku master

これでBot本体はクラウド上で稼働し始めました。


メッセンジャー側にWebhook URLを設定する

Webhook URLとはBot本体がメッセンジャーからのイベント通知を受け取るためのURLです。このURLは自由に設定できますが、本記事の設定では下記のフォーマットになります。

https://あなたのアプリケーション名.herokuapp.com/webhook

このWebhookのURLをLINEまたはFacebook Messenger側に設定します。


LINEの場合

下記のチュートリアルを参照し、Messaging APIのChannelを作成します。そのChannelの設定に、WebhookのURLを設定します。

https://qiita.com/nkjm/items/38808bbc97d6927837cd#channel%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B


Facebook Messengerの場合

Facebookで自分が管理するアプリ一覧からBot用のアプリを選択します。左サイドバーのプロダクトからMessengerを選択し、右側のメインウィンドウの中からWebhooksセクションまでスクロールし、Webhookの設定ボタンをクリックします。

コールバックURLにWebhook URLを入力し、トークンにはFacebook App Secretを入力します。さらにmessagesとmessaging_postbacksにチェックして「確認して保存」ボタンをクリックします。

Bot本体側にテストアクセスが行われ、トークンが正しく検証できればWebhookが追加されるはずです。トークンの検証はbot-expressがおこなってくれます。


テストしよう

まず、「ピザを注文したいのですが」と話しかけてみます。するとBotは必要なパラメータを隈なく確認した上で注文を受付ます。

次に、「マリナーラのMサイズをお願いしたいのですが」と話しかけてみます。すると今回はピザの種類とサイズは確認されません。届け先と名前だけ確認され、それで注文が完了します。

これはdialogflowが自然言語処理の段階でpizza:マリナーラ, size:Mというパラメーターを抽出してくれ、それを受けたbot-expressがパラメーターを文脈情報にすでにセットしているのであらためてユーザーに確認する必要なしと判断されたためです。


DEBUG

うまく動かない、という場合はDEBUGしましょう。

環境変数にDEBUGを追加して下記のように設定します。

$ heroku config:set DEBUG=bot-express:*

そしてHeroku上のログをheroku logsコマンドにてリアルタイムに眺めます。

$ heroku logs --tail

この状態でBotにメッセージを送信してみてください。どこかでエラーになっている可能性が高いと思います。

よくある設定ミスを下記に挙げておきます。


  • 環境変数が正しくセットされていない => heroku configコマンドで環境変数名とその値をチェックしてください。

  • ソースコードがShift-JISになっている => Windowsの方は要注意。すべてUTF8で保存してください。

  • ソースのファイル名がまちがっている => 拡張子、大文字小文字、ファイルの場所をチェックしてください。


次のステップ

今回は最小限構成のスキルファイルを作成しましたが、実際にはfinish()メソッドに注文をデータベースに保存する処理を追加したり、ユーザーが返信した内容が各パラメーターに適しているか検査する処理が必要になるでしょう。

後者は各パラメータごとにparserを作成することで実装できるようになっています。parserを含むスキルファイルがbot-expressのsample_skill/handle_pizza_order.jsにありますので見てみてください。

スキルファイルの仕様について詳しくはbot-expressのドキュメントを参照してみてください。