AI言い訳ジェネレーター ができるまで — バイラル狙いのエンタメAIの設計と実装
「遅刻したとき、どう言い訳する?」
誰もが一度は頭を抱えたこの問いに、AIが真剣に答えてくれたら?
それが AI言い訳ジェネレーター の出発点でした。単なるジョークツールに見えて、実は技術的にかなり面白い挑戦が詰まっています。この記事では、企画から実装まで全部お見せします。
🎭 なぜ「言い訳」なのか
コンセプトの背景
このサービスの開発テーマは「AIサービスで本当に収益化できるか検証すること」。そのためにはバイラル性が重要です。
実用系ツール(翻訳・要約など)は競合が多く、SNSで自然に拡散されにくい。一方で、
「AIが生成した爆笑言い訳」はシェアしたくなる
というユーザー行動は容易に想像できます。実際、Xで流れてくる「ChatGPTにXXさせてみた」系投稿は高エンゲージメントを叩き出しています。
ターゲット設定の考え方
| 軸 | 設定 | 理由 |
|---|---|---|
| 年齢層 | 10代〜30代 | SNS利用率が高い。ネタコンテンツを楽しむ文化 |
| 利用シーン | 困ったとき・暇つぶし | 検索意図が明確(「遅刻 言い訳」など) |
| SNS親和性 | 高 | 生成結果をそのままポスト可能 |
| 再訪率 | 中〜高 | レベルやシナリオを変えると毎回違う結果 |
バイラル性と検索需要の両方を狙える、珍しいコンセプトです。
🏗️ システム設計
全体アーキテクチャ
ブラウザ
│
├─ GenerateTab(Client Component)
│ ├─ シナリオ選択(6種)
│ ├─ レベルスライダー(1〜5)
│ └─ POST /api/services/excuse-generator/generate
│ └─ Claude Haiku(temperature: 0.9)
│
├─ FeedTab(Client Component)
│ └─ GET /api/services/excuse-generator/posts?sort=new
│
└─ RankingTab(Client Component)
└─ GET /api/services/excuse-generator/posts?sort=likes
データモデル
model ExcuseGeneratorPost {
id String @id @default(cuid())
scenario String // "late" | "deadline" | ...
level Int // 1〜5
excuse String // 生成された言い訳テキスト
likes Int @default(0)
createdAt DateTime @default(now())
@@index([createdAt(sort: Desc)])
@@index([likes(sort: Desc)])
}
複数のサービスを同一DBで運用しているため、テーブル名はサービス名のプレフィックスを付けて名前空間を分離しています。
API設計
| エンドポイント | メソッド | レート制限 | 役割 |
|---|---|---|---|
/api/…/generate | POST | 10回/分 | AIで言い訳を生成 |
/api/…/posts | GET | なし | 投稿一覧取得 |
/api/…/posts | POST | 20回/分 | 投稿を保存 |
/api/…/posts/[id]/like | POST | 30回/分 | いいねを加算 |
⚡ AIプロンプト設計
ここが一番の肝です。Claude Haiku に「面白い言い訳」を生成させるには、どうプロンプトを設計するかが全てです。
壮大さレベルの定義
| Lv | ラベル | プロンプト指示 | 出力傾向 |
|---|---|---|---|
| 1 | ギリ信用される | 現実的、少しユニーク | 「電車が5分遅れて…」 |
| 2 | ちょっと怪しい | 盛り気味、なくはない | 「突然の雷雨で…」 |
| 3 | 完全にフィクション | 明らかに嘘、でも面白い | 「タイムパラドックスで…」 |
| 4 | ハリウッド映画級 | 映画のような展開 | 「スパイに追われて…」 |
| 5 | 宇宙規模 | SF・ファンタジー | 「銀河連邦から召集されて…」 |
実際のプロンプト構造
あなたは日本語の言い訳を作る専門家です。
以下の条件で言い訳を1つ生成してください。
シチュエーション: {scenarioLabel}({scenarioDescription})
壮大さレベル: {level}/5 - {levelLabel}
レベルの説明: {levelDescription}
要件:
- 日本語で200文字前後
- {level <= 2 ? "現実的で..." : "創造的で..."}
- 言い訳のみ出力(説明不要)
temperature: 0.9 は重要な設定です。0.7だと「それっぽいが面白くない」出力になりがちで、1.0以上だと文章が破綻します。0.9はクリエイティビティと一貫性のバランスが最も良い値でした。
A/Bテストの結果
| プロンプトバリエーション | 面白さスコア(主観10回評価) | 採用 |
|---|---|---|
| 「嘘の言い訳を作って」 | 4/10 | ✗ |
| 「言い訳の専門家として」 | 6/10 | ✗ |
| レベル定義を詳細に記述 | 8/10 | ✓ |
| シナリオ文脈を英語で渡す | 5/10 | ✗(英語出力が混入) |
🎨 UX設計
タブ構成の理由
[ 生成する ] [ みんなの言い訳 ] [ ランキング ]
3タブ構成は意図的です。
- 生成する: コア体験。すぐ使える
- みんなの言い訳: UGCによる滞在時間延長
- ランキング: 競争要素でリピート訪問を促す
「生成して終わり」ではなく、他人の面白い言い訳を見て笑い、自分の言い訳を投稿してリアクションを待つ——そのループを設計しています。
レベルスライダーのUX
| 設計要素 | 決定内容 | 理由 |
|---|---|---|
| デフォルト値 | Lv3(完全にフィクション) | 面白い出力が出やすく、初回体験を最大化 |
| スライダー色 | 紫グロー(#8b5cf6) | 「AIっぽさ」「非日常感」を演出 |
| バッジの色 | レベルで5色変化 | 変化を視覚的にフィードバック |
| 変更時の挙動 | 結果をクリア | 混乱を防ぐ。「これどのレベルの結果?」をなくす |
SNSシェアボタンの配置
┌─────────────────────────────────────┐
│ 生成された言い訳テキスト │
│ │
│ [𝕏 でポスト] [LINE で送る] [📋コピー] │
│ │
│ [📋 みんなに共有する] [🔄 再生成] │
└─────────────────────────────────────┘
SNSシェアは2段構成にしました。上段は外部SNSへの直接シェア、下段はサービス内への投稿。どちらも1タップで完了します。
X(Twitter)のシェアURLにはハッシュタグ #AI言い訳ジェネレーター を自動付与しています。投稿が増えるとトレンドになる可能性があります(←希望的観測)。
🔒 スパム対策の設計
認証なしで動かすため、スパム・乱用対策を複数層で実装しています。
レート制限の実装
// src/lib/rate-limit.ts のラッパー
const limiter = rateLimit({ interval: 60_000, uniqueTokenPerInterval: 500 })
// 生成API: 10回/分
await limiter.check(res, 10, "GENERATE_" + ip)
// 投稿API: 20回/分
await limiter.check(res, 20, "POST_" + ip)
// いいねAPI: 30回/分
await limiter.check(res, 30, "LIKE_" + ip)
いいね重複防止
認証なしでいいね重複を防ぐのは難しい問題です。今回はシンプルに localStorageにいいね済みIDを保存 する方針を取りました。
| 方法 | メリット | デメリット |
|---|---|---|
| localStorage | 実装が簡単 | シークレットモードで回避可能 |
| Cookie | より強固 | 設定が面倒 |
| IP + UserAgent | サーバー側で完結 | プライバシー懸念、VPNで回避可能 |
| 認証必須 | 完全に防止 | 離脱率が上がる |
エンタメ系サービスにおいて「いいね数の完全な正確性」より「ユーザー離脱ゼロ」の方が重要と判断しました。localstorage方式で十分です。
📊 期待するトラフィック経路
想定される流入パターン
Google検索
「遅刻 言い訳 面白い」
「仕事 休む 理由 ユニーク」
│
▼
サービスページ
│
生成 → SNSシェア
│
X / LINE 拡散
│
新規ユーザー流入
KPI設定(3ヶ月後目標)
| 指標 | 目標 | 根拠 |
|---|---|---|
| 月間PV | 5,000 | 類似ジョーク系サービスの実績値 |
| 生成回数/DAU | 3回以上 | 「レベルを変えてもう一回」行動 |
| SNSシェア率 | 15% | 面白い出力はシェアしたくなる |
| 投稿数(累計) | 500件 | コミュニティ感の醸成に必要な最低ライン |
😅 実装でハマったこと
1. Prismaマイグレーション中のファイルロック問題
開発サーバー(Next.js)が起動している状態で prisma generate を実行するとEPERMエラーが発生しました。Next.jsがPrismaクライアントのファイルをロックしているためです。
解決策: マイグレーション実行前に開発サーバーを停止する。シンプルですが、気づかないとハマります。
2. temperature 0.9 の「安定感」
最初は temperature: 1.0 で試していました。確かに面白い出力が出ることもあるのですが、時々日本語が破綻したり、意味不明なテキストが混入したりしました。
0.9 に下げた途端、「十分クリエイティブで、かつ読める日本語」に落ち着きました。この0.1の差は思った以上に大きいです。
3. ローカルストレージ×SSR
window.localStorage はサーバーサイドでは存在しません。FeedTabコンポーネント内でlocalStorageを直接参照するとハイドレーションエラーが発生しました。
解決策: useEffect 内でのみlocalStorageにアクセスするよう修正。SSR時は空配列を返します。
const [likedIds, setLikedIds] = useState<string[]>([])
useEffect(() => {
const stored = localStorage.getItem(LIKED_KEY)
if (stored) setLikedIds(JSON.parse(stored))
}, [])
📈 今後の展開
このサービスをどう育てていくか、いくつかアイデアがあります。
| アイデア | 難易度 | 期待効果 |
|---|---|---|
| カスタムシナリオ入力 | ★★☆ | ロングテール検索流入 |
| 言い訳の完成度スコア表示 | ★★★ | ゲーミフィケーション強化 |
| 週間ランキングリセット | ★☆☆ | リピート訪問の動機付け |
| OG画像の動的生成 | ★★☆ | SNSシェア時のクリック率向上 |
| 多言語対応(英語) | ★★★ | 海外ユーザー獲得 |
💡 まとめ
AI言い訳ジェネレーターを作って気づいたことを一言で言うと:
「役に立つ」より「シェアしたくなる」の方が難しい
プロンプト設計、UX設計、SNS導線——全てを「これ、友達に送りたい!」という体験から逆算して決めました。
エンタメ系AIサービスは実用系と違い、**品質の評価軸が「笑えるか」「驚けるか」**にあります。これはAIの創造性と人間のセンスが交差する、面白い挑戦でした。
PVとシェア数の結果は、この記事の続編でご報告します。