TECH MEMO / FIELD NOTEAIエージェントを使い倒せ!Android CLIとSkillsでネイティブアプリをゼロから作る
はじめに
以前、JavaでAndroidアプリを開発していました。ただ、その後の変化を継続して追ってきたわけではなく、私の知識はJavaで開発していた時代で止まっています。Kotlin、Jetpack Compose、Coroutinesなどを使う現在のAndroid開発は、ほぼ一から学び直す必要がありました。
そんな状態でAndroidアプリを作ろうと思ったのは、バイブコーディングという言葉が広まり、AIエージェントを使えばアイデアから動くものまで一気に進められそうだと感じたからです。現在のAndroid開発を詳しく知らない私でも、AIと一緒なら形にできるのか。自分で試してみたくなりました。
もう一つのきっかけは、GoogleからAndroid CLIとAndroid Skillsが公式に提供されていたことです。AIへコードを書かせるだけでなく、Androidに特化した知識と、ビルドやエミュレータ操作などの道具まで公式に用意されているなら、試すにはちょうどよいタイミングだと思いました。
そこで、競輪風インターバルタイマー「Bell Lap Timer」をゼロから作りました。Androidプロジェクトの作成から、Jetpack Composeによる画面実装、タイマーとサウンドの制御、テスト、エミュレータでの動作確認、アプリアイコンの調整まで、ひと通りAIエージェントと進めています。
ただ、いちばん印象に残ったのは、アプリを短期間で形にできたことではありません。実際に進めてみると、AIにコードを書かせること自体は、開発全体の一部分にすぎませんでした。
AIが力を発揮したのは、プロジェクトのルールと必要な知識を参照でき、実装した結果を自分で確かめられる状態を作ってからです。その環境がないまま大きな指示を渡したときは、もっともらしいコードができても、細かな認識違いや未検証の実装が積み重なりました。
この記事ではアプリの実装方法そのものよりも、Androidのエージェント向けツールを実際に試して得た「AIエージェントに仕事を任せるための勘所」を中心にまとめます。
作ったもの
Bell Lap Timerは、室内サイクルトレーナーで使うAndroid向けのインターバルタイマーです。競輪の青板、赤板、打鐘、最終周回という流れを、トレーニングのフェーズとして取り入れています。
なお、このアプリは現時点ではGoogle Playなどのストアへ公開しておらず、個人での利用に留めています。
利用中は自転車をこいでいるため、画面をじっくり読めません。そのため、次のような要件を置きました。
- 残り時間を大きく表示する
- 画面の色と周回告知板で、現在のフェーズを一目で判断できるようにする
- 打鐘を遅延の少ない方法で再生する
- 縦向きと横向きの画面で使えるようにする
- タイマーの一時停止や再開で時間がずれないようにする
タイマー、音声、画面描画、端末ごとのレイアウトなど、久しぶりに触るAndroidには考えることがいくつもありました。AIエージェントにどこまで任せられるかを試す題材として、ちょうどよい難しさでした。
AIエージェント開発から得た学び
1. プロンプトより先に、AIが働く環境を整える
最初にぶつかったのは、一度だけ詳しいプロンプトを書いても、プロジェクト全体の品質を保つのは難しいという問題でした。
会話の中で伝えたルールは、その場のタスクでは守れても、別の作業では抜けることがあります。また、毎回すべての前提を書くと、指示する人間にも負担がかかります。
そこで、リポジトリにAGENTS.mdを置き、開発中に繰り返し必要になるルールをまとめました。今回記載したのは、主に次の内容です。
- コードを1行も書く前に、変更対象ファイル、実装方針、UI仕様を提示し、人間の合意を得る
- 作業はIssueとブランチに分ける
- コミットを小さな単位にする
- ビルドやテストだけでなく、画面の確認結果も残す
- ユーザー体験に関わる判断を、AIだけで確定しない
この中で特に効いたのが、「コードを書く前に変更対象と方針を提示してもらい、私が確認する」という手順です。これを決める前は、AIが良かれと思って関係のないファイルまでリファクタリングしたり、独自の解釈で必要以上の仕様を盛り込んだりしました。実装前に一度立ち止まるだけで、完成後に方向性からやり直す手戻りを減らせました。
IssueをAIのコンテキスト境界にする
今回の開発は、機能追加や不具合修正ごとにGitHub Issueを作成するIssueドリブンで進めました。Issueには目的、変更範囲、受け入れ条件を書き、AIエージェントはIssueごとにブランチを作成して実装します。
私がIssueに期待したのは、タスク管理だけではありません。「今回AIが考えてよい範囲」を区切るためにも使いました。作業範囲が明確になり、関係のないファイルへの変更や、依頼していない機能まで作り込む過剰な実装を抑えられます。途中で会話が長くなっても、Issueへ戻れば目的と完了条件を確認できます。
実装後は、テスト結果やエミュレータの画面キャプチャをPull Requestへ残しました。人間はコードの差分だけでなく、Issueに書いた受け入れ条件を満たしているかという観点でレビューできます。
この進め方によって、Issueがプロンプトの代わりになるわけではありません。Issueは、会話だけでは流れてしまう目的、範囲、完了条件を保持する外部コンテキストとして機能しました。
指示の量を増やすより、誰が何を判断するのかを明確にするほうが効果的でした。実装方法の選択や定型作業はAIに任せられます。一方、「運動中に本当に見えるか」「どの情報を削るか」「この見た目で完成とするか」といったプロダクト上の判断は私が行います。この分担が曖昧だと、AIは空白をもっともらしい推測で埋めてしまいます。
AGENTS.mdは単なる禁止事項の一覧ではなく、AIが迷ったときに立ち戻る基準として機能しました。
2. 曖昧な指示は、トークンと時間を消費する
開発を始めた頃は、「タイマー画面を見やすくして」「横画面にも対応して」といった短い指示を渡していました。私も、プロンプトが短いぶん効率的だと思っていたからです。
ところが、目的や完了条件が曖昧なままだと、AIは不足している情報を補うために関連ファイルを広く調べ、複数の可能性を検討します。実装後に私の意図との違いが分かれば、コードを読み直して修正し、ビルドや画面確認もやり直しです。そのたびに入力と出力が増え、結局はトークンも時間も多く消費しました。
特に手戻りが増えやすかったのは、次の情報が抜けている指示です。
- 何のために変更するのか
- 利用者がどのような状況で使うのか
- どのファイルや機能まで変更してよいのか
- 何を満たせば完了なのか
- どの方法で結果を確認するのか
たとえば「タイマー画面を見やすくして」ではなく、次のように目的と制約を渡します。
ローラー台で高強度の運動をしている最中でも、残り時間を一目で認識できるようにしたい。
タイマーのロジックは変更せず、表示する情報を整理して残り秒数を最優先にする。
縦向きと横向きの画面をキャプチャし、文字の見切れがないことを確認する。
ここでいう具体的な指示は、実装方法を細部まで指定することではありません。背景、達成したい結果、変更範囲、完了条件を明確にすることです。実装手段には余白を残したほうが、AIから自分では思いつかなかった案が出ることもあります。
短いプロンプトが、少ないトークンで終わるとは限りませんでした。最初の指示に必要な文脈を含めるほうが、探索とやり直しを減らせました。
3. Skillsは知識ではなく、判断の型を渡すもの
Android開発には、画面のEdge-to-Edge対応、ウィンドウサイズへの適応、画面遷移、テスト環境など、個別に考慮すべき領域があります。
今回は、Android CLIを使ってAndroid Developersで公開されているAndroid Skillsを開発環境へ導入しました。そのうち、今回の実装で特に知見を反映できたのは次のSkillsです。
android-cli: Android CLIによる環境確認、実行、画面確認の進め方edge-to-edge: システムバーとコンテンツの重なりやInsetsの扱いnavigation-3: Navigation 3を使った画面遷移testing-setup: 単体テストやCompose UIテストの構成
adaptiveとstylesも導入しましたが、現在の実装では専用APIを本格的に適用するところまでは進めていません。今後、画面サイズへの適応やデザインシステムを見直す際に利用する予定です。
Skillsを使ってみて、APIの断片的な知識を渡すだけでは足りないと分かりました。
たとえばEdge-to-Edge対応では、単に背景を画面いっぱいに広げるだけでは、ボタンや文字がステータスバー、ナビゲーションバーと重なる可能性があります。私自身、このあたりはJavaで開発していた頃の知識だけでは判断しにくい部分でした。edge-to-edgeのSkillを参照することで、背景を描画する領域と、safeDrawingPadding()で守る操作領域を分けるという判断の型を取り入れられました。
汎用的なAIエージェントに、プロジェクト固有のルールをAGENTS.mdで渡し、技術領域ごとの進め方をSkillsで補う。この2つは似ていますが、役割が異なります。
| 与えるもの | 主な役割 |
|---|---|
AGENTS.md | このプロジェクトで守るルールと判断の境界を伝える |
| Skills | 特定の技術領域で、何を確認しながら進めるかを伝える |
プロンプトにすべてを書き込まず、長く使う前提はリポジトリへ、専門的な手順はSkillへ分ける。この形にしてから、個々の指示を短く保てるようになりました。
4. AIに「手」と「目」を持たせる
ルールと知識を渡しても、それだけではAIエージェントの作業は実装したところで止まりがちです。自分の変更が正しかったかを確認する手段も必要でした。
今回、その役割を担ったのがAndroid CLIでした。たとえば、次のような操作をエージェント自身が実行できます。
# Android開発環境を確認する
android info
# アプリをビルドし、接続中の端末やエミュレータで起動する
android run
# 端末の画面を画像として取得する
android screen capture
# 画面のレイアウトツリーを確認する
android layout --pretty
# Androidの公式ドキュメントを検索する
android docs search "検索したい内容"
実際の開発では、次の流れをAI自身に繰り返してもらいました。
- 要件とプロジェクトのルールを読む
- Skillsや公式ドキュメントから実装時の注意点を確認する
- コードを変更する
- ビルドとテストを実行する
- エミュレータでアプリを起動する
- 画面画像とレイアウトを確認する
- 問題があれば修正し、もう一度確認する
この流れを作ったことで、私がエラーログをAIへ貼り直したり、スクリーンショットを毎回手作業で渡したりする回数が減りました。AIが実装だけで止まらず、結果を観測して次の修正へ進めるようになったからです。
それまでは、私がエミュレータを見て「文字が見切れている」「形が崩れている」と伝え、必要ならスクリーンショットを撮ってAIへ渡していました。android screen captureがあると、AI自身が画面を撮影し、マルチモーダルモデルとして自分の描画結果を目で確認できます。
実際にトラックゲージを調整した際も、画面キャプチャから円弧と直線のつながりの歪みを確認し、描画処理を修正して、もう一度撮影するところまで同じループの中で進められました。コードを書けるだけでなく、自分の出力を画面として見られることが、AIに「目」を持たせるという意味です。
コードを書く権限だけでなく、結果を確かめる手段まで渡して、ようやく一連の作業を任せられました。
5. ビルド成功は、完成の証拠にならない
Androidアプリでは、コンパイルが通っていても、実際の画面で初めて気づく問題が多くあります。
Bell Lap Timerの初期画面には、残り周回、フェーズ名、説明、残り時間など、多くの情報を表示していました。コード上は整っており、止まった状態で見ると一般的なタイマー画面として不自然ではありません。しかし、実際にローラー台で使うと印象が変わりました。心拍数が180bpm近くまで上がり、車体が左右に揺れ、汗が目に入る状況では、ゲージ内の小さな文字や重複したラベルを読む余裕がありませんでした。
そこで、「中央には62spの残り秒数だけを大きく表示し、行動指示は1行に絞る」という方針を決め、画面キャプチャを見ながら次のように削っていきました。
- ゲージ内のラベルをなくし、残り時間だけを大きくする
- フェーズの説明を、短い行動指示へ置き換える
- 色と周回告知板で、文字を読まなくても状態が分かるようにする
- 縦向きだけでなく、横向きでも主要な情報が見えるか確認する
ここでは、AIによる画面確認と、私が実際の利用状況で評価することの両方が必要でした。AIは配置の崩れや文字の見切れを発見できますが、「運動中の自分にこの情報量が読めるか」は決められません。使って感じた違和感を拾い、何を捨てるかは私が判断しました。
テストも同じです。タイマーのフェーズ遷移や一時停止、再開は自動テストで確認できますが、鐘が期待したタイミングと音量で聞こえるかは、端末で使ってみなければ分かりません。
コード、テスト、画面、実際の体験を重ねて確認して、AIによる「できました」を自分が納得できる完成へ近づけました。
6. 失敗を、次の指示ではなく仕組みに戻す
開発中に印象に残った失敗が、アプリアイコンです。Pixelのランチャーに表示すると、キャラクターの顔やヘルメットの一部が黒く抜けてしまいました。
原因は、四隅の背景を色で判定して透過していたことです。背景と近い色を取り除こうとした結果、イラスト内部にあるヘルメットのバイザー、顔のハイライト、白線まで同時に透明になり、ダーク背景のランチャーでは黒く欠けたように見えていました。
そこで、色による判定をやめました。中央のイラスト領域を不透明のまま保護し、外周だけを抜く幾何学的なマスク処理へ変更しています。さらに、完成条件にランチャー画面のキャプチャ確認を加えました。画像ファイルだけを見て正しいと判断せず、実際にマスクが適用された表示でも壊れていないことを確かめるためです。
この失敗で見直したのは、画像処理の方法だけではありません。「次は気をつけて」と会話で伝えるだけでは、同じ種類の問題を防ぎにくいため、次のような形で開発環境へ戻しました。
- 何を壊してはいけないかをルールに追加する
- 入出力を機械的に調べる検証を追加する
- 完成条件に画面キャプチャの確認を含める
- 大きな変更を小さな作業へ分け、差分を追いやすくする
AIエージェントは、エラーが見えれば修正案を考えられます。しかし、期待との差を観測できなければ、その能力を使えません。私は今回の失敗を個別の注意で終わらせず、次回から検出できる仕組みに変えることにしました。
7. AIに任せるためにも、基礎知識は必要になる
AIエージェントを使うと、AndroidのAPIやKotlinの構文をすべて暗記していなくても、アプリを形にできます。Java時代で知識が止まっていた私にとって、これは大きな助けでした。それでも、何も知らないまま開発のすべてを任せられるわけではありませんでした。
たとえば、タイマーがずれる原因を考えるには、処理の遅延と経過時間の違いを理解している必要があります。画面回転で状態が失われる問題に気づくには、AndroidのライフサイクルやViewModelの役割を知っている必要があります。音が鳴れば完成ではなく、ほかの音楽アプリとの共存を考えるには、オーディオフォーカスの考え方が必要です。
AIが出したコードの細部をすべて自分で書ける必要はなくても、少なくとも次のことは人間が判断しなければなりません。
- どのような問題が起こり得るか
- AIの提案が要件に合っているか
- どこに責務を持たせるべきか
- 何をテストすれば正しさを確かめられるか
- エラーや違和感を、どの領域の問題として調べるか
そこで頼りになったのが、以前のAndroid開発で身につけた考え方と、状態、時間、並行処理、責務分割、テストといったコンピューターサイエンスやソフトウェアエンジニアリングの基礎です。現在のAPIに詳しくなくても、起こり得る問題を想像できれば、AIに具体的な問いを立て、返ってきた提案の良し悪しを判断できます。
今回の経験から、AIによってエンジニアの知識が不要になるのではなく、知識の使い方が変わるのだと感じました。構文を思い出しながら一行ずつ書く機会は減っても、問題を分解し、適切な指示を出し、結果を評価する場面では基礎知識を使います。
AIは知らないことを調べ、実装を補助してくれます。しかし、何を尋ねるべきか、どの回答を採用すべきか、どこを疑うべきかまでは完全に任せられません。AIに仕事を任せるためにも、エンジニアには基礎が必要でした。
8. 人間の役割は、コードを書くことから判断することへ移る
今回の開発でも、私がまったくコードを見なくてよくなったわけではありません。変わったのは、見るべきものと、時間を使う場所でした。
AIに任せやすかったのは、次のような作業です。
- 既存ルールに沿った実装
- テストケースの追加と実行
- ビルドエラーの原因調査と修正
- 複数の実装案の比較
- エミュレータの起動と画面キャプチャ
- 定型的なGitやGitHubの操作
一方、人間が判断したほうがよかったのは、次のような部分です。
- その機能が本当に必要か
- 実際の利用状況で使いやすいか
- どの情報を画面から削るか
- 提案された案のどれを採用するか
- どの状態を完成とみなすか
AIに実装を任せるほど、私は要件を細かく書くことより、結果を見て違和感を言葉にすることに時間を使いました。
「レイアウトを修正して」ではなく、「ローラー台で追い込んでいるときは説明文を読めないので、残り秒数だけを一目で認識したい」と伝えるほうが、AIは適切な選択肢を出しやすくなります。実装手段ではなく、利用者が置かれる状況と達成したい結果を渡すのが効果的でした。
AIエージェント開発で使えるチェックリスト
今回、実際に手戻りを減らすのに役立ったことを、別のプロジェクトでも確認できる形に整理しました。
作業を始める前
- AIが繰り返し参照できる場所に、プロジェクトのルールがあるか
- AIへ任せる判断と、人間が行う判断を分けているか
- 必要なSkillsや公式ドキュメントへ到達できるか
- 一度に任せる作業が大きすぎないか
- 目的、変更範囲、完了条件、確認方法を指示に含めたか
- AIの提案を評価するために、分からない基礎知識を調べたか
実装中
- AI自身がビルドとテストを実行できるか
- ログ、画面、レイアウトなど、結果を観測する手段があるか
- 失敗したとき、原因を特定できる大きさの差分になっているか
- 実装前の前提と、実装後の結果が一致しているか
完了前
- 「ビルドできた」以外の証拠があるか
- 実際の端末やエミュレータで主要な操作を確認したか
- 人間が利用者の立場で触って違和感を確認したか
- 今回の失敗を、ルールやテストへ戻せないか
おわりに
Android CLIとSkillsを使ってBell Lap Timerを作り、AIエージェントの性能だけで開発の成否が決まるわけではないと分かりました。
プロジェクトのルールを置き、専門的な判断の型をSkillsで渡し、実装結果をAI自身が確認できるようにする。ここまで用意すると、AIはコードを生成するだけでなく、実装と検証を往復できるようになりました。
一方で、私の役割も、基礎知識の必要性もなくなりませんでした。何を作るか、誰がどのように使うか、どこまでできれば完成かは、私が決めなければなりません。AIの提案を理解し、適切な指示を出すためにも、Androidとコンピューターサイエンスの基礎は欠かせませんでした。
今回作ったのはAndroidアプリですが、ここで得た進め方は、ほかの開発にも応用できるはずです。AIに渡す言葉だけを工夫するのではなく、AIが迷わず動き、失敗に気づき、修正できる環境を作る。これが、今回見つけたAIエージェントを使い倒すための勘所でした。
今後の開発でも、まず目的と完了条件を明確にし、Issueで作業範囲を区切り、必要なSkillsと検証手段を用意してからAIへ任せます。うまくいかなかったことは、その場の追加指示だけで終わらせず、ルールやテスト、検証手順へ戻して次の作業に活かします。バイブコーディングの速さを取り入れながら、自分の基礎知識で方向を決め、結果を確かめる。今回得た学びを、これからの開発につなげていこうと思います。
