離れて暮らしている母親はスマホが使えない。コミュニケーションの際には電話をかける必要があり、少々コミュニケーションコストがかかる。このわずかなコストがコミュニケーション機会を減らす要因となって積み重なって、疎遠になることを危惧している。というと大袈裟だが、電話することでもない些細な事を気兼ねなく伝えることができれば何かと便利ではないか、と考えた。
スマホ操作も電話通話程度しかできないため、シンプルなUIでLINEメッセージを簡単に確認できて、2択くらいの回答が可能なデバイスを作成する。+αの機能として普段はフォトフレームとして動作するようなキオスク端末である。
「生成AIの力を借りて」というのは、当然ながら開発はCursorやChatGPT越しに各種LLMモデルを使ってコードを書いたり、インフラもCLIコマンドを生成してもらって構築している。また表示するメッセージに対する回答をキオスク端末からもできるように、メッセージにしっくりくる2択の回答を生成AIで作成している。
完成した様子が以下である。
アーキテクチャ図は以下のような形になった。
本記事でカバーすること
- 各機能ごとに大まかな構成と実装ポイントや考慮ポイント
- 生成AI開発を通じて感じたこと
本記事でカバーしないこと
- インフラの詳しい構成
- アプリの詳しい実装
- リポジトリは以下
開発をどのように進めていったか
- 大まかな方針をChatGPTでプロジェクトを作成して方向性を明確化
- パーツごとに実装をCursor+WSLでコード化(インフラもコード化すれば良かったと後悔。AIと共通認識を持つことができ、より開発がスムースだったかと思う)
- ローカルで動作確認後、ラズパイ環境へデプロイ(静的なサイトとNode.jsのローカルプロキシサーバに必要なファイルのみをrsync)
開発条件
- セキュリティ・バイ・デザイン
- 職業柄ここは手を抜かない(抜くとまずい)
- 後からリモートでメンテナンスできるようサーバに実装を極力寄せる
- ラズパイ側もコンテナ化して更新し易くしたいが、3B+のリソースサイズと相談
- 眠っているラズパイ3B+を活用
- タッチパネルディスプレイのみブラックフライデーで購入
- ユーザはスマホが使えないくらい現代のデバイス操作には不慣れなためキオスク端末のUIは簡素化
- メッセージの送信元は普段のLINEを使ったコミュニケーション体験を維持する(私を含めた兄妹)
キオスク端末セットアップ
Raspberry Pi 3B+にRaspberry Pi OS Lite 64bitを入れて、最小のX環境をセットアップした。手順はImagerをWindows環境にインストールしてmicroSDにイメージ焼いて、ラズパイに挿し込めば完了。あとはXとウィンドウマネージャとして軽量なopenbox、ブラウザにchromiumをインストールして .xinitrcやautostart、.bash_profileなどのスクリプトを少し書いて完了。
ポイント
- 最小構成を意識したおかげか、今のところ、14インチ/FHDなディスプレイに画面映すのも特にもたつき等の問題なく動作してる
Web UI
Chrominiumブラウザからローカルプロキシサーバ( http://localhost:8080 )からコンテンツを取得する。
ポイント
- 特になし
Google Photo情報取得
CloudRunでGoogle Photoからの情報取得処理を実装。別途手動認証で取得したリフレッシュトークンをSecretManagerから参照し、クラウド上のワークロードからGoogle Photoへの永続的なアクセスを実現している。サブの機能ですが一番手間だった。
ポイント
- Google Photoからの画像情報取得はいろいろ制約があり、結論として参照するアプリ自身がアップロードした画像だけしか情報取得できないことが判明
- 本フォトフレームアプリ向けに取得したOAuthのクライアントIDで画像のアップロードから取得まで一貫して行ってやる必要があります
- CloudRunからGoogle Photo上の画像情報取得には、OAuthの認証プロセスで取得したリフレッシュトークンを流用する。ラズパイ上のフォトフレームアプリからAPI Gatewayを経由した画像情報取得リクエストに基づき、このリフレッシュトークンをPhoto Retrieverで再利用して画像情報を取得してやる形になる
LINE Botへのメッセージ処理と永続化、端末へのPush配信
LINEボットの設定は割愛しますが、ボットが同居するユーザチャットやグループチャットへメッセージが投稿されるタイミングで呼び出されるWebhookのエンドポイントをCloudRun上に実装。LINEのボットSDKを使ってメッセージの署名検証を実施している。
グループチャットでは、Botへのメンションを付けたメッセージがキオスク端末に転送される仕組み。該当するメッセージは、このタイミングでPub/Subに一旦PublishすることでWebhookの処理を完了させる。
Pub/Subを介して、メッセージは非同期に処理され、Firestoneに永続化される。
サーバからキオスク端末へのPush通信は、SSE(Server-Sent Event)でWebSocket等よりも簡易に実現。
ポイント
- 特になし
Geminiによる回答候補の生成
メッセージをFirestoreに永続化するタイミングでGemini APIを呼び出して、2択の回答候補を生成し、回答も含めてFirestoneに記録する。モデルは gemini-2.0-flash-exp を利用した。
2択回答候補生成プロンプト:
以下のメッセージに対して、適切な2択の回答を生成してください。
メッセージ: "${message}"
以下のJSON形式で回答してください:
{
"choice1": "選択肢1の内容",
"choice2": "選択肢2の内容",
"reasoning": "この2択を提案する理由"
}
2択は互いに対照的で、明確な違いがあるようにしてください。
以下のようなメッセージから2つの選択肢が生成される
次にやること
- キオスク端末側で各メッセージに対して選択した回答結果を、各メッセージ入力者のLINEに返却する
- インフラのコード化
さいごに
正月に本キオスク端末の設置を目標に、Qiitaの記事執筆をきっかけとして本開発に着手したが、結果として、生成AIの力を全面的に借りることで、就寝前の限られた時間を使いながらも、わずか4〜5日でここまで形にすることができた。
実装にあたって、私はエンジニアとしてのバックグラウンドを持っているものの、実際にはコードを一切書いていない。それでもこのレベルのプロトタイプを構築できたという事実は、生成AIによって「市民開発」の敷居が大きく下がったことを示していると感じている。
今後、実際の利用を通じたユーザフィードバックや機能改善の要望が得られれば、その結果についてもフォローアップとして共有していきたい。





