Watson Conversationを使って天気予報をするSlack botを作ってみました。
この投稿では、その作成手順を紹介します。
以前投稿した記事では、ランダムに天気を答えさせていましたが、今回は天気予報APIを呼び出して実際に天気予報を行います。
処理のイメージは以下の図のような感じです。
Watson Conversationで入力文章を解析し、天気予報API(Weather Company Data)を呼び出します。
今回Slackとのやり取りと処理フローの作成にはNode-REDを使っています。
作成手順は以下のようになります。
- Bluemix上にNode-REDをセットアップ
- Watson Conversation、Weather Company DataをNode-REDにバインド
- Watson ConversationのWorkspace作成
- SlackにBotを追加
- Node-REDフローの作成
Weather Company Data APIとは
Bluemixのサービスのひとつで、天気情報が取得できるAPIです。
ドキュメントはこちら→Weather Company Data の概要
Node-REDとは
Node-REDについては以下の投稿が参考になります。
BluemixのNode-REDで簡単Webアプリ開発
今回、Slackアクセスするためのノードを追加します。
Node-REDパレットに新しいノードを追加する方法については以下の投稿が参考になります。
BluemixのNode-REDパレットにノードを追加する
1. Bluemix上にNode-REDをセットアップ
「カタログ」の「ボイラープレート」カテゴリの中から「Node-RED Starter」を選択します。
任意のアプリ名、ホスト名を付けて、「作成」ボタンを押します。
作成後、「DOWNLOAD STARTER CODE」ボタンを押して、STARTER CODEをDownloadします。
zipファイルがDownloadされるので、任意のディレクトリに解凍します。
解凍するとpackage.jsonがあるので、これに**"node-red-contrib-slack":"*"**の記述を追加します。この追加したライブラリがSlackにアクセスするためのノードです。
追加する場所は以下の修正例を参考にしてください。
{
"name" : "node-red-bluemix",
"version" : "0.5.0",
"dependencies": {
"when": "~3.x",
"mongodb": "~1.4.x",
"nano": "~5.11.0",
"cfenv":"~1.0.0",
"feedparser":"~0.19.2",
"redis":"~0.10.1",
"node-red": "0.x",
"node-red-bluemix-nodes":"1.x",
"node-red-node-watson":"0.x",
"node-red-node-openwhisk":"0.x",
"node-red-node-cf-cloudant":"0.x",
"node-red-contrib-scx-ibmiotapp":"0.x",
"node-red-contrib-ibmpush":"0.x",
"node-red-contrib-bluemix-hdfs":"0.x",
"node-red-nodes-cf-sqldb-dashdb":"0.x",
"node-red-contrib-slack":"*"
},
"scripts": {
"start": "node --max-old-space-size=384 node_modules/node-red/red.js --settings ./bluemix-settings.js -v"
},
"engines": {
"node": "4.x"
}
}
package.jsonのあるディレクトリで、以下のコマンドでNode-REDをBluemixにデプロイします。
コマンド:cf push
========
以下は実行結果(以下のようにstateがrunningになれば正常にデプロイ出来ています)
requested state: started
instances: 1/1
usage: 512M x 1 instances
urls: xxxxxx.mybluemix.net
last uploaded: Sat Nov 19 14:58:25 UTC 2016
stack: unknown
buildpack: SDK for Node.js(TM) (ibm-node.js-4.6.0, buildpack-v3.8-20161006-1211)
state since cpu memory disk details
#0 running 2016-11-20 12:00:48 AM 0.0% 318.8M of 512M 448.2M of 1G
2. Watson Conversation、Weather Company DataをNode-REDにバインド
Bluemixのコンソールでアプリケーションの画面を開き「接続」メニューの「新規に接続」ボタンを押します。
「Watson」カテゴリの「Conversation」を選択します。
続けて「新規に接続」ボタンを押して、Weather Company Dataを追加します。
「データ&分析」カテゴリの「Weather Company Data」を選択します。
3. Watson ConversationのWorkspace作成
この投稿では、会話定義は行わず作成済みの会話をImportしてWorkspaceを作成します。
以下の会話定義をコピーして、任意の名前(例:workspace-WeatherCompany.json)で保存します。(注意!!文字コードはUTF-8で保存してください。拡張子はjsonにしてください)
{"name":"天気予報","created":"2016-09-30T08:35:40.682Z","intents":[{"intent":"あいさつ","created":"2016-11-19T13:27:08.415Z","updated":"2016-11-19T13:28:07.949Z","examples":[{"text":"おはよう","created":"2016-11-19T13:27:36.419Z","updated":"2016-11-19T13:27:36.419Z"},{"text":"オハヨウ","created":"2016-11-19T13:27:30.984Z","updated":"2016-11-19T13:27:30.984Z"},{"text":"こんにちは","created":"2016-11-19T13:27:15.601Z","updated":"2016-11-19T13:27:15.601Z"},{"text":"こんばんは","created":"2016-11-19T13:27:47.696Z","updated":"2016-11-19T13:27:47.696Z"},{"text":"コンニチハ","created":"2016-11-19T13:27:26.419Z","updated":"2016-11-19T13:27:26.419Z"},{"text":"はじめまして","created":"2016-11-19T13:27:59.902Z","updated":"2016-11-19T13:27:59.902Z"},{"text":"おはようございます","created":"2016-11-19T13:27:42.055Z","updated":"2016-11-19T13:27:42.055Z"},{"text":"Hello","created":"2016-11-19T13:28:03.555Z","updated":"2016-11-19T13:28:03.555Z"},{"text":"Hi","created":"2016-11-19T13:28:07.949Z","updated":"2016-11-19T13:28:07.949Z"},{"text":"今日は","created":"2016-11-19T13:27:21.230Z","updated":"2016-11-19T13:27:21.230Z"},{"text":"今晩は","created":"2016-11-19T13:27:54.174Z","updated":"2016-11-19T13:27:54.174Z"}],"description":null},{"intent":"天気","created":"2016-10-03T05:45:11.444Z","updated":"2016-11-19T13:53:28.837Z","examples":[{"text":"予報","created":"2016-10-03T05:45:11.444Z","updated":"2016-10-03T05:45:11.444Z"},{"text":"今日の天気は?","created":"2016-10-03T05:45:11.444Z","updated":"2016-10-03T05:45:11.444Z"},{"text":"天候が気になる","created":"2016-11-19T13:53:16.757Z","updated":"2016-11-19T13:53:16.757Z"},{"text":"天気","created":"2016-10-03T05:45:11.444Z","updated":"2016-10-03T05:45:11.444Z"},{"text":"天気は?","created":"2016-10-03T05:45:11.444Z","updated":"2016-10-03T05:45:11.444Z"},{"text":"天気いいかな?","created":"2016-11-19T13:53:08.689Z","updated":"2016-11-19T13:53:08.689Z"},{"text":"天気がきになる","created":"2016-11-19T13:53:28.837Z","updated":"2016-11-19T13:53:28.837Z"},{"text":"天気予報","created":"2016-10-03T05:45:11.444Z","updated":"2016-10-03T05:45:11.444Z"},{"text":"どんな感じ?","created":"2016-11-19T13:52:51.367Z","updated":"2016-11-19T13:52:51.367Z"},{"text":"晴れかな?","created":"2016-10-03T05:45:11.444Z","updated":"2016-10-03T05:45:11.444Z"},{"text":"曇りかな?","created":"2016-10-03T05:45:11.444Z","updated":"2016-10-03T05:45:11.444Z"},{"text":"雨かな?","created":"2016-10-03T05:45:11.444Z","updated":"2016-10-03T05:45:11.444Z"}],"description":null}],"updated":"2016-11-19T15:13:51.941Z","entities":[{"type":null,"entity":"place","source":null,"values":[{"value":"三重","created":"2016-10-04T06:19:58.683Z","updated":"2016-10-04T06:19:58.683Z","metadata":null,"synonyms":[]},{"value":"京都","created":"2016-10-04T06:17:45.335Z","updated":"2016-10-04T06:17:45.335Z","metadata":null,"synonyms":[]},{"value":"佐賀","created":"2016-10-04T06:18:12.604Z","updated":"2016-10-04T06:18:12.604Z","metadata":null,"synonyms":[]},{"value":"兵庫","created":"2016-10-04T06:19:34.236Z","updated":"2016-10-04T06:19:34.236Z","metadata":null,"synonyms":[]},{"value":"北海道","created":"2016-10-03T05:54:57.004Z","updated":"2016-10-03T05:55:08.368Z","metadata":null,"synonyms":["ほっかいどう"]},{"value":"千葉","created":"2016-10-04T06:18:36.975Z","updated":"2016-10-04T06:18:36.975Z","metadata":null,"synonyms":[]},{"value":"和歌山","created":"2016-10-04T06:20:32.361Z","updated":"2016-10-04T06:20:32.361Z","metadata":null,"synonyms":[]},{"value":"埼玉","created":"2016-10-04T06:18:08.635Z","updated":"2016-10-04T06:18:08.635Z","metadata":null,"synonyms":[]},{"value":"大分","created":"2016-10-04T06:16:50.603Z","updated":"2016-10-04T06:16:50.603Z","metadata":null,"synonyms":[]},{"value":"大阪","created":"2016-10-04T06:16:55.584Z","updated":"2016-10-04T06:16:55.584Z","metadata":null,"synonyms":[]},{"value":"奈良","created":"2016-10-04T06:19:23.624Z","updated":"2016-10-04T06:19:23.624Z","metadata":null,"synonyms":[]},{"value":"宮城","created":"2016-10-04T06:20:02.371Z","updated":"2016-10-04T06:20:02.371Z","metadata":null,"synonyms":[]},{"value":"宮崎","created":"2016-10-04T06:20:10.637Z","updated":"2016-10-04T06:20:10.637Z","metadata":null,"synonyms":[]},{"value":"富山","created":"2016-10-04T06:18:59.191Z","updated":"2016-10-04T06:18:59.191Z","metadata":null,"synonyms":[]},{"value":"山口","created":"2016-10-04T06:20:20.496Z","updated":"2016-10-04T06:20:20.496Z","metadata":null,"synonyms":[]},{"value":"山形","created":"2016-10-04T06:20:16.563Z","updated":"2016-10-04T06:20:16.563Z","metadata":null,"synonyms":[]},{"value":"山梨","created":"2016-10-04T06:20:27.283Z","updated":"2016-10-04T06:20:27.283Z","metadata":null,"synonyms":[]},{"value":"岐阜","created":"2016-10-04T06:17:50.881Z","updated":"2016-10-04T06:17:50.881Z","metadata":null,"synonyms":[]},{"value":"岡山","created":"2016-10-04T06:16:59.595Z","updated":"2016-10-04T06:16:59.595Z","metadata":null,"synonyms":[]},{"value":"岩手","created":"2016-10-04T06:16:40.879Z","updated":"2016-10-04T06:16:40.879Z","metadata":null,"synonyms":[]},{"value":"島根","created":"2016-10-04T06:18:30.877Z","updated":"2016-10-04T06:18:30.877Z","metadata":null,"synonyms":[]},{"value":"広島","created":"2016-10-04T06:19:38.397Z","updated":"2016-10-04T06:19:38.397Z","metadata":null,"synonyms":[]},{"value":"徳島","created":"2016-10-04T06:18:41.298Z","updated":"2016-10-04T06:18:41.298Z","metadata":null,"synonyms":[]},{"value":"愛媛","created":"2016-10-04T06:16:45.164Z","updated":"2016-10-04T06:16:45.164Z","metadata":null,"synonyms":[]},{"value":"愛知","created":"2016-10-04T06:16:12.728Z","updated":"2016-10-04T06:16:12.728Z","metadata":null,"synonyms":[]},{"value":"新潟","created":"2016-10-04T06:19:29.165Z","updated":"2016-10-04T06:19:29.165Z","metadata":null,"synonyms":[]},{"value":"東京","created":"2016-10-03T05:54:36.913Z","updated":"2016-10-03T05:54:53.054Z","metadata":null,"synonyms":["とうきょう","東京都"]},{"value":"栃木","created":"2016-10-04T06:18:47.632Z","updated":"2016-10-04T06:18:47.632Z","metadata":null,"synonyms":[]},{"value":"沖縄","created":"2016-10-03T06:32:08.037Z","updated":"2016-10-03T06:32:12.926Z","metadata":null,"synonyms":["おきなわ"]},{"value":"滋賀","created":"2016-10-04T06:18:15.995Z","updated":"2016-10-04T06:18:15.995Z","metadata":null,"synonyms":[]},{"value":"熊本","created":"2016-10-04T06:17:55.294Z","updated":"2016-10-04T06:17:55.294Z","metadata":null,"synonyms":[]},{"value":"石川","created":"2016-10-04T06:16:31.029Z","updated":"2016-10-04T06:16:31.029Z","metadata":null,"synonyms":[]},{"value":"神奈川","created":"2016-10-04T06:17:15.690Z","updated":"2016-10-04T06:17:15.690Z","metadata":null,"synonyms":[]},{"value":"福井","created":"2016-10-04T06:19:42.306Z","updated":"2016-10-04T06:19:42.306Z","metadata":null,"synonyms":[]},{"value":"福岡","created":"2016-10-04T06:19:46.204Z","updated":"2016-10-04T06:19:46.204Z","metadata":null,"synonyms":[]},{"value":"福島","created":"2016-10-04T06:19:50.400Z","updated":"2016-10-04T06:19:50.400Z","metadata":null,"synonyms":[]},{"value":"秋田","created":"2016-10-04T06:16:26.604Z","updated":"2016-10-04T06:16:26.604Z","metadata":null,"synonyms":[]},{"value":"群馬","created":"2016-10-04T06:18:00.033Z","updated":"2016-10-04T06:18:00.033Z","metadata":null,"synonyms":[]},{"value":"茨城","created":"2016-10-04T06:16:37.051Z","updated":"2016-10-04T06:16:37.051Z","metadata":null,"synonyms":[]},{"value":"長崎","created":"2016-10-04T06:19:02.952Z","updated":"2016-10-04T06:19:02.952Z","metadata":null,"synonyms":[]},{"value":"長野","created":"2016-10-04T06:19:07.350Z","updated":"2016-10-04T06:19:07.350Z","metadata":null,"synonyms":[]},{"value":"青森","created":"2016-10-04T06:16:21.055Z","updated":"2016-10-04T06:16:21.055Z","metadata":null,"synonyms":[]},{"value":"静岡","created":"2016-10-04T06:18:20.868Z","updated":"2016-10-04T06:18:20.868Z","metadata":null,"synonyms":[]},{"value":"香川","created":"2016-10-04T06:17:04.111Z","updated":"2016-10-04T06:17:04.111Z","metadata":null,"synonyms":[]},{"value":"高知","created":"2016-10-04T06:18:04.365Z","updated":"2016-10-04T06:18:04.365Z","metadata":null,"synonyms":[]},{"value":"鳥取","created":"2016-10-04T06:18:53.614Z","updated":"2016-10-04T06:18:53.614Z","metadata":null,"synonyms":[]},{"value":"鹿児島","created":"2016-10-04T06:17:09.051Z","updated":"2016-10-04T06:17:09.051Z","metadata":null,"synonyms":[]}],"created":"2016-10-03T05:54:29.463Z","updated":"2016-10-03T05:59:22.146Z","open_list":false,"description":null}],"language":"ja","metadata":null,"description":"Weather Company Data APIで天気予報","dialog_nodes":[{"go_to":null,"output":{"text":"<? input.text ?>!あいさつ以外に 天気予報が出来ます。(質問例:天気を教えて、東京の天気は?)"},"parent":null,"context":null,"created":"2016-11-19T13:26:42.726Z","metadata":null,"conditions":"#あいさつ","description":null,"dialog_node":"node_4_1479562007404","previous_sibling":"node_4_1475474627358"},{"go_to":null,"output":{"text":"$(place)の天気は1時間後には<? input.text ?>になりそうです。"},"parent":"node_5_1475474674293","context":null,"created":"2016-11-19T13:16:28.061Z","metadata":null,"conditions":"true","description":null,"dialog_node":"node_3_1479561392701","previous_sibling":null},{"go_to":null,"output":{"text":"weather.search"},"parent":"node_4_1475474627358","context":{"place":"@place"},"created":"2016-10-03T06:04:32.913Z","metadata":null,"conditions":"@place","description":null,"dialog_node":"node_5_1475474674293","previous_sibling":null},{"go_to":null,"output":{"text":"よくわかりません。もう一度言ってください(質問例:天気を教えて?、千葉の天気は?)"},"parent":null,"context":null,"created":"2016-10-03T01:17:45.854Z","metadata":null,"conditions":"anything_else","description":null,"dialog_node":"node_2_1475457466501","previous_sibling":"node_4_1479562007404"},{"go_to":null,"output":{"text":"こんにちは、あいさつと天気予報が出来ます。(質問例:こんにちは、天気は?、東京の天気は?)"},"parent":null,"context":null,"created":"2016-10-03T02:06:24.477Z","metadata":null,"conditions":"conversation_start","description":null,"dialog_node":"node_6_1475460385696","previous_sibling":null},{"go_to":{"return":null,"selector":"condition","dialog_node":"node_5_1475474674293"},"output":{},"parent":null,"context":null,"created":"2016-10-03T06:03:45.975Z","metadata":null,"conditions":"#天気","description":null,"dialog_node":"node_4_1475474627358","previous_sibling":"node_6_1475460385696"},{"go_to":{"return":null,"selector":"user_input","dialog_node":"node_5_1475474674293"},"output":{"text":"どこの天気ですか?(回答例:東京、千葉、北海道)"},"parent":"node_4_1475474627358","context":null,"created":"2016-11-19T13:11:59.121Z","metadata":null,"conditions":"true","description":null,"dialog_node":"node_1_1479561123876","previous_sibling":"node_5_1475474674293"}],"workspace_id":"dd5d8e91-49ca-46cd-b8c7-22eeea4d7a09"}
Conversation toolを起動します。
Conversation Serviceの「管理」タブから「Launch tool」ボタンを押して起動します。
「Create」ボタンの右にある、「Import」ボタンを押します。
先ほど保存した会話定義をImportします。
ImportのタイプはEverythingを選択します。「Import」ボタンを押します。
Importが完了すると「天気予報」という名前のWorkspaceが出来るので、右上にあるメニューから「View details」を選択します。
詳細画面では、WorkspaceIDが表示されます。
こちらのIDは後ほど使用しますので控えておいてください。
4. SlackにBotを追加
以前の投稿で説明した"2. SlackにBotを追加"を実施してください(下記リンク参照)。
Bot作成後、BotのAPI Tokenは後ほど使用しますので控えておきます。
以前の投稿:会話エンジンにWatson Conversationを使ったSlack botを作ってみた
5. Node-REDフローの作成
Bluemixコンソールのアプリケーションの画面から「アプリ表示」ボタンを押して、Node-REDにアクセスします。
「Go to your Node-RED flow editor」ボタンを押して、フローエディターを開きます。
右上にあるメニューから、「Import」→「Clipboard」を選択し、下のNode-REDフロー定義を貼り付けます。
[{"id":"43908569.0dd2ec","type":"watson-conversation-v1","z":"4f545adf.c1ce54","name":"Watson Conversation","workspaceid":"","multiuser":false,"context":true,"x":354.25006103515625,"y":213.7142333984375,"wires":[["aa34f61e.c3abd8"]]},{"id":"18e9ea7e.6d77f6","type":"function","z":"4f545adf.c1ce54","name":"返答テキスト抽出","func":"msg.payload = msg.payload.output.text[0]\nreturn msg;","outputs":1,"noerr":0,"x":445.2501220703125,"y":337.9642333984375,"wires":[["84aec0d0.e007d"]]},{"id":"aa34f61e.c3abd8","type":"switch","z":"4f545adf.c1ce54","name":"アクション(外部API呼び出し)判定","property":"payload.output.text","propertyType":"msg","rules":[{"t":"eq","v":"weather.search","vt":"str"},{"t":"else"}],"checkall":"true","outputs":2,"x":187.75006103515625,"y":324.4642333984375,"wires":[["3ab74942.a298a6"],["18e9ea7e.6d77f6"]]},{"id":"3ab74942.a298a6","type":"function","z":"4f545adf.c1ce54","name":"都道府県名(エンティティ)抽出","func":"msg.payload = encodeURI(msg.payload.entities[0].value)\nreturn msg;","outputs":1,"noerr":0,"x":481.25,"y":295.7142333984375,"wires":[["adb754b8.876338"]]},{"id":"adb754b8.876338","type":"http request","z":"4f545adf.c1ce54","name":"地点名を経度・緯度に変換(with Google API)","method":"GET","ret":"obj","url":"https://maps.googleapis.com/maps/api/geocode/json?address={{payload}}&sensor=false","tls":"","x":791.3572387695312,"y":297.60711669921875,"wires":[["99def6ac.efece8"]]},{"id":"99def6ac.efece8","type":"function","z":"4f545adf.c1ce54","name":"経度・緯度抽出","func":"var location = msg.payload.results[0].geometry.location;\nvar lat = location.lat;\nvar lng = location.lng;\nmsg.payload = lat + \",\" + lng;\nreturn msg;","outputs":1,"noerr":0,"x":332.71435546875,"y":81,"wires":[["d7cc8e48.a381b"]]},{"id":"95cc8b2f.6af5f8","type":"function","z":"4f545adf.c1ce54","name":"1時間後の天気抽出","func":"msg.payload = msg.forecasts[0].phrase_32char;\nreturn msg;","outputs":1,"noerr":0,"x":694.7857666015625,"y":81,"wires":[["43908569.0dd2ec"]]},{"id":"d7cc8e48.a381b","type":"weather_insights","z":"4f545adf.c1ce54","name":"天気予報","service":"/forecast/hourly/48hour.json","geocode":"","units":"m","language":"ja","x":514.96435546875,"y":81.392822265625,"wires":[["95cc8b2f.6af5f8"]]},{"id":"c1be6300.b3c0c","type":"Slack Bot In","z":"4f545adf.c1ce54","name":"Slack In","apiToken":"","channel":"","x":72,"y":215.4642333984375,"wires":[["43908569.0dd2ec"]]},{"id":"84aec0d0.e007d","type":"Slack Bot Out","z":"4f545adf.c1ce54","name":"Slack out","apiToken":"","channel":"","x":643,"y":338.4642333984375,"wires":[]}]
Importすると以下のようなフローが作成されます。
このフローにBotのAPI TokenとWatson ConversationのWorkspace IDを設定します。
「Slack In」のノードをクリックして開き、前のステップで控えておいたBot API Tokenを入力します。入力後、「Done」ボタンを押します。
「Slack Out」のノードも同様に、クリックして開き、前のステップで控えておいたBot API Tokenを入力します。入力後、「Done」ボタンを押します。
「Watson Conversation」のノードをクリックして、前のステップで控えておいたWorkspaceIDを入力します。入力後、「Done」ボタンを押します。
「Deploy」ボタンを押して、Node-REDフローをデプロイします。
Node-REDのフローをデプロイ後、SlackのBotがactiveになります。
DIRECT MESSAGESで何か話しかけると以下のような会話ができます。
これで、Watson Conversationを使って天気予報をするSlack botが完成しました。