AI口コミ生成 プロンプト・API設計
| 項目 | 内容 |
|---|---|
| ステータス | 🟡 議論中 |
| 関連 | #10 AI口コミ自動生成 |
口コミ自動生成機能のプロンプト設計・API設計について。
1. プロンプト設計
1.1 プロンプト一覧
| # | プロンプト名 | 用途 | 使用LLM | 呼び出し元 |
|---|---|---|---|---|
| 1 | generate_review | 口コミ文を生成 | Claude Sonnet 4 | generate_review ノード |
| 2 | quality_check | 生成文の品質チェック | Claude Haiku 4.5 | quality_check ノード |
| 3 | regenerate_with_feedback | 品質NG時にフィードバック付きで再生成 | Claude Sonnet 4 | retry_generate ノード |
| 4 | regenerate_with_tone | トーン変更して再生成 | Claude Sonnet 4 | /api/reviews/regenerate |
1.2 口コミ生成プロンプト(generate_review)
入力変数:
| 変数名 | 型 | 説明 | 例 |
|---|---|---|---|
tone | string | 口コミのトーン | 丁寧 / カジュアル / ビジネス |
shop_name | string | 店舗名 | 銀座ヘアサロンA |
rating | int | 評価(1〜5) | 5 |
store_keywords | string | 店舗キーワード(カンマ区切り) | 丁寧な接客, 駅近, 清潔感 |
survey_answers | string | アンケート回答内容 | 下記参照 |
survey_answers フォーマット:
Q: 接客について
A: とても良い
Q: 清潔感について
A: 清潔でした
Q: また来たいと思いますか?
A: ぜひまた来たい
Q: 自由記述
A: スタッフの方がとても親切で、カウンセリングも丁寧でした。仕上がりも大満足です。System Prompt:
あなたは実際の顧客として口コミを書くアシスタントです。
アンケート回答を元に、自然で信頼性のある口コミ文を生成してください。
【トーン】{tone}
【トーン別の文体ガイド】
- 丁寧: 敬語を使用。「〜していただきました」「大変満足しております」。30〜50代の落ち着いた印象。
- カジュアル: 友人に話すような口調。「〜だった!」「めっちゃ良かった」。20〜30代の明るい印象。
- ビジネス: 客観的で簡潔。「サービスの質が高い」「費用対効果が良い」。ビジネスパーソンの印象。
【生成ルール】
1. 文字数: 200〜400文字
2. 構成: 来店のきっかけ → 体験の詳細 → 感想・まとめ の流れで書く
3. 具体性: アンケートの回答内容を具体的に反映する(ただし質問文は含めない)
4. キーワード: 指定キーワードを文脈に合わせて自然に1〜3個含める。無理に全て入れる必要はない
5. 自然さ: 以下を避ける
- 箇条書き形式
- 「まず」「次に」「最後に」のような明確な段落構成語
- 過度な褒め言葉の連続
- 全てのキーワードを機械的に詰め込む
- 「〜がおすすめです」のような宣伝口調
6. 多様性: 毎回異なる書き出し・構成・表現を使う
7. 人間らしさ: 小さな感想や個人的なエピソードを含めて、実体験感を出すHuman Prompt:
以下のアンケート回答から口コミ文を生成してください。
【店舗名】{shop_name}
【評価】★{rating}
【店舗キーワード】{store_keywords}
【回答内容】
{survey_answers}LLMパラメータ:
| パラメータ | 値 | 理由 |
|---|---|---|
| model | claude-sonnet-4-20250514 | 日本語品質◎ |
| temperature | 0.8 | 多様な文体を生成するため高めに設定 |
| max_tokens | 800 | 400文字(約600tokens)+ 余裕 |
| top_p | 0.95 | 自然な表現の幅を広げる |
出力例(トーン: 丁寧):
友人の紹介で銀座ヘアサロンAを初めて利用いたしました。駅から近く迷わずに到着できたのが助かりました。店内は清潔感があり、落ち着いた雰囲気でリラックスできます。担当していただいたスタイリストの方のカウンセリングがとても丁寧で、髪の悩みや希望をしっかり聞いてくださいました。施術中も細やかな気配りがあり、仕上がりにはとても満足しています。次回もぜひお願いしたいと思います。
1.3 品質チェックプロンプト(quality_check)
チェック項目と配点(100点満点):
| # | チェック項目 | 配点 | 合格基準 |
|---|---|---|---|
| 1 | 文字数 | 20点 | 200〜400文字: 20点 / 範囲外: 0〜10点 |
| 2 | 自然さ | 30点 | AI感がない: 30点 / 不自然: 0〜15点 |
| 3 | 不適切表現 | 20点 | 問題なし: 20点 / あり: 0点 |
| 4 | キーワード反映 | 15点 | 1〜3個自然に含む: 15点 |
| 5 | アンケート反映 | 15点 | 具体的に反映: 15点 |
合格基準: 70点以上 → 合格 / 70点未満 → 再生成(最大3回)
使用モデル: Claude Haiku 4.5(temperature: 0.1 — 安定した判定のため)
出力JSON形式:
{
"passed": true,
"score": 85,
"breakdown": {
"char_count": {"score": 20, "detail": "280文字。範囲内。"},
"naturalness": {"score": 25, "detail": "概ね自然。"},
"inappropriate": {"score": 20, "detail": "問題なし。"},
"keywords": {"score": 10, "detail": "2個含まれている。"},
"survey_reflection": {"score": 10, "detail": "回答内容が反映されている。"}
},
"feedback": ""
}1.4 再生成プロンプト
品質NG時(regenerate_with_feedback):
- generate_review のプロンプト + 前回スコア + 改善フィードバックを追加
- temperature: 0.9(前回と異なる文を出すため高め)
トーン変更時(regenerate_with_tone):
- generate_review のプロンプト + 「以前の文章の言い回しを流用しない」指示を追加
- temperature: 0.8
1.5 チューニング設定値
| 設定 | 初期値 | 調整可能範囲 |
|---|---|---|
| 合格スコア | 70 | 50〜90 |
| 最大リトライ回数 | 3 | 1〜5 |
| 文字数下限 | 200 | 100〜300 |
| 文字数上限 | 400 | 300〜600 |
app/config.pyで定数管理。将来的には管理画面から変更可能にする。
2. API設計
2.1 システム構成
フロント (Vue.js) → Laravel API (既存) → Python FastAPI (新規) → Claude API- フロント → Laravel: 認証・セッション管理は既存Laravel側で処理
- Laravel → Python: 内部HTTP通信(同一VPC内)、APIキーで認証
- Python → Anthropic API: 外部HTTPS通信
2.2 Python FastAPI エンドポイント
POST /api/v1/generate — 口コミ新規生成
Request:
{
"survey_id": 123,
"shop_name": "銀座ヘアサロンA",
"rating": 5,
"tone": "丁寧",
"store_keywords": ["丁寧な接客", "駅近", "清潔感"],
"survey_answers": [
{"question": "接客について", "answer": "とても良い"},
{"question": "自由記述", "answer": "スタッフの方がとても親切で..."}
]
}Response:
{
"status": "success",
"data": {
"review_text": "友人の紹介で銀座ヘアサロンAを初めて利用いたしました。...",
"char_count": 280,
"quality_score": 85,
"quality_breakdown": { ... },
"retry_count": 0,
"tone_used": "丁寧"
}
}タイムアウト: 30秒
POST /api/v1/regenerate — トーン変更・再生成
Request:
{
"survey_id": 123,
"shop_name": "銀座ヘアサロンA",
"rating": 5,
"tone": "カジュアル",
"store_keywords": ["丁寧な接客", "駅近", "清潔感"],
"survey_answers": [...],
"previous_tone": "丁寧",
"previous_review": "友人の紹介で銀座ヘアサロンAを..."
}GET /api/v1/health — ヘルスチェック
{"status": "ok", "version": "1.0.0", "llm_available": true}2.3 Laravel API エンドポイント
GET /api/surveys/{id}/for-review — アンケート回答取得(Python側から呼び出し)
{
"survey_id": 123,
"shop_id": 456,
"shop_name": "銀座ヘアサロンA",
"rating": 5,
"answered_at": "2026-04-08T14:30:00+09:00",
"answers": [
{"question_id": 1, "question": "接客について", "answer_type": "select", "answer": "とても良い"},
{"question_id": 5, "question": "自由記述", "answer_type": "text", "answer": "スタッフの方がとても親切で..."}
],
"store_keywords": ["丁寧な接客", "駅近", "清潔感"]
}
store_keywordsは既存の KeywordSettings(/mappy/keywords)から取得。
POST /api/reviews/generate — フロントから呼び出し(Laravel → Python内部呼び出し)
Request: {"survey_id": 123, "tone": "丁寧"}
処理フロー:
- セッション認証チェック
- survey_id からアンケートデータ取得
- shop_id から store_keywords 取得
- Python FastAPI
/api/v1/generateを呼び出し - 結果をフロントへ返却
POST /api/reviews/regenerate — トーン変更・再生成
Request: {"survey_id": 123, "tone": "カジュアル", "previous_review": "..."}
POST /api/reviews/submit — 口コミ保存 + Google投稿URL返却
Request:
{
"survey_id": 123,
"review_text": "友人の紹介で銀座ヘアサロンAを...",
"tone": "丁寧",
"edited": true,
"quality_score": 85
}Response:
{
"success": true,
"data": {
"review_id": 789,
"google_review_url": "https://search.google.com/local/writereview?placeid=XXXXX",
"message": "口コミが保存されました。下記URLからGoogleに投稿してください。"
}
}実際の投稿はユーザーが自分のGoogleアカウントで手動で行う(Google対策)。
GET /api/surveys/high-rated — 高評価アンケート一覧
Query: ?shop_id=456&min_rating=4&page=1&per_page=20
{
"data": [
{"survey_id": 123, "rating": 5, "answered_at": "2026-04-08", "summary": "接客: とても良い / 自由記述あり", "has_generated_review": false}
],
"meta": {"total": 45, "page": 1, "per_page": 20}
}2.4 DB設計(新規テーブル)
reviews テーブル:
| カラム | 型 | 説明 |
|---|---|---|
| id | bigint (PK) | |
| survey_id | bigint (FK) | アンケートID |
| shop_id | bigint (FK) | 店舗ID |
| review_text | text | 最終口コミ文 |
| tone | varchar(20) | 使用トーン |
| quality_score | int | 品質スコア |
| edited | boolean | ユーザーが手動編集したか |
| submitted_at | timestamp | Google投稿日時 |
| created_at / updated_at | timestamp |
review_generation_logs テーブル:
| カラム | 型 | 説明 |
|---|---|---|
| id | bigint (PK) | |
| survey_id | bigint (FK) | アンケートID |
| review_text | text | 生成された口コミ文 |
| tone | varchar(20) | トーン |
| quality_score | int | 品質スコア |
| quality_breakdown | json | 品質チェック詳細 |
| retry_count | int | リトライ回数 |
| generation_type | varchar(20) | initial / retry / regenerate / tone_change |
| llm_model | varchar(50) | 使用モデル |
| input_tokens / output_tokens | int | トークン数 |
| cost_yen | decimal(10,4) | コスト(円) |
| created_at | timestamp |
2.5 エラーハンドリング
| エラーコード | HTTP | 説明 |
|---|---|---|
SURVEY_NOT_FOUND | 404 | アンケートが存在しない |
LOW_RATING | 400 | 評価が低い(★3以下) |
GENERATION_FAILED | 500 | LLM生成失敗(リトライ上限超過) |
LLM_UNAVAILABLE | 503 | Anthropic APIが応答しない |
RATE_LIMIT | 429 | API制限超過 |
INVALID_TONE | 400 | 無効なトーン指定 |
2.6 セキュリティ・パフォーマンス
| 項目 | 内容 |
|---|---|
| Python API認証 | X-API-Key ヘッダーで内部APIキー検証 |
| ネットワーク | 同一VPC内のみアクセス可(外部非公開) |
| レート制限 | 1店舗あたり100件/日 |
| ログ | 全リクエストを review_generation_logs に記録 |
| 初回生成レスポンス | 10秒以内(リトライなし) |
| 最大レスポンス | 30秒以内(リトライ込み) |
| 同時リクエスト | 10件まで |
3. Google口コミポリシー対応
3.1 Googleの検出方法と対策
Googleは以下の方法でAI生成レビューを検出している:
| # | Googleの検出手法 | 説明 | 本システムの対策 |
|---|---|---|---|
| 1 | テキストパターン分析 | 同一店舗の口コミに類似した文体・構成・表現が繰り返される | temperature 0.8 + トーン3種 + 「毎回異なる書き出し・構成」指示 |
| 2 | 投稿タイミング分析 | 短時間に大量の口コミが投稿される | 1回のフローで1件のみ、バッチ生成なし |
| 3 | IP/デバイス分析 | 同一IP・デバイスから複数の口コミが投稿される | 顧客本人の端末・Googleアカウントで投稿(システムは下書きのみ) |
| 4 | アカウント行動分析 | 口コミ専用の新規アカウントや、不自然な投稿頻度 | 顧客の既存Googleアカウントで投稿 |
| 5 | 自然言語処理(NLP) | AI特有の文体(過度に整った構成、感情表現の均一さ)を検出 | プロンプトで「箇条書き禁止」「過度な褒め禁止」「人間らしさ」を指示 |
| 6 | キーワード密度分析 | 不自然なキーワード挿入(SEOスパム) | 「無理に入れない」「1〜3個まで」をプロンプトで制御 |
| 7 | 内容とアンケートの整合性 | 実体験に基づかない汎用的な内容 | 実際のアンケート回答データから生成、具体的な体験を反映 |
3.2 Googleのポリシー(2025年時点)
Google Maps の コンテンツポリシー では以下を禁止:
- 虚偽のコンテンツ: 実際の体験に基づかない口コミ
- なりすまし: 他人になりすましての投稿
- スパム: 同一内容の繰り返し、宣伝目的の投稿
- 自動生成コンテンツ: ボットやスクリプトによる自動投稿
重要: Googleが禁止しているのは「AI自動投稿」であり、「AIで下書きを作成し、本人が確認・編集して手動で投稿する」行為は明確に禁止されていない。ただしグレーゾーンのため、以下の対策を必ず実施する。
3.3 必須対策
| # | 対策 | 実装方法 | 優先度 |
|---|---|---|---|
| 1 | 顧客本人が投稿 | submit後にGoogle Review URLを表示し、顧客が自分のアカウントで投稿。API自動投稿は行わない | 必須 |
| 2 | 編集を強く推奨 | プレビュー画面で「内容をご自身の言葉で編集してから投稿してください」と目立つ形で表示 | 必須 |
| 3 | 多様な文体生成 | temperature 0.8、トーン3種、プロンプトで「毎回異なる構成」指示 | 必須 |
| 4 | キーワード自然注入 | 「無理に入れない」「1〜3個まで」をプロンプトで制御 | 必須 |
| 5 | 1件ずつ生成 | バッチ一括生成機能は作らない。1回のフローで1件のみ | 必須 |
| 6 | 投稿間隔の推奨 | UI上で「投稿は1日1件程度をおすすめします」と表示 | 推奨 |
| 7 | 品質チェックにAI検出項目 | quality_check で「AI生成と分かる表現」を減点対象に | 必須 |
| 8 | ログ記録 | 全生成・投稿をログに記録し、問題発生時に追跡可能に | 必須 |
3.4 UI上の注意表示(必須)
プレビュー画面に以下の文言を表示:
⚠ この口コミはAIが作成した下書きです。
投稿前に、ご自身の言葉で内容を確認・編集してください。
実際の体験と異なる内容は修正をお願いいたします。投稿完了画面:
口コミの下書きが保存されました。
下記のリンクからGoogleマップを開いて、ご自身のアカウントで投稿してください。
[Googleマップで口コミを書く →]