はじめに
巷ではAIと名がつくものが多くなってきてます。色々なCMでもAIが…とか枕詞のようについていたりしますね。特に疑問や、どう思う?という同意を得たい時にもChatGPTに聞いて疑問を解消したりと使用方法も多岐にわたる昨今ではあります。
今回は、これからコーディングを覚えたいと思う初心者の方向けに、「アプリを作ってみたくてAndroid Studioなどのツールは用意したけど、どうしたらいいんだろう??」という疑問に対して、こういった方法もあるんだよ。という一つの提案をしてみたいと思います。
動作環境
この記事の手順は以下の環境で確認しています。
- 使用AIツール:VS Code + Claude Sonnet 4.6
- Android Studio:Android Studio Panda 3 | 2025.3.3 Patch 1
- Kotlin:2.0.21
- Jetpack Compose:BOM2025.04.00
- HTTPクライアント:OkHttp 4.12
※ お使いの環境に合わせて読み替えてください。
前提知識
この記事では以下の作業が必要になります。初めての方は各リンクを参照してください。
- Android Studio のインストール・セットアップ → 公式ドキュメント
- OpenAI アカウント作成・APIキー発行 → OpenAI Quickstart
※ APIキーの利用には課金が発生します。事前に使用量上限(Usage limits)を設定しておくことを推奨します。 - 実機へのAPKインストール(提供元不明アプリの許可設定) → 開発者向けガイド
コードを(自分では)書かないアプリ
前々から「アキネーターの逆版をよくYouTubeで見るけど、これアプリにできるんじゃない?」と思っていたので、今回、逆アキネーターアプリを考えてみました。
AIと色々意見交換を交わしつつ指示書を作成します。今回は作成したアプリを簡単に端末に入れて確認できるよう、Android指定で作ってみましょう。
指示書
# 🧠 逆アキネーター(AI有名人当てゲーム)仕様書
## 🎯 概要
ユーザーが質問を投げ、AIが「はい / いいえ」などで回答しながら、
AIが内部で決めた「有名人」を当てる推理ゲーム。
---
## 🎮 ゲームフロー
1. カテゴリ選択(初期は「有名人」固定)
2. AIが有名人を1人決定(非公開)
3. ユーザーが質問(最大20回)
4. AIが5択+理由で回答
5. ユーザーは任意のタイミングで回答可能
6. 正解 or 不正解で終了
---
## 📊 ゲームルール
### ■ 質問制限
* 最大20問
### ■ 回答形式
AIは必ず以下のいずれかで回答:
* はい
* いいえ
* どちらかといえばはい
* どちらかといえばいいえ
* わからない
+1文の理由を付ける
---
### ■ 回答方法(ユーザー)
* 「回答する」ボタンから入力
* 例:イチロー、マイケル・ジャクソン など
---
### ■ 勝敗判定
* 正解:ゲームクリア
* 不正解:ゲームオーバー(正解表示)
---
### ■ スコア(任意)
* 5問以内:S
* 10問以内:A
* 20問以内:B
---
## 🖥 UI構成
### ■ メイン画面(チャット)
* 残り質問数表示(例:残り 13 / 20)
* チャット履歴
* 入力フィールド
* ボタン
* 質問する
* 回答する
---
### ■ 回答ダイアログ
* テキスト入力欄
* 決定ボタン
---
### ■ 初期ヒント表示
以下を初期表示:
* 人間ですか?
* 日本人ですか?
* 男性ですか?
* 存命ですか?
---
## 🧠 AI設計
### ■ 役割
AIは「キャラ」ではなく「判定役」として振る舞う。「なりきり」させず、ユーザーへの回答は常に5択+理由の形式を厳守する。
---
### ■ 内部保持情報
* 名前
* 職業
* 国籍
* 性別
* 生死
※ユーザーには非公開
---
### ■ 運用上の注意点
* 正解情報(人物名)は必ずゲーム開始時に固定し、変更しない
* プロンプト設計で一貫性を強制する(過去回答との矛盾を禁じる)
* ユーザーの入力に予期しない内容が含まれる場合はAIに再入力を促す(エラーフェイルセーフ)
---
## 🤖 プロンプト設計
### ■ SYSTEM_PROMPT
```
あなたは推理ゲームの判定AIです。
# ゲーム設定
- 有名人を1人選び、内部で保持する
- この人物は絶対に変更しない
# 回答ルール
質問には必ず以下のいずれかで答える:
- はい
- いいえ
- どちらかといえばはい
- どちらかといえばいいえ
- わからない
# 追加ルール
- 必ず1文の短い理由を付ける
- 過去の回答と矛盾しない
- 曖昧な質問には「どちらかといえば」か「わからない」
- 名前や答えを直接・間接的に示さない
- 同じ質問には同じ意味で答える
# 出力形式(厳守)
「回答(理由)」
例:
はい(人間です)
いいえ(スポーツ選手ではありません)
```
---
## ⚙️ 技術構成
### ■ フロント
* Kotlin
* Jetpack Compose
### ■ 状態管理
```kotlin
data class GameState(
val remainingQuestions: Int = 20,
val messages: List<Message>,
val isLoading: Boolean
)
```
---
### ■ API
* **エンドポイント:** `POST https://api.openai.com/v1/chat/completions`
* OpenAI Chat Completions API を使用
#### 推奨設定
```json
{
"model": "gpt-5.4-nano",
"temperature": 0.3
}
```
#### 💰 料金の目安
`gpt-5.4-nano` で20問プレイした場合、入出力合計で概ね3,000〜5,000トークン(約0.1〜0.2円)程度が目安です。ただし **会話履歴をそのまま送り続けるとトークン数は線形に増加します**。要約機能(下記)を実装しない場合、20問目頃には初期の3〜4倍のトークンが必要になり、コスト増・レスポンス遅延・コンテキスト超過のリスクがあります。
---
## 🔄 会話管理
### ■ 履歴
* 全履歴 or 要約保持
### ■ 要約(推奨)
5〜10ターンごとに以下のプロンプトで要約し、古い履歴を圧縮する:
```
これまでの会話(質問と回答のペア)を、ゲームの一貫性を保てる形で簡潔に箇条書きで要約してください。
確定した情報(例:男性である、日本人でない)を列挙すること。
```
---
## 🧪 判定ロジック
### ■ 方法①(簡易)
* 文字列一致 or 類似一致
### ■ 方法②(推奨)
AIに判定させる
```
ユーザーの回答「{user_answer}」と正解「{correct_answer}」が一致しているか判定してください。
表記ゆれや読み方の違いも考慮してください。「一致」または「不一致」のみ返答してください。
```
> **⚠️ 重要:** 判定は**ゲーム進行用とは独立したコンテキスト(別のAPIリクエスト)** で実行してください。ゲーム進行用の会話履歴に正解(人物名)を含めてしまうと、AIが質問への回答の中で先読みして答えを漏らす設計バグが起こる可能性があります。
---
## 🚀 MVP範囲
### ■ 必須機能
* チャットUI
* AI応答(5択)
* 質問回数制限
* 回答機能
* 正誤判定
---
## 💡 今後の拡張
* カテゴリ追加(アニメ、動物など)
* 難易度調整
* ランキング
* SNSシェア
* 音声入力
---
## ⚠️ 注意点(運用時)
* ユーザー発話のサニタイズ(予期しない文字列・プロンプトインジェクション対策)
* エラー時のフェイルセーフ(API障害時はエラーメッセージを表示し、ゲームを継続可能にする)
---
## 🎯 コンセプトまとめ
* シンプル
* 短時間で遊べる
* 繰り返しプレイ可能
* AIの曖昧さを"面白さ"に変える
---画面周りの情報が少ないかもしれないですが、一通りの指示書になってるかと思います。上記の内容を.mdファイルとして保存しAIツールに読み込ませます。
今回はVSCodeのAI(Claude Sonnet 4.6)に対して、出来上がった指示書を読み込ませて「このマークダウンファイルに則ってAndroidアプリを作成してください」という指示を出してみました。

おお、作った。
出来上がったプロジェクトをAndroid Studioで開いてみます。

生成されたコードを読んでみる
AIが生成したコードのうち、状態管理とAPI呼び出し部分の抜粋を見てみましょう。
// ViewModel.kt(AIが生成したコードの抜粋)
class GameViewModel : ViewModel() {
private val _state = MutableStateFlow(
GameState(remainingQuestions = 20, messages = emptyList(), isLoading = false)
)
val state: StateFlow<GameState> = _state.asStateFlow()
// OpenAI APIに渡す会話履歴(system + user/assistant のリスト)
private val history = mutableListOf(
mapOf("role" to "system", "content" to SYSTEM_PROMPT)
)
fun sendQuestion(text: String) {
viewModelScope.launch {
_state.update { it.copy(isLoading = true) }
history.add(mapOf("role" to "user", "content" to text))
val reply = callOpenAiApi(history) // ← ここでAPIを叩く
history.add(mapOf("role" to "assistant", "content" to reply))
_state.update { s ->
s.copy(
remainingQuestions = s.remainingQuestions - 1,
messages = s.messages
+ Message(role = "user", content = text)
+ Message(role = "assistant", content = reply),
isLoading = false
)
}
}
}
}注目ポイントは history リスト。ユーザーの質問とAIの回答を交互に積み上げて、毎回まるごとAPIに渡すことで「文脈を理解した回答」が返ってくる仕組みです。
viewModelScope.launch は「画面が消えたら処理も止める」という非同期の仕組み、StateFlow は「状態が変わったら自動でUIに反映する」ための仕組みです。
こういった処理の意味を一つずつ調べながら読み解いていくと、自然とKotlinやJetpack Composeの考え方が身についていくと思います。
コードの横に何を行う処理なのかをコメントとして追記していくと覚えやすいと思います。コレおすすめ。
APIキーの設定(重要:セキュリティに関する注意事項)
出来上がったプロジェクトをAndroid Studioで開き、ビルド後、APKにしてみます。その際にAI用のAPIキーを設定しますが注意してください。
⚠️ 【重要】この手順は「学習・検証用のローカルビルド」に限定してください。
APKファイルにAPIキーを埋め込む方法は、local.properties や BuildConfig 経由であっても安全ではありません。 APKは apktool / jadx 等のツールで誰でも逆コンパイルでき、埋め込まれたキーは容易に抽出されます。キーが漏洩すると、第三者がそのキーを使ってOpenAIの課金枠を使い切るリスクがあります。OpenAIのセキュリティガイドラインでも、モバイルアプリにAPIキーを直接配布することは非推奨とされています。
本来の正しい構成: バックエンドサーバー(Cloud Functions / AWS Lambda / 自前サーバーなど)を挟み、APIキーはサーバー側にのみ置く。アプリはそのサーバーのエンドポイントを叩く構成にします。
学習目的でローカルビルドする場合は、以下のように local.properties にキーを記載し、コードから BuildConfig 経由で参照しますが、このファイルは必ず .gitignore に含めて、Gitにコミットしないようにしてください。絶対だぞ!!
# local.properties(.gitignore に追加されていることを確認)
openai.api.key=sk-proj-xxxxxxxxxxxxxxxx// app/build.gradle.kts
android {
buildFeatures { buildConfig = true }
defaultConfig {
val key = project.findProperty("openai.api.key")?.toString() ?: ""
buildConfigField("String", "OPENAI_API_KEY", "\"$key\"")
}
}// 使用箇所(Kotlin コード内)
val apiKey = BuildConfig.OPENAI_API_KEYアプリを起動する
APIキーの設定が終わったら、実際にアプリを起動してみましょう。方法は2つあります。
方法①:エミュレータで起動する(実機不要)
- Android Studio の上部ツールバーから「Device Manager」を開き、仮想デバイス(AVD)を作成する
- ツールバーのデバイス選択プルダウンで作成した仮想デバイスを選択
- ▶(Run)ボタンをクリックするとエミュレータが起動してアプリがインストールされる
初めての場合はエミュレータを使う方が手軽です。詳細は公式ドキュメント(Create and manage virtual devices)を参照してください。
方法②:実機(Androidスマートフォン)にインストールする
- スマートフォンの「開発者向けオプション」を有効化し、USBデバッグをオンにする
- USBケーブルでPCと接続
- Android Studio でデバイスを選択し ▶(Run)で直接インストールできる
または、Build > Build Bundle(s) / APK(s) > Build APK(s) でAPKファイルを生成し、スマートフォンに転送してインストールすることも可能です(「提供元不明のアプリ」の許可が必要)。
なお、開発者向けオプションを有効化する方法は端末ごとに違いがあるため、検索してみてください。
詳細は開発者向けガイド(実機でのアプリ実行)を参照してください。
実行してみた

動く…動くぞこいつ…

動作確認 〜AIとのラリーでブラッシュアップ〜
動作確認できましたが、UIの操作性や空文字などの数点の問題が散見できます。そういった気になる点をAIに再度通知し、修正を繰り返すことでブラッシュアップすることができます。実際に対応してみましょう。
空欄のまま送信してしまう問題
発生した問題: 質問フィールドが空のまま「質問する」ボタンをタップすると、空文字列がAPIに送られてAIが意図しない返答をしてしまう。
問題対応もAIに行ってもらいます。発生している問題とどうして欲しいかを纏めたプロンプトを作成します。
AIへの修正依頼(プロンプト):
質問フィールドが空欄のまま「質問する」ボタンをタップすると、
空の文字列がAPIに送信されてしまいます。
入力が空(スペースのみも含む)の場合はボタンを非活性にしてください。上記プロンプトをAIに渡すと以下のように修正してくれました。
修正前のコード:
Button(onClick = { viewModel.sendQuestion(inputText) }) {
Text("質問する")
}修正後のコード:
Button(
onClick = { viewModel.sendQuestion(inputText) },
enabled = inputText.isNotBlank() // 空欄・スペースのみなら非活性
) {
Text("質問する")
}isNotBlank() は「空文字でもスペースだけでもなければtrue」を返すKotlinの便利な関数です。AIに「こういう問題がある、こう直して」と伝えるだけでこの修正が一瞬で返ってきます。自分で修正できなくても、問題を言語化してAIに投げるというサイクルで、少しずつコードへの理解が深まっていきます。
また、指示書内、技術構成の指定する言語を、SwiftUIなどの言語に切り替えつつ、データ構造を修正すればiOS用アプリなど、AIにゼロからアプリを作ってもらうことが出来るようになっています。
まとめ
今回は、自分自身でコードを1行も書かず、AIに全てコードを書いてもらうことでアプリができるのかを実証してみました。
指示書からAIがプロジェクトを作成し、大まかな動作を行えるアプリを作成することができることから、言語やアプリ構造を一から覚えようとしている方への第一歩として、AIにこれから作成しようと思うアプリの土台を作ってもらうという方法は、覚えるキッカケとして有効なのではないでしょうか。
AIが作成したプログラムを参考に「こういう処理がこう動くんだな」と読みながら覚えるのもコーディング技術向上につながると思います。
これに頼りすぎるのも良くはない気がしますが、一つの選択肢として「AIにまず書いてもらう」というのもアリなのではないかと思う今日この頃です。
