🔗

SlackでKibelaのURLを中身が分かるように展開してみる

2020/12/15に公開

はじめに

この記事はSlackアドベントカレンダーの 14 日目の記事です。
Kibela 記事の URL に対してUnfurlを行う Slack アプリですが、一工夫して内容を把握しやすくしてみました。

現時点での結果

Kibela 記事の URL を Slack に貼ると以下のように展開されます。まあ、見て分かる通り、青空文庫の「銀河鉄道の夜」をテキストにしたものを記事本文にしました。

なぜGoogle Meetの背景ぼかしが最強なのかの Markdown をコピってきた記事だとこんな感じ。

ソースコードはkounoike/slack-kibela-appにて公開しています。インストール手順などは現時点で未記載です。ごめんなさい。

本格的に作るまでの流れ

Kibela APIのcontentSummaryHtmlの場合

Kibela の graphql API では記事(note)について contentSummaryHtml というフィールドでサマリが得られます。しかし、実際にはサマリと言っても単に先頭からある程度の分量を取ってきているだけにすぎません。記事の内容を「要約」しているとは言えない場合が多いです。

例 1: 銀河鉄道の夜

 ジョバンニは勢いよく立ちあがりましたが、立ってみるともうはっきりとそれを答えることができないのでした。ザネリが前の席からふりかえって、ジョバンニを見てくすっとわらいました。ジョバンニはもうどぎまぎしてまっ赤になってしまいました。先生がまた言いました。
「大きな望遠鏡で銀河をよっく調べると銀河はだいたい何でしょう...

例 2: Meet 最強記事のコピー

はじめに
最近ついに、Google Meetに背景ぼかし機能が利用可能になりましたよね。日本語だとインプレスのケータイ Watchの記事などで紹介されてます。確か2020年9月末前後で順次リリースされていたと思います。 このときは「背景ぼかし...

最初はこの contentSummaryHtml を使って URL 展開していたのですが、時々妙に改行が多く縦長になってしまう記事が出たりするなど、イマイチ利便性が上がらない状態でした。

WordCloudの導入

ある日、全然別のことを調べてる過程で自然言語処理のことを調べることになり、「そういえば WordCloud ってあったなー」って見ていたりしました。そのときに「WordCloud なら要約っぽくなるんじゃないか?」と閃きました。当初は TypeScript のBoltを使っていたため、kuromoji や d3-cloud などを使っていました。Bolt のサンプルをベースにして Heroku の Free dyno で動かしていました。

しかし、軽く動かすくらいは出来たもののどうも安定しないようでした。調べてみると単純にメモリ使用量が free dyno の制限を超えていました。ならば、と思って Python に移植してみたもののやはりメモリ制限の壁は厳しかったです。時々アクセスがあって URL 展開するだけのものに月数ドルもかけるのは微妙にもったいないです。同じ観点で AWS の各種サービスで継続的に動かすのは NG。Lambda であれば使用量もそれほどではないので良さそうなものの、各種ライブラリやフォント、形態素解析用の辞書諸々を含めると Zip の展開後サイズ 250MB の壁が立ちはだかりました。

Lambda container image support

そんな中、AWS の 2020 年の re:Invent イベントでAWS Lambda の新機能 – コンテナイメージのサポートが発表されました。コレです!コレならイメージが大きくなっても Lambda で動かすことができます。

さて、道具は揃いました。後は作るだけです。

構造

リポジトリのトップで 3 つのディレクトリがあります。それぞれに対応して 3 つの部分に分かれています。

cdk

環境構築するのにAWS CDKを使っています。このディレクトリに CDK 関連のソースが入っています。

bolt-app

Slack サーバとの対話するBolt for Pythonのアプリがあります。Slack App なので 3 秒以内に応答しなければならない中で工夫がしてあるのは1日目の記事の通りです。ゆっくり実行してよいところで後述のwordcloud-appを呼び出して WordCloud 画像生成などを処理させます。1 つにまとめるとコンテナイメージの肥大化やライブラリ初期化などの問題で 3 秒ルールを守りにくくなります。こちらのbolt-appでは普通の Zip デプロイする Lambda にして、依存関係を最小限にしてあります。

wordcloud-app

WordCloud を生成する本体です。単に Lambda で作っても良かったのですが、今回はちょっと方向性を変えて Step Functions を活用してみることにします。Step Functions を使うことで条件分岐(画像が生成済みかなどのチェック)を Step Functions 側に回します。これにより、Lambda の起動自体を抑えたり、一定個数の並列度で並列ループを回したりすることが簡単に実現できました。

WordCloud生成の仕組み

ワードクラウドを単純に作る場合、以下のような流れになります。


記事 URL -(Kibela API)-> HTML -(クレンジング)-> テキスト -(形態素解析)-> 分かち書き -(WordCloud ライブラリ)-> WordCloud 画像

しかし、この流れでは 1 つの記事の中での単語出現頻度(TF と言うそうです)しか見ていません。ある Kibela チームであれば、共通で頻出する単語(例えば Web 開発だったら HTML とかブラウザとか)があります。記事をまたいで頻出する単語は重要度が低く、他の文書で出てこない単語が頻出する場合の重要度を上げる仕組みをTF-IDFと呼ぶそうです。

ステップの構造

最初に全記事を形態素解析して画像を生成しておくステップがこちら。複数を束ねているところが並列ループで回るようになっています。

記事が作成/更新されたときのステップがこちら。とりあえず決め打ちで全部更新することにしています。

URL がリンクとして貼られたときのステップがこちら。TF をため込んだタブ区切りテキストファイルやワードクラウド画像と記事の最終更新時刻を比較して不要なステップをスキップしています。

おわりに

CDK の中ではコンテナイメージをディレクトリ指定するだけで生成できていたりします。また、SSM のパラメータストアを使って必要な設定をしていたりすることなど解説することはまだまだありますが、とりあえずこの辺で…。

明日(もう今日になってますが)はk-akieさんで「GCP: Google Cloud Functions で Slack アプリを試してみた」とのことです。今回私は AWS べったりでやってみましたが、GCP だとどうなのでしょうね、気になる記事です。

Discussion