Generic Camera とは
漁港、農園、検品ラインといった「目で見て判定する」業務には共通の課題があります。 カメラで対象を撮影し、その場で AI に判定させ、結果を即座に返す — このフローを最短経路で実装した汎用 PWA が Generic Camera です。
ドメイン (判定対象の領域) を YAML で定義するだけで、UI もスキーマもそのまま流用できます。リリース時点では「汎用判定」「魚種判定」「みかん成熟度判定」の 3 ドメインを同梱しており、画面上のタブを切り替えるだけで判定対象を切り替えられます。
ソフトウェアエンジニアの寺島和宏 (howlrs) が、現場の判定業務を「スマホ 1 台 + ブラウザ」で立ち上げられるようにするために設計しました。

何を解決するのか
現場の判定業務をデジタル化しようとすると、たいてい次のような壁にぶつかります。
- ドメインごとに専用アプリを作ると保守コストが膨らむ
- ネイティブアプリにすると配布・端末管理・OS 追従が重い
- 営業デモ用と本番運用用でコードベースが分かれてしまう
Generic Camera は「カメラ + 判定」という共通の骨格を 1 つの PWA に固定し、判定ロジックの差分を YAML 1 ファイルで吸収する 設計にしました。新しい判定対象にも、UI の共通部分を保ったままドメイン定義を追加して対応できます。
アクセスコード方式で営業デモに直行する
最初の画面はアクセスコード入力です。?code=XXXX のクエリで起動するか、画面上のフォームに入力すれば、その値が localStorage に保存されて以降は省略されます。
このアクセスコードは Next.js Route Handler 側で API キーにマップし、ブラウザへのレスポンスには API キーを含めない構造です。共有先には有効期間を限定したコードを別経路で案内し、デモ環境への入口として使います。
商談が終わってアクセスコードを失効させたければ、サーバ側のシークレットを書き換えるだけ。ブラウザに残ったコード自体は鍵としての効力を失うので、端末回収不要で「貸出デモ」を回せます。
確信度は星 5 段階で出す
判定結果は LLM の生確率や confidence 数値ではなく、星 5 段階 で表示します。「0.83」と「0.84」を比べる時間は現場には無いので、人間が瞬時に直感できる粒度に丸めるのが正解、という設計判断です。
確信度の生数値は折り畳み JSON ビューに格納してあります。営業先から「これ、内部的にはどんなパラメータで判定してるの?」と問われた瞬間に展開して見せるためです。説明責任は果たしつつ、メイン UI は誤解されにくいレベルに整えています。
モバイル前提でレイアウトする
PWA として実機に入れて使う想定なので、すべての UI は最初からモバイル幅で組まれています。max-w-md mx-auto でレイアウトを固定し、デスクトップで開いても崩れません。

カメラ起動時には getUserMedia({ facingMode: "environment" }) で背面カメラを優先し、撮影画像は 長辺 1024px / JPEG q=0.85 にリサイズしてから API に送ります。回線が細い現場でもタイムアウトしにくいよう、ローディングメッセージは 10 秒・30 秒・60 秒で段階的に表現を切り替え、利用者を不安にさせません。
バックエンドは Rust + axum + Gemini
API 側 (apps/api/) は Rust + axum で書かれており、エンドポイントは /v1/recognize / /v1/domains / /health の 3 本だけ。受け取った画像を Gemini API (gemini-3.1-flash-lite-preview) に流し、ドメイン定義 YAML に紐付いた JSON Schema で結果を再検証してから返します。
Gemini が出力スキーマを破ったときは、 label="parse_error" で必ず生存 します。「現場でアプリが落ちる」を最も避けたい挙動として、部分パースを許容する設計にしました。営業デモ中に LLM がコケても画面は動き続けます。
レート制限は API キー単位で governor クレートを使って RATE_LIMIT_PER_MIN=30 に。撮影画像は base64 でサーバを 1 度通過するだけで、永続化は一切しません。
デプロイは GitHub Actions → Cloud Run
インフラは Cloud Run (asia-northeast1) の staging 環境で運用しています。API と PWA の 2 サービスを GitHub Actions からデプロイします。
api-deploy.yml/pwa-deploy.ymlで main push → staging へ自動反映
prod 環境は終了しており、利用者にはアクセスコード付きの staging URL を案内しています。
Secret Manager で API キーやアクセスコードを管理し、Cloud Run にはランタイムで注入。Service Account github-actions-deployer には必要最低限の 7 ロールしか持たせていません。
ビルドイメージにもこだわりがあり、 API 側の distroless イメージは sha256 digest pin で再現性を確保しつつ、Bookworm ベースの builder と glibc を揃えています (Trixie ベースの rust:slim を使うと distroless cc-debian12 で GLIBC_2.38 not found で落ちる罠を踏み抜きました)。
staging 環境を今すぐ試す
Generic Camera は prod を終了し、現在は staging 環境で提供しています。公開URLは以下です。利用条件と画面上の案内を確認してから試してください。
https://generic-camera-pwa-staging-giyklvtu5q-an.a.run.app/
リンクを開いた後、画面上で「汎用判定」、魚種判定、みかん成熟度判定を切り替えられます。
QR で今すぐ試す
スマホでは上記のURLを直接開いてください。背面カメラと中央ガイド枠が立ち上がったら、手元にあるものを撮影して AI 判定を試せます。
設計上のこだわり
12 専門家ペルソナ × Gemini Pro レビュー
設計仕様 (docs/superpowers/specs/2026-04-27-generic-camera-design.md) は 12 専門家ペルソナの議論と Gemini Pro による deep レビューを経て確定しました。MVP に何を残し、何を Plan 2 / Plan 3 に送るかを 1 件ずつ判断し、carry-forward 注記まで仕様書に残してあります。
Plan 1 → Plan 2 / Plan 3 の差分管理
最初の Plan 1 は「Hello from API / PWA を Cloud Run に乗せる」ところまで。ここで許容した妥協 (例: digest pin なし、graceful shutdown なし、USER root、package-lock.json 未コミット) は、すべて Plan 2 / Plan 3 の plan markdown に carry-forward 項目として転記してあります。「あとで直す」が泡のように消えない仕組みです。
iOS Safari ダークモードの罠を踏み抜く
color-scheme: light dark を入れた状態で値の <span> に明示的な text-color を付けないと、iOS Safari がダークモード時に UA 既定で白文字にしてしまい、bg-white のカード上で値が完全に消えるという地雷を発見しました。修正は color-scheme: light 固定 + 値側にも明示的な色クラス。同じ罠を踏まないように設計知見として残しています。
既知の制約
- アクセスコードは平文
localStorage保存です。営業デモ用途で割り切っており、ローテーション前提で運用しています - Gemini Free tier ではレートが不足するため、有償 API キー前提です
- 撮影画像は base64 で 1 度だけサーバを通過し、永続化はしません
まとめ
「カメラで撮るだけで何かを判定する」という、現場でよく出てくる業務パターンを、 ドメイン定義を差し替えるだけで再利用できる形 に切り出した PWA です。現在は staging 環境で、スマホ 1 台から利用を始められます。
- staging: Generic Camera を試す
- 作者: 寺島和宏 (howlrs)