冷蔵庫の余った食材からレシピを提案するボットをリリースしました

2016年10月より正式公開された LINE Messaging API（旧 BOT API）を使って、レシピ提案ボットを作りました。
家事の助けになればと思い、当時妊娠中だった妻のために作ったものですが、わりと出来がいいので皆さんにも使って頂けるように一般公開しました。
このボットの特徴は、食材名を元にレシピを提案する点と、レシピの表示にカルーセル型のメッセージを利用していることです。
本稿では、そのボットの紹介と、仕様面および実装面の設計についてお話ししたいと思います。

追記：
※ 本サービスは最終的に100名以上の方に登録いただきましたが、2018年3月をもって運用を終了しました。ご愛顧ありがとうございました。

# ボットの紹介
「食材 de レシピ」という名前の LINE ボットです。
ユーザから送信された食材名を元に、レシピを提案します。

- まず、ユーザが食材名をテキスト送信します。
- すると数秒後にボットから、その食材を使った料理の写真と説明が送られてきます。
- 気に入った料理の「レシピを開く」ボタンを押すことで、レシピが掲載されたページに遷移できます。
※レシピ情報は楽天 API から取得していて、楽天レシピの該当ページに飛びます。

![トーク画像1](https://qiita-image-store.s3.amazonaws.com/0/33826/a0ac5f0e-beb2-6b6d-8f9f-ab574ac258d4.jpeg)　![トーク画像2](https://qiita-image-store.s3.amazonaws.com/0/33826/89cbee97-271a-4c24-e1c7-159a3e525301.jpeg)　![トーク画像3](https://qiita-image-store.s3.amazonaws.com/0/33826/c57a1800-8c2c-b1f9-71aa-bc2c5edc4f48.jpeg)
※画像は開発中にキャプチャしたもので、現在公開している内容と違う点が含まれています。

このボットは、料理にまつわる以下の場面で使われることを想定して作りました。
食材名からレシピを検索したいというのは、非常に一般的なニーズだと思います。

- 冷蔵庫の余った食材を使ってしまいたい
- 足りない栄養を補うためにこの食材を使いたい
- らでぃっしゅぼーやのセットに調理法のわからない野菜が入っていた

LINE のアプリから以下の QR コードを読み取ることで、友達追加できます。
せっかく作ったものなので、ぜひぜひお使いくださいませ。
![ボット友達追加用の QR コード](https://qiita-image-store.s3.amazonaws.com/0/33826/836bfefd-cc74-a1cb-e447-b50e64133663.png)

# 仕様面での工夫

### ボットの特徴

- その１：食材名を条件にして、レシピを検索します。

レシピをカテゴリから検索したり、ランキング形式で提示するというデザインも可能でしたが、これらは実用性に乏しいと判断して、食材名での検索に機能を絞りました。

- その２：検索結果（レシピ）を並べる UI として、カルーセル型のテンプレートメッセージを採用しています。

複数のレシピを並べるインタフェースとして、カルーセルは非常に効果的でした。
レシピのような、画像がひもづくモデルと相性が良いように思います。

※参考：API Reference ではカルーセル型メッセージの使用例として、天気予報に使われています。
![API Reference でのカルーセル型メッセージ使用例](https://qiita-image-store.s3.amazonaws.com/0/33826/4d2f2bca-7ac7-3a22-6500-b6927a762017.jpeg)


### アイコン画像
ターゲットは20~30代の主婦層ということで、朝の情報番組で料理コーナーをされているイケメン俳優の方をイメージして作りました。
![ボットアイコン画像](https://qiita-image-store.s3.amazonaws.com/0/33826/717c8afa-8b1f-920f-9f1c-fc62a2212e4a.png)

アイコン制作には「ちゃんりお」というアバター画像作成サービスを利用させて頂きました。
（ロボットではなく）人をモチーフとした、愛着の持てるデザインを採用することで、ボットの持つ機械っぽさを感じさせないように意識しています。
[ちゃんりおメーカー -Chanrio maker-](https://chanrio.com/)
![ちゃんりおメーカー WebUI キャプチャ画像](https://qiita-image-store.s3.amazonaws.com/0/33826/99f8058b-5c3c-3981-183e-983a2e2bd633.jpeg)

# 設計と実装

### サーバ構成と処理の流れ

「食材 de レシピ」ボットのサービスの全体像として、全体の構成とサーバ間のデータのやり取りを図示しました。
緑の矢印がユーザへのリプライ処理に伴う処理の流れで、ピンクの矢印がバッチ処理に伴う処理の流れになります。
各コンポーネントの詳細に関して、以降の節で順に説明していきます。
![Network Diagram.png](https://qiita-image-store.s3.amazonaws.com/0/33826/dec95475-0760-ffa6-487d-64b61e04abb5.png)

### ボットサーバ

LINE ボットを提供するには、友達追加やユーザからのメッセージ送信時など、イベント発生時に LINE サーバ側からコールしてもらう Webhook API が必要です。
「食材 de レシピ」では、この API サーバを Go 言語を採用して実装しました。
LINE から公式の SDK が提供されていることと、***標準ライブラリとして SSL 対応の HTTP サーバを提供している***点から、リリースまでのスピード感が得られる点を評価して選びました。

また、Go プログラムをデーモンとして走らせるために、「食材 de レシピ」では Supervisor を利用しています。

+ [Supervisor: A Process Control System — Supervisor 3.3.1 documentation](http://supervisord.org/) 
+ [Supervisorで簡単にデーモン化 - Qiita](http://qiita.com/yushin/items
/15f4f90c5663710dbd56)

デーモン実行用のスクリプトを書く手間が省けるのと、不正終了時の自動再起動など運用に便利な機能も含まれているので、個人でのサービス運用には強い味方となるプロダクトです。
ちなみに、CentOS 6 標準の Python 2.6 で走らせたときに、デーモンの stop が機能しない問題が発生しました。おそらく、Python 2.6 の subprocess モジュールのバグが原因だと思うのですが、インタプリタを Python 2.7 に換えることで解決しました。

インフラは以前から契約していた「さくらの VPS」を、SSL 対応には「Let's Encrypt」のサーバ証明書を利用しました。
サーバ証明書は自動更新できるように cron にコマンド（certbot-auto renew）を仕込んでいますが、443 ポートを空けておかないと certbot-auto がエラーを吐くので、API サーバ（HTTPS なのでデフォルトは 443 ポート）は別のポートを使うようにしました。

### レシピデータの取得

レシピの取得には、楽天提供の API を利用させて頂きました。

+ [楽天ウェブサービス: 楽天レシピカテゴリ一覧API(version:2012-11-21) | API一覧](https://webservice.rakuten.co.jp/api/recipecategoryranking/)

この API を利用することで、楽天レシピに掲載されているレシピ情報を取得することが出来ます。
API で提供されている機能は以下の2つです。

+ カテゴリ別のレシピランキング取得
+ カテゴリ一覧の取得

実は、***API で取得できるのはレシピのランキング情報のみ***で、検索機能はもはや、取得可能なレシピデータはランキング上位のもののみです。
一見するとこれはきつい制限に見えるのですが、カテゴリの種類はかなりの数がありますので、各ランキングをさらえば相当数のレシピが取得できます。
そこで「食材 de レシピ」ボットでは、レシピは日次のバッチでまとめてキャッシュしておいて、検索機能に関しても自前で用意することにしました。
※カテゴリの数はかなりあるので、「食材 de レシピ」では大カテゴリと中カテゴリのランキングのみをキャッシュ（小カテゴリは除外）しています。

### 材料名によるレシピ検索

レシピ検索を自前で用意するために、検索サーバとして Elasticsearch を導入しました。
Elasticsearch はドキュメント指向 DB であることから柔軟性が高く、Java アプリであることから導入も簡単です。さらに REST API をデフォルトで提供していることから利便性も高く、非常に生産性の高いプロダクトです。
インデクシングに関しても、材料名のフィールドに全文検索用の analyze 設定を仕込んでおけば、あとはレシピ検索用の index にレシピデータを PUT するだけです。

材料名の analyze ですが、n-gram で tokenize して行っています。
形態素による tokenize と比較して空間効率が悪い点については、今回のサービスでは材料名という比較的小さなサイズのテキストを analyze 対象とする点から影響が小さいと考えました。
また、n-gram はユーザの意図しない結果が得られる性質がやり玉に挙げられることが多いですが（例：「京都」で「東京都」がヒットする）、n-gram であれば日本語であれ英語であれ安定した精度が得られるというメリットがありますし、個人的には解析エンジンの精度によって結果が漏れることがあるに比べれば、遥かに良いのではないかと思います。

### その他

追記（2016/12/24）：
ソースコードを公開しました。ご参考にどうぞ。
https://github.com/recipe-linebot

# まとめ
本稿では、公開した LINE ボットの仕様面および実装面に関して、どのような設計になっているか、どうしてそのようになっているかというところを、図やキャプチャ画像を交えて紹介しました。
今回の実装を通して、ボットはアイデア次第で様々なサービスを少ない工数で実装・リリースできる点で、非常に魅力的なプラットフォームであると感じました。
一般的には、企業の提供するサービスにおいてサポート分野での応用が期待されることの多いボットですが、そういった点では個人によるサービス開発にも適していて、フリーな活動をする多くのエンジニアの手によってボットの可能性が開かれていくことに期待したいですね。:rainbow: