はじめに
こんにちは、松延です。ZEN大学(通信制)の2年生で、AIエンジニアを目指して勉強しています。
個人で、iOSアプリ「カミメグ」を作っています。御朱印を写真で残して、近くの神社を探し、参拝を記録できるデジタル御朱印帳です。無料で使えて、アカウント登録はありません。記録は端末の中に保存されます。
2026年10月にv1.0を公開し、2026年11月にv1.1.0を出しました。v1.1.0の目玉は、ボタンを押すと**「お供の神様」が神社を紹介してくれる生成AI機能**です。
この記事では、その機能をアプリに載せるまでに考えたことをまとめます。コードの紹介ではなく、次の3つの判断の話です。
- APIキーを持つ場所をどこにするか(サーバーレスにした理由)
- 「記録は端末の中」というアプリの約束を、AI機能を入れても崩さないにはどうするか
- 最初に検討したBedrockやClaude APIではなく、なぜ最終的にGeminiにしたか
カミメグの構成(AI機能を入れる前)
もともとのカミメグは、ほぼ端末の中だけで完結するアプリでした。
| 役割 | 使っているもの |
|---|---|
| アプリ本体 | Expo / React Native(TypeScript) |
| 記録の保存 | SQLite(端末内) |
| 近くの神社の検索 | Google Places API |
| 広告 | Google AdMob |
自前のサーバーはなく、アカウントもありません。「御朱印の写真や参拝記録は自分の端末にだけある」というのは、作っている自分としても大事にしたかった点です。
v1.0の公開時には、App Storeの審査で追加のやり取りが必要になりました。対応したうえで通過して公開できましたが、「個人開発でも、ストアに出す以上は説明できる作りにしておく」ことの大切さを感じました。これが、AI機能を設計するときの前提にもなっています。
やりたかったこと
神社を探して参拝を記録していると、「この神社はどんなところなんだろう」と思う瞬間があります。そこで、神社ごとに短い紹介文を出せたら楽しいと考えました。ただ、説明文をただ表示するだけでは味気ないので、アプリの中の「お供の神様」が紹介してくれる形にしています。
紹介文は、自動では作りません。ユーザーがボタンを押したときにだけ生成します。理由は後ほどプライバシーの節で書きます。
あわせて、v1.1.0では名前で神社を検索したときに、その神社だけが表示されるようにしています。アプリの表示名も「カミメグ」にそろえています。
判断1:APIキーをアプリに入れない(API Gateway → Lambda → Gemini)
生成AIのAPIをアプリから使う方法として、一番簡単なのは「アプリから直接APIを呼ぶ」ことです。ですが、それだとAPIキーをアプリの中に入れることになります。アプリのバイナリに入ったキーは取り出される可能性があり、個人開発で費用の上限を自分で管理している身としては避けたいところでした。
そこで、アプリとAIのあいだにAWSのサーバーレス構成を1枚はさむことにしました。
流れはシンプルで、アプリ → Amazon API Gateway → AWS Lambda → Google Gemini API です。GeminiのAPIキーはサーバー側にだけ置き、アプリには持たせません。
サーバーレスにした理由は次のとおりです。
- 常時起動のサーバーを持ちたくなかった:紹介文はボタンを押したときにしか生成しないので、リクエストがあるときだけ動けば十分です。
- アプリ側を変えずに、裏側を差し替えられる:アプリが話す相手はAPI Gatewayだけなので、後ろのLambdaの中でどのAIを呼ぶかはアプリのアップデートなしで変えられます。実際、この性質が後述のGeminiへの切り替えで役に立ちました。
- 利用制限をサーバー側でかけられる:1日に使える回数の上限をサーバー側で持てます(後述)。
判断2:「記録は端末の中」という約束を崩さない
AI機能を入れるということは、何かしらの情報を外に送るということです。カミメグは「アカウントなし、記録は端末内」を売りにしているので、ここは一番慎重に考えました。
決めたルールは次のとおりです。
- 送るのは神社名と住所だけ。紹介文を作るのに必要な最小限です。
- 送るのは、ユーザーが同意したあとだけ。同意がなければAI機能は使いません。
- 御朱印の写真、参拝の記録、位置情報は送らない。
- アカウントは作らない。AI機能のためにログインを求めることもしていません。
- 1日の利用回数に上限がある。上限の管理には、端末ごとのランダムなIDを使っています。個人を特定する情報ではありません。
- 生成した紹介文は、開発者のサーバーに最大90日間保存されることがある。この点はプライバシーポリシー(PRIVACY.md)に明記しています。
「ボタンを押したときだけ生成する」のも、このルールの一部です。画面を開いただけで勝手に外部へ送られる、という動きにはしたくありませんでした。ユーザーが自分で押したときにだけ、同意した範囲の情報が送られる。そのほうが説明しやすく、使う側も安心できると考えています。
利用回数の上限は、費用を守るためでもあります。個人開発では、想定外にAPIが呼ばれ続けると、そのまま自分の負担になります。上限をサーバー側で持てるのも、アプリから直接APIを呼ばない構成にしてよかった点です。
判断3:なぜGeminiにしたか
正直に書くと、最初からGeminiに決めていたわけではありません。当初は Amazon Bedrock と Claude API も候補として検討していました。
ところが、実際に進めてみると、利用の上限や支払いまわりの条件が個人開発の自分の状況と合わず、そのままでは本番まで持っていけないことがわかりました。そこで Gemini API に切り替え、最後まで仕上げて本番に出しています。
ここで助かったのが、判断1の構成です。アプリはAPI Gatewayしか知らないので、AIの呼び先を変えるのはLambdaの中の話で済みました。「どのAIを使うか」を後から変えられる余地を残しておいたことが、結果的にリリースを止めずに済んだ理由だと思っています。
この経験から学んだのは、モデルの性能比較の前に、そのサービスを「自分の立場で本番に使えるか」を早めに確かめることです。上限や支払い条件は、試しに数回呼ぶだけでは見えにくい部分でした。
公開しない情報を決めておく
アプリと記事を公開するにあたって、出さない情報も決めました。APIのエンドポイントURL、アプリ用のキー、AWSのアカウントやリージョン、利用上限の具体的な数値などです。この記事でもそれらは書いていません。
構成図で「API Gateway → Lambda → Gemini」と流れを示すのは、仕組みを説明するうえで問題ありません。一方で、具体的な接続先や上限の値は、知られると不正利用の手がかりになりえます。個人開発だと「全部オープンにして見せたい」気持ちにもなりますが、ここは線を引くようにしています。
注意点・これから
- 生成AIの紹介文は、内容が正確とは限りません。作る側としては、紹介文は「入り口」と考えていて、詳しいことは神社の公式情報で確かめてもらうのがよいと思っています。
- 紹介文はサーバー経由で生成するので、同意したうえで通信できることが前提です。
- 今後も、アプリの約束(アカウントなし、記録は端末内)を崩さない範囲で機能を足していきたいと考えています。
まとめ
- 生成AIのAPIはアプリから直接呼ばず、API Gateway → Lambda → Gemini のサーバーレス構成にして、キーと利用上限をサーバー側に置いた
- 送るのは同意後の神社名と住所だけ。写真・記録・位置情報は送らず、アカウントも作らない
- BedrockやClaude APIも検討したが、上限や支払い条件の都合でGeminiに切り替えた。アプリが裏側を知らない構成だったので、切り替えてもリリースを止めずに済んだ
アプリに生成AIを載せるときは、モデル選びより先に「何を送り、何を送らないか」と「後から差し替えられるか」を決めておくと、あとで楽になると感じました。同じように個人でアプリを作っている方の参考になればうれしいです。
カミメグはApp Storeで公開しています。御朱印めぐりが好きな方はぜひ触ってみてください。
https://apps.apple.com/jp/app/カミめぐ/id6821107881