読者です 読者をやめる 読者になる 読者になる

UZABASE Tech Blog

株式会社ユーザベースの技術チームブログです。 主に週次の持ち回りLTやセミナー・イベント情報について書きます。

NewsPicks:記事をオススメするLINE BOTをつくってみた

はじめまして。NewsPicks技術チームの井原です。

チャットボット(Bot)という言葉を聞いたことがあるでしょうか。スマホやWebのメッセンジャー上で動く自動会話ロボットで、4/7にLINEがBOT API Trialを公開したことでエンジニア界隈で一躍ホットなトピックとなりました。今週にはFacebookが対応を発表し、NewsPicksでも話題になりました。

newspicks.com

私もBOT API Trialの公開で大喜びしたクチで、さっそく週末と帰宅後の時間でLINE BOTを作ってみました。

うま

こちらが作成したBotです。「うま」(NewsPicks非公式Bot)といいます。

f:id:ryoju:20160414174014p:plain

ユーザーが知りたいことをうまに伝えると、良さそうなNewsPicksの記事を探してお教えします。記事の内容が気になったら、画像をタップするとページが開きます。

さて、うまがどういうふうにできているのかご説明いたしましょう。

インフラ環境

うまはAWSで動いています。EC2(w/ Elastic IP) + ELB + CloudFront + Certificate Manager + Route 53という構成です。Certificate Managerを使うためにバージニア北部のリージョンで動かしています。

LINE BOTを動かすために以下の対応が必要でした。

  • LINE BOTのコールバックを受けるためにはSSLが必須で、かつ証明書を選り好みするため、Certificate Managerの証明書を使っています。私が所有しているドメインに対して作成した証明書をCloudFrontに紐付けて、さらにCloudFrontのエンドポイントをRoute 53のAレコードで設定しています。ここの設定に苦労したので、キャプチャを貼付しておきます。よかったら参考にしてください。

    • Certificate Manager
      コールバックURLに使いたいドメインで証明書を作成します。ワイルドカードである必要はありません。
      f:id:ryoju:20160414202224p:plain:w400
    • CloudFront
      ディストリビューションを作成し、証明書を設定します。CNAMEにもコールバックURLのドメインを設定します。
      f:id:ryoju:20160414202227p:plain:w400
    • Route 53
      ドメインのAタグにディストリビューションを設定します。
      f:id:ryoju:20160414202230p:plain:w200
  • LINE BOTはAPIの呼び出し元のIPアドレスを事前に登録しておく必要があり、固定グローバルIPが必要となります(可変だと突然呼び出しが通らなくなるおそれがある)。そのためにElastic IPをEC2インスタンスに付けています。

ちなみに、私はさくらのVPSを1台契約しており、最初はそれを使おうとしたのですが、証明書の問題でうまくいきませんでした。仕方なくAWSに環境を作って、開発中のプログラム一式も移したという経緯がありました。おそらくLINE BOT APIのサーバ側でチェックしているのでしょうが、無償で取れるSSL証明書ではこちらの用意したコールバックURLを呼んでくれない(?)ようで、GMO GlobalSignの試用証明書、EcoCertの試用証明書と試したのですが、いずれもうまくいきませんでした。SSL証明書はLINE BOTを作ろうとする趣味プログラマのハードルとなっていて、BOT API Trialの公開から数日は「この証明書なら大丈夫」「これはダメ」みたいな話題が多かったです。

メッセージの処理と記事推薦

ユーザーが知りたいことを送信してから、うまが記事を提示するまでの流れは以下のようになっています。

  1. 形態素解析
  2. 特徴語抽出
  3. 記事の検索
  4. 記事の選択
  5. 記事の提示

また、大まかなデータのやりとりはこのような形になります。

f:id:ryoju:20160414213825p:plain

1. 形態素解析

ユーザーがLINEでうまとのトークを開始し、知りたいことを入力すると、LINEのサーバを経由してコールバックURLへテキストメッセージがPOSTされます。テキストメッセージからユーザーの入力した知りたいことを取り出して形態素解析を行い、単語に切り分けます。 形態素解析エンジンにはMeCabとIPA辞書(オフィシャルサイトで配布されているもの)を使っています。

2. 特徴語抽出

ユーザーは自然な(口語的な)表現で知りたいことを入力するため、検索の前処理として不要な単語を落としています。ここはバリエーションの考えられる処理ですが、現状では単純に名詞だけを残し、それ以外の品詞はすべて落としています。

ただ、ログを見るとやはり適切ではない分割をされているケースが多いです。NewsPicksが経済ニュースアプリであるためか、企業名等の固有名詞をメッセージに含む場合が多いのですが、分割しすぎているケースが頻繁に見られます。提示するニュースの適切さをあげるには、ここの改善が大事だなという印象があります。企業・業界情報プラットフォームであるSPEEDAの辞書を使えばもっと良くなるかもしれません。

3. 記事の検索

抽出された特徴語を用いてNewsPicksの記事を検索します。ここではNewsPicksの検索機能は使わず、Google検索を使っています。NewsPicksの検索機能は有料会員にだけ開放されている機能なので、誰にでもオープンなGoogle検索にしました。GoogleのWeb検索で上位10件を取得しています。

4. 記事の選択

うまは、1つの知りたいことに対して1件だけ記事を提示するため、検索結果の中から提示する記事を選択する必要があります。画像を合わせて提示したいので、検索結果の上位から画像があるかをチェックしていき、画像があるものが見つかればそれを選択しています。

ここも将来的な改善の余地が大きい箇所です。たとえば、検索結果の記事ごとにPick数を取得して重み付けをする、みたいなことはすぐに思いつきます。ただ、やりだすと終わりがありませんし、記事の内容を判定に使うためにはクローリングやページの処理が必要で、レスポンスタイムがそのぶん伸びてしまうこともあり、このような処理としています。

5. 記事の提示

リッチメッセージを作ってLINEサーバにPOSTすると、ユーザーに記事が提示されます。

以上の処理がワンセットで、テキストメッセージを受け付けるたびにこれを実行します。

社内公開

だいたい動くようになったところで、社内(ニューズピックス/ユーザベース)のSlackでうまを公開しました。けっこう好評で皆に使ってもらえたのですが、やっぱりユーザーの実際の使い方というのは、開発者に様々な気づきやアイディアを与えてくれるものです。以下はNewsPicksのメンバーがSlackにポストしてくれたキャプチャです。

時間の指定

f:id:ryoju:20160414204703j:plain:w250

f:id:ryoju:20160414204706p:plain:w250

いまの処理方法では、時間(記事の公開時刻)をまったく考慮していません。「今日の」「昨日の」のような時間指定のあるメッセージだと、ユーザーの意図を踏まえないイマイチな答えになってしまいます。改善ポイントですね。

形容詞の使用

f:id:ryoju:20160414204734p:plain:w250

現在の処理方式では特徴語抽出で名詞だけを残しており、「盛り上がった」は形容詞なので、検索結果に何ら反映されません。この場合は「ニュース」だけで検索した結果を返すことになっています。

まあ、うまは生まれたばかりだからね……。開発者としてはがんばって賢くしてやりたいものです。

ソースコード

現状のソースコードをGithubに上げたので、よろしければご覧ください。まだα版くらいの感じなので、ごちゃごちゃ弄っている最中でコメントも皆無と、綺麗とは言いがたいのですが。そのうち整理されてくるはず……たぶん。

実装にはPythonを使っています。フレームワークはDjango(w/ Django REST Framework)です。 どこかで動かしてみる時には、newspicks/settings_line.pyにあなたのLINE BOTのChannel ID, Channel Secret, MIDを記載してください。