結論から言えば、個人開発者でも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はキャッシュ設計が整ってから追加すればよい。
プロンプト管理の実践——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は、開発を助けるツールとしてだけでなく、ユーザーが受け取る価値の中に入れると、また別の景色が見えてくる。個人開発だからこそ、小さく始めて、段階的に育てていくアプローチが有効だ。
よくある質問
Q. 個人開発者でもAI機能は実装できますか?
はい、実装できます。ただし、LLMに丸投げするのではなく、ローカル分析とLLMの役割分離を設計し、コスト管理の仕組みを整えることが重要です。TrainNoteとBook Compassでは、段階的な実行レベルとキャッシュの活用で個人でも運用可能な設計にしています。
Q. AI機能の実装にどのくらいの費用がかかりますか?
LLM APIの利用料が主なコストです。常時実行を避けてオンデマンド+キャッシュの設計にすれば、個人開発の規模なら月額数百〜数千円に抑えられます。重要なのは、不要なLLM実行を避ける設計を最初から組み込むことです。
Q. SwiftでLLM APIを呼び出すのは難しいですか?
API呼び出し自体はHTTPリクエストなので難しくありません。重要なのはAPI呼び出しの前後の設計——プロンプトの管理、入力コンテキストの構造化、出力のパース、エラーハンドリング、キャッシュ——を整えることです。
Q. プロンプト管理はどうしていますか?
PromptCatalogパターンを採用しています。役割ごとにプロンプトを分離し、IDとバージョンを付与。生成結果にはpromptVersionやmodelIDを保存し、後から品質変化を追跡できるようにしています。開発初期はアプリ内に持ち、運用段階ではバックエンドに移行予定です。
Q. AI機能を入れてアプリの開発負担はどう変わりましたか?
初期の設計負担は増えますが、ユーザー体験の差別化効果は大きいです。特にコーチングや対話型の機能は、ルールベースでは実現しにくい柔軟さを提供できます。運用面ではプロンプト調整やコスト監視が継続的に必要になります。
Q. DoubleHubとの連携はどう設計していますか?
各アプリが単体で価値を持ちつつ、構造化データをexport-for-double形式で外部連携可能にする設計です。生ログではなく要約やinsightを渡す前提にすることで、プライバシーと横断活用のバランスを取っています。DoubleHubは現在開発中(Coming Soon)です。
Q. AIの安全性にはどう配慮していますか?
医療判断は行わない、体調不良時は安全側へ倒す、断定しすぎない、根拠のないアドバイスを避けるなどの原則を設けています。AIが前に出すぎず、あくまでユーザーの意思決定を補助する設計にしています。