何を解決するものか
「ウミガメのスープ」は本来、出題者役がいないと遊べません。ウミスーは Gemini を出題者役に立てて「はい / いいえ / どちらとも言えない」を判定させることで、一人でも遊べるようにしました。
公式問題のプレイ、ユーザーによる問題投稿、複数人で同じ問題を解くマルチプレイまで対応しています。
担当範囲
企画・実装・DB設計・認証設計・AI組み込み・デプロイのすべて。
技術的に難しかった点と、どう解いたか
1. 真相をクライアントに送らない
このゲームは真相がバレた瞬間に成立しなくなります。素直に作ると問題データと一緒に真相もクライアントへ流れてしまうため、真相はサーバー側の判定API だけが読む構成にしました。ブラウザの DevTools を開いても答えは見えません。
現状の RLS は public read のままなので、Supabase REST 経由では読めてしまいます。カラムレベルの権限か専用ビューで塞ぐのが次の課題として残っています。
2. AIの判定がブレる
「はい/いいえ」を自然文で返させると、表記ゆれでパースが壊れます。Gemini の Structured Output(responseSchema) を使い、{answer, comment} という形で必ず返させるようにしました。あわせて temperature=0.2 に落とし、同じ質問への回答が揺れないようにしています。
モデル名は lib/gemini.ts の一箇所に集約し、モデル更新時に一行で切り替えられるようにしました。
3. ゲスト参加とRLSの衝突
マルチプレイでは未ログインのゲストも参加させたいのですが、RLS のポリシーは auth.uid()::text = member_id を前提にしており、匿名の guest_xxxx では通りません。
ここは サーバー側の API ルートから service_role キーで書き込む形にして解決しました。当然クライアントバンドルには含めず、Vercel 側で Sensitive 扱いの環境変数として管理しています。
4. Next.js 16 への追随
開発中に middleware が proxy へリネームされる破壊的変更が入りました。バージョン差分を読んで追随しています。最新版を使う以上、こうした変更に自分で対応できることも実力のうちだと考えています。
学んだこと / 次にやること
LLM を製品に組み込むときは「出力を信用しない設計」が要でした。構造化出力・低temperature・サーバー側での秘匿という3点が揃って、はじめて遊べる品質になります。
次は評価機能(満足度・難易度)とお気に入りを実装中です。