結論から言えば、個人開発者でもAI機能は実装できる。ただし、LLMに丸投げする設計ではうまくいかない。重要なのは「何をローカルで処理し、何をLLMに任せるか」の分離設計と、運用コストを制御する仕組みを最初から組み込むことだ。
この記事では、TrainNote の AI Coach と Book Compass の AI 読書整理という2つのアプリで実際にAI機能を設計・実装した全工程を振り返る。技術選定からプロンプト管理、コスト管理、そしてDoubleHubエコシステムを見据えたデータ設計まで、個人開発者向けに実践知をまとめた。
なぜAI機能を入れようと思ったか
きっかけはBook Compassの開発だった。読書メモの整理や傾向の可視化を考えていく中で、ある気づきがあった。
AIは開発ツールとしてだけでなく、ユーザー体験(UX)の一部として機能する。
GitHub CopilotやClaude Codeなどの開発支援AIは、コードを書く側の生産性を上げるツールだ。それはそれで価値がある。しかし、Book Compassの開発を進める中で、AIがユーザーの体験そのものに入り込むことで、ルールベースでは実現しにくい柔軟なサポートが可能になることに気づいた。
読書メモの整理は人によって「何が整理されている状態か」が違う。筋トレのアドバイスもユーザーの目標・環境・体調によって最適解が変わる。こうした一人ひとり異なるニーズへの対応が、AI機能の本当の価値だと考えた。
TrainNote AI Coach の技術設計——4層構造
TrainNote の AI Coach V2 では、4つの層で構成される設計を採用した。「単なる定型アドバイス表示ではなく、ユーザーの記録と会話をもとに育っていくAIパーソナルトレーナー」を目指す設計だ。
第1層: Local Analyzer
トレーニング記録から事実を抽出するローカル分析レイヤー。LLMを使わず、アプリ内で完結する。
- 今週の実施回数・部位ごとの最終実施日
- 特定種目の直近推移
- データ不足、停滞、頻度不足、再開タイミングの判定
- LLMに渡す前提情報の整理
この層があることで、LLMに生データを無秩序に渡すことを防げる。事実整理はアプリ側で行い、柔軟な説明や深掘りだけをLLMに任せる——この分離が設計の根幹だ。
第2層: Coach Memory
ユーザーとのやり取りで得た継続的な情報を保持する層。会話ログ全文を渡すのではなく、意味のある情報へ抽出して保持する。
- 目標(方向性レベル+構造化した定量目標)
- 週の希望頻度、1回の目安時間、器具環境
- コーチの雰囲気の好み(厳しめ/優しめ)
- 苦手な種目、提案から外したい部位
- 説明済みの知識、過去の提案への反応
一時的な反応(「今日は違う」)と長期的な設定(「有酸素は不要」)を別レイヤーで管理する点が重要だ。
第3層: LLM Coach
Local Analyzerの事実、Coach Memory、必要最小限の会話履歴要約をもとに、柔軟な提案・説明・対話を返す層。
LLMが担うのは「ユーザーに伝わる文章化」「深掘り質問への回答」「コーチらしい対話」「状況に合う言い方の選択」だ。一方、生データの無秩序な解釈、安全ポリシーの最終判断、重量アップ可否の唯一の根拠にはしない。
第4層: Insight Cache
生成済みのコーチング結果をキャッシュする層。ホーム表示や再表示で無駄な再生成を防ぐ。
- トレーニング完了後の短いレビュー
- 翌日の一言コーチング
- 週次レビュー
- 詳細コーチング要約
この4層構造により、LLMの実行を価値がある場面に集中させつつ、ユーザーには途切れのないコーチング体験を提供できる。AIパーソナルトレーナーとヒトのトレーナーの違いでも解説しているが、この設計は「リアルのパーソナルトレーナーの代替に少しずつ近づく」ことを意識している。
Book Compass のAI設計——読書パートナーとして
Book Compassでは、AIの役割をTrainNoteとは異なるかたちで設計した。目指したのは「何でも答えてくれるAI先生」ではなく、読書パートナーという位置づけだ。
メモ整理: 読書中の気づきを構造化する
読書中の「つぶやき」をただ保存するだけでなく、AIがそれらを整理して、後から見た時に思考の流れが追いやすい状態にする。ここで大事なのは本の一般的な要約を返すことではなく、「この人はこの本をどう読んだのか」を起点に整理すること。知識の代行ではなく、理解の補助だ。
傾向可視化: 自分の読書傾向を目に見える形にする
読書メモや記録から、以下のような傾向を可視化する。
- どんな観点に反応しやすいのか
- どんなテーマを繰り返し考えているのか
- 自分の関心がどの方向に広がっているのか
単に「何冊読んだか」ではなく、読書を通じた自分の思考パターンを可視化する。これはライフデータで自分を知るというテーマにも直結する。
対話型チャット: 壁打ち相手としてのAI
チャット機能では「上から教え込まない」「それっぽいことを断定しない」「自分の読書記録を根拠にして一緒に整理する」という方針を貫いている。
「あなたが以前こういうことを気にしていたから、今回もここが引っかかっているのかもしれない」——このように、ユーザーの記録に根拠を持った対話を目指す。
AIが前に出すぎると、便利そうに見えても体験としては浅くなる。少し控えめなくらいが読書の支援としてはちょうどいい。
LLM実行の基本方針——常時実行ではなくオンデマンド
AI機能でもっとも注意すべきはLLMの実行タイミングだ。アプリ起動のたびにLLMを呼ぶ設計は、コストとUXの両面で破綻する。
走らせない場面
- アプリ起動のたび
- ホーム表示のたび
- ユーザーがAI機能を見ていない場面
段階的な実行レベル
| レベル | 処理内容 | LLM使用 | 用途 |
|---|---|---|---|
| レベル0 | アプリ内分析のみ | なし | データ不足時、ホームの軽量プレビュー |
| レベル1 | 短いLLMコーチングを生成+キャッシュ | トリガー時のみ | ホームの入口表示、トレーニング後レビュー |
| レベル2 | 詳細シートまたはチャットで深い分析 | 明示的に開いた時 | 詳細コーチング、対話 |
MVPでは「レベル0 + レベル2の最小版」から始めるのが現実的だ。レベル1はキャッシュ設計が整ってから追加すればよい。
AI機能を搭載したアプリを体験する
TrainNote の AI Coach、Book Compass の AI 読書整理——実際に動くAI機能を確認できます。
DoubleHub サービス一覧を見るプロンプト管理の実践——PromptCatalogパターン
プロンプトをコードに直書きすると、調整のたびにアプリ全体のビルドが必要になる。TrainNoteではPromptCatalogパターンでプロンプトを管理している。
設計の要点
- 役割ごとに分離: coach_preview、coach_detail、coach_chat_reply、post_workout_review、weekly_reviewなど
- バージョン管理: 各プロンプトにIDとバージョンを付与
- 入力コンテキストの型を固定: LLMに渡す情報を構造化
- 出力形式の構造化: なるべくJSON等で受け取り、パース可能にする
- トレーサビリティ: 生成結果にpromptVersion、modelID、knowledgeVersionを保存
Swiftでのインターフェース例:
CoachPromptTemplate にはid、version、systemPrompt、developerPrompt、outputSchemaを持たせる。CoachPromptCatalog プロトコルでtemplateメソッドを定義し、View や Service 本体にプロンプト文面を直書きしない。
開発初期はアプリ内でテンプレートとして持ち、運用を見据えた段階でバックエンド側(Supabase Edge Functionsなど)に寄せていく方針だ。これにより、App Store審査を待たずにプロンプト調整が可能になる。
コスト管理の工夫
個人開発でAI機能を運用するうえで、コスト管理は避けて通れない。以下の工夫で、AIを活用した習慣化のための機能を持続可能にしている。
キャッシュ活用
- Insight Cacheで生成済み結果を再利用
- ホーム表示ではキャッシュ済みの一言コーチングを表示
- 同じデータ状態で再度開いても再生成しない
不要なLLM実行を避ける設計
- Local Analyzerでデータ不足を事前判定——データが足りなければLLMを呼ばない
- ユーザーがAI機能を見ていない場面では実行しない
- 週次レビューなど低頻度タスクは決まったタイミングでのみ生成
構造化入力でトークン数を削減
- 会話ログ全文ではなく、Coach Memoryの構造化データを渡す
- Local Analyzerが事実を整理した状態でLLMに渡す
- 毎回全件送信ではなく、差分や要約を活用
DoubleHub連携を見据えた設計
TrainNoteやBook Compassは単体で価値が閉じる設計にしつつ、将来のDoubleHub連携(Coming Soon)を見据えたデータ設計を組み込んでいる。
整合している点
- 各アプリが単体で完結する価値を持つ
- 生の会話全文ではなく、構造化したMemoryやInsightを重視
- 毎回リアルタイムに全件取得ではなく、要約やキャッシュを活用する前提
- 将来的にLLM実行やプロンプト管理をバックエンドへ寄せられる設計
export-for-double を意識したデータ分離
| データ種別 | ローカル専用 | 将来エクスポート可能 |
|---|---|---|
| 画面状態 | 一時的なUI反応 | — |
| トレーニング記録 | セッション途中経過 | 週次・月次サマリー |
| 会話データ | 未整理の下書き | 構造化されたchat_insights |
| コーチング結果 | — | 要約、高レベルな洞察 |
| ユーザー設定 | — | 共有してよいプロフィール項目 |
DoubleHubは生ログより構造化データを受け取る方がよい。TrainNote側で意味のある粒度へ要約しておくことが、将来の横断活用とプライバシーの両面で扱いやすい。
比較表: AIを入れる前と入れた後
| 項目 | AI導入前 | AI導入後 |
|---|---|---|
| ユーザーへの提案 | ルールベースの固定メッセージ | 記録・目標・好みに合わせた柔軟な提案 |
| パーソナライズ | 設定項目ベース(静的) | 会話から学習し、継続的に進化 |
| 開発の複雑さ | ロジックの実装のみ | プロンプト設計+API連携+キャッシュ設計が追加 |
| 運用コスト | ほぼゼロ | LLM API利用料(設計次第で制御可能) |
| ユーザー体験の深さ | 画一的 | 個人に寄り添った対話型サポート |
| 差別化の度合い | 機能比較で勝負 | 体験そのものの質で差別化 |
| データ活用 | 記録の表示のみ | 記録からインサイトを生成し、次の行動に繋げる |
個人開発でAI機能を入れるために意識すべきこと
2つのアプリでAI機能を実装して見えてきた、個人開発者向けの教訓をまとめる。
- ローカル分析とLLMの役割を明確に分離する——LLMに丸投げしない
- コストを設計の初期段階で考える——「あとで最適化」では遅い
- プロンプトを管理可能な単位にする——PromptCatalog、バージョン管理
- キャッシュを前提とした設計にする——同じ結果を何度も生成しない
- 安全面の配慮を忘れない——医療判断はしない、体調不良時は安全側へ
- 将来の連携を意識してデータを構造化する——生ログより構造化insightを重視
AIは、開発を助けるツールとしてだけでなく、ユーザーが受け取る価値の中に入れると、また別の景色が見えてくる。個人開発だからこそ、小さく始めて、段階的に育てていくアプローチが有効だ。
DoubleHub サービス一覧
TrainNote(筋トレ記録 × AIコーチ)、Book Compass(読書記録 × AI整理)、そしてDoubleHub(Coming Soon)。AI機能を搭載したアプリ群をご覧ください。
サービス一覧を見る →