レシピ#6-5: 【事例研究】育児・発達支援アプリをBYOAで開発する

このレシピでは、BYOA開発の具体例として、育児・発達支援アプリの開発プロセスを詳しく学びます。

この事例の本質:AIエージェント機能が必須

育児・発達支援アプリでは、アプリ内でAIが継続的にユーザーをサポートすることが必須です。

【重要な違い】

開発支援ツール(Copilot/Cursor等):
└─ 開発者がコードを書く時だけAIを使う
   └─ 完成したアプリにはAIは含まれない
   └─ ユーザーはAIと対話しない

育児・発達支援アプリ:
└─ アプリ自体がAIエージェントを内蔵
   ├─ 24時間いつでも相談できる
   ├─ 観察記録からアドバイス生成
   ├─ 文脈を保持した継続的な会話
   ├─ 個別の子供の状況を理解
   └─ ユーザー(親)の孤独感を軽減

つまり、Claude API(または同等のAI API)をアプリに統合することが必須条件です。


対象とする課題

誰のためのアプリか

解決したい課題

✅ 診断はあるが、強みを見つける手段がない
✅ 療育施設は数ヶ月待ち、高額(月3-10万円)
✅ 夜中に不安になっても、相談できない
✅ 「今すぐ」聞きたいことが聞けない
✅ 既存アプリは記録だけで、アドバイスがない
✅ クラウド保存は子供のデータで不安

従来のアプローチ vs BYOA開発

【従来1】育児記録アプリ(AI機能なし)

例: ぴよログ(評価4.8/5)、育児日記アプリ

✅ 授乳、おむつ替え、睡眠、成長記録
✅ 成長グラフ、PDF出力
✅ シンプルで使いやすい
✅ 音声入力(Siri/Alexa/Google)対応
✅ パートナーとの共有機能

❌ 記録を溜めても、「どうしたらいいか」は教えてくれない
❌ データの分析やアドバイスがない
❌ 孤独は解消されない(記録するだけ)
❌ 発達の強みは見つけられない
❌ 「今すぐ相談したい」には対応できない

コスト: 完全無料(広告なし)
備考: 日本で最も人気の育児記録アプリ

【従来2】AI相談サービス(自治体提供)

例: 東京都「子育て支援情報チャットボット」、渋谷区AI相談等

✅ AIと相談できる(24時間)
✅ 基本的な育児情報を提供
✅ 無料で利用できる(住民向け)

❌ 汎用的な回答(個別の子供を理解していない)
❌ 会話の連続性がない(毎回リセット)
❌ 観察記録との連携なし
❌ 発達の強みは見つけられない
❌ データはクラウド保存(プライバシー不安)
❌ 自治体によって利用可否が異なる

コスト: 無料(住民限定)
備考: こども家庭庁が10-15自治体で試験導入中
   商用の育児AI相談サービスはほぼ存在しない

【従来3】専門家への相談(療育施設)

児童発達支援施設、発達相談センター

✅ 専門的なアドバイス
✅ 信頼性が高い
✅ 直接会って相談できる
✅ 補助金で実質負担は低い

❌ 予約から初回まで数ヶ月待ち(最大の問題)
❌ 週2回程度、1-4時間(頻度制限)
❌ 「今すぐ」相談できない
❌ 日々の小さな疑問は相談しづらい
❌ 通うのが大変(時間、交通費)
❌ 待機中は何もサポートがない

コスト:
- 一般所得世帯: 月4,600円(補助金適用後)
- 3-6歳児: 無料(2019年10月〜幼児教育無償化)
- 東京都: 第一子も無料化予定(2025年9月〜)

備考: コストより「待機期間の長さ」が深刻な問題
   待機中の数ヶ月間、親は孤独に悩む

【BYOA】ローカルAIエージェント内蔵アプリ

自分で作る、AI内蔵育児支援アプリ

✅ 24時間、「今すぐ」相談できる(待機なし)
✅ 自分の子供のデータを完全に理解している
✅ 過去の会話を全て覚えている(継続的な関係)
✅ 観察記録と連携したアドバイス
✅ データは全てローカル(Mac mini内)
✅ プライバシー完全保護
✅ カスタマイズ自由(自分で機能追加)
✅ データは永続的に自分のもの
✅ 事業化も可能(学習塾として)

❌ 初期投資が必要(15万円)
❌ 開発に時間がかかる(3-6ヶ月)
❌ プログラミング学習が必要

コスト:
- 初期: 15万円(Mac mini + 周辺機器)
- 月額: $20(約3,000円)(Claude Pro - 定額)+Claude API利用料(従量課金)

備考: 療育施設の待機期間(数ヶ月)を活用して開発できる
   完成後は、療育と併用して日常をサポート

BYOAだからこそ対応できる「切実な問題」

問題1: 深夜の不安、誰にも相談できない孤独

当事者の声:

「夜中の2時、子供が泣き止まない。
 明日、療育の先生に聞けばいいのはわかってる。
 でも『今』不安なんです。『今』聞きたいんです。
 でも誰もいない。この孤独感、絶望感。」

BYOAでの対応: - ✅ 深夜2時でも、いつでも相談できる - ✅ 過去のデータを知っているから、的確なアドバイス - ✅ 「昨日も同じ時間に泣いてましたね。昼寝の時間を調整してみては?」 - ✅ ジャッジされない、否定されない - ✅ 何度聞いても大丈夫(定額だから)

「一人じゃない」と思える


問題2: 療育施設で「こんな小さなこと、聞いていいのかな」

当事者の声:

「週2回、1時間の療育。先生に聞きたいことは山ほどある。
 でも『今日はこれを優先的に』と思うと、小さな疑問は飲み込んでしまう。
 『こんなこと聞いたら、モンペと思われるかな』
 本当は毎日聞きたいことがある。」

BYOAでの対応: - ✅ 日々の小さな疑問を全部記録、全部相談できる - ✅ 「レゴばかりやってて大丈夫?」 - ✅ 「今日、友達と遊べなかった。どう声かける?」 - ✅ 療育の先生には「重要なこと」だけ相談 - ✅ BYOAで整理した内容を先生に共有

療育との補完関係


問題3: 「できないこと」ばかり指摘される辛さ

当事者の声:

「療育では『できないこと』の改善。発達検査も『遅れている項目』ばかり。
 この子の『できること』『得意なこと』は?誰も教えてくれない。
 『ダメな子』って思われてる気がする。
 私もそう思ってしまいそうで怖い。」

BYOAでの対応: - ✅ 「できたこと」「得意なこと」を記録 - ✅ AIが繰り返しパターンから「強み」を発見 - ✅ 「レゴで3時間集中→空間認識能力の可能性」 - ✅ 「図鑑を暗記→視覚的記憶の強さ」 - ✅ 「この子はこれが得意なんだ」と親が気づける

ポジティブな子育て観


問題4: 記録しても「で、どうすればいいの?」

当事者の声:

「ぴよログで毎日記録してる。授乳、睡眠、おむつ替え。
 でも、それで?データは溜まる。グラフも見られる。
 でも『どうすればいいか』は教えてくれない。
 結局、自分で考えるしかない。正解がわからない。」

BYOAでの対応: - ✅ 記録 + 分析 + アドバイス - ✅ 「夕方の昼寝が長い日は夜泣きが多い」→ パターン発見 - ✅ 「明日試してみてはどうでしょう」→ 具体的提案 - ✅ 「前回これで改善しましたね」→ 過去の成功を参照

データが「武器」になる


問題5: 「親がなんとかしなきゃ」というプレッシャー

当事者の声:

「療育も、医者も、最後は『お家で頑張ってください』。
 24時間365日、親の責任。休めない、誰も代わってくれない。
 でも正解がわからない。『私のやり方、間違ってないかな』と不安。
 一人で抱え込んで、潰れそう。」

BYOAでの対応: - ✅ 24時間の「相談相手」がいる - ✅ 「これで合ってますか?」と聞ける - ✅ 「頑張ってますね」と励ましてくれる - ✅ 判断に迷った時、背中を押してくれる

孤独なプレッシャーからの解放


問題6: 同じ悩みを持つ人と繋がれない孤立感

当事者の声:

「周りに同じ悩みを持つ人がいない。
 ママ友には理解されない。『うちの子と何が違うの?』と言われる。
 同じ立場の人と話したい。でも療育施設では深い話はできない。
 ネットで情報を探すけど、『うちの子』に合うとは限らない。」

コミュニティでの開発: - ✅ GitHub等で同じ課題を持つ親と繋がる - ✅ 「うちの子はこれで改善した」という知見の共有 - ✅ プライバシーを守りつつ、パターンは共有 - ✅ 一人で開発は大変、みんなで作る - ✅ 「同じ悩みを持つ人がいる」という安心感 - ✅ コードの改善提案、バグ報告を一緒に

コミュニティ開発の例:

GitHub Issues: 「こういう機能が欲しい」
Discussions: 「うちではこうしてます」
Pull Request: 「この改善、試してみてください」

※個人情報は出さない、でも知見は共有

「一人じゃない」「みんなで良くしていける」


なぜClaude Proか:定額の重要性

家庭利用には定額制が最適

【Claude Proの利点】

✅ 月額$20(約3,000円)の定額制
   └─ 使いすぎても料金は変わらない
   └─ 家計管理しやすい
   └─ 安心して使える

✅ Webチャット + Claude Code の両方が使える
   └─ 開発時: Webで相談、Claude Codeでコード生成
   └─ 開発にかかるAI利用が定額でカバーされる
   └─ エージェント型コーディング(Claude Code)まで定額に含まれるのが強み

✅ 開発中の相談は実質無制限
   └─ 一般的な使用では制限に達しない
   └─ 夜中に何度相談してもOK

⚠️ アプリからのAPI利用は別契約(従量課金)
   └─ 運用時にアプリが呼び出すClaude APIは、
      console.anthropic.com で別途APIキーを取得し、
      使ったトークン分だけ課金される
   └─ 家庭利用の頻度なら月数百円〜数千円程度が目安
   └─ 事業化するならAPIコストの試算が必要

他のAIサービスとの比較

サービス 月額 定額 Webチャット API 備考
Claude Pro $20(約3,000円) APIは別料金。Claude Code込み
Claude API 従量 例: Sonnet 4.5は100万トークンあたり入力$3/出力$15
ChatGPT Plus $20(約3,000円) APIは別料金
ChatGPT API 従量 使った分だけ課金
Gemini Advanced 2,900円 APIは別料金
Gemini API 従量 使った分だけ課金

各社のビジネス戦略の違い

それぞれのAIサービスには、異なるビジネス戦略があります:

【ChatGPT(OpenAI / Microsoft)】
- ChatGPT Plus: 定額でWebチャット
- ChatGPT API: 別契約(従量課金)
- 背景: Azure、GitHub、Office等の巨大エコシステム
- 結果: 定額サービスとAPIは分離

【Gemini(Google)】
- Gemini Advanced: 定額でWebチャット
- Gemini API: 別契約(従量課金)
- 背景: Google Cloud、Android、Chrome等の巨大プラットフォーム
- 結果: 定額サービスとAPIは分離

【Claude(Anthropic)】
- Claude Pro: 定額でWebチャット + Claude Code(エージェント型コーディングCLI)
- Claude API: 別契約(従量課金)— この点はOpenAI/Googleと同じ
- 背景: AIモデル開発に特化、大規模プラットフォームなし
- 結果: 開発作業(相談・コード生成)が定額に収まる

家庭利用での実際の影響:

どのサービスを選んでも共通: - アプリでのAPI利用: 別契約の従量課金(Claude API / ChatGPT API / Gemini API) - → 運用時のAPIコストは使った分だけ発生する

Claudeを使う場合の強み: - 開発中の相談・コード生成: Claude Pro(Webチャット + Claude Code、定額) - → 開発フェーズのAI利用コストは月額$20(約3,000円)に固定できる - アプリでのAPI利用: Claude API(console.anthropic.comで別途契約、従量課金)

どちらが良い・悪いではなく、「家庭利用の目的」に合うか:

ChatGPT/Gemini: 大規模エコシステムとの統合が強み Claude: エージェント型コーディング(Claude Code)まで定額に含まれるのが強み

BYOA開発のように「AIと相談しながら自分で作る」スタイルでは、開発作業が定額で完結するClaude Proが適している

家庭での育児支援アプリの開発には、Claude Proが向いています。運用時のAPI利用料(従量課金)は別途見込んでおきましょう。


具体的な開発プロセス

このアプリの役割:「寄り添うパートナー」であって「専門家」ではない

やること・やらないことの明確化

✅ このアプリがやること: - 日々の観察を記録 - パターンを見つける - ポジティブなアドバイス - 親の不安に寄り添う - 「できること」「得意なこと」を見つける - 専門家への相談材料を整理

❌ このアプリがやらないこと: - 医学的診断(「発達障害です」等) - 治療方針の決定 - 緊急時の対応(深刻な症状はすぐ病院へ) - 専門家の代替

システムプロンプトに必ず入れる重要な制約

final systemPrompt = '''
あなたは${childName}ちゃんの成長を見守るAIアシスタントです。

【あなたの役割】
親の「相談相手」「励まし役」です。医師ではありません。

【重要な制約】
- 医学的診断は絶対にしない
- 「〜の可能性があります。専門家に相談してみては?」と提案
- 心配な症状: 「すぐに病院へ相談してください」
- 最終判断は親と専門家が行う

【基本姿勢】
- 親を励ます、肯定する
- 小さな成功を一緒に喜ぶ
- 「できないこと」より「できること」に注目
- 深夜の不安には特に優しく寄り添う
- ジャッジしない、否定しない

【禁止事項】
- 「〜障害です」という診断
- 「病院に行く必要はない」という判断
- 「これだけで大丈夫」という保証
''';

親の自己責任で運用する

このアプリは「専門家との連携」を前提に使う


Week 1-2: 最小限のAI会話機能(0円で開始)

まず、既存のPCで最小限の機能を作ります。

// lib/services/ai_agent.dart
import 'package:anthropic_sdk_dart/anthropic_sdk_dart.dart';
import 'package:isar/isar.dart';

/// 育児支援AIエージェント
///
/// ユーザーのClaude APIキー(console.anthropic.comで取得、従量課金)を使用して、
/// ローカルで動作するAIエージェント
class ChildcareAIAgent {
  final AnthropicClient claude;
  final Isar database;

  // 会話履歴を保持(エージェント機能の要)
  final List<Message> conversationHistory = [];

  ChildcareAIAgent({
    required String userApiKey,
    required this.database,
  }) : claude = AnthropicClient(apiKey: userApiKey);

  /// ユーザーと会話する
  ///
  /// 過去の会話履歴と、子供の記録を考慮して返答
  Future<String> chat(String userMessage) async {
    // 1. 子供のプロフィールと最近の観察記録を取得
    final childProfile = await _loadChildProfile();
    final recentObservations = await _loadRecentObservations();

    // 2. システムプロンプト(子供の情報を含む)
    final systemPrompt = _buildSystemPrompt(
      childProfile,
      recentObservations,
    );

    // 3. 会話履歴にユーザーメッセージを追加
    conversationHistory.add(Message(
      role: MessageRole.user,
      content: MessageContent.text(userMessage),
    ));

    // 4. Claude APIを呼び出し(会話履歴を全て送る)
    final response = await claude.messages.create(
      model: 'claude-sonnet-4-5', // モデルIDは最新のものに置き換えてください
      maxTokens: 1024,
      system: systemPrompt,
      messages: conversationHistory, // ← エージェント機能の鍵
    );

    // 5. AIの返答を会話履歴に追加
    final assistantMessage = response.content.first.text;
    conversationHistory.add(Message(
      role: MessageRole.assistant,
      content: MessageContent.text(assistantMessage),
    ));

    // 6. 会話をローカルDBに保存
    await _saveConversation(userMessage, assistantMessage);

    return assistantMessage;
  }

  /// システムプロンプト(子供の個別情報を反映)
  String _buildSystemPrompt(
    ChildProfile profile,
    List<Observation> observations,
  ) {
    return '''
あなたは、${profile.name}ちゃん(${profile.age}歳)の
成長をサポートするAIアシスタントです。

【${profile.name}ちゃんの特徴】
${profile.strengths.map((s) => '- $s').join('\n')}

【最近の様子(過去7日)】
${observations.map((o) => '- ${o.date.toString().substring(0, 10)}: ${o.description}').join('\n')}

【あなたの役割】
1. 親の不安に寄り添い、共感する
2. ${profile.name}ちゃん個別の状況を考慮したアドバイス
3. ポジティブで、実践的な提案
4. 診断ではなく、「気づき」を提供

【重要な配慮】
- 「〜すべき」ではなく、「〜してみてはどうでしょう」
- 専門的な医療判断が必要な場合は、専門家への相談を促す
- 親を励まし、肯定する
- 深夜の相談には特に優しく
''';
  }

  Future<ChildProfile> _loadChildProfile() async {
    return await database.childProfiles.get(1) ?? ChildProfile.empty();
  }

  Future<List<Observation>> _loadRecentObservations() async {
    final weekAgo = DateTime.now().subtract(Duration(days: 7));
    return await database.observations
      .filter()
      .dateGreaterThan(weekAgo)
      .sortByDateDesc()
      .limit(10)
      .findAll();
  }

  Future<void> _saveConversation(String user, String assistant) async {
    final conversation = Conversation()
      ..timestamp = DateTime.now()
      ..userMessage = user
      ..assistantMessage = assistant;

    await database.writeTxn(() async {
      await database.conversations.put(conversation);
    });
  }
}

ポイント: - ✅ 会話履歴を保持(conversationHistory) - ✅ 子供の情報を常に考慮(systemPrompt) - ✅ 全てローカルに保存(プライバシー) - ✅ ユーザーのAPIキーを使用(コスト透明) - ✅ API利用は従量課金なので、使った分だけの支払い(家庭利用なら少額)


Week 3-4: UI実装(24時間相談画面)

// lib/screens/ai_chat_screen.dart
class AIChatScreen extends StatefulWidget {
  @override
  State<AIChatScreen> createState() => _AIChatScreenState();
}

class _AIChatScreenState extends State<AIChatScreen> {
  final TextEditingController _controller = TextEditingController();
  final List<ChatMessage> _messages = [];
  late ChildcareAIAgent _agent;
  bool _isLoading = false;

  @override
  void initState() {
    super.initState();

    // ユーザーのClaude APIキーを使用
    // (console.anthropic.comで別途取得。Claude Proとは別契約・従量課金)
    _agent = ChildcareAIAgent(
      userApiKey: UserSettings.instance.claudeApiKey,
      database: IsarService.instance.isar,
    );

    // 過去の会話を復元(エージェント機能)
    _loadPreviousConversations();
  }

  Future<void> _loadPreviousConversations() async {
    // 今日の会話を復元
    final today = DateTime.now();
    final conversations = await IsarService.instance.isar.conversations
      .filter()
      .timestampBetween(
        DateTime(today.year, today.month, today.day),
        DateTime.now(),
      )
      .findAll();

    setState(() {
      for (var c in conversations) {
        _messages.add(ChatMessage(text: c.userMessage, isUser: true));
        _messages.add(ChatMessage(text: c.assistantMessage, isUser: false));

        // エージェントの会話履歴にも追加
        _agent.conversationHistory.add(Message(
          role: MessageRole.user,
          content: MessageContent.text(c.userMessage),
        ));
        _agent.conversationHistory.add(Message(
          role: MessageRole.assistant,
          content: MessageContent.text(c.assistantMessage),
        ));
      }
    });
  }

  Future<void> _sendMessage() async {
    final text = _controller.text.trim();
    if (text.isEmpty) return;

    setState(() {
      _messages.add(ChatMessage(text: text, isUser: true));
      _isLoading = true;
      _controller.clear();
    });

    try {
      // AIエージェントに送信
      final response = await _agent.chat(text);

      setState(() {
        _messages.add(ChatMessage(text: response, isUser: false));
        _isLoading = false;
      });
    } catch (e) {
      setState(() {
        _messages.add(ChatMessage(
          text: 'エラーが発生しました: $e\n\nClaude APIキーを確認してください。',
          isUser: false,
          isError: true,
        ));
        _isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        // AppBarにsubtitleはないため、titleにColumnで2行表示する
        title: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('AIに相談'),
            Text(
              '24時間いつでもOK',
              style: TextStyle(fontSize: 12),
            ),
          ],
        ),
      ),
      body: Column(
        children: [
          // 会話履歴
          Expanded(
            child: ListView.builder(
              reverse: true,
              itemCount: _messages.length,
              itemBuilder: (context, index) {
                final message = _messages[_messages.length - 1 - index];
                return ChatBubble(message: message);
              },
            ),
          ),

          // 入力中の表示
          if (_isLoading)
            Padding(
              padding: EdgeInsets.all(16),
              child: Row(
                children: [
                  CircularProgressIndicator(),
                  SizedBox(width: 16),
                  Text('考え中...'),
                ],
              ),
            ),

          // 入力欄
          Padding(
            padding: EdgeInsets.all(8),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _controller,
                    decoration: InputDecoration(
                      hintText: '何でも相談してください...',
                      border: OutlineInputBorder(),
                    ),
                    maxLines: null,
                    onSubmitted: (_) => _sendMessage(),
                  ),
                ),
                IconButton(
                  icon: Icon(Icons.send),
                  onPressed: _sendMessage,
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

実際の使用例:

【深夜2時、授乳中の会話】

親: 「夜泣きがひどくて辛いです。
     もう3時間寝てません...」

AI: 「お疲れ様です。深夜の授乳、本当に大変ですよね。
     今、頑張っているあなたは本当にすごいです。

     ○○ちゃん(3ヶ月)は、昨日の記録を見ると、
     夕方のお昼寝が2時間ありましたね。
     夕方のお昼寝が長い日は夜泣きが増える傾向があります。

     明日試してみてはどうでしょう:
     - 夕方のお昼寝は30分までにする
     - 16時以降は明るい場所で過ごす

     ただ、今はまず、可能なら
     誰かに30分でも代わってもらって、
     少し休んでください。

     完璧じゃなくて大丈夫です。
     あなたは十分頑張っています。」

親: 「ありがとうございます。
     少し楽になりました。
     明日、夕方のお昼寝を短くしてみます」

AI: 「そうしてみてください。
     また明日、様子を教えてくださいね。
     今夜はゆっくり休めますように。」

従来のサービスとの決定的な違い: - ✅ 「今すぐ」相談できる(深夜2時でもOK) - ✅ 子供の個別の記録を理解している(昨日の記録を参照) - ✅ 過去の会話を覚えている(継続的な関係) - ✅ 共感してくれる(孤独感の軽減) - ✅ 実践的なアドバイス(明日試せること) - ✅ 定額だから何度でも相談できる


Month 2-3: Mac mini + PostgreSQL(本格運用)

家庭で継続的に使う、または小規模な学習塾として運営する場合、Mac mini + PostgreSQLにアップグレードします。

なぜMac miniか

【Mac miniを推奨する理由】

1. 子供の安全性
   ✅ 本体を隠せる(棚の裏、高い場所)
   ✅ 子供が触っても壊れない
   ✅ ノートPCは画面を閉じられる、倒される
   ✅ 飲み物をこぼされるリスクから守れる

2. 周辺機器の交換容易性
   ✅ ディスプレイ:1-3万円で交換
   ✅ キーボード・マウス:数千円
   ✅ 本体(Mac mini)は安全な場所に設置
   ✅ 壊れても、安価な周辺機器だけ交換

3. 24時間稼働に適している
   ✅ 低消費電力(月500円以下)
   ✅ 静音(寝室に置いても問題なし)
   ✅ 発熱が少ない

4. 長期的な投資
   ✅ 5-7年は使える
   ✅ macOS/iOS開発も可能
   ✅ 家族のメインPCとしても使える

価格: 10-15万円(初期投資のみ)

PostgreSQLで本格的なデータ管理

-- PostgreSQLセットアップ
-- brew install postgresql@16
-- createdb childcare_support_db

-- 子供のプロフィール
CREATE TABLE children (
  id SERIAL PRIMARY KEY,
  name TEXT NOT NULL,
  birth_date DATE,
  created_at TIMESTAMP DEFAULT NOW()
);

-- 観察記録
CREATE TABLE observations (
  id SERIAL PRIMARY KEY,
  child_id INT REFERENCES children(id),
  observation_date TIMESTAMP DEFAULT NOW(),
  context TEXT,  -- 状況(「朝食中」「公園で」等)
  behavior TEXT,  -- 観察した行動
  strengths_noted JSONB,  -- 気づいた強み
  created_at TIMESTAMP DEFAULT NOW()
);

-- AI会話履歴(長期保存)
CREATE TABLE conversations (
  id SERIAL PRIMARY KEY,
  child_id INT REFERENCES children(id),
  timestamp TIMESTAMP DEFAULT NOW(),
  user_message TEXT,
  assistant_message TEXT,
  context JSONB,  -- その時の子供の状態等
  category TEXT  -- 自動分類('夜泣き', '発達', '食事' 等)
);

-- 全文検索(過去の相談を検索)
CREATE INDEX idx_conversations_search
ON conversations
USING gin(to_tsvector('japanese', user_message || ' ' || assistant_message));

-- 強みの記録
CREATE TABLE strengths (
  id SERIAL PRIMARY KEY,
  child_id INT REFERENCES children(id),
  strength_type TEXT,  -- '視覚的思考', '集中力' 等
  description TEXT,
  confidence_score FLOAT,  -- AIの確信度
  first_observed DATE,
  last_confirmed DATE,
  UNIQUE(child_id, strength_type)  -- ON CONFLICT (child_id, strength_type) に必要
);

-- 週次レポート
CREATE TABLE weekly_reports (
  id SERIAL PRIMARY KEY,
  child_id INT REFERENCES children(id),
  week_start DATE,
  analysis TEXT,  -- AIによる分析
  patterns JSONB,  -- 発見されたパターン
  suggestions JSONB,  -- 提案
  created_at TIMESTAMP DEFAULT NOW()
);

Month 4-6: 高度なエージェント機能

/// 観察記録からパターンを自動分析
class ObservationAnalyzer {
  final ChildcareAIAgent agent;
  final PostgreSQLConnection db;

  ObservationAnalyzer({
    required this.agent,
    required this.db,
  });

  /// 週次レポート自動生成
  Future<WeeklyReport> generateWeeklyReport(int childId) async {
    // 1週間の観察記録を取得
    final observations = await db.query('''
      SELECT * FROM observations
      WHERE child_id = @childId
        AND observation_date >= NOW() - INTERVAL '7 days'
      ORDER BY observation_date DESC
    ''', substitutionValues: {'childId': childId});

    // AIに分析依頼
    final analysis = await agent.chat('''
【週次レポート作成依頼】

この1週間の観察記録から、以下を分析してください:

1. 繰り返し現れるパターン
2. 成長が見られた点
3. 気をつけるべき点
4. 来週試してみると良さそうなこと

【観察記録】
${observations.map((r) => '${r[1]}: ${r[3]} - ${r[4]}').join('\n')}
''');

    // レポートをDBに保存
    await db.query('''
      INSERT INTO weekly_reports (child_id, week_start, analysis)
      VALUES (@childId, @weekStart, @analysis)
    ''', substitutionValues: {
      'childId': childId,
      'weekStart': DateTime.now().subtract(Duration(days: 7)),
      'analysis': analysis,
    });

    return WeeklyReport(
      weekOf: DateTime.now(),
      analysis: analysis,
    );
  }

  /// 強みの発見(継続的)
  Future<List<Strength>> discoverStrengths(int childId) async {
    // 長期的な観察記録から強みを発見
    final allObservations = await db.query('''
      SELECT * FROM observations
      WHERE child_id = @childId
      ORDER BY observation_date DESC
      LIMIT 100
    ''', substitutionValues: {'childId': childId});

    final result = await agent.chat('''
【強みの発見依頼】

これまでの観察記録全体から、この子の強みを見つけてください。

特に:
- 何度も現れる得意な行動
- 他の子と比べて秀でている点
- 将来伸ばせそうな才能

各強みについて、確信度(0.0-1.0)も教えてください。

【観察記録】
${allObservations.map((r) => '${r[1]}: ${r[4]}').join('\n')}
''');

    final strengths = _parseStrengths(result, childId);

    // 強みをDBに保存
    for (var strength in strengths) {
      await db.query('''
        INSERT INTO strengths (child_id, strength_type, description, confidence_score)
        VALUES (@childId, @type, @desc, @score)
        ON CONFLICT (child_id, strength_type)
        DO UPDATE SET
          description = @desc,
          confidence_score = @score,
          last_confirmed = NOW()
      ''', substitutionValues: {
        'childId': childId,
        'type': strength.type,
        'desc': strength.description,
        'score': strength.confidenceScore,
      });
    }

    return strengths;
  }

  /// 過去の類似ケースを検索
  Future<List<Conversation>> searchSimilarCases({
    required int childId,
    required String category,
    required String query,
  }) async {
    return await db.mappedResultsQuery('''
      SELECT * FROM conversations
      WHERE child_id = @childId
        AND category = @category
        AND to_tsvector('japanese', user_message || ' ' || assistant_message)
            @@ to_tsquery('japanese', @query)
      ORDER BY timestamp DESC
      LIMIT 5
    ''', substitutionValues: {
      'childId': childId,
      'category': category,
      'query': query,
    }).then((results) =>
      results.map((r) => Conversation.fromMap(r['conversations']!)).toList()
    );
  }
}

/// 過去の経験を活用する高度なエージェント
class AdvancedAIAgent extends ChildcareAIAgent {
  final ObservationAnalyzer analyzer;

  AdvancedAIAgent({
    required super.userApiKey,
    required super.database,
    required this.analyzer,
  });

  @override
  Future<String> chat(String userMessage) async {
    // 1. 相談内容を自動分類
    final category = await _categorize(userMessage);

    // 2. 過去の類似相談を検索
    final similarCases = await analyzer.searchSimilarCases(
      childId: 1,  // 現在の子供ID
      category: category,
      query: _extractKeywords(userMessage),
    );

    // 3. 似たケースでの解決策を参考にする
    if (similarCases.isNotEmpty) {
      final contextWithHistory = '''
【今回の相談】
$userMessage

【過去の類似ケース】
${similarCases.map((c) => '''
日時: ${c.timestamp}
相談: ${c.userMessage}
対応: ${c.assistantMessage}
''').join('\n---\n')}

上記の過去の経験を参考に、今回の状況に合わせたアドバイスをください。
''';

      return await super.chat(contextWithHistory);
    } else {
      return await super.chat(userMessage);
    }
  }

  Future<String> _categorize(String message) async {
    // 簡易的な分類(実際はもっと高度に)
    if (message.contains('夜泣き') || message.contains('寝ない')) return '睡眠';
    if (message.contains('食べ') || message.contains('食事')) return '食事';
    if (message.contains('発達') || message.contains('成長')) return '発達';
    return '一般';
  }

  String _extractKeywords(String message) {
    // 簡易的なキーワード抽出
    return message.replaceAll(RegExp(r'[、。!?\s]+'), ' & ');
  }
}

コミュニティの巻き込み方

開発知識がない親も参加できる

【Level 1: 経験を共有】
「うちの子(6歳、ディスレクシア疑い)は、
 文字より図で説明すると理解が早いです。
 レゴブロックで1時間集中して遊べます」

→ このデータがシステムに蓄積される
→ 同じ特性の子への提案に活用

【Level 2: 機能リクエスト】
「兄弟間の比較機能があると嬉しい」
「学校の先生と共有できるレポート機能」

→ エンジニアが実装を検討
→ コミュニティで投票・優先順位決定

【Level 3: テスト参加】
実際に使って報告:
「この画面、4歳の子には難しい表現です」
「ここに励ましの言葉があると嬉しい」

→ UI/UX改善に直結

【Level 4: データ入力・整理】
Notionで知識データベース構築:
「年齢別の発達マイルストーン」
「よくある相談とその対応例」

→ AIの学習データに

なぜ「診断」ではなく「強み発見」なのか

診断は専門家に任せる、強みは親が見つける

診断書: すでに持っている(病院でもらえる) 親が本当に知りたいこと: 「この子をどう伸ばすか」「何が得意か」


「強み発見」が人生を変える実例

実例1: 数字への強いこだわり

診断的視点: 「こだわりが強い(改善すべき課題)」
↓
強み発見視点: 「数的思考の才能の芽」
↓
対応: プログラミング、数学パズルを提供
↓
結果: 小学生でScratch、中学でPython
      高校で情報オリンピック出場
      大学で情報工学、今はエンジニア

実例2: 図鑑を丸暗記

診断的視点: 「興味の偏り(社会性の課題)」
↓
強み発見視点: 「視覚的記憶力の強さ」
↓
対応: 生物学、地理、歴史の本を提供
      博物館、水族館に連れて行く
↓
結果: 生物への深い興味
      大学で生物学、今は研究者

実例3: レゴに何時間も集中

診断的視点: 「他の遊びができない(柔軟性の課題)」
↓
強み発見視点: 「空間認識、集中力の高さ」
↓
対応: 建築、3Dデザインツールを提供
      マインクラフト、CADソフトに触れる
↓
結果: 建築、工学への興味
      将来の進路が明確に

BYOAで「強み発見エンジン」を作る

/// 観察記録から強みを自動発見
class StrengthDiscovery {
  final ChildcareAIAgent agent;
  final Isar database;

  StrengthDiscovery({
    required this.agent,
    required this.database,
  });

  /// 繰り返し現れるパターンから強みを見つける
  Future<List<Strength>> discoverStrengths(int childId) async {
    // 長期的な観察記録を取得
    final observations = await database.observations
      .filter()
      .childIdEqualTo(childId)
      .sortByDateDesc()
      .limit(100)
      .findAll();

    // AIに「強み発見」依頼
    final prompt = '''
【強み発見の依頼】

これまでの観察記録から、この子の「得意なこと」「才能の芽」を見つけてください。

注目すべきポイント:
- 何度も繰り返し現れる行動
- 長時間集中できること
- 他の子と比べて秀でている点
- 本人が楽しそうにやっていること

【観察記録】
${observations.map((o) => '${o.date}: ${o.description}').join('\n')}

【出力形式】
各強みについて:
1. 強みの名前(例: 空間認識能力、視覚的記憶)
2. 根拠となる観察記録
3. 伸ばし方の提案
4. 将来の可能性(進路、職業等)
5. 確信度(0.0-1.0)
''';

    final response = await agent.chat(prompt);
    return _parseStrengths(response);
  }

  List<Strength> _parseStrengths(String response) {
    // AI回答をパースして強みリストに変換
    // 実装は省略
    return [];
  }
}

親の意識が変わる

変化前 変化後
「できない子」 「個性的な才能を持つ子」
「不安」 「希望」
「改善しなきゃ」 「伸ばしたい」

なぜBYOAが向いているか

項目 既製品 BYOA
視点 万人向けの診断的視点 我が子専用の強み発見
カスタマイズ 固定機能 我が子の特性に合わせる
データ 企業のもの 親のもの(永続的)
方向性 課題の改善 才能の開花
コミュニティ なし GitHub等で知見共有

コミュニティで知見を共有

プライバシーを守りつつ: - ❌ 「うちの子のデータ」は公開しない - ✅ 「こういうタイプの子には、これが効果的だった」は共有

GitHub Discussionsでの例:

【タイトル】レゴ好きの子への声かけパターン

うちの子(6歳)はレゴに何時間も集中します。
「空間認識能力」の強みとして記録していますが、
他の親御さんはどうアプローチしていますか?

→ 返信: 「うちも同じです!建築の本を見せたら興味津々でした」
→ 返信: 「マインクラフトを始めたら、プログラミングにも興味が」

コード改善のPull Request:

feat: 強み発見の確信度アルゴリズム改善

繰り返しパターンの検出精度を向上させました。
- 3回以上出現 → 確信度 0.7
- 5回以上出現 → 確信度 0.9

一人で悩まず、みんなで良くしていける


事業化の可能性:学習塾モデル

投資回収の試算

【初期投資】
- Mac mini: 12万円
- ディスプレイ等: 3万円
合計: 15万円

【月額コスト】
- Claude Pro: $20(約3,000円)
- Claude API利用料: 従量課金(利用規模により変動)
- 電気代: 500円
合計: 約3,500円/月+API利用料

【収益モデル(小規模塾)】
- 月謝: 3万円/子供
- 定員: 5-10人

生徒3人: 月9万円
  ├─ コスト: 約3,500円+API利用料
  └─ 利益: 8万円台半ば

→ 初期投資は2ヶ月で回収
→ 3ヶ月目から利益

【差別化ポイント】
✅ データに基づく個別支援
✅ 詳細な成長レポート
✅ 保護者への透明性
✅ AIによる24時間サポート
✅ プライバシー完全保護

まとめ:なぜBYOAが必須か

既製品では不可能なこと

要求 既製品 BYOA
24時間相談 △ 自治体のみ ✅ いつでも
個別理解 △ 汎用的 ✅ 完全に理解
会話の継続性 ❌ なし ✅ 全て記憶
データ所有 ❌ 企業/自治体 ✅ 自分
プライバシー △ クラウド ✅ ローカル
カスタマイズ ✅ 自由
待機時間 数ヶ月(療育) なし
事業化 ✅ 可能

Claude Pro(定額)が開発に向いている理由

✅ 開発中の相談は使いすぎの心配なし(定額$20・約3,000円)
✅ 夜中に何度相談してもOK
✅ 家計管理しやすい
✅ Webチャット + Claude Code(エージェント型コーディング)が定額に含まれる
⚠️ アプリが呼び出すClaude APIは別契約の従量課金
   (console.anthropic.comでAPIキーを取得。この点はChatGPT/Geminiと同じ)

育児・発達支援アプリは、AIエージェント内蔵が必須です。 開発はClaude Proの定額で進め、運用時のAPI利用料は従量課金として見込んでおきましょう。

次のレシピでは、開発ツールの事例を見ていきましょう!