fix
This commit is contained in:
@@ -646,27 +646,7 @@ public class MarketingAnalysisService {
|
||||
MarketingAnalysisResponseV2 structureTemplate = buildAnalysisV2ExampleForPrompt(request, competitors);
|
||||
String jsonTemplate = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(structureTemplate);
|
||||
|
||||
// 3. Формируем инструкцию пользователю (User Prompt)
|
||||
String userInstruction = """
|
||||
Твоя задача — сгенерировать маркетинговый анализ для: "%s" (Регион: %s).
|
||||
|
||||
НИЖЕ ПРИВЕДЕН ШАБЛОН JSON. ТЫ ДОЛЖЕН ВЕРНУТЬ JSON ТОЧНО ТАКОЙ ЖЕ СТРУКТУРЫ.
|
||||
ЗАМЕНИ ВСЕ ЗАГЛУШКИ (слова в [[...]] и нули) НА РЕАЛЬНЫЕ ДАННЫЕ ДЛЯ ЭТОЙ НИШИ.
|
||||
|
||||
!!! ВАЖНОЕ ПРАВИЛО ПРО FinancialPerformance !!!
|
||||
В объекте 'financial_performance' есть вложенные объекты (marketing_expenses, clients и т.д.).
|
||||
У них есть только ДВА поля: "month" и "year".
|
||||
ТЫ ДОЛЖЕН ЗАПИСАТЬ ТУДА ЗНАЧЕНИЯ (строки).
|
||||
Пример правильного заполнения: "month": "1.5 млн KZT", "year": "18 млн KZT".
|
||||
ЗАПРЕЩЕНО добавлять поле "value" или любые другие поля!
|
||||
|
||||
ШАБЛОН ДЛЯ ЗАПОЛНЕНИЯ:
|
||||
%s
|
||||
""".formatted(
|
||||
request.getBusinessNiche(),
|
||||
request.getRegion(),
|
||||
jsonTemplate
|
||||
);
|
||||
String userInstruction = buildDetailedInstruction(request,jsonTemplate);
|
||||
|
||||
String contextJson = objectMapper.writeValueAsString(Map.of(
|
||||
"user_request", request,
|
||||
@@ -780,182 +760,88 @@ public class MarketingAnalysisService {
|
||||
""";
|
||||
}
|
||||
|
||||
private String buildDetailedInstruction(MarketingAnalysisRequest request, List<String> competitors) {
|
||||
String niche = request.getBusinessNiche();
|
||||
String region = (request.getRegion() != null && !request.getRegion().isEmpty())
|
||||
? String.join(", ", request.getRegion())
|
||||
: "Казахстан";
|
||||
|
||||
return """
|
||||
Твоя задача — провести глубокий маркетинговый анализ для ниши: "%s" в регионе: "%s".
|
||||
|
||||
ТЫ ОБЯЗАН ИСПОЛЬЗОВАТЬ ПРЕДОСТАВЛЕННЫЕ ДАННЫЕ ИЗ 'search_data'.
|
||||
ЕСЛИ ДАННЫХ НЕТ — ИСПОЛЬЗУЙ ЛОГИКУ И ОБЩИЕ ЭКОНОМИЧЕСКИЕ ПОКАЗАТЕЛИ КАЗАХСТАНА (Инфляция, ВВП, Сезонность этой ниши).
|
||||
|
||||
ЗАПРЕТ:
|
||||
1. ЗАПРЕЩЕНО использовать примеры из фитнеса, если ниша не фитнес.
|
||||
2. ЗАПРЕЩЕНО писать "+18.5%% YoY", если это не подтверждено данными. Генерируй реалистичные цифры для ЭТОЙ ниши.
|
||||
3. Не возвращай Markdown. Только чистый JSON.
|
||||
|
||||
СТРУКТУРА ОТВЕТА (строго следуй этому формату JSON, поля должны быть заполнены аналитикой):
|
||||
|
||||
{
|
||||
"report_title": "Стратегический анализ рынка: [Название Ниши]",
|
||||
"sections": {
|
||||
"market_overview": {
|
||||
"title": "Обзор рынка",
|
||||
"metrics": {
|
||||
"online_demand_index": "Оцени спрос от 1 до 10 (строка, например 'Высокий (8/10)') на основе популярности ниши.",
|
||||
"online_potential": "Трендовое описание (например, 'Стабильный рост' или 'Сезонный спад').",
|
||||
"demand_growth_rate_yoy": "Процент роста за год (реалистичный для %s, например +5%% для хлеба, +30%% для AI).",
|
||||
"seasonality_coefficient_spring": "Коэффициент (например, 1.2). Учитывай, когда сезон у %s.",
|
||||
"seasonality_range": "Месяцы высокого сезона (например, 'Май - Август' для кондиционеров)."
|
||||
},
|
||||
"monthly_online_index": {
|
||||
// Заполни Double значениями от 0.5 до 1.5 для каждого месяца (january...december).
|
||||
// ВАЖНО: Эти цифры должны отражать РЕАЛЬНУЮ сезонность ниши "%s".
|
||||
},
|
||||
"geography": {
|
||||
// Map<String, Integer>. Ключ - город Казахстана, Значение - индекс интереса (0-100).
|
||||
// Учитывай регион "%s". Если регион вся страна - укажи топ городов.
|
||||
},
|
||||
"conclusion": "Аналитический вывод о состоянии рынка (2-3 предложения)."
|
||||
},
|
||||
"target_audience": {
|
||||
"title": "Портрет целевой аудитории",
|
||||
"metrics": {
|
||||
"total_audience_size": "Оценка кол-ва потенциальных клиентов (число).",
|
||||
"leading_segment_share": "Кто основной покупатель? (Пол/Возраст).",
|
||||
"above_average_income_share": "Доля аудитории с доходом выше среднего (%%)."
|
||||
},
|
||||
"income_by_age": {
|
||||
// Map<String, Integer>. Средний доход в KZT для групп: '18-24', '25-34', '35-44', '45+'.
|
||||
// Используй реальные данные по зарплатам в Казахстане.
|
||||
},
|
||||
"segments": [
|
||||
// List of objects {name, share}. Сегментируй аудиторию этой ниши (например 'Новички', 'Профи' или 'B2B', 'B2C').
|
||||
],
|
||||
"age_structure": {
|
||||
// Map<String, String>. Распределение по возрастам в процентах.
|
||||
},
|
||||
"conclusion": "Вывод о портрете клиента."
|
||||
},
|
||||
"competitive_environment": {
|
||||
"title": "Конкурентный ландшафт",
|
||||
"metrics": {
|
||||
"leader_market_share": "Оценка доли лидера рынка.",
|
||||
"competition_level": "Уровень конкуренции (Низкая/Средняя/Высокая/Красный океан).",
|
||||
"ad_pressure": "Давление рекламы (CPC, активность).",
|
||||
"market_leader_name": "Название типа лидеров (например, 'Маркетплейсы' или 'Сетевые игроки')."
|
||||
},
|
||||
"competitors": [
|
||||
// Используй список конкурентов из context: %s.
|
||||
// Для каждого создай объект:
|
||||
// {
|
||||
// "name": "Имя",
|
||||
// "website": "Сайт (если есть) или заглушка",
|
||||
// "visibility_score": Число 0-100,
|
||||
// "key_strengths": ["Сила 1", "Сила 2"],
|
||||
// "price_segment": "Economy/Middle/Premium"
|
||||
// }
|
||||
],
|
||||
"conclusion": "Вывод о конкуренции."
|
||||
},
|
||||
"demand_and_behavior": {
|
||||
"title": "Спрос и поведение",
|
||||
"metrics": {
|
||||
"search_volume_growth": "Рост поисковых запросов.",
|
||||
"interactions_before_purchase": "Сколько касаний нужно для покупки в нише %s (например, недвижимость - 20, еда - 1).",
|
||||
"repeat_refusal_probability": "Вероятность отказа."
|
||||
},
|
||||
"search_query_types": {
|
||||
// Типы запросов (ключ) и их доля (значение). Например: 'Информационные', 'Транзакционные'.
|
||||
},
|
||||
"decision_factors": {
|
||||
// Факторы принятия решения (Цена, Качество, Отзывы) и их вес.
|
||||
},
|
||||
"reasons_for_refusal": {
|
||||
// Почему не покупают? (Дорого, Долгая доставка и т.д.).
|
||||
},
|
||||
"conclusion": "Вывод о поведении."
|
||||
},
|
||||
"customer_journey": {
|
||||
"title": "Путь клиента",
|
||||
"funnel_metrics": {
|
||||
"ad_traffic": "Примерный трафик (строка)",
|
||||
"site_visitors": "Посетители",
|
||||
"leads": "Лиды (число)",
|
||||
"bookings": "Заказы/Записи",
|
||||
"payments": "Оплаты"
|
||||
// Рассчитай воронку с реалистичной конверсией для ниши %s.
|
||||
},
|
||||
"conversion_rates": {
|
||||
"lead_to_booking": "%% конверсии",
|
||||
"booking_to_payment": "%% конверсии",
|
||||
"lead_to_payment_total": "Общая конверсия"
|
||||
},
|
||||
"funnel_bottlenecks": {
|
||||
"lead_to_booking": "Главная проблема на этом этапе для ниши %s",
|
||||
"booking_to_payment": "Главная проблема оплаты"
|
||||
},
|
||||
"conclusion": "Вывод по воронке."
|
||||
},
|
||||
"acquisition_channels": {
|
||||
"title": "Каналы привлечения",
|
||||
"metrics": {
|
||||
"search_traffic_share": "%% поиска",
|
||||
"average_lead_cost": "Стоимость лида в KZT (CPL). Реалистичная для ниши!",
|
||||
"growth_potential": "Какой канал растет?"
|
||||
},
|
||||
"channel_shares": {
|
||||
// Доли каналов (Instagram, Google, TikTok, Kaspi).
|
||||
// ДЛЯ B2B - LinkedIn/Google. ДЛЯ B2C ТОВАРОВ - Kaspi/Instagram.
|
||||
},
|
||||
"channel_efficiency": {
|
||||
// Map<String, {cost, status}>. Оцени эффективность каналов.
|
||||
},
|
||||
"conclusion": "Вывод по каналам."
|
||||
},
|
||||
"marketing_economics": {
|
||||
"title": "Экономика маркетинга",
|
||||
"metrics": {
|
||||
"customer_acquisition_cost_cac": "CAC в KZT.",
|
||||
"repeat_sales_conversion": "Retention rate %%.",
|
||||
"roi": "Ожидаемый ROI %%."
|
||||
},
|
||||
"financial_performance": {
|
||||
// Заполни объекты MonthYearValue ("month": "...", "year": "...")
|
||||
// marketing_expenses, clients, cac, revenue_from_client, profit_from_client.
|
||||
// Цифры должны биться с нишей (недвижимость - миллионы, носки - тысячи).
|
||||
},
|
||||
"conclusion": "Финансовый вывод."
|
||||
},
|
||||
"bottlenecks": {
|
||||
"title": "Узкие места",
|
||||
"statistics": {
|
||||
// Статистика потерь (%%).
|
||||
},
|
||||
"details": {
|
||||
"landing": ["Проблема 1", "Проблема 2"],
|
||||
"lead_management": ["Проблема 1"],
|
||||
"retention": ["Проблема 1"]
|
||||
},
|
||||
"conclusion": "Вывод по проблемам."
|
||||
},
|
||||
"recommendations": {
|
||||
"title": "Рекомендации",
|
||||
"core_pillars": [
|
||||
// Список стратегий {strategy, action}.
|
||||
// ДАЙ КОНКРЕТНЫЕ СОВЕТЫ ДЛЯ НИШИ %s (Например, 'Запустить Kaspi Ads' для товаров).
|
||||
],
|
||||
"conclusion": "Финальный совет."
|
||||
}
|
||||
},
|
||||
"sources": [
|
||||
// Список URL из search_data
|
||||
]
|
||||
private String buildDetailedInstruction(MarketingAnalysisRequest request, String structureExampleJson) {
|
||||
String regionsList;
|
||||
if (request.getRegion() != null && !request.getRegion().isEmpty()) {
|
||||
regionsList = String.join(", ", request.getRegion());
|
||||
} else {
|
||||
regionsList = "Алматы, Астана, Шымкент, Караганда";
|
||||
}
|
||||
""".formatted(niche, region, niche, niche, region, niche, competitors != null ? competitors.toString() : "[]", niche, niche, niche, niche, niche);
|
||||
|
||||
// Логика возрастных групп
|
||||
String ageRangesInstruction = "";
|
||||
if (request.getTargetAudience() != null && request.getTargetAudience().containsKey("ageRanges")) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> ranges = (List<String>) request.getTargetAudience().get("ageRanges");
|
||||
if (ranges != null && !ranges.isEmpty()) {
|
||||
ageRangesInstruction = "В полях 'age_structure' и 'income_by_age' используй СТРОГО только эти возрастные группы: " + String.join(", ", ranges) + ".";
|
||||
}
|
||||
}
|
||||
|
||||
String template = """
|
||||
Твоя роль: Ты — Ведущий Маркетинговый Стратег (Senior Strategy Consultant) по рынку Казахстана.
|
||||
Задача: Провести глубокий анализ для РЕАЛЬНОГО бизнеса (B2C/B2B). Никакой воды. Только факты, цифры и применимые советы.
|
||||
|
||||
ВХОДНЫЕ ДАННЫЕ:
|
||||
Ниша: {{NICHE}}
|
||||
Продукт: {{PRODUCT}}
|
||||
Регионы для анализа: [{{REGIONS}}]
|
||||
|
||||
================================================================================
|
||||
КРИТИЧЕСКИЕ ИНСТРУКЦИИ (KZ STANDARDS & NO NULLS):
|
||||
|
||||
0. **ЗАПРЕЩЕНО ВОЗВРАЩАТЬ NULL**:
|
||||
- Каждая секция (`demand_and_behavior`, `bottlenecks`, `recommendations`) ДОЛЖНА быть заполнена.
|
||||
- Если данных нет, используй экспертную оценку или бенчмарки по нише.
|
||||
- НИКАКИХ ПУСТЫХ ПОЛЕЙ.
|
||||
- ВСЕГДА ИСПОЛЬЗУЙ ПРАВИЛЬНЫЙ JSON, ПО ПЛАНКЕ КОТОРУЮ МЫ СКИНУЛ, И ЧТО БЫ ВСЕ ПРАВАИЛЬНО
|
||||
|
||||
1. **ГЕОГРАФИЯ (Market Overview):**
|
||||
- Ты ОБЯЗАН включить в анализ (`geography`) ТОЛЬКО указанные города: [{{REGIONS}}].
|
||||
- Если указан "Весь Казахстан" или список пуст — бери Топ-5 (Алматы, Астана, Шымкент...).
|
||||
- Распределение: Алматы обычно лидер (40-50%), Астана (30%), регионы меньше.
|
||||
|
||||
2. **КОНКУРЕНТЫ (Competitive Environment):**
|
||||
- Используй данные из `market_evidence_from_search`. Найди 3-5 РЕАЛЬНЫХ игроков.
|
||||
- **ВАЖНО:** В поле `name` пиши ТОЛЬКО БРЕНД (например, "Fitnation", а не "ТОО Fitnation Group"). Без кавычек и юр. форм.
|
||||
- В поле `website` попытайся найти реальный домен или ссылку на Instagram.
|
||||
- Оценка видимости (`visibility_score`) должна быть разной (например: 95, 82, 60, 45). Не ставь всем 100.
|
||||
|
||||
3. **КАНАЛЫ (Acquisition Channels):**
|
||||
- Приоритет в Казахстане (B2C): **Instagram (Reels/Stories), TikTok, Kaspi (магазин/платежи), 2GIS (карты)**.
|
||||
- Google Ads — дорогой канал, ставь эффективность "Medium".
|
||||
- `average_lead_cost`: Пиши текстом с валютой, диапазон (напр. "2500 - 4500 KZT").
|
||||
|
||||
4. **ЭКОНОМИКА (Marketing Economics):**
|
||||
- **БЮДЖЕТ:** Рассчитывай модель для малого/среднего бизнеса (SME).
|
||||
- Бюджет на маркетинг: **500 000 - 3 000 000 KZT** в месяц. НЕ БОЛЬШЕ.
|
||||
- `cac` (Стоимость привлечения): Адекватная (2000 - 7000 KZT для масс-маркета услуг).
|
||||
- `roi`: Реалистичный (150% - 300%). Не пиши фантастические 1000% на старте.
|
||||
|
||||
5. **ТИПЫ ДАННЫХ (Строго соблюдай типы):**
|
||||
- `income_by_age`: Значения - ТОЛЬКО ЧИСЛА (Integer). Пример: 350000. (Никаких кавычек, запятых, "KZT").
|
||||
- `monthly_online_index`: Дробные числа (0.0 - 1.0).
|
||||
|
||||
6. **ИСТОЧНИКИ:**
|
||||
- Собери все ссылки из `evidence` и добавь авторитетные источники (stat.gov.kz, kase.kz, forbes.kz) в массив `sources` в самом конце.
|
||||
|
||||
================================================================================
|
||||
{{AGE_INSTRUCTION}}
|
||||
|
||||
ФОРМАТ ОТВЕТА:
|
||||
Верни СТРОГО валидный JSON, соответствующий структуре ниже.
|
||||
Замени все значения `EXAMPLE__` и `-9999` на реальные аналитические данные, специфичные для ниши {{NICHE}}.
|
||||
|
||||
ЭТАЛОННАЯ СТРУКТУРА JSON (Следуй ей неукоснительно):
|
||||
{{JSON_STRUCTURE}}
|
||||
""";
|
||||
|
||||
return template
|
||||
.replace("{{NICHE}}", request.getBusinessNiche())
|
||||
.replace("{{PRODUCT}}", request.getProduct())
|
||||
.replace("{{REGIONS}}", regionsList)
|
||||
.replace("{{AGE_INSTRUCTION}}", ageRangesInstruction)
|
||||
.replace("{{JSON_STRUCTURE}}", structureExampleJson);
|
||||
}
|
||||
private String buildJsonStructureExample() {
|
||||
return """
|
||||
|
||||
Reference in New Issue
Block a user