adish intelligence

アディッシュ株式会社のエンジニアブログです。

運用フローまで考えるということ

前回の記事から1ヶ月空いてしまいましたがこんにちは。井上です。 今回は開発運用を行う雪風チームのメンバとしての、運用にまつわるお話を1つ。

良くない善意

「善意」って基本的にポジティブな捉え方をされることだと思いますが、長い目で見ると悪であることがあります。あくまで「ことがある」です。全ての善意が悪なわけではないです。 例えば、特定の曜日にゴミ回収日が設定されているわけですが、収集車が去った跡のゴミ置き場はいつもゴミが散らかって汚いとします。見かねた近くのとある住人が「善意」で毎回掃除するようになった。 有り難いことなので同じ地域の人は感謝します。「ありがとう」の声も掛ける。

良い話のようですが、これは長い目で見ると「良くない善意」となる場合があります。見方を変えればその人の自己満足と承認欲求のために掃除しているようなものだからです。

何故これが「良くない」かと言うと、「ゴミ収集車が去ったらあの人が掃除してくれる」のがあたりまえになってしまい、同じ地域の人はそれに甘んじる。しかし、その掃除をしてくれる住人が引っ越しをした途端、ゴミ置き場が汚くなってしまう。或いは、その人の生活スタイルが変わり掃除出来なくなると、その人が「なんで掃除してくれないの!?」と糾弾されてしまうことが想定されるからです。

この住人は「自らが掃除する」ではなく「収集車が去った後に掃除をする運用が廻る仕組みを作る」ことをする方がモアベターですね。 (基本的に各地域のゴミ置き場は自治体でそうした動きをしていると思いますが)

コレはそうした人を貶めるための記事ではありません。 「良くない善意」を行うことだけが悪い話ではなく、その善意に甘えてしまうことも悪い話なので。その善意が行われている、と知った時点で仕組み化を進めるべきです。 知らなかったら何も出来ないのですが。 「ゴン、お前だったのか。いつも掃除をしてくれていたのは」

ちょっと違う話かもしれませんが、これらは「当たり前を当たり前にしない」ための考え方だと思います。

実際にあった例

運用を行うAさんが、ブラウザに組み込むuser scriptを自分で組んで、とある部分の作業の効率化をしていました。運用メンバに配布して皆で使ってもらうことで効果も大きく重宝されていました。しかしその情報を把握しているのはAさんしかおらず、Aさんが辞められた後に「あの機能が動いていない。どうなっている?」と引き継ぎがされていないことも発覚。慌てて対応を行うことになりました。

Aさんは「自分でメンテするから大丈夫」と考えていたのでしょう。

運用化する

これらの善意を運用に落とし込むのは、属人化を排除するということでもあります。また運用フローに落とし込む方法はドキュメント化することをはじめ、たくさんありますし、どうやっていくべきかの話は別にありますね。ただ一方でそうした運用が行われていることに気付くことがなければ難しい点であることも事実です。

人のふり見て

他人の事例での話になってしまいましたが、自分自身気付かないところでこうしたことを行っている可能性があります。お互い指摘していければ良いですね。

この記事書きながら、自分の背中にブーメランが刺さりまくってる気がします。

マニラで進めているプロジェクトや、現地のIT開発会社について

日本に出張で一時帰国しました。久しぶりの帰国で体重が増加しました岡安です。

フィリピン事情について語る連載最後の記事です。

  1. 私が感じるフィリピンという国<前編> <後編>
  2. 皆が嫌いな英語
  3. マニラで進めているプロジェクトや、現地のIT開発会社について <- 今日はここ。

f:id:adinoue37:20170119181221j:plain

私のミッション

突然ですがadishでの主要ビジネスの内容をご覧ください。 https://www.adish.co.jp/business

主に4つの事業があり、各事業スピードに合わせてシステムでのサポートすることが私に課せられたミッションです。

マニラで開発組織を作り、会社の業務システムの開発を行う。これが第一の目標としてマニラに赴任しました。現地に他の事業組織があり、オフィスはありましたが採用担当の助けを受けつつ一人で作りました。
初年度は2名+私で幾つかの社内業務システムの構築を行い、2シーズン目の現在は開発体制を4名追加し、引き続き大きな業務システムの構築に従事しております。
今後は需要に応じた業務システムの開発を重ね、将来的にはサービス向けのシステムの開発などもできるような体制を目指しています。

フィリピンで働いていて

何より嬉しいのは自分たちが作ったシステムが他部署の人達から褒めてもらう時。
作ってもらったシステムで仕事が楽になりました、と言われるとこの仕事をしてて本当によかったと感じますし、テレビ会議を使ってマニラの開発メンバーにも社内の利用者からの声を伝えるようにしています。
全員が英語を話せるわけではないのでそこは僕が通訳をしています。遠隔地にいるという事はなかなか同じプロジェクトを進めていても一体感が薄くなりがちです。
これは全体のモチベーションにも大きく影響することで、厳しい意見だったとしても現場の声をちゃんと伝えることで堅牢な組織ができます。誰でも人の役に立ってると思われたいものです。
今も進めているプロジェクトがあり、また既に来年以降にリリースする計画もあり、まだまだやることは尽きません。それらが会社で仕事をする仲間が楽に効率よく仕事ができるようになれば僕の仕事は成功です。

どれも興味深いシステムなのですが、いずれも社外秘ですのでお伝えできるところが限られてて残念です。

フィリピンのIT企業

話は変わりますが、マニラに日系IT企業は見渡すとぱっと思いつくだけで10社以上はあります。
いずれも仲が良くIT飲み会というものが開催されていたりもします。何社かはお仕事の話もさせていただきますし市場動向などの情報交換、既にトライした事など皆さん惜しげもなく教えて下さいましたので参加した当初は大変助かりました。
その内の何名かは仲良くしていただき、一緒にゴルフに行ったり、ご飯を食べに行ったり仲良くしていただいています。
狭い世界ですのでお互い助けあってという意識のほうが強いです。
日本でも同業種の交流会は盛んですが、こちらは企業の数が限られており、会社の名前を伺えば、ああ○○さんですね。と担当者さんの1名程度は面識があったりします。

各社課題を持っていますし、たまたま同じ内容だったりするとまるで仲間のように話せる濃い関係が個人的には気に入ってます。
皆さんこのマニラで成功させてやる!という気合が入ってますのでとても楽しい皆様です。ごはん屋さんでよく遭遇もします。

僕が周囲の方にアドバイスを求めた内容は採用についてが多く、皆さん真摯に答えていただきました。
日本でも採用経験はありましたが、そこは今までの常識は通用せず、頭が真っ白になりました。この話はいずれまた。

最後に

知り合いの会社の多くは日系企業ですが、マニラではIT技術者の需要が伸びており、給与水準も年々上昇傾向であると感じます。また日本企業より韓国やアメリカの企業名も良く聞きますし日本企業としてのアドバンテージはあまりないのが現状です。少数派ですし。
マニラは転職も多い文化ですので人材維持は今後も悩みの種になるかとは思いますが、そこも挑戦だと思い業務を続けていきます。

クリスマスハッカソン2016 レポート

メリークリスマス! @sakaimoです。

今回は12/26に技術開発部が行ったハッカソンの紹介です。

朝集合して、参加者8人が各自取り組むことを決めて作りたいものを作る(やりたいことやるw)という一日です。 今回はReact関連のテーマを設定するメンバーが多くいました。いくつかのプロダクトではガンガン使っていますが、ただいま開発部内でフロントサイドの技術を強めようとしており、普段はサーバーサイドを書いているメンバーが社内研修用のReact入門をやったりしていました。

↓作業風景はこんな感じ。普段のオフィスルームから抜け出して、ミーティングスペースに集まって作業を進めました。

f:id:tech-adish:20161226234216p:plain

もちろんクリスマスなので、お昼にはみんなでチキンをいただきます!

トリビアとしてケンタッキーの肉の部位は5種類あるということを知りました。恥ずかしながら(?)これまで私は「でっかいの」と「骨付き」くらいの区別しかできてませんでした。。。

f:id:tech-adish:20161226225348p:plain

メンバーごとのテーマを紹介します。

坪井(@ufo_ocha)

以前から相談を受けていた社内ツールをRailsで作ろうとしていたらしいのですが、brew upgradeによるMySQL多重インストール問題や、macOSのアップデートでxcodeのコマンドラインツールがおかしくて再インストールしたりだとかで、午前潰すことに。午後はログインまわりのテストコード上手くできないかと考えていたら、あまり実装は進まない結果になったようです。

ただ、普段まぁいいかで済ませてたものをちゃんと調べられたので良かったそうです。 残りは年末一人ハッカソンだとか、そうじゃないとか。

島筒

Reactの勉強を兼ねて、社内研修の資料を元にしてTODOリストを実装していました。 午前中の成果としては、Reactを勉強しようとして、ポーランド記法にたどり着いた話をQiitaにまとめたところまで。 午後からは、順調に進んで研修資料の半分まで進んだようです。

その中で、JavaScriptの省略記法に凄い驚きを覚え、こちらもQiitaにまとめが上がっています。 c => this.input = c

田原

adventCalendarで作った「秘書bot」について、もっと少しだけ賢くなるように機能追加をされていました。 Real Time Messaging APIで秘書botを作ってみる

どんな機能を追加する予定だったかというと、

  • iCal形式のファイルを読み込んでいた箇所を、動的にAPIで取得する
  • 今日だけでなく、指定した日の予定を取得する。場所の登録があれば場所も教えてくれる
  • 指定した日の、会議の予定と場所を教えてくれる
  • 指定した日の、空き時間を教えてくれる

ということらしいです。「予定だった」とありますが、結局、「今日のスケジュールと場所がわかれば十分」ということで、残りは保留にしたらしいです。 その代わりに、「運用スタッフの方からSkype経由で依頼されて、MySQLのコンソールをたたいて調べる作業」がちょくちょくあったので、これを「Slack-Bot経由で運用スタッフの方が、開発を介さず直接確認できるようにする」という作業に取り組みました。運用スタッフ(非エンジニア)がslack経由でシステム操作...いいですね! DjangoのREST-Frameworkを使用して、Slack-BotからリクエストするAPIを作ってましたが、なんか、エラーにはまって、結局そこは完成しなかったようです。今後の彼の野望(?)にとってキモとなるみたいなので、正月返上?でやるとかやらないとか。

井上

Ruby on Railsの勉強を兼ねて自社ツールに機能追加をしようと思って環境構築をしたら、実はsinatraで、しかもReactだったというオチが付いたようです。その後は社内研修用のReact Trainingに着手していました。


f:id:tech-adish:20161226231953p:plain

白木

ブラウザ画面上に画像が表示されていて、その画像に対してドラッグで範囲指定を行うと、サーバー側で範囲指定された分だけ画像切り抜いて(crop)くれるツールの作成をしていました。 画面のレンダリングと範囲指定の座標点の取得はReact.jsで、画像の切り抜き・保存はサーバー側のPythonで実装とのこと。

片山

iOSアプリ:ここだよ君(仮) アプリ概要:ディープラーニング+Metal+Siriを組み合わせたもの探しアプリ

イメージとしては、アプリを起動して、Siriに「メガネ」と言うと、アプリが画面内にあるメガネがあるか解析して、アプリがメガネを認識したら、その場所に画面で円をつけて「ここだよ」と知らせてくれるアプリ。 よく物をなくす嫁向けに開発したかったようですが、今回は時間がなく構想だけで断念・・・無念!

境野

社内の業務ツールとして、指定した文字列(複数)をブラウザ上でハイライトさせたいというニーズがあります。その実装をChromeエクステンションで行いました。(完成には至りませんでしたががが) 使う人(複数人)が容易にキーワードの追加編集ができるように、キーワードの設定はGoogleスプレッドシートで行い、設定した値をS3にjsonで置いておき、エクステンションがそのjsonを取ってきて、、、という壮大な(?)仕組みになる予定です。

西谷

都合であまり参加できませんでしたが、WebデベロッパーのためのReact開発入門の書籍を元に、写経をメインに進めていました。

JSFiddleという、ブラウザ上でHTML/CSS/JavaScriptを書いて実行できる環境上に、React.jsスクリプトを書いて実行、を繰り返しました。ブラウザはChromeを使用し、デバッグ時はChromeデベロッパーツールを使用して、コンソールログを表示とのこと。

時間がなく2章までしか進められなかったようですが、残りは年末年始で進めるとか進めないとか。



↓最後の「発表会」の様子

f:id:tech-adish:20161226231340p:plain


まとめ

今年の7月1日から始めたadish 技術開発部ブログ adish intelligenceですが6ヶ月で18本の投稿ができました。日々の業務に追われてブログでの情報発信が重く感じてしまうこともありますが、そこは「情報発信していると自分のところに情報が集まってくる!」を信じて、これからもメンバー全員が少しずつでも記事を書いていけるように続けていこうと思います。

年内の投稿はこれが最後になります。来年もよろしくお付き合いください!

◯◯にはエンジニアの大切な全てのことが詰まっている。

井上です。Gaiax Advent Calendar 2016の16日目の記事を書きます。 書くんですが、何の話をしようかずっと悩んでいました。もう16日目当日です。

私もエンジニアの端くれ。何か実装例のような話だったりを書き連ねてみたいと思わないわけではないです。しかしながら丁度よい塩梅のネタが思いつかず、ひとまずえいやっと筆を走らせてみようと思います。(実際はキーボードを叩いているんですが!)

と、こうした独白めいた文章が、今まさに読んでいるアンディ・ウィアー「火星の人」の文体に似ていると思ったので、この辺の話をしようと思います。
ジャストアイデア。閃きって大事ですね。

「火星の人」って?

今年2月に公開されたSF映画「オデッセイ」の原作小説です。 火星DASH村と言ったほうが通りが良いでしょうか。 火星で芋を作る話という認識をされている方も多いかもしれませんが、実際はそうではありません。後述します。

なんで「火星の人」?

私は個人的にSF小説やSF映画が好きで、よく映画館に行く方です。スペースファンタジーやスペースオペラより、実直なサイエンス・フィクションや、"すこし不思議"なSFが好きです。「ゼロ・グラビティ」や「インターステラー」なども非常に良いSF映画でした。小説では「戦闘妖精雪風」。漫画では今井哲也さんの作品が好きです。

ちなみに私は「オデッセイ」を観てから原作小説である「火星の人」を読んだ派になるのですが、現在「火星の人」は上下巻分冊で発行がされています。しかし私は敢えて1冊に納めた旧版で購入して読んでいます。ああ、こういう話は不要ですか。不要ですね。と言いますか「なんで?」の説明に全くなっていないですね。

「オデッセイ」公開初日に、面白いらしいぞという話を見かけ、それ以上ネタバレを見ないよう細心の注意を払いつつ映画館へすっ飛び観た結果、 「困難に際して知識と技術と諦めない心で乗り越えていく、まさにエンジニアのための映画だ」と感じたのです。 よし、技術開発部のブログ記事らしいちゃんとした理由ですね。

当時「これは2016年最高の映画だ」と思ったのですが、その後まさか「シン・ゴジラ」や「君の名は。」や「この世界の片隅に」で「2016年の最高」をことごとく塗り替えられるとは思っても居ませんでしたが。
はい。余談ですね。
余談ついでに、原作小説作者のアンディ・ウィアーは現役プログラマだそうですよ。

ざっくり「火星の人」あらすじ

有人火星探査中のクルーの一人、マーク・ワトニーが不運な事故によりたった一人火星に取り残されてしまう。不毛な赤い砂が広がる火星の大地で、残された限りある物資で、果たして彼は地球からの助けが来るまで生き延びることが出来るのか!?

作中、火星側の描写は全て「ワトニーが書き残したログ」の体(てい)になっているため、「独白めいた文章」になっています。また、ワトニーがポジティブシンキングのみならずユーモアセンスあふれる性格であるため、文章も痛快で楽しめる作品になっています。

見て見て!おっぱい!─(.Y.)
(実際に作品中に登場する一節です。本当ですって)

「火星の人」とエンジニアスピリッツ

劇中、限られた資源の中で生き延びるため、ワトニーは食料を確保しなければいけません。そこで芋を作ることになります。その描写が「火星DASH村」の評に繋がるのですが、芋を作るために必要なものが何か。彼は計算に計算を重ねて必要なものを用意しきります。

まず耕作をする場所が必要です。火星の大気は薄く、ほぼ真空であるため水分はあっという間に蒸発します。つまり外で耕作はできません。クルーの生活する場所であるテント(ハブと言います)しかありません。しかしハブは狭く必要十分な耕作地が得られません。彼は実際に助けが来るまでに掛かる日数から必要なカロリーを計算し、取りうる複数パターンの農法から生産量を算出、可能な限り最大の効果が得られる手法を選択します。
耕作地が出来たとしても、植物が育つにはバクテリアの活動がなくてはなりません。しかし火星の大地にバクテリアは存在していません。しかし彼は"あるもの"を使って解決します。
さらに水が必要です。植物を育てるため必要な水の量を計算し、ハブに備えられていた水では足らないと判断。この課題について化学反応で水を生成すれば良いとし、必要な元素を確保し、危険マージンを"可能な限り"取った上で反応させて解決します。

芋を作るために水素と酸素を化学反応させる。これぞエンジニアリング。
いえ、そこではありません。課題に対して一つずつ対処をし、乗り越え、目的を達する。これこそが素晴らしいエンジニアリングだと思います。

他にもワトニーは、200キロの資材をローバー(大型トレーラーみたいなものです)に載せるために、岩を積み上げ斜路を作って積み込んだり。
地球と交信するために、地球側が操作できる360度回転してパノラマ撮影するカメラを活用したり。(さぁこれはどうやるか想像できるでしょうか!?)
ローバーで長距離移動をするため、まずテストとして近隣を走り、バッテリーを持たすために暖房を切っていると冷えすぎてしまい重ね着をしても耐えられないという問題点を見つけては、車内を暖める方法を検討し、対処をした上で再びテストを実行する。今度は暖まりすぎるため、ローバー内の断熱材を削り剥がしたりもします。この時もテストを繰り返して調整をするのです。

目的のために必要な課題解決をするため取捨選択をする。使わないものは取り外し、バックアップを考慮した上で必要なものだけを機能させる。
どうでしょう。制限された状態で、手持ちの道具を使って、必要であれば道具を作り出して、最終的なゴールにたどり着く為に乗り越えるべき課題を解決するために、様々なものを組み合わせて解決する。まさにエンジニアリングですね!

この他にも「行き詰まったらあえて寝る」といった教訓とすべきポイントだったり、エンジニアあるあるな話だったり・・・。そう、システムをハックするシーンなんかもあるんです。

如何ですか。エンジニアのための作品だと、お分かりいただけたでしょうか。

まとめ

「火星の人」にはエンジニアの大切な全てのことが詰まっている。

ぜひ映画「オデッセイ」を観て下さい。
もしくは「火星の人」をぜひとも読んでみて下さい。
エンジニアでない方も、面白いので是非観て読んで下さい。

Real Time Messaging APIで秘書botを作ってみる

はじめに

みなさま、こんにちは。来年いよいよ生まれて半世紀を迎えます、adish技術開発部.雪風チーム所属の田原と申します。 12月1日に始まったGaiax Advent Calendar 2016 - Qiitaも、折り返し地点を過ぎました。 紅白の出場者も決まり、流行語大賞も決まり、今年を表す漢字一文字も決まり、どんどん年末に向かって時間が進んでいきますね。 そうそう、去年は有馬記念で10万馬券をGetして大興奮したのですが、あれから1年経つなんて、本当に早いですね... 今年も当んないかな...

...失礼しました。技術ブログの話に戻します。

私は普段は主にPerlで仕事をしていますが、今度Pythonのお仕事をすることになり、50の手習いではないですが、最近Pythonの勉強を始めました。「タプル」とか「イミュータブル」とかまだ馴れないですね。 そんなレベルなのですが、無謀にもPython系の記事を書いてみたいと思います。

みんな大好きSlackのbotを、Real Time Messaging APIというものを利用して作ってみます。

Real Time Messaging APIとは

Real Time Messaging API | Slack

リアルタイムメッセージングAPIはWebSocketベースのAPIで、スラックからのイベントをリアルタイムで受信し、ユーザーとしてメッセージを送信できます。単に「RTM API」と呼ばれることもあります。
※本家サイト冒頭より抜粋

以降「RTM API」と呼ぶことにします。

事前準備

Slack環境の準備

まずは、botのテストができるslackの環境を準備します。会社で使っていて、なおかつ管理者権限とかあるならばそこで試してもいいですが、そうでない場合はFreeプランを契約してしまうのもいいと思います。

ご存じの方も多いと思いますが、Slackにはインテグレーションサービス(という名のbot)がラインナップされており、実は自分でbotを作らなくても、そこそこ秘書みたいな仕事をさせることは可能なように思えます。

  • インテグレーションサービスストア

App Directory | Slack

  • Slackの料金プラン

Pricing | Slack

制限はありますが、botサービスを試したり、自作botを試したり、スケジュール管理とかメモの投稿とか、ちょっとした秘書ツールとして使うには充分だと思います。

SlackのSign Inの方法については、ネット上にあふれていると思いますので、そちらを参考にしてください。

Slack botアカウントの準備

Slackのbotアカウントを作成します。ここは管理者権限が必要です。

bot用アカウントを作る

権限のあるアカウントでログインしている場合、以下の画面が出ると思います。

f:id:hige-hage:20161214154234p:plain

「@username」に、botアカウントのユーザー名を入れて、「Add bot integration」ボタンを押下します。

f:id:hige-hage:20161214145245p:plain

「API Token」は、botを作成する際に必要になりますので控えておいてください。「Customize Icon」や「Customize Name」で作成したbotの名前やアイコン画像を変更できます。変更内容を「Save Integration」で保存します。

テスト用のチャンネルの作成とボットアカウントの登録

テスト用にプライベートなチャンネルを作成します。

f:id:hige-hage:20161214145437p:plain

「Send invite to」の欄に、先ほど作ったボットアカウントを指定します。

bot稼働環境の準備

Python3が動く環境を準備してください。

私の場合は、Windows8.1 + Vagrant の環境に以下を準備しました。

  • Ubuntu16.04 LTS
  • Python3

※環境構築にあたっては以下のページを参考にさせていただきました。どうもありがとうございました。

VirtualBox + VagrantでUbuntu16.04の環境を構築する - Yoshinorin in HEXO

データサイエンティストを目指す人のpython環境構築 2016 - Qiita

RTM APIのインストール

RTM APIのソースコードは以下にあります。

GitHub - lins05/slackbot: A chat bot for Slack (https://slack.com).

pipでインストールできます。

ubuntu@test1:~$ sudo apt-get pip install slackbot

これで、botを作る準備が整いました。

さっそく作ってみる

それでは、簡単なbotを作ってみたいと思います。

作るにあたっては、以下のページを参考にさせていただきました。どうもありがとうございました。

PythonでSlackbotを作る(1)~(4) – ビットログ

簡単なbot

任意の場所にBot用のディレクトリを任意の名称で作ります。 以下のように作りました。

ubuntu@test1:~$ mkdir -p bot/test0020
ubuntu@test1:~$ tree bot
bot
└── test0020
ubuntu@test1:~$ 
ubuntu@test1:~$ cd bot/test0020

bot用の設定ファイルを作り、先ほど控えた「API Token」を入れておきます。

test0020/slackbot_settings.py

API_TOKEN = "test0020のAPI TOKEN"
default_reply = "すみません、聞き取れませんでした"

default_reply は想定していない言葉を投げられた場合に返すメッセージになります。

次に、botを起動するコードを記述します。

test0020/run.py

from slackbot.bot import Bot


def main():
    bot = Bot()
    bot.run()

if __name__ == "__main__":
    main()

では、botを起動してみます。

ubuntu@test1:~/bot/test0020$ python run.py

slackbotライブラリのデフォルトプラグイン(slackbot.plugin)に、疎通確認用に設定されているものがあるので、メンションの特定の言葉に反応したり、チャンネルに投稿された言葉に反応します。

f:id:hige-hage:20161214145633p:plain

Pythonのslackbotライブラリは、このようにプラグインを作ることで、独自にダイレクトメッセージやbotが参加しているチャンネル内でのメンションや投稿内の特定の言葉に反応する機能を実装することができます。

忠臣蔵ごっこ

botが動く環境が確認できたので、ちょっと遊んでみましょう。

ところで、今日は何の日かご存知でしょうか?

今日12月14日は、忠臣蔵のクライマックス、赤穂浪士討ち入りの日です。 若い方たちはご存じないかもしれませんが、昔は「紅白」「レコ大」「忠臣蔵」と言われたくらい、年末の風物詩でした。

忠臣蔵 主君の仇を討った忠義の士たちの戦いの全容

秘密裏にことを進めるために、合言葉を設けたといいます。 「やま」といえば、「かわ」と返す、まさにbotで遊ぶのにぴったりのネタではないでしょうか。 ということで、忠臣蔵ごっこができるbotを作ってみましょう。

まず、kuranosukeというbotアカウントを作り、テスト用の部屋に招待しておきます。

次に、ごっこ遊び用のbotディレクトリとごっこ遊び用プラグインを配置するためのディレクトリを作成します。

ubuntu@test1:~/bot mkdir -p ako47/plugins

ちなみに、ako47というのは討ち入りに参加した人数が47人というのにちなんでです。A●B48とは関係ないです。 次に、Slackbotのプラグインとして読み込まるディレクトリはモジュールである必要があるので、plugins配下に「_init__.py」を作成しておきます。

ubuntu@test1:~/bot touch ako47/plugins/__init__.py

_init__.py は空ファイルで構いません。

ごっこ遊び用のプラグインを作ります。

plugins/aukotoba.py

from slackbot.bot import respond_to

@respond_to('山')
@respond_to('やま')
def res(message):
    message.reply('川!')

関数"res()"に対して、"respond_to()"というslackbotにあるデコレータを付与します。 "respond_to"の引数に、マッチするキーワードを指定することで、プラグインロード時に"res()"関数がbotに対するメンションに反応するように登録されます。

次に、slackbot_settings.pyを作ります。プラグインロードが行われるように「PLUGINS」を指定します。

slackbot_settings.py

API_TOKEN = "kuranosukeのAPI TOKEN"
PLUGINS = [
    'plugins',
]
default_reply = "おのれ、くせ者!!~"

ここまでで、以下のような状態になります。

ubuntu@test1:~/bot$ tree ako47
ako47
├── plugins
│   ├── aikotoba.py
│   └── __init__.py
├── run.py
└── slackbot_settings.py

3 directories, 7 files
ubuntu@test1:~/bot$ 

それでは、slackbotを起動して遊んでみましょう。

f:id:hige-hage:20161214145822p:plain

あれ、逆じゃない?...とか、細かいことは言わないでください...どうしても、今日の出来事に結び付けたかったので。

大きく寄り道してしまいましたが、秘書botを作りましょう。

秘書botを作る

秘書の仕事にはいろいろあると思いますが、一番に浮かぶのはスケジュールを確認してくれることかと思います。 そこで、スケジュール確認できる秘書のbotを作ってみます。

弊社ではiQubeというクラウド型グループウエアを利用しており、基本的にスケジュールもここで管理しています。 iQubeの自分の設定画面から発行されたAPIのURLをたたくことで、iCalenderフォーマットで自分のスケジュールを取得することができますので、今回はこれを使って今日のスケジュールを教えてくれる秘書を用意したいと思います。

「今日のスケジュール」と問い合わせたら、iQube上に登録されている今日のスケジュールを教えてくれる秘書を作ってみます。 最初に作ったtest0020のbotに機能を追加する形で作ります。

  • iCalenderフォーマットをpythonで扱うためのパッケージ

iCalendar package — icalendar 3.9.1.dev0 documentation

※iCalenderフォーマットを扱うのであれば、このページを参考にパッケージをインストールしておいてください。

まず、slackbot_settings.pyを修正します。

slackbot_settings.py

API_TOKEN = "test0020のAPI TOKEN"
default_reply = "すみません、聞き取れませんでした"

PLUGINS = [
    'plugins',
]

次に、実際にデータを取得してメッセージングするプラグインスクリプト「secretary.py」を作成します。

secretary.py

import codecs ----------(1)
from pytz import timezone ----------(2)
from datetime import date, datetime ----------(3)

from slackbot.bot import respond_to
from slackbot.bot import listen_to ----------(4)
from icalendar import Calendar, Event ----------(5)

@listen_to('(.*)のスケジュール') ----------(6)
def check_schedule(message, something):
    param = "{0}".format(something)

    # 日付のセット
    if param == '今日':
        p_date = datetime.now()
        s_date = p_date.strftime('%Y/%m/%d')
        fin = codecs.open('plugins/schedule.txt') ----------(7)
        cal = Calendar.from_ical(fin.read())
        schedules = []

        for ev in cal.walk():
            if ev.name == 'VEVENT': ----------(8)
                start_dt = ev.decoded("dtstart") ----------(9)
                print(start_dt.astimezone(timezone('Asia/Tokyo')))
                summary = ev['summary']
                if start_dt.astimezone(timezone('Asia/Tokyo')).strftime("%Y/%m/%d") == s_date:
                    schedules.append(start_dt.astimezone(timezone('Asia/Tokyo')).strftime("%Y/%m/%d %H:%M") + ' ' + summary)
        # ユーザーにメッセージを返す
        send_user = message.channel._client.users[message.body['user']][u'name']

        post = {
            'pretext': '{}さんの今日のスケジュールです。'.format(send_user),
            'title': '今日のスケジュール',
            'author_name': send_user,
            'text': '\n'.join(schedules),
            'color': 'good'
        }

        ret = message._client.webapi.chat.post_message(
            message._body['channel'],
            '',
            username=message._client.login_data['self']['name'],
            as_user=True,
            attachments=[post]
        )
    else:
        message.reply('申し訳ありません。今日のスケジュールしか答えられません')

初心者の身分で畏れ多いですが、スクリプトを簡単に解説します。

PEP8の流儀は勉強中なので、そこはご容赦ください...

(1) テキストエンコード用の基底クラスをimportしてます。(9)のicalデータから開始日データを抽出する際に使います。

(2) タイムゾーンパッケージをimportしてます。icalデータに入ってる日付データのタイムゾーンを「Asia/Tokyo」にする際に使います。

(3) 日付ハンドリング用のパッケージをimportしてます。

(4) listen_toデコレータをimportします。respond_toデコレータがメンションに対して反応するのに対し、listen_toデコレータは部屋に投稿された特定のキーワードに対して反応します。パターンマッチングを使って値を切り出すことができます。

(5) icaleder形式のデータを扱うためのパッケージをimportしてます。

(6) パターンマッチングを使って値を切り出すことができます。

(7) iQubeから取得した、iCalenderフォーマットのデータファイルを読み込んでます。「urllib.request」パッケージを使って、取得したストリームを直接処理するのが正解ですが、なぜかエラーとなってしまうので、ここではサンプルとして出すことができればいいので、ファイルを読むようにしてます。

(8) これが含まれるforブロックでは、iCalenderオブジェクトを順番に見ています。「VEVENT」というキーが、スケジュールイベントに該当します。

(9) イベントオブジェクトのイベント開始日を取得してます。今日の日付に該当したら、メッセージ出力用の変数(schedules)に入れてます。

全部準備したら、下記のようなディレクトリ&ファイル構成になります。

test0020
├── plugins
│   ├── __init__.py
│   ├── schedule.txt
│   └── secretary.py
├── run.py
└── slackbot_settings.py

1 directory, 5 files
ubuntu@test1:~/bot$ 

それでは、秘書に働いてもらいましょう。

ubuntu@test1:~/bot/test0020$ python run.py 

f:id:hige-hage:20161214154526p:plain

いかがでしょうか? ソースを見ての通り、今日のスケジュールしか答えられないのはプアですが、botアプリを作るだいたいの感じはつかめたのではないでしょうか?

このように、RTM API + Python3 を使うと、割と簡単にbotアプリが作れますので、みなさんも自分の秘書を作ってみてください。

hitoboにおけるWebSocketとAPIの使い分け

こんにちは。2年目新卒エンジニアの坪井(@ufo_ocha)です。普段はhitoboの主にフロント周りを開発しています。この記事は Gaiax Advent Calendar 2016 の12日目の記事です。

最近WebSocketを使って、わざわざブラウザの更新しなくても、リアルタイムに画面が更新されるWebアプリも増えてきましたね。チャットサポートを提供するhitoboでもメイン機能のチャット機能を提供するためにWebSocketを使用しています。その際に、 Server => Clientへの通信はWebSocketを使うしかないのですが、悩むのが、Client => Serverの通信を、 「API経由で行うか?」 それとも、 「WebSocket経由で行うか?」 ということです。この記事では、WebSocketとAPIの使い分けについて、hitoboでの方針をお話します。

Socket.io

まずはWebSocketの実装ですが、定番のSocket.ioを使っています。Socket.ioはWebSocketを対応していないブラウザに対して、ポーリングで対応してくれたりと互換性を担保してくれます。また、それだけではなくnamespaceやroomの概念を提供してくれるので、チャットのルームなどの実装を楽に行うことができます。

hitoboではこれにスケールアウトできるように、socket.io-redisを用いています。socket.io-redisを用いて、redisを挟むと以下のようにソケットサーバが増えても対応することができます。

f:id:ufo_ocha:20161213150017p:plain

このあたりは色々な記事で説明されているので、詳細には触れません。が、これが本題のWebSocketとAPIの使い分けに役に立ちます。

WebSocketとAPIの使い分け

さて、本題ですが、結論から言うとhitoboではなるべくWebSocketではなく、APIに寄せるようにしています。

理由としては、 Socketサーバーが複雑になりメンテしづらい というのがあります。例えば、チャットを実装するとすると以下のようなコードになります。

const io = require('socket.io');

io.on('connection', socket => {
  // ここにコードを書いていく必要がある
  
  // ユーザからの投稿イベントを受け取る
  socket.on('post', message => {
    // 受け取ったメッセージを他のユーザに配信する
    socket.emit('display', message);
  });
});

クライアントサイドからのユーザの投稿イベントをemitし、ソケットサーバでそれを受け取る。受け取ったメッセージを他のユーザにemitする。といった流れです。これぐらいだと複雑に思えないかもしれませんが、ここに他のイベントも記述されていったり、受け取ったメッセージをDBに保存するなどと言った処理が増えると、かなりメンテしづらくなってしまいます。

こういった投稿をWebSocket経由ではなく、ユーザ投稿APIを用意することで、今までのサーバーサイドと同じように実装することができます。となると、 Client => Server はAPIで通信可能ですが、受け取ったメッセージを Server => Client に送るのはどうしたらいいのでしょう。ここでsocket.io-emitterを使用します。

socket.io-emitterとは

socket.io-emitterとは、socket.ioに先程少し触れたsocket.io-redisを組み込んだ実装をした場合に、直接redisに同じ形式でpublishすることで、socket.ioの外部のプロセスからイベントをemitすることができます。

f:id:ufo_ocha:20161213150111p:plain

これによって、API経由でユーザの投稿を受取り、socket.io-emitterを通じてemitすることで、DBに保存するなどはいつもどおり、サーバーサイドで実装することが可能になります。

こうすることによって、実装が複雑になりがちなソケットサーバはルームへのjoinやdisconnect時の処理など、ソケットサーバにしかできないような処理のみを記述するだけでよく、今までと同じようなAPIでのやり取りで実装することができます。もちろん、実際はユーザの投稿が失敗した時のリトライできるような処理が必要になったりと、こんな単純ではありませんが、APIの実装に集中できるのはメリットだと思います。

socket.io-emitterの注意

===2016/01/10 追記===
先日、socket.io-redisの3.0.0がリリースされました。
これによって依存パッケージが msgpack-lite に変更されたので、以下の注意は必要ないでしょう。
逆に今まで正常に動いていた場合、v3.0.0にアップデートする際は注意してください。
=== ===

socket.io-emitterはJavaScriptにかぎらず、RubyやPerl, PHPと様々な言語での実装がOSSとして公開されています。ここで少し注意なのが、(2016/12/12の時点で)socket.io-redisの内部で使っている msgpack-js が、msgpackの最新の仕様ではないという罠があります。JS版を利用するときはsocket.io-emitter内部で同じsocket.io-redisを使用しているので問題ないのですが、Ruby版を使う場合は少し注意が必要で gem install socket.io-emitter でインストールできるRuby実装のemitterではmsgpackが最新の仕様となっており、emitが上手くできません。

ただ、socket.io-redisも先日内部で使ってるパッケージを msgpack-js から msgpack-lite に変更するコミットがなされており、これによってmsgpackが最新の仕様となったため、正常に動作するようになりました。ただ、このコミットはまだリリースされていないため、現時点でRubyのsocket.io-emitterを使う場合は

$ npm install -S git+https://github.com/socketio/socket.io-redis.git

と、リポジトリを指定して、直接masterリポジトリをインストールすると良いでしょう。他の言語実装でどうなっているかまでは調べきれていませんが、socket.io-emitterを使用するときや、socket.io-redisのアップデートの際は気をつけた方がいいと思います。

まとめ

  • hitoboでの開発ではsocket.io-emitterを用いて、APIに寄せた開発をしている
  • APIに寄せると今までと同じようにサーバーサイドの開発ができ、ソケットサーバが複雑にならないというメリットがある
  • socket.io-emitterを使う場合は内部で使われているmsgpackの仕様に注意する必要がある

さて、hitoboでのWebSocketとAPIの使い分けのお話をさせていただきました。もちろん、APIに比べて、WebSocketの方が通信量が少なくて済むため、それを優先し、WebSocketに寄せてしまうこともあると思います。しかしながら、今までの資産をそのまま使いつつ、WebSocketを導入することができるので、既存のプロダクトに導入する際や、やり取りが多くない画面を実装する際は、APIに寄せて実装することをオススメします。とはいえ、hitoboでもこれがベストプラクティスだとは思っていません。「自分のチームではこうしてるよ!」などあればぜひ教えてください!

また、hitoboチームでは効率のよいチャットサポートを実現するフロントの設計や、大量のメッセージをさばくインフラ設計がしたいエンジニアを募集しております!少しでも興味のある方はお気軽にご連絡ください!

P.S. 12日担当なのに投稿遅れてしまい申し訳ありません!もうちょっと、計画的に準備すればよかったと反省しております...

~3分でわかる~ TensorFlowで評価アルゴリズムを書いてみた

こんにちは、アディッシュ株式会社(以下adish)・ALICEチーム*1の岩渕です。こちらはGaiax Advent Calender 11日目の記事です。

現在adishでは、深層学習を用いたサービスの開発を行っています。開発にあたって、pythonを利用していることもあり、本日は深層学習ライブラリであるTensorFlowの簡単な説明と、評価アルゴリズムの正解率(accuracy),F値(f-value)の実装例を紹介します。

F値などの評価アルゴリズムについては、過去に白木さんが説明した記事がありますので、こちらを御覧ください

過誤(分割表)と二値分類の性能評価について - adish intelligence

TensorFlow

普段、pythonのような動的プログラムを使っていることで、型宣言を全然しなくなった!なんてことはありませんか?僕は型宣言しない楽な世界にどっぷりハマっていたことも有り、TensorFlowを触り始めた当初は、型が違うと怒られることが多々ありました。 以下は、今回利用したTensorFlowのpython用APIの一部です。

値の扱い

  • 定数: tf.constant()
  • 変数: tf.Variable(): 今回は利用しません。
  • データ格納予定場所: tf.placeholder()

わかりやすい解説記事がこちら

TensorFlowを算数で理解する - Qiita

演算

  • 足し算: tf.add()
  • 引き算: tf.sub()
  • 掛け算: tf.scalar_mul()
    • スカラー値を掛け算するため
  • 割り算: tf.truediv()
    • floatの結果を返す
    • python2系の // に相当、python3系なら /

日本語で演算のAPIを解説されている記事がこちら

評価コード

今回はTensorFlowを用いて、評価アルゴリズムを簡単に実装してみた場合を紹介したいと思います。コードは以下のようになります。

import tensorflow as tf

def accuracy(result):
  """
      return : (True-positive + True-negative) / (all-data-size)
  """
  size = tf.add(result[0], result[2])
  correct = tf.add(result[1], result[3])
  ac = tf.truediv(correct, size)

  return ac

def recall(result):
  """
      return : True-positive / (all-positive-data-size)
  """

  size = result[0]
  TP = result[1]
  rec = tf.truediv(TP, size)

  return rec

def precision(result):
  """
     return : True-positive / (True-positive +  False - Negative)
  """

  TP = result[1]
  size = tf.add(result[1], tf.sub(result[2],result[3]))
  prec = tf.truediv(TP, size)

  return prec

def f_value(result):
  """
    return : 2*precision*recall / (precision + recall)
  """

  prec = precision(result)
  rec = recall(result)

  n = tf.scalar_mul(2,tf.scalar_mul(prec,rec))
  d = tf.add(prec, rec)

  F = tf.truediv(n,d)

  return F


if __name__ == '__main__':
    # 100個ずつデータがあると仮定
    positive_datas = tf.constant([100], tf.int64)
    negative_datas = tf.constant([100], tf.int64)

    # True positive,True negativeの数は引数
    tp = tf.placeholder(tf.int64)
    tn = tf.placeholder(tf.int64)

    # 判定結果とデータサイズを結合
    positive = tf.concat(0, [positive_datas, tp])
    negative = tf.concat(0, [negative_datas, tn])

    # [positive_datas, tp, negative_datas, tn] が作られる
    result = tf.concat(0, [positive, negative])

    # initialize
    sess = tf.Session()
    init = tf.initialize_all_variables()
    sess.run(init)

    # モデル定義
    ac_ = accuracy(result)
    F_ = f_value(result)

    # 実行
    ac, F = sess.run([ac_, F_] feed_dict={tp:[80], tn:[100]})
    
    print("Accuracy: %.3f  | F value :  %.3f  " % (ac, F))

以前の記事を参考に作成すると、以上のようなコードになるかと思います。TensorFlowでは、contribにあるmetricsに、性能評価のアルゴリズムが書かれてあります。今回はtf.contrib.metrics.accuracy()のみ紹介し、それ以外のtf.contrib.metricsについては追って記事を作成できればと思います。

tf.contrib.metrisの利用

もし、以下のように、あるバッチサイズ分の二値出力を行ったとします。

predictions

# shape = (batch_size, 2)
[[0.4, 0.6], [0.2,0.8] ... [0.1, 0.9]]

labels

# shape = (batch_size,)
[0, 1, 0, 1,... 1]

predictionsとlabelsのshapeをあわせます。

predictions = tf.argmax(predictions, dimension=1) 

最後に、それらを用いることで使用することができます。

accuracy = tf.contrib.metrics.accuracy(predictions, labels)

まとめ

今回は簡単にですが、TensorFlowでの使用例を載せてみました。わかっているつもりを無くしていく作業は大事だと思っているので、是非ご自身の手で書いてみましょう!

*1:adishの機械学習に関する開発を行っているチームです