![見出し画像](https://assets.st-note.com/production/uploads/images/87720509/rectangle_large_type_2_2d8b6492ca1de663988be0798b79be9b.png?width=1200)
ハッカソン優秀賞の設計
ハッカソンとは
ハッカソンとは短期間でサービスやプロダクトを作ることです。ハックとマラソンを掛け合わせた造語です。
今回作ったもの
Alexaに話しかけるだけで絵日記が書けるサービスを開発しました。
作る動機
動機としては、Alexaが好きな後輩がAlexaを使って何かしたいと言ってきたので二人で案出しをしました。Alexaを動かしたりしたら可愛いんじゃないかとか色々な案が出ましたが、Alexaの良さは声というインターフェースを使って簡単に何かをすることができるというところだと思っているのでそこを活かせるアイデアを考えました。そこで思いついたのが、Stable Diffusion(※1)を用いた声だけで絵日記が書けるサービスでした。
(※1)Stable Diffusion:https://github.com/CompVis/stable-diffusion
前提条件
今回決まっていたことは、Alexaで今日あったことを話す。その言葉から画像などを生成し絵日記にしてWebで表示する。入力場所と出力する場所、そして絵日記を作るということが決まっていました。
設計
使用技術選定
今回作らなければならないのは、Alexaスキル、バックエンド(画像生成+DB)、フロントエンドの三つです。Alexaスキルに関しては後輩に一任していたので今回の話からは除外します。
バックエンド
まず、ユーザの今日あったことがテキストとして入力されます。それに、さまざまな処理を施し、絵日記にしてフロントエンドに渡すということです。
まず絵日記の画像生成に関してはStable Diffusionを使用します。これはPythonで使うしかないのでFlask(※2)にのせます。他にもPythonでサーバを立てるとなると、Django(※3)やFastAPI(※4)がありますが、今回はテキストの入力を受けつけて画像を生成しその結果をDBに保存するためにGraphQL APIを叩くというシンプルな構成のため軽量なFlaskで十分だと判断しました。ここに関してはハッカソンで単純に外部のAPIを叩くだけなどのシンプルなAPIを作成する場合は、慣れたものを使うでいいと思います。また生成した画像をそのままDBに保存するわけにはいかないので、AzureのBlob Storageを使用しました。Flaskサーバで行っていることは次の通りです。
(※2) Flask:https://flask.palletsprojects.com/en/2.2.x/
(※3) Django:https://docs.djangoproject.com/ja/4.1/
(※4 )FastAPI:https://fastapi.tiangolo.com/ja/
入力されたテキストをDeepLAPIで英語に変換
テキストからStable Diffusionで画像生成
生成した画像をAzure Blob Storageに保存
テキストから感情推定
テキスト、画像URL、感情データなどを保存するためにGraphQL APIを叩く
前述した画像生成APIでも言った通りこのサービスには、DBが必要になります。今回のハッカソンではAzure(※5)が使用できたので、AzureのMySQLを選定しました。使い慣れておりRDBの方がNoSQLより慣れているので選定しました。
(※5)Azure:https://azure.microsoft.com/ja-jp/
DBを直接フロントエンド、やバックエンドのFlaskから操作することもできますが、DBの安全性やどんなデータが入るのかを監視する、またフロントからの利用の利便性を考えてDB操作用のAPIを作成することになりました。
ハッカソンでは、フロントの仕様がフレキシブルに変わる可能性があり、突然この情報が欲しいと言われることもあります。また、エンドポイントを合わせるのもお互い余裕がないハッカソンにおいては厳しいです。そこで今回はRESTAPIではなくGraphQLを選定しました。
GraphQLの利点としてバリデーションを自動で行ってくれたり、フロントエンド側で必要な情報を選択できるということなどがあります。そこでPythonではなく、Goを選択しました。Goは型付き言語でありGraphQLとの親和性もかなり高いと感じています。GoでGraphQLサーバを作ろうとするならgqlgenというライブラリが一番有名だと思います。今回はgqlgenを使用します。また、MySQLを使用するのでGorm(※6)も使用します。
(※6)GORM:http://gorm.io/ja_JP/docs/index.html
最後にこれは後付けで作ったのですが、画像生成にどうしても10秒程度かかるため、Alexaのタイムアウトに引っかかりAlexa側でエラー文が流れてしまうということがありました。そこでGoの機能であるGoroutineを使用してとりあえずAlexaに終了ステータスを返すAPIを作成しました。PythonのAPIを叩くだけのシンプルなAPIなのでechoを(※7)使用しました。
(※7)echo:https://echo.labstack.com/
構成としては
GraphQLサーバ(DB)
gqlgen
gorm (orm)
mysql (DB)
画像生成サーバ
Flask
Stable diffusion (text2image)
gql (GraphQLクライアント)
アレクサが叩くサーバ
echo
フロントエンド
今回のアプリではTwitterのタイムラインのような全ての人の絵日記が見れるページや余裕があればマイページやフォローといったこともできるようにしようと考えていました。ページ数がそんなに増えない予定であることやメンバーの経験からReactを選定しました。
また、バックエンドにGoを採用しておりGraphQLを使用しているので型があった方が良いだろうということでTypeScriptを採用しました。
また、ReactでGraphQLAPIを叩くためのGraphQLクライアントを選定する必要があります。種類としては、Apollo、Relay、Urqlがあります。全て使ったことがなかったのですが記事の多さとFetchのための関数が自動的に生成されるApolloを選択しました。Apolloにtypescript-react-apolloというプラグインを入れることで自動的にFetchのための関数を生成してくれる。これでフロントエンドエンジニアは通信のためのコードを書く必要がなくなります。
上記でも書いた通りハッカソンにおいてフロントエンドがほしいデータを簡単に手に入れる環境が作れたことは本当に大きかったと思います。実際今回のハッカソンでも感情のデータやテキストの英訳情報など後からフロントから欲しいと言われて追加したデータがあります。その場合でもバックエンドの改修が終わればGraphQLのスキーマを渡してnpm startをしてもらえば新しいスキーマに対応したFetch関数がまた作られるということになります。
TypeScriptをハッカソンで採用するとpropsの型やデータfetchの際の型定義に時間を取られることが多いと思います。その手間をすべてなくせたというのは、時間だけでなくフロントエンドがよりUI、UXを凝るための助けになったと考えています。
フロントエンド
React
Apollo
本番環境
開発はフロントエンド2人、バックエンド1人、アレクサ担当1人で行っていました。デプロイしなければならないのは、GoのEchoサーバとGraphQLサーバ、PythonのFlaskサーバ、フロントのReactの4つです。
AzureにはApp serviceという便利なホスティングサービスがありますが、Flaskで感情推定などを行ったせいでライブラリ関連が複雑になってしまい、App serviceへのデプロイは断念しました。そこでAzureのVMサービスを使用しました。VMにはGlobal IPがふられているのでFirewallの設定を変更しFlaskが使用する5000番ポートを解放しました。
残すはGoのサーバです。残念ながら、App ServiceはGoに対応していないので、Graph QLサーバに関しては、GCPのApp Enginを使用しました。一方Goのechoサーバは最後の最後に作ったため、デプロイで苦労したくなかったため、VMをもう一つ作ってそこで運用しました。
Azure App ServiceはGUIでGitHub Actionsの設定ができ、自動デプロイの設定ができますが、VMほど自由度がなく、App Serviceになかなかデプロイできない時はVMも検討すべきだと思います。
最後にReactはVercelにデプロイしました。発表ではVercelへのデプロイでエラーが出ていたので、もう一つVMを立ち上げてそこでReactを立ち上げるというとても無駄なことをしました。VMが扱えると急なエラーにも対応できるので便利です。
最後に
ハッカソンにおいてスタートを間違えてしまうとそのマイナスをずっと抱えて数日間開発する必要があります。設計の部分を少し考えるだけでハッカソン中悩むことが少しは少なくなると思います。ぜひ少し設計にも目を向けてみてください。
この記事が気に入ったらサポートをしてみませんか?