This commit is contained in:
arys
2026-01-26 22:02:02 +05:00
parent d766c10449
commit 94cccd2230
@@ -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 """