diff --git a/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java b/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java index 9ddbe1a..267dc67 100644 --- a/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java +++ b/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java @@ -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 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. Ключ - город Казахстана, Значение - индекс интереса (0-100). - // Учитывай регион "%s". Если регион вся страна - укажи топ городов. - }, - "conclusion": "Аналитический вывод о состоянии рынка (2-3 предложения)." - }, - "target_audience": { - "title": "Портрет целевой аудитории", - "metrics": { - "total_audience_size": "Оценка кол-ва потенциальных клиентов (число).", - "leading_segment_share": "Кто основной покупатель? (Пол/Возраст).", - "above_average_income_share": "Доля аудитории с доходом выше среднего (%%)." - }, - "income_by_age": { - // Map. Средний доход в KZT для групп: '18-24', '25-34', '35-44', '45+'. - // Используй реальные данные по зарплатам в Казахстане. - }, - "segments": [ - // List of objects {name, share}. Сегментируй аудиторию этой ниши (например 'Новички', 'Профи' или 'B2B', 'B2C'). - ], - "age_structure": { - // Map. Распределение по возрастам в процентах. - }, - "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. Оцени эффективность каналов. - }, - "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 ranges = (List) 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 """