Skip to content

AI口コミ生成 プロンプト・API設計

項目内容
ステータス🟡 議論中
関連#10 AI口コミ自動生成

口コミ自動生成機能のプロンプト設計・API設計について。


1. プロンプト設計

1.1 プロンプト一覧

#プロンプト名用途使用LLM呼び出し元
1generate_review口コミ文を生成Claude Sonnet 4generate_review ノード
2quality_check生成文の品質チェックClaude Haiku 4.5quality_check ノード
3regenerate_with_feedback品質NG時にフィードバック付きで再生成Claude Sonnet 4retry_generate ノード
4regenerate_with_toneトーン変更して再生成Claude Sonnet 4/api/reviews/regenerate

1.2 口コミ生成プロンプト(generate_review)

入力変数:

変数名説明
tonestring口コミのトーン丁寧 / カジュアル / ビジネス
shop_namestring店舗名銀座ヘアサロンA
ratingint評価(1〜5)5
store_keywordsstring店舗キーワード(カンマ区切り)丁寧な接客, 駅近, 清潔感
survey_answersstringアンケート回答内容下記参照

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パラメータ:

パラメータ理由
modelclaude-sonnet-4-20250514日本語品質◎
temperature0.8多様な文体を生成するため高めに設定
max_tokens800400文字(約600tokens)+ 余裕
top_p0.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形式:

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 チューニング設定値

設定初期値調整可能範囲
合格スコア7050〜90
最大リトライ回数31〜5
文字数下限200100〜300
文字数上限400300〜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:

json
{
  "survey_id": 123,
  "shop_name": "銀座ヘアサロンA",
  "rating": 5,
  "tone": "丁寧",
  "store_keywords": ["丁寧な接客", "駅近", "清潔感"],
  "survey_answers": [
    {"question": "接客について", "answer": "とても良い"},
    {"question": "自由記述", "answer": "スタッフの方がとても親切で..."}
  ]
}

Response:

json
{
  "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:

json
{
  "survey_id": 123,
  "shop_name": "銀座ヘアサロンA",
  "rating": 5,
  "tone": "カジュアル",
  "store_keywords": ["丁寧な接客", "駅近", "清潔感"],
  "survey_answers": [...],
  "previous_tone": "丁寧",
  "previous_review": "友人の紹介で銀座ヘアサロンAを..."
}

GET /api/v1/health — ヘルスチェック

json
{"status": "ok", "version": "1.0.0", "llm_available": true}

2.3 Laravel API エンドポイント

GET /api/surveys/{id}/for-review — アンケート回答取得(Python側から呼び出し)

json
{
  "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": "丁寧"}

処理フロー:

  1. セッション認証チェック
  2. survey_id からアンケートデータ取得
  3. shop_id から store_keywords 取得
  4. Python FastAPI /api/v1/generate を呼び出し
  5. 結果をフロントへ返却

POST /api/reviews/regenerate — トーン変更・再生成

Request: {"survey_id": 123, "tone": "カジュアル", "previous_review": "..."}

POST /api/reviews/submit — 口コミ保存 + Google投稿URL返却

Request:

json
{
  "survey_id": 123,
  "review_text": "友人の紹介で銀座ヘアサロンAを...",
  "tone": "丁寧",
  "edited": true,
  "quality_score": 85
}

Response:

json
{
  "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

json
{
  "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 テーブル:

カラム説明
idbigint (PK)
survey_idbigint (FK)アンケートID
shop_idbigint (FK)店舗ID
review_texttext最終口コミ文
tonevarchar(20)使用トーン
quality_scoreint品質スコア
editedbooleanユーザーが手動編集したか
submitted_attimestampGoogle投稿日時
created_at / updated_attimestamp

review_generation_logs テーブル:

カラム説明
idbigint (PK)
survey_idbigint (FK)アンケートID
review_texttext生成された口コミ文
tonevarchar(20)トーン
quality_scoreint品質スコア
quality_breakdownjson品質チェック詳細
retry_countintリトライ回数
generation_typevarchar(20)initial / retry / regenerate / tone_change
llm_modelvarchar(50)使用モデル
input_tokens / output_tokensintトークン数
cost_yendecimal(10,4)コスト(円)
created_attimestamp

2.5 エラーハンドリング

エラーコードHTTP説明
SURVEY_NOT_FOUND404アンケートが存在しない
LOW_RATING400評価が低い(★3以下)
GENERATION_FAILED500LLM生成失敗(リトライ上限超過)
LLM_UNAVAILABLE503Anthropic APIが応答しない
RATE_LIMIT429API制限超過
INVALID_TONE400無効なトーン指定

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件のみ、バッチ生成なし
3IP/デバイス分析同一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個まで」をプロンプトで制御必須
51件ずつ生成バッチ一括生成機能は作らない。1回のフローで1件のみ必須
6投稿間隔の推奨UI上で「投稿は1日1件程度をおすすめします」と表示推奨
7品質チェックにAI検出項目quality_check で「AI生成と分かる表現」を減点対象に必須
8ログ記録全生成・投稿をログに記録し、問題発生時に追跡可能に必須

3.4 UI上の注意表示(必須)

プレビュー画面に以下の文言を表示:

⚠ この口コミはAIが作成した下書きです。
投稿前に、ご自身の言葉で内容を確認・編集してください。
実際の体験と異なる内容は修正をお願いいたします。

投稿完了画面:

口コミの下書きが保存されました。
下記のリンクからGoogleマップを開いて、ご自身のアカウントで投稿してください。
[Googleマップで口コミを書く →]