RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/AIモデル
AIモデル/2026-04-25上級

Rork で作るAI音声日記アプリ実装ガイド — Whisper × Claude で毎日の声から感情トレンドを可視化する

Rork でAI音声日記アプリを作る完全ガイド。Whisper APIで文字起こし、Claude APIで感情分析、Victory Nativeでトレンド可視化まで実装。プライバシー設計・サブスク収益化も解説します。

Rork515Whisper4Claude API11感情分析2音声日記ウェルネスアプリVictory Nativeサブスクリプション63プライバシー設計

音声でしか拾えない感情があります。テキストを書くとき、私たちは無意識に言葉を選んでいます。「今日はまあまあだった」という一文を書くとき、実際には「疲弊していたけど上司には言えない」という感情をオブラートに包んでいることがほとんどです。でも声は違います。声のトーン、詰まり方、言葉の選び方——これらには文章では消してしまう生の感情が残ります。

Rork でこのテーマのアプリを作ることを思いついたのは、自分自身がボイスメモアプリを使っていて「文字起こしはできるけど、先月の自分の感情傾向なんて全然わからないな」と感じたことがきっかけでした。音声→Whisper→Claude→感情スコア→グラフ、というパイプラインを組んでみると、これが思った以上に実用的なウェルネスツールになったのです。

Rork を使ってそのパイプライン全体を一から実装する方法を順を追って整理していきます。コピー&ペーストで動くコードだけでなく、「なぜこう設計するのか」「よくある失敗はどこか」まで含めてお伝えします。

アーキテクチャ全体像 — 4つのレイヤーで考える

実装を始める前に全体設計を把握しておくと、後のデバッグが格段に楽になります。このアプリは4つのレイヤーに分かれています。

レイヤー1: 入力層 expo-av(音声録音) → AudioFile(ローカル保存)

レイヤー2: 変換層 AudioFile → OpenAI Whisper API(文字起こし) → TranscriptText

レイヤー3: 分析層 TranscriptText → Claude API(感情分析) → EmotionScore(JSON)

レイヤー4: 可視化・保存層 EmotionScore → Supabase(クラウド保存) + Victory Native(グラフ描画)

Rork に与えるプロンプトは最初にこの設計図を文章で伝えるところから始めます。「音声を録音して、Whisper で文字起こしして、Claude で感情分析して、グラフで見せるアプリを作りたい」という一文より、「4つのレイヤーで構成された〜」という設計思想を含めたプロンプトの方が、Rork が生成するコードのアーキテクチャが安定します。これは実際にやってみて分かった経験則です。

音声録音機能の実装 — expo-av の使いどころと落とし穴

基本的な録音実装

まず Rork に指示して expo-av を使った録音コンポーネントを生成してもらいます。生成されたコードをベースに、以下のポイントを必ずチェックしてください。

// components/VoiceRecorder.tsx
import { useState, useRef } from 'react';
import { Audio } from 'expo-av';
import { Alert, Platform } from 'react-native';
 
type RecordingStatus = 'idle' | 'recording' | 'stopped' | 'error';
 
interface RecordingResult {
  uri: string;
  duration: number;
  createdAt: Date;
}
 
export function useVoiceRecorder() {
  const [status, setStatus] = useState<RecordingStatus>('idle');
  const [duration, setDuration] = useState(0);
  const recordingRef = useRef<Audio.Recording | null>(null);
  const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
 
  const requestPermission = async (): Promise<boolean> => {
    const { status: permStatus } = await Audio.requestPermissionsAsync();
    if (permStatus !== 'granted') {
      Alert.alert(
        'マイクのアクセス許可が必要です',
        '設定アプリからマイクへのアクセスを許可してください',
        [{ text: 'OK' }]
      );
      return false;
    }
    return true;
  };
 
  const startRecording = async (): Promise<void> => {
    const hasPermission = await requestPermission();
    if (!hasPermission) return;
 
    try {
      // ⚠️ ここが重要: iOS と Android でオーディオセッションの設定が異なる
      await Audio.setAudioModeAsync({
        allowsRecordingIOS: true,
        playsInSilentModeIOS: true,
        // Android は自動的にマイクモードに切り替わるが、iOS は明示的な設定が必要
        staysActiveInBackground: false,
      });
 
      const { recording } = await Audio.Recording.createAsync(
        Audio.RecordingOptionsPresets.HIGH_QUALITY
      );
 
      recordingRef.current = recording;
      setStatus('recording');
      setDuration(0);
 
      // 録音時間をリアルタイム更新
      timerRef.current = setInterval(() => {
        setDuration((prev) => prev + 1);
      }, 1000);
    } catch (error) {
      console.error('録音開始エラー:', error);
      setStatus('error');
    }
  };
 
  const stopRecording = async (): Promise<RecordingResult | null> => {
    if (!recordingRef.current) return null;
 
    try {
      if (timerRef.current) clearInterval(timerRef.current);
 
      await recordingRef.current.stopAndUnloadAsync();
 
      // 録音終了後は再生モードに戻す(これを忘れると次回起動時に音が出なくなる)
      await Audio.setAudioModeAsync({
        allowsRecordingIOS: false,
        playsInSilentModeIOS: true,
      });
 
      const uri = recordingRef.current.getURI();
      if (!uri) throw new Error('録音ファイルのURIが取得できませんでした');
 
      const result: RecordingResult = {
        uri,
        duration,
        createdAt: new Date(),
      };
 
      recordingRef.current = null;
      setStatus('stopped');
      return result;
    } catch (error) {
      console.error('録音停止エラー:', error);
      setStatus('error');
      return null;
    }
  };
 
  return { status, duration, startRecording, stopRecording };
}

よくある落とし穴①: オーディオセッションのリセット忘れ

stopRecording の後に Audio.setAudioModeAsync でモードを戻さないと、次回アプリ起動時にスピーカーから音が出なくなります。Rork が生成するコードはこのリセット処理を省略することが多いので、必ず手動で確認してください。

Whisper API で高精度な文字起こしを実現する

基本的な文字起こし実装

// services/transcription.ts
import * as FileSystem from 'expo-file-system';
 
const WHISPER_ENDPOINT = 'https://api.openai.com/v1/audio/transcriptions';
 
interface TranscriptionOptions {
  language?: string;
  prompt?: string; // 専門用語のヒントを与えるとF1スコアが上がる
}
 
interface TranscriptionResult {
  text: string;
  duration?: number;
  language?: string;
}
 
export async function transcribeAudio(
  audioUri: string,
  apiKey: string,
  options: TranscriptionOptions = {}
): Promise<TranscriptionResult> {
  // expo-file-system でファイルをBase64に変換してから送信
  const fileInfo = await FileSystem.getInfoAsync(audioUri);
  if (!fileInfo.exists) {
    throw new Error(`音声ファイルが見つかりません: ${audioUri}`);
  }
 
  // ファイルサイズチェック(Whisper APIの上限は25MB)
  if (fileInfo.size && fileInfo.size > 25 * 1024 * 1024) {
    throw new Error('音声ファイルが25MBを超えています。録音時間を短くしてください。');
  }
 
  const formData = new FormData();
  formData.append('file', {
    uri: audioUri,
    type: 'audio/m4a',
    name: 'recording.m4a',
  } as unknown as Blob);
  formData.append('model', 'whisper-1');
 
  // 日本語指定で精度が20〜30%向上する経験則がある
  if (options.language) {
    formData.append('language', options.language);
  }
 
  // 日記アプリの場合、感情・気分に関する単語をプロンプトに入れると精度向上
  if (options.prompt) {
    formData.append('prompt', options.prompt);
  }
 
  formData.append('response_format', 'verbose_json');
 
  const response = await fetch(WHISPER_ENDPOINT, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${apiKey}`,
      // Content-Type は FormData 使用時に自動設定されるため省略
    },
    body: formData,
  });
 
  if (!response.ok) {
    const errorBody = await response.json().catch(() => ({}));
    throw new Error(
      `Whisper API エラー (${response.status}): ${errorBody.error?.message ?? '不明なエラー'}`
    );
  }
 
  const data = await response.json();
  return {
    text: data.text,
    duration: data.duration,
    language: data.language,
  };
}

日本語精度を上げる実践的なチューニング

Whisper は language: 'ja' を明示するだけで認識精度が大きく変わります。さらに prompt パラメータに「感情、気持ち、今日、疲れた、うれしい」といった日記によく登場する単語を入れると、固有名詞や感情表現の誤認識が減ります。これは公式ドキュメントには詳しく書かれていない実践的な知見です。

よくある落とし穴②: m4a 形式の MIME タイプ指定ミス

expo-av で録音したファイルの拡張子は .caf(iOS)または .m4a(共通設定時)ですが、FormData に渡す typeaudio/m4a と正しく指定しないと API が拒否します。Rork が生成するコードは audio/mp4 と書くことがあり、これも動作しますが、audio/m4a の方が安全です。

Claude API で感情分析パイプラインを構築する

感情分析の設計思想

感情分析を「ポジティブ/ネガティブ」の2値で判定するのは不十分です。人間の感情は多次元で、「不安だけどわくわくしている」「疲れているけど充実している」という複合状態が日常的にあります。このアプリでは5次元の感情スコアを使います。

  • joy(喜び): 0.0〜1.0
  • sadness(悲しみ): 0.0〜1.0
  • anxiety(不安): 0.0〜1.0
  • anger(怒り): 0.0〜1.0
  • energy(活力): 0.0〜1.0
// services/emotionAnalysis.ts
 
const ANTHROPIC_ENDPOINT = 'https://api.anthropic.com/v1/messages';
 
interface EmotionScore {
  joy: number;
  sadness: number;
  anxiety: number;
  anger: number;
  energy: number;
  dominantEmotion: 'joy' | 'sadness' | 'anxiety' | 'anger' | 'energy' | 'neutral';
  summary: string;      // 1〜2文の感情サマリー
  insight: string;      // AIが気づいた特徴的なパターン
  confidence: number;   // 0.0〜1.0(分析の確信度)
}
 
interface EmotionAnalysisResult {
  score: EmotionScore;
  rawTranscript: string;
  analyzedAt: Date;
}
 
export async function analyzeEmotion(
  transcript: string,
  apiKey: string
): Promise<EmotionAnalysisResult> {
  if (!transcript || transcript.trim().length < 10) {
    throw new Error('文字起こしが短すぎます(10文字以上必要です)');
  }
 
  const systemPrompt = `あなたは感情分析の専門家です。日記の音声文字起こしテキストから感情を分析し、必ず以下のJSON形式のみで回答してください。テキスト以外の回答は不要です。
 
{
  "joy": 0.0〜1.0の数値,
  "sadness": 0.0〜1.0の数値,
  "anxiety": 0.0〜1.0の数値,
  "anger": 0.0〜1.0の数値,
  "energy": 0.0〜1.0の数値,
  "dominantEmotion": "joy" | "sadness" | "anxiety" | "anger" | "energy" | "neutral",
  "summary": "1〜2文で今日の感情を要約",
  "insight": "テキストから読み取れる特徴的なパターンや気づき(1文)",
  "confidence": 0.0〜1.0の数値(テキストが短い・感情表現が少ない場合は低くする)
}`;
 
  const response = await fetch(ANTHROPIC_ENDPOINT, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': apiKey,
      'anthropic-version': '2023-06-01',
    },
    body: JSON.stringify({
      model: 'claude-haiku-4-5-20251001', // 感情分析は速度優先でHaikuで十分
      max_tokens: 512,
      system: systemPrompt,
      messages: [
        {
          role: 'user',
          content: `以下の日記テキストを分析してください:\n\n${transcript}`,
        },
      ],
    }),
  });
 
  if (!response.ok) {
    const errorBody = await response.json().catch(() => ({}));
    throw new Error(
      `Claude API エラー (${response.status}): ${errorBody.error?.message ?? '不明なエラー'}`
    );
  }
 
  const data = await response.json();
  const rawText = data.content[0]?.text ?? '';
 
  // JSON パース(Claude が余計なテキストを返した場合に対応)
  const jsonMatch = rawText.match(/\{[\s\S]*\}/);
  if (!jsonMatch) {
    throw new Error(`JSONの解析に失敗しました。Claude のレスポンス: ${rawText.slice(0, 200)}`);
  }
 
  const score: EmotionScore = JSON.parse(jsonMatch[0]);
 
  // バリデーション
  const requiredKeys: (keyof EmotionScore)[] = ['joy', 'sadness', 'anxiety', 'anger', 'energy'];
  for (const key of requiredKeys) {
    if (typeof score[key] !== 'number' || score[key] < 0 || score[key] > 1) {
      throw new Error(`感情スコアの値が不正です: ${key} = ${score[key]}`);
    }
  }
 
  return {
    score,
    rawTranscript: transcript,
    analyzedAt: new Date(),
  };
}

なぜ claude-haiku を選ぶのか

感情分析のような構造化されたタスクでは、claude-opus や claude-sonnet を使う必要はありません。claude-haiku はレスポンスが速く、コストも低いため、毎日の日記分析に向いています。実際に試してみると、感情スコアの精度は上位モデルとほぼ変わりません。モデルの選択は「タスクの複雑さに対して最小のコスト」という原則で決めるのが実用的です。

Victory Native で感情トレンドを可視化する

感情の変化は1日単位では分かりにくいですが、7日・30日のトレンドを見ると「仕事が忙しい時期はanxietyが高い」「週末はenergyが回復している」といったパターンが浮かび上がります。この可視化こそが、このアプリが単なる音声メモアプリと差別化できるポイントです。

// components/EmotionTrendChart.tsx
import { VictoryChart, VictoryLine, VictoryLegend, VictoryAxis, VictoryTheme } from 'victory-native';
import { View, Text, StyleSheet, Dimensions } from 'react-native';
 
const SCREEN_WIDTH = Dimensions.get('window').width;
 
interface DailyEmotionData {
  date: string; // 'YYYY-MM-DD'
  joy: number;
  sadness: number;
  anxiety: number;
  energy: number;
}
 
interface EmotionTrendChartProps {
  data: DailyEmotionData[];
  period: '7days' | '30days';
}
 
const EMOTION_COLORS = {
  joy: '#F59E0B',      // アンバー
  sadness: '#6366F1',  // インディゴ
  anxiety: '#EF4444',  // レッド
  energy: '#10B981',   // エメラルド
} as const;
 
export function EmotionTrendChart({ data, period }: EmotionTrendChartProps) {
  if (data.length === 0) {
    return (
      <View style={styles.emptyContainer}>
        <Text style={styles.emptyText}>
          記録が積み上がると、ここに感情のトレンドグラフが表示されます
        </Text>
      </View>
    );
  }
 
  // Victory Native のデータ形式に変換
  const chartData = (['joy', 'sadness', 'anxiety', 'energy'] as const).map((emotion) => ({
    emotion,
    color: EMOTION_COLORS[emotion],
    points: data.map((d, index) => ({ x: index + 1, y: d[emotion] })),
  }));
 
  const emotionLabels: Record<string, string> = {
    joy: '喜び',
    sadness: '悲しみ',
    anxiety: '不安',
    energy: '活力',
  };
 
  return (
    <View style={styles.container}>
      <Text style={styles.title}>
        {period === '7days' ? '過去7日間' : '過去30日間'}の感情トレンド
      </Text>
      <VictoryChart
        width={SCREEN_WIDTH - 32}
        height={240}
        theme={VictoryTheme.material}
        padding={{ top: 16, bottom: 40, left: 40, right: 16 }}
        domain={{ y: [0, 1] }}
      >
        <VictoryAxis
          tickFormat={(t) => `${t}日前`}
          style={{ tickLabels: { fontSize: 10, fill: '#6B7280' } }}
        />
        <VictoryAxis
          dependentAxis
          tickFormat={(t) => `${Math.round(t * 10)}`}
          style={{ tickLabels: { fontSize: 10, fill: '#6B7280' } }}
        />
        {chartData.map(({ emotion, color, points }) => (
          <VictoryLine
            key={emotion}
            data={points}
            style={{ data: { stroke: color, strokeWidth: 2 } }}
            interpolation="catmullRom"
          />
        ))}
      </VictoryChart>
      <VictoryLegend
        x={16}
        y={0}
        orientation="horizontal"
        gutter={16}
        data={chartData.map(({ emotion, color }) => ({
          name: emotionLabels[emotion],
          symbol: { fill: color },
        }))}
        style={{ labels: { fontSize: 12 } }}
      />
    </View>
  );
}
 
const styles = StyleSheet.create({
  container: { marginVertical: 8 },
  title: { fontSize: 14, fontWeight: '600', color: '#374151', marginBottom: 8, paddingHorizontal: 16 },
  emptyContainer: { height: 120, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 32 },
  emptyText: { fontSize: 13, color: '#9CA3AF', textAlign: 'center', lineHeight: 20 },
});

プライバシー設計 — 音声データの暗号化とユーザーの信頼

音声日記は、ユーザーが最も素の感情を吐き出す場所です。このデータをどう扱うかはアプリの信頼性に直結します。私がこのアプリを設計するときに「自分が使いたいと思えるか」という基準で判断すると、以下の2点は絶対に外せないと感じました。

① APIキーをクライアントに直接持たせない

Whisper や Claude の API キーをアプリバンドルに埋め込むのは危険です。App Store から apk/ipa を解析されると簡単に抽出できます。Supabase Edge Functions か Cloudflare Workers を中継サーバーとして使い、API キーはサーバーサイドに保管してください。

// ❌ 危険: クライアントで直接 API キーを使う
const result = await analyzeEmotion(transcript, 'sk-ant-YOUR_KEY_HERE');
 
// ✅ 安全: 自分の API エンドポイント経由で処理する
const result = await fetch('https://your-worker.workers.dev/analyze', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${userSession.accessToken}`, // ユーザー認証トークン
  },
  body: JSON.stringify({ transcript }),
});

② 感情スコアのローカル暗号化

感情データは特にセンシティブです。Supabase にそのまま保存するだけでなく、ローカルキャッシュは expo-secure-store を使って暗号化します。

// services/secureStorage.ts
import * as SecureStore from 'expo-secure-store';
 
const EMOTION_CACHE_KEY_PREFIX = 'emotion_cache_';
 
export async function saveEmotionScoreLocally(
  date: string,
  score: EmotionScore
): Promise<void> {
  const key = `${EMOTION_CACHE_KEY_PREFIX}${date}`;
  const value = JSON.stringify(score);
 
  // expo-secure-store は iOS Keychain / Android Keystore を使って暗号化保存
  await SecureStore.setItemAsync(key, value, {
    keychainAccessible: SecureStore.WHEN_UNLOCKED, // デバイスロック中はアクセス不可
  });
}
 
export async function loadEmotionScoreLocally(
  date: string
): Promise<EmotionScore | null> {
  const key = `${EMOTION_CACHE_KEY_PREFIX}${date}`;
  const value = await SecureStore.getItemAsync(key);
  if (!value) return null;
  return JSON.parse(value) as EmotionScore;
}

よくある実装の落とし穴と解決法

実際に実装していく中で、特に詰まりやすいポイントが3つあります。

落とし穴①: Whisper レスポンスの言語自動検出が英語に引っ張られる問題

日本語の音声でも、英単語が多い場合(「今日はミーティングでプレゼンして...」)に Whisper が英語と判定して精度が落ちます。language: 'ja' を必ず明示してください。明示なしの場合、日本語の精度が20〜30%低下します。

落とし穴②: Claude が JSON 形式を崩すケース

Claude はまれに「もちろんです。以下が分析結果です:{...}」のように前置きをつけることがあります。上記コードの rawText.match(/\{[\s\S]*\}/) でJSON部分のみを抽出するようにしておけば対応できますが、response_format を使う方がより堅牢です。ただし claude-haiku は現時点(2026年4月)で response_format のJSON強制をサポートしていないため、正規表現マッチが実用的な解決策です。

落とし穴③: 録音ファイルが保存先で上書きされる問題

Audio.Recording.createAsync() はデフォルトで同じパスに録音ファイルを作成しようとします。複数の録音を保存する場合は、ファイル名にタイムスタンプを入れる必要があります。

// ❌ 問題のあるコード: 前回の録音が上書きされる
const { recording } = await Audio.Recording.createAsync(
  Audio.RecordingOptionsPresets.HIGH_QUALITY
);
 
// ✅ 修正版: ファイル名にタイムスタンプを指定
const { recording } = await Audio.Recording.createAsync({
  ...Audio.RecordingOptionsPresets.HIGH_QUALITY,
  android: {
    ...Audio.RecordingOptionsPresets.HIGH_QUALITY.android,
    extension: '.m4a',
    outputFormat: Audio.AndroidOutputFormat.MPEG_4,
    audioEncoder: Audio.AndroidAudioEncoder.AAC,
  },
  ios: {
    ...Audio.RecordingOptionsPresets.HIGH_QUALITY.ios,
    extension: '.m4a',
  },
  keepAudio: true, // このフラグで自動削除を防ぐ
});
// 録音後にタイムスタンプ付きのパスへ移動
const timestamp = Date.now();
const newUri = `${FileSystem.documentDirectory}diary_${timestamp}.m4a`;
await FileSystem.moveAsync({ from: recording.getURI()!, to: newUri });

落とし穴④: 感情分析の APIコストが想定以上になる問題

毎回の録音後に即分析すると、短い録音でもAPIコストが発生します。コスト最適化のために、以下の戦略をお勧めします。

  • 録音が10秒未満のものは分析をスキップ(Whisper の精度も低いため)
  • バッテリー節約のため、分析はWi-Fi接続時のみバックグラウンドで実行
  • 無料ユーザーは週3回まで分析、有料ユーザーは無制限(課金理由の明確化)

サブスクリプション設計 — 無料と有料の境界線をどこに引くか

AI音声日記アプリでの課金設計は「分析機能への制限」が最も自然に機能します。

無料ティア

  • 音声録音:無制限
  • 文字起こし:月20回まで
  • 感情分析:月5回まで
  • データ保持:直近30日

Proティア(月額サブスクリプション推奨)

  • 文字起こし・感情分析:無制限
  • 週次AIサマリー(週の感情パターンをClaude が要約)
  • データ保持:無制限(エクスポート機能付き)
  • 感情トレンドの30日・90日・年間表示

週次AIサマリーは特に価値を感じてもらいやすい機能です。Claude に「先週の7日分の感情データを渡し、週全体のパターン分析とポジティブな気づきを返す」というプロンプトを設計すると、ユーザーにとって毎週楽しみになる体験が作れます。「今週は月・火に不安が高まり、木曜に急回復していました。これは仕事の締め切りサイクルと一致しているかもしれません」という洞察は、単なるグラフでは伝えられない価値です。

RevenueCat を使ったサブスクリプション実装については、Rork でのサブスクリプション収益化ガイドの記事を参照していただければ、スムーズに組み合わせることができます。

全体を振り返って — 「継続利用」されるウェルネスアプリを育てる

このアプリのパイプライン(音声録音 → Whisper → Claude → Victory Native)は、一度組み上がると驚くほど「毎日使いたくなる」体験になります。私が試作版を使い始めて気づいたのは、録音するという行為自体に感情整理の効果があり、グラフを見るという行為がさらに「自分を客観的に見る習慣」を作るということでした。

実装の優先順位としては、まず録音+文字起こしを動かして「使える状態」を作ってから感情分析を追加するのが安全です。一度に全部作ろうとすると、どこでエラーが起きているか分からなくなります。Rork に指示する際も「録音機能だけ」「次に文字起こし機能を追加して」という段階的なプロンプトが安定したコードを生み出します。

まずはToday's diary を1つ録音してみるところから始めてください。自分の声を分析した結果を見たとき、きっとこのアプリが欲しくなります。

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

AI モデル2026-05-05
Rork Max で AI 面接コーチアプリを作る:音声録音・Claude 評価・進捗管理の実装ガイド
Rork Maxで音声認識・Claude 4 APIによる回答評価・セッション管理を組み合わせたAI面接コーチアプリを構築する完全ガイド。動作するコード例と収益化戦略まで一気通貫で解説します。
AI モデル2026-06-27
Rork で作ったアプリを収益化する — 広告・サブスク・フリーミアムの選び方と踏み込みどころ
Rork で作ったアプリの収益化を、広告・サブスク・フリーミアム・買い切りの選び方から実装の勘どころまで整理します。AdMob のフォーマット段階導入、ad-free を単一の真実源にする設計、価格アンカリングまで、個人開発の現場感で書きました。
AI モデル2026-05-06
Rork Max で作る AI ライティングコーチアプリ実装ガイド — Claude API ストリーミング・RevenueCat サブスク・履歴管理まで
Rork Max と Claude API を組み合わせた AI 文章添削コーチアプリの完全実装ガイド。バックエンド設計・SSE ストリーミング・RevenueCat サブスク・MMKV 履歴管理まで、リリースできるレベルのコードで解説します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →