フロー&ガードレール設計 — AI口コミ生成
| 項目 | 内容 |
|---|---|
| ステータス | 🟡 検討中 |
| 関連 | AI口コミ生成 |
1. ガードレール概要
システムには品質と安全性を確保する5層の保護がある:
2. ① Rate Limit — 頻度制限
設計
| 項目 | 内容 |
|---|---|
| 制限 | 100回生成/日/店舗 |
| カウント対象 | generate-review + regenerate-review (Sonnet呼び出し、コスト発生) |
| 適用方法 | mappy_review_generation_logsのDBカウント |
| リセット | 日次(サーバー日付基準) |
ロジック
実装
php
private function checkRateLimit(int $shopId): void
{
$todayCount = ReviewGenerationLog::where('shop_id', $shopId)
->whereIn('generation_type', ['initial', 'regenerate', 'tone_change'])
->whereDate('created_at', today())
->count();
if ($todayCount >= config('services.review_generator.daily_limit', 100)) {
throw new RateLimitExceededException();
}
}なぜ100/日なのか?
| 店舗規模 | アンケート/日 | 生成/アンケート | 合計/日 |
|---|---|---|---|
| 小(1店舗) | 5-10 | 約2回(1生成 + 1再生成) | 10-20 |
| 大(5店舗) | 20-50 | 約2回 | 40-100 |
100は大規模店舗に十分。調整が必要な場合 →
.envのREVIEW_DAILY_LIMIT。
3. ② Promptガードレール — Prompt内の制約
LLMが望ましくない出力を生成するのを防ぐため、制約をsystem promptに直接埋め込む:
品質の制約
| # | 制約 | Prompt内の記述 | 目的 |
|---|---|---|---|
| 1 | 文字数200-400 | 文字数: 200〜400文字 | 短すぎ/長すぎを防止 |
| 2 | 固定構成 | 来店のきっかけ → 体験の詳細 → 感想・まとめ | ロジックの確保 |
| 3 | キーワードの自然さ | 無理に全て入れる必要はない | キーワード詰め込み防止 |
| 4 | 多様性 | 毎回異なる書き出し・構成・表現を使う | 口コミ間の重複防止 |
AI検出防止の制約
| # | 制約 | Prompt内の記述 | 目的 |
|---|---|---|---|
| 5 | 箇条書き禁止 | 箇条書き形式を避ける | AIは箇条書きを生成しがち |
| 6 | 機械的な接続詞禁止 | 「まず」「次に」「最後に」を避ける | 典型的なAIの特徴 |
| 7 | 過度な賞賛禁止 | 過度な褒め言葉の連続を避ける | 不自然 |
| 8 | 宣伝口調禁止 | 「〜がおすすめです」を避ける | Googleポリシー違反 |
| 9 | 人間的な要素の追加 | 小さな感想や個人的なエピソードを含める | 実体験感を演出 |
toneの制約
| Tone | Prompt内の制約 |
|---|---|
| 丁寧 | 敬語を使用。30〜50代の落ち着いた印象 |
| カジュアル | 友人に話すような口調。20〜30代の明るい印象 |
| ビジネス | 客観的で簡潔。ビジネスパーソンの印象 |
4. ③ 品質チェック — ハイブリッド品質検査
フロー概要
ステップ1: ルールベース(即時、無料)
| # | 基準 | 最大点 | ロジック | 早期失敗 |
|---|---|---|---|---|
| 1 | 文字数 (200-400) | 20 | mb_strlen() | 100未満または600超 → LLMスキップ |
| 2 | キーワード反映 (1-3語) | 15 | str_contains() | — |
| ステップ1合計 | 35 | < 15 → LLMスキップ、即座にリトライ |
判定マトリクス:
| 文字数 | キーワード検出 | ルール点数 | アクション |
|---|---|---|---|
| 280 (OK) | 2/3 | 30 | → LLMチェックへ |
| 150 (NG) | 1/3 | 5 | → LLMスキップ、即座にリトライ |
| 350 (OK) | 0/3 | 20 | → LLMチェックへ |
| 50 (NG) | 0/3 | 0 | → LLMスキップ、即座にリトライ |
ステップ2: LLMチェック (Haiku 4.5)
| # | 基準 | 最大点 | チェック内容 |
|---|---|---|---|
| 3 | 自然さ(AIっぽくない) | 30 | 構成、接続詞、多様性 |
| 4 | 不適切な表現 | 20 | 宣伝、誇張、事実と異なる |
| 5 | 焦点の反映(選択された項目) | 15 | 指定された最高星数の項目に口コミが集中しているか |
| ステップ2合計 | 65 |
点数の集計
| 最大 | 最低合格点 | |
|---|---|---|
| ステップ1 (ルール) | 35 | — |
| ステップ2 (LLM) | 65 | — |
| 合計 | 100 | >= 70 |
quality_breakdown JSONフォーマット
合格(LLMチェックあり):
json
{
"rule_based": {
"char_count": { "score": 20, "detail": "280文字。範囲内。" },
"keywords": { "score": 10, "detail": "2/3 keyword found" }
},
"llm": {
"naturalness": { "score": 25, "detail": "概ね自然。" },
"inappropriate": { "score": 20, "detail": "問題なし。" },
"topic_reflection": { "score": 12, "detail": "指定項目(接客)が中心に反映されている。" },
"feedback": ""
},
"total": 87,
"passed": true
}失敗(LLMスキップ):
json
{
"rule_based": {
"char_count": { "score": 0, "detail": "52文字。範囲外。" },
"keywords": { "score": 5, "detail": "1/3 keyword found" }
},
"llm": null,
"total": 5,
"passed": false
}5. ④ リトライ戦略
フロー
リトライ詳細
| 回数 | Temperature | 特徴 |
|---|---|---|
| 1 (初回) | 0.8 | 通常の生成 |
| 2 (リトライ1) | 0.9 | 高く設定 → より異なる出力 |
| 3 (リトライ2) | 0.9 | 前回からの具体的なフィードバック付き |
リトライに渡す情報
【前回のスコア】52/100
【改善フィードバック】「まず」「次に」の接続詞が目立つ。箇条書き的な構成を避け...
※ 前回と異なる書き出し・構成で書いてください。予測統計
| 結果 | 割合 |
|---|---|
| 1回目で合格 | ~80% |
| リトライ1後に合格 | ~15% |
| リトライ2後に合格 | ~4% |
| 失敗(3回で終了) | ~1% |
6. ⑤ エラーフォールバック — エラー処理
原則
AIが失敗した場合、常にGoogle Reviewボタンにフォールバック。 顧客のフローを決してブロックしない。
判定マトリクス
| エラー | HTTP | フロントエンド表示 | フローをブロック? |
|---|---|---|---|
| Anthropic APIタイムアウト | 503 | 通常のお礼画面 + Google Reviewボタン | しない |
| 生成失敗(3回リトライ終了) | 500 | "生成に失敗しました" + "もう一度試す"ボタン + Google Reviewボタン | しない |
| Rate limit | 429 | "本日の生成上限に達しました" + Google Reviewボタン | しない |
| 無効なtone | 400 | 発生しない(UIが3つのオプションのみ提供) | — |
| アンケートが存在しない | 404 | 発生しない(送信直後のデータを使用) | — |
| JSONパース失敗(quality-check) | — | 自動リトライ(スコア0) | しない |
フロントエンドフロー
7. Googleポリシー対策
GoogleがAI口コミを検出する方法
| # | 方法 | システムでの対策 |
|---|---|---|
| 1 | テキストパターン分析 — 同一店舗で繰り返し | temperature 0.8 + 実際の評価項目に基づく内容 + "毎回異なる構成" |
| 2 | 投稿時間分析 — 連続投稿 | 1件/フロー、バッチ生成なし |
| 3 | IP/デバイス分析 — 同一IPから複数投稿 | 顧客が自身のデバイス/アカウントから投稿 |
| 4 | NLP AI検出 — 文体が完璧すぎる | Promptで箇条書き・過度な賞賛を禁止、人間的要素を追加 |
| 5 | キーワード密度 — SEOスパム | "1〜3個"、"無理に入れない" |
| 6 | 一般的な内容 — 実体験がない | 実際のアンケートデータから生成 |
UIでの必須対策
口コミ画面(必須):
⚠ この文章はお客様のアンケート回答をもとにAIが作成した下書きです。
投稿される口コミはお客様ご自身の感想として公開されます。
実際のご体験に合っているかご確認のうえ、必要に応じて編集してからご投稿ください。投稿ボタン押下後:
口コミの下書きが保存されました。
下記のリンクからGoogleマップを開いて、ご自身のアカウントで投稿してください。
[Googleマップで口コミを書く →]コンプライアンスチェックリスト
| # | 対策 | 設計済み? |
|---|---|---|
| 1 | 顧客が自分で投稿(自動投稿なし) | ✅ Submit → Google Mapsにリダイレクト |
| 2 | 編集を推奨 | ✅ AI警告 + textareaで編集可能 |
| 3 | 多様な文体 | ✅ temperature 0.8 + 評価項目に基づく内容 + トーン変更 |
| 4 | 自然なキーワード | ✅ "1〜3個" + "無理に入れない" |
| 5 | 1件/フロー | ✅ バッチ生成なし |
| 6 | 品質チェックでAI検出 | ✅ naturalness score (30点) |
| 7 | 全ログ記録 | ✅ mappy_review_generation_logs |
8. モニタリング & メトリクス
監視すべきメトリクス
| メトリクス | クエリ | アラート条件 |
|---|---|---|
| 1回目合格率 | WHERE retry_count = 0 AND quality_score >= 70 | < 70% |
| 完全失敗率 | WHERE retry_count >= 3 | > 5% |
| 日次コスト | SUM(cost_yen) GROUP BY DATE | > ¥1,000 |
| 平均レイテンシ | リクエスト → レスポンスの時間 | > 15s |
| Rate limitヒット数 | 429レスポンスのカウント | > 10/日 |
既存のログテーブル
全メトリクスはmappy_review_generation_logsからクエリ可能:
quality_score,retry_count→ 合格/失敗率cost_yen→ コストinput_tokens,output_tokens→ usagecreated_at→ レイテンシ(リクエストログと組み合わせ)