Azure Logic AppsとCognitive Servicesを使った画像認識Slack Bot
人にAzureを使ったサンプルアプリを教えたときの資料です。長めです。
Azureを使ったことがない人でもできるよう説明やスクショを入れてあります。
つくるアプリ
AzureのLogic AppsとCognitive Serviceを使って、Slackに画像がアップロードされたらその画像の説明をするBotを作る。
Azure Functionを使ったカスタムコードの実行方法と、DocumentDBへのデータの格納、取り出し方法も末尾にあります。
得られるもの
- Azureの使い方
- Azure Logic Appsの使い方
- Azure Cognitive Servicenの使い方
- Azure DocumentDBの経験
- Azure Functionの経験
- Slack Botの作り方
- Slack event subscriptionnの理解
- Slack apiの叩き方
必要なもの
- Azureアカウント
- Slackチーム(App作成権限が必要)
- やりとげる精神力
Azure
Azureとはマイクロソフトが提供しているパブリッククラウド。
https://azure.microsoft.com/ja-jp/overview/what-is-azure/
- IaaSやPaaS,SaaSなど、100以上のサービスを提供している
- GPUや、FPGAとかもある
- 34のリージョンを持つ。
- 日本には東日本と西日本のリージョンを持ち、東阪のリージョンを使ったDR構成がとられることが多い。
- 機械学習、コンテナ、IoT,サーバレスアーキテクチャなど流行にも敏感
- MS製品だけでなく、OSS関連もあります
https://azure.microsoft.com/ja-jp/regions/
アーキテクチャや設計ガイド
https://docs.microsoft.com/ja-jp/azure/index#pivot=architecture&panel=architecture1
Logic Apps
Logic AppsとはAzureのサービスで、クラウド上でのスケーラブルな統合やワークフローを簡略化し実装するための手段を提供(種類としてはiPaaS(統合プラットフォーム as a service))。
ロジックをビジュアルデザイナーで定義可能(とはいいつつCodeを触ることがおおい)。
定義済みのマネージドAPIが用意されており、Service BusやBlobなどのAzureサービスやO365のサービスがコーディング無しでロジックを記述可能。
必要に応じてAzure Funcitonなどを利用してカスタムロジックを利用可能。
競合
nodered
https://nodered.org/
IBM Bluemixが採用しているiPaaSで、Bluemixとの親和性が高い。
OSSソフトウェアであるため、自分でカスタマイズもでき、GUI上でのフローの定義もしやすい。
現状、noderedのほうがエディタの使いやすさ、コネクションの管理、移植性などの面でLogic Appsより出来が良いと言える。
amazon simple workflow
Logic Appsの主な要素
要素 | 説明 |
---|---|
ワークフロー | Logic Apps では、ビジネス プロセスを一連の手順やワークフローとしてグラフィカルにモデル化できます。 |
マネージ コネクタ | ロジック アプリにはデータとサービスへのアクセスが必要です。 マネージ コネクタは、データへの接続とデータの操作を支援する目的で作成されています。 マネージ コネクタに関するページで、現在利用できるコネクタの一覧を確認してください。 |
トリガー | 一部のマネージ コネクタは、トリガーとしても動作します。 トリガーは、電子メールや Azure のストレージ アカウントの変更の到着などの特定のイベントに基づいて、ワークフローの新しいインスタンスを開始します。 |
アクション | ワークフローにおけるトリガーの後の各ステップは、アクションと呼ばれます。 通常、各アクションはマネージ コネクタまたはカスタム API アプリでの操作にマップされます。 |
Enterprise Integration Pack | Logic Apps には、高度な統合シナリオ向けに BizTalk の機能が含まれています。 BizTalk は、Microsoft による業界屈指の統合プラットフォームです。 Enterprise Integration Pack コネクタにより、Logic Apps ワークフローに検証や変換などを簡単に含めることができます。 |
基本的にはトリガー => アクション(この時にコネクタ使用) => 条件分岐 => アクションとつなげてワークフローを作る。 定義済みのアクションで実現できないときはアクションとしてAzure Functionなどを適宜使う。
トリガーには以下の種類がある
トリガー | 説明 |
---|---|
Request | ロジック アプリを、呼び出すエンドポイントにします |
Recurrence | 定義されているスケジュールに基づいて起動します |
HTTP | HTTP Web エンドポイントをポーリングします。 HTTP エンドポイントは、202 非同期パターンを使うか、またはアレイを返すことによって、特定のトリガー コントラクトに従う必要があります |
ApiConnection | HTTP トリガーと同様にポーリングしますが、Microsoft が管理する API を利用します |
HTTPWebhook | 手動トリガーと同様にエンドポイントを開きますが、指定された URL を呼び出して、登録および登録解除を行います |
ApiConnectionWebhook | Microsoft が管理する API を利用して、HTTPWebhook トリガーと同様に動作します |
アクション
基本的には以下がある
アクション | 説明 |
---|---|
HTTP | このアクションは、HTTP Web エンドポイントを呼び出します。 |
ApiConnection | このアクションは HTTP アクションと同様に動作しますが、Microsoft が管理する API を使います。 |
ApiConnectionWebhook | HTTPWebhook と似ていますが、Microsoft が管理する API を使います。 |
Response | このアクションは、着信呼び出しの応答を定義します。 |
Wait | この簡単なアクションは、一定の時間または特定の時刻まで待機します。 |
Workflow | このアクションは、入れ子になったワークフローを表します。 |
詳細は以下を参照
https://docs.microsoft.com/ja-jp/azure/logic-apps/logic-apps-workflow-actions-triggers
スキーマや、アクション等の中で使用できる関数などはこちらを参照
https://docs.microsoft.com/ja-jp/azure/logic-apps/logic-apps-workflow-definition-language
価格
2017年4月26日情報
アクション実行回数/月 | アクション実行あたりの料金 |
---|---|
最初の 250,000 アクション | ¥0.0816/アクション |
250,000 ~ 1,000,000 アクション | ¥0.0408/アクション |
1,000,000 ~ 50,000,000 アクション | ¥0.0153/アクション |
50,000,000 ~ 100,000,000 アクション | ¥0.00918/アクション |
100,000,000+ アクション | ¥0.005508/アクション |
他にもApp Service上に乗せることができて、その場合は従量課金ではなく、回数制限制。
Cognitive Service
AzureのAIサービス。現在18種類のAPIを提供中。
https://azure.microsoft.com/ja-jp/services/cognitive-services/
Cognitive ServiceをBotと使っていこうよというコミュニティもある。https://cogbot.connpass.com/
構築アプリ概要
Botが参加しているチャンネルに画像がアップロードされたら、
その画像をCognitive APIのcomputer vision apiを使って、説明文を返すSlack Botを作る。
以下は処理の大まかな流れ。
- Slack Appのevent subscriptionを使って、file createdイベントをLogic Appsに通知
- Logic Appsにメッセージが来たら、まずペイロードのtokenをチェックし、想定しているSlack Appからのものかチェックする。(末尾で実装しない)
- イベントのtypeをチェックする
- url_verificationであればペイロードのchallengeの中身を返す
- even_callback(file createdの時に使われる)であれば、とりあえずstatus 200を返す。
- その後ファイルの詳細情報を取得する
- ファイルが画像であるかチェックする
- 画像であればダウンロードし、cognitive apiを呼び出す。
- 最後にcognitive apiの結果をslackにポストする。
↑を作るとLogic Appsの画面は以下のようになります。
構築手順
safariではLogic Appsが正常に動作しないため、chromeかfirefoxで実施してください
- Slack - App登録
- Azure - ログイン
- Logic Apps - 作成
- Logic Apps - エンドポイント作成
- Slack - Event Subscription
- Logic Apps - レスポンス
- Logic Apps - ファイル取得
- Logic Apps - Describe image呼び出し
- Logic Apps - SlackへPost
- 動作確認
Slack - App登録
SlackにBot Appの登録を行う
-
アプリ登録
-
Botとして登録
-
Install APP
- https://api.slack.com/apps//oauth
- 上からInstall APP to teamを実行
-
Bot User OAuth Access Tokenのところに書いてある文字列がToken。メモしておく
(オプション) Team event Subscription
チームのイベント(チャンネル作成とか、botユーザが参加していない所のイベント)を取りたい場合はPermission Scopeを設定し、OAuth Access Tokenを使用。
権限を変更したい場合は、Permission Scopeを変更してから、Reinstallを実行
または、以下を実行
1. アプリマイページからClientIDとClientSecret取得1
1. redirect_url設定
- https://api.slack.com/apps/A4YRXQWGM/oauth
- のredirect urlにlogic appsのurlを指定(logic apps側でinput methodをGETにする)
1. 以下のurlをブラウザにペースト
- <app>.slack.com/oauth/authorize?client_id=xxx&scope=read
1. redirect_urlにcode=xxxxというクエリ付きでgetがくるので、code=以下をコピペ
1. Token取得
- https://hoge.slack.com/api/oauth.access?client_id=xxxxxxxxx&client_secret=xxxxxxxxxx&code=xxxxxxxxx&pretty=1
また、このaccess tokenは有効期限がないので取り扱いは注意
Azure - ログイン
言語設定
この資料は言語を英語に設定したもので作成されています。
なので、言語を英語にすることを強く推奨します。
Logic Apps - 作成
Azureポータルからログイン。
↓画像のように、+ボタンを押下後、検索バーに「logic app」と入力
リソースグループ
リソースグループとは、Azureのリソースをグループとして管理するもの。
すべてのリソースはいづれかのリソースグループに属している必要があり、リソースグループ単位で削除や複製といったことができる。1プロジェクトで1リソースグループや、1モジュールで1リソースグループなど、意味のある単位で管理されることが多い。
Logic Apps - エンドポイント作成
Logic Apps作成後、↓画像の赤枠のボタンからLogic Appsを作ったリソースグループを参照する。
作成したLogic Appsをクリックすると以下のテンプレートの画面が表示されるので、赤枠で囲っているところをクリックする。
デザイナー画面が表示されるので、
requestのUse sample payload to generate schemaリンクを押し、以下をペーストする
{"token":"XXXXXXXXXXXXXXXX","team_id":"ididididid","api_app_id":"hogehoge","event":{"type":"file_created","file":{"id":"hogeR"},"file_id":"hogehoge","user_id":"hoehoge","event_ts":"1492136993.415165"},"type":"event_callback","authed_users":["whowho"],"event_id":"hogehoge","event_time":1492136993}
event typeによって分岐
Add a switch caseを選択。以下のようにする。caseにはevent_callback,case2にはurl_verificationを指定。switchにはtype(2番目のほう)を指定。
詳細な値はCode viewから確認できる。
url_vericitcation
slackからeventを受け取る、event subscription設定の際に、eventを送るurlが正しいものか証明する必要がある。slackでevent subscriptionを設定した時に、下記のようなメッセージがくる。
{
"token": "Jhj5dZrVaK7ZwHHjRyZWjbDl",
"challenge": "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P",
"type": "url_verification"
}
それに対し、以下のようにレスポンスする必要がある。
HTTP 200 OK
Content-type: application/x-www-form-urlencoded
3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P
その処理をこれから実装する。
Logic Appsのcase 2のAdd an actionから、request/responseを追加する
ここで、Code Viewを開き、以下のように入力し、保存する。
"Case_2": {
"actions": {
"Responding_to_the_challenge": {
"inputs": {
"body": "@triggerBody()['challenge']",
"headers": {
"Content-type": "application/x-www-form-urlencoded"
},
"statusCode": 200
},
"runAfter": {},
"type": "Response"
}
},
"case": "url_verification"
}
保存後、requestのHTTP POST to this URLのところにエンドポイントURLが表示されるので
メモしておく。
Slack - Event Subscription
ファイルがslackに投稿されたことを検知するために、event subscriptionを登録する。
https://api.slack.com/apps//event-subscriptions
- Enable EventsをOnにする
- Request URLに先ほどメモしたエンドポイントURLを入力
- Verifiedになることを確認する
- Subscribe to Bot Eventsからfile_createdを追加する
- 保存
Logic Apps - レスポンス
slackがeventを送ってきて、3秒以内に2xxのレスポンス(200推奨)を3秒以内に返す必要がある。
レスポンスがないと、slackはeventの送信に失敗したと判断し、3回までevent送信をリトライする。
ちなみにevent送信量の制限は、5000/時間
バースト時は、数秒間に2000eventまで許可。
event subscriptionに関する詳細は以下を参照。
https://api.slack.com/events-api#subscriptions
switchのcaseにAdd an actionからResponseを追加し、status code 200を返すようにする。
名前をResponse_200としておく。
Logic Apps - ファイル取得
ファイル情報取得
file_createdのイベントだけではどのようなファイルがアップロードされたのか分からない為、
files.info APIを呼び出してファイル情報を取得する。
Add an actionからHTTPアクションを追加する。
Code Viewを開き、9行目の以下を
"HTTP": {
"inputs": {
"method": "",
"uri": ""
},
"runAfter": {
"Response_200": [
"Succeeded"
]
},
"type": "Http"
}
以下に変える。
"Retrieve_file_info": {
"inputs": {
"method": "GET",
"queries": {
"file": "@{triggerBody()['event']['file']['id']}",
"pretty": 1,
"token": "@parameters('slack-token')"
},
"uri": "https://slack.com/api/files.info"
},
"runAfter": {
"Response_200": [
"Succeeded"
]
},
"type": "Http"
}
また、以下のようになっているところを(63行目)
"parameters": {},
以下のように変える
"parameters": {
"slack-token": {
"defaultValue": "xoxb-XXXXXXXXXXXXX(うえで取得したAccess Token)",
"type": "String"
}
},
ファイルが画像かチェック
上記のfiles.info APIのレスポンス、pretty_type
がDescribe image APIが対応している画像フォーマット、JPEG, PNG, GIF, BMPであれば処理を続行するようにする。
Add a conditionからcondition追加
edit in advance modeにして、以下を入力
@contains(parameters('allowed_format'), body('Retrieve_file_info')?['file']?['pretty_type'])
Code viewにして、paramaterのとこにエントリを追加する。
"parameters": {
"slack-token": {
"defaultValue": "xoxb-XXXXXXXXXXXXX(うえで取得したAccess Token)",
"type": "String"
},
"allowed_format": {
"defaultValue": [
"JPEG",
"PNG",
"GIF",
"BMP"
],
"type": "Array"
}
ファイルダウンロード
IF YES, DO NOTHINGのところから、Add an actionを選び、HTTPアクション追加。
Code viewを開き、
"HTTP": {
"inputs": {
"method": "",
"uri": ""
},
"runAfter": {},
"type": "Http"
}
↑を↓に変更する。
"Retrieve_file": {
"inputs": {
"headers": {
"Authorization": "@concat('Bearer ', parameters('slack-token'))"
},
"method": "GET",
"uri": "@{body('Retrieve_file_info')['file']['url_private']}"
},
"runAfter": {},
"type": "Http"
}
Logic Apps - Describe image呼び出し
Describe image apiを呼び出し、画像の説明文を取得する。
Azureのポータルから、cognitive service apisを追加する。
作成後、ポータルからcognitive service apiのkeyを取得する。(key1だけでよい)
key取得後、Logic Appsにもどり、
Add an actionからDescribe imageを追加する。
Describe imageにコネクションを追加する。
(account keyにkey1を入力)
image sourceにimage content, image contentにretrieve fileのbodyを指定する。
Logic Apps - SlackへPost
最後にSlackへポストする。
Add an actionからSlack post messageを追加する。
サインインを行い、managed apiが使う用のトークンを取得する。
channelにgeneral, message textにcaption textを指定。caption textを指定すると
自動的にfor eachが組まれるがそのままにする。
動作確認
slackの適当なチャンネル(general推奨)で/invite @ でbotを入れる。
そのチャンネルに画像をアップロードするとgeneralチャンネルにbotからの説明文が投稿されるか
確かめる。
一応これで目的の画像認識を行うBot作成ができました。
セキュリティ面
今回はTokenとかをコードの中に書いているが、それはいろいろとよくないので、KeyVaultなどで鍵を管理し、Azure Function経由で鍵をインポートするなどの処理を入れるのが望ましい。
また、slackのeventペイロードの中に、想定しているアプリケーションからのものか確認するために、verification tokenが含まれている。なので、requestが来たら想定しているtokenを持っているか確認することが望ましい(不特定多数の第3者からのアクセスを受け付けないようにするため)。
token verification
先頭のrequestの下に、+New step -> Add a conditionから、ペイロードのtokenがverification Tokenと一致しているかチェックする機能を追加する。
verification tokenは、https://api.slack.com/apps//general から確認可能。
先頭にConditionを追加すると、後ろのロジックをCode Viewでif yesの中に移動させる必要があるので、
この辺はイケてないところだと思います。アクションとかの順序がデザイナー上で自由に変えられたらいいんですが...(noderedはできる)
作ってるときにおかしくなったら
保存したタイミングの状態がすべて保持されているので、JSONがおかしくなったら、
↓のversionsから好きな状態に戻ったり、参照することができる
追加コンテンツ
カスタムロジックやデータベースを使いたい場合は多いだろうということで、
Azure Functionの使い方とDocumentDBを使った例を紹介します。
カスタムコードの実行方法
これまではAPIを呼んで組み合わせることだけを行ったが、取得したデータに対して何らかの処理をするカスタムロジックを書きたい場合もあると思われる。
そういった場合は、Azure FunctionというFaaSを使う。
作成後、Logic Appsで、add an actionからAzure Functionを選ぶ。
先ほど自分が作成したFunction名を選び、create new functionを押す。
Functionに渡したいパラメータをJSON形式で作成する。
functionの第2引数dataにrequest bodyの中身が入る。あとはnodejsの書き方で好きな処理を行える。ここではbodyを文字列に変換して返すことをしている。
post_messageで、functionの値を投げるよう指定。(一旦Logic AppsをリフレッシュしないとFunctionの値が表示されないかも)
詳細はこちら
https://docs.microsoft.com/ja-jp/azure/logic-apps/logic-apps-azure-functions
データベース
データベースを使いたい場合は、いくつか選択肢がある。(DocumentDB, SQL Database, clearDB, mysqlなど)
ここでは、NosqlデータベースであるdocumentDBを使った例を紹介する。
↓documentdbのリソースイメージ
collectionid, PARTITION KEY, databaseには好きな値を入れる。
おおまかにいうと、documentDBはJSONを格納するDBです。
JSONはいづれかのCollectionの下に格納され、すべてのJSONはidとプロパティを持ち、idとpartition keyの複合キーがいわゆるプライマリキーとして扱われる。
add an actionからdocumentdbを追加する(場所はdescribe imageの後ならどこでもいい)。
connection nameは適当に、documentdbに先ほど作成したものを選ぶ。
以下のように指定(DocumentはCode Viewで編集)。
requestId等は表示されないので、Code Viewを開き編集する。
"Create_or_update_document": {
"inputs": {
"body": {
"api": "describeimage",
"body": "@body('Describe_Image')",
"id": "@{body('Describe_Image')['requestId']}"
},
DBからデータを取り出す処理を追加する。
add an actionから以下を追加する(場所はcreate or update documentの下ならどこでも)
以下のようにクエリを設定する。
クエリはこちらselect * from cognitive c where c.body.description.captions[0].confidence > 0.1
今回はdocumentdbに以下のようなJSONがいくつか入ることになる。
"body": {
"body": {
"description": {
"tags": [
"thing",
"object",
"clock"
],
"captions": [
{
"text": "a clock on each of it s sides",
"confidence": 0.4118103538429474
}
]
},
"requestId": "626250bb-28cc-4f91-b1f8-b62ac02dcb4e",
"metadata": {
"width": 450,
"height": 362,
"format": "Jpeg"
}
},
"id": "626250bb-28cc-4f91-b1f8-b62ac02dcb4e",
"api": "describeimage"
}
上のクエリはJSONデータのうち、confidenceの項目が0.1以上のJSONデータをすべて返せ、という内容になる。
documentdbにおけるsql文の書き方は以下
https://docs.microsoft.com/ja-jp/azure/documentdb/documentdb-sql-query
最後にslackにポストする部分を追加
code viewを開き、queriesに以下を指定。
"queries": {
"channel": "general",
"text": "@{outputs('Query_documents')['body']['Documents']}"
}
動作確認
confidenceが0.1以下のものを追加してもクエリーの結果には入らない