From bb1f280e6c87985c6e614fe97641bf9a8ae6ac08 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 7 Jan 2026 21:48:29 +0500 Subject: [PATCH] . --- .../service/MarketingAnalysisService.java | 56 ++++++++++++++++--- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java b/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java index abfb515..390e3b8 100644 --- a/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java +++ b/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java @@ -649,13 +649,28 @@ public class MarketingAnalysisService { Map researchPack = buildResearchPack(request); // 2) Build strict schema instructions + provide an example JSON structure - String exampleJson = objectMapper.writeValueAsString(buildAnalysisV2ExampleForPrompt()); + String exampleJson = objectMapper.writeValueAsString(buildAnalysisV2ExampleForPrompt(request)); String systemPrompt = "" + "Ты — партнер Big Four (управленческий консалтинг) и senior marketing analyst.\n" + "Пиши уверенно, структурно, с причинно-следственной логикой, без «воды».\n" + "КРИТИЧЕСКИ ВАЖНО: Твой ответ должен быть ТОЛЬКО валидный JSON, без markdown, без ```.\n"; + // Извлекаем возрастные группы из запроса для инструкции + String ageRangesInstruction = ""; + Map targetAudienceData = request.getTargetAudience(); + if (targetAudienceData != null && targetAudienceData.containsKey("ageRanges")) { + @SuppressWarnings("unchecked") + List ageRanges = (List) targetAudienceData.get("ageRanges"); + if (ageRanges != null && !ageRanges.isEmpty()) { + ageRangesInstruction = "\n" + + "7) КРИТИЧЕСКИ ВАЖНО для target_audience.age_structure: Используй ТОЛЬКО те возрастные группы, которые указаны в запросе (request.targetAudience.ageRanges). " + + "Указанные возрастные группы: " + String.join(", ", ageRanges) + ". " + + "НЕ добавляй возрастные группы, которых нет в запросе. " + + "Если в запросе указаны только \"25-34\" и \"35-44\", то в age_structure должны быть ТОЛЬКО эти две группы.\n"; + } + } + String instruction = "" + "Сгенерируй один JSON-объект, который СТРОГО соответствует схеме MarketingAnalysisResponseV2.\n" + "Обязательные правила:\n" @@ -671,8 +686,9 @@ public class MarketingAnalysisService { + " - market_overview.geography => object with city names as keys and interest_index as integer values: {\"Алматы\": 20, \"Астана\": 30}\n" + "5) Пиши на русском языке.\n" + "6) НИКОГДА не копируй значения из примера. В примере намеренно стоят плейсхолдеры, " - + "которые начинаются с 'EXAMPLE__' и числа -9999/-9999.0 — в твоем ответе их быть НЕ должно.\n\n" - + "Ниже пример правильной структуры (пример только для формы/полей; все значения должны быть заменены на значения из evidence):\n" + + "которые начинаются с 'EXAMPLE__' и числа -9999/-9999.0 — в твоем ответе их быть НЕ должно." + + ageRangesInstruction + + "\nНиже пример правильной структуры (пример только для формы/полей; все значения должны быть заменены на значения из evidence):\n" + exampleJson; String contextJson = objectMapper.writeValueAsString(Map.of( @@ -833,8 +849,11 @@ public class MarketingAnalysisService { * Example payload used ONLY for prompting the AI with the expected JSON * schema/shape. * This is not returned to the client. + * + * @param request Запрос на анализ для динамического формирования примера + * age_structure */ - private MarketingAnalysisResponseV2 buildAnalysisV2ExampleForPrompt() { + private MarketingAnalysisResponseV2 buildAnalysisV2ExampleForPrompt(MarketingAnalysisRequest request) { MarketingAnalysisResponseV2 response = new MarketingAnalysisResponseV2(); response.setReportTitle("EXAMPLE__REPORT_TITLE"); @@ -877,12 +896,31 @@ public class MarketingAnalysisService { "EXAMPLE__AUDIENCE_SEGMENT_SHARE_2"), new MarketingAnalysisResponseV2.AudienceSegment("EXAMPLE__AUDIENCE_SEGMENT_3", "EXAMPLE__AUDIENCE_SEGMENT_SHARE_3"))); + // Динамически формируем age_structure на основе реальных данных из запроса Map ageStructure = new LinkedHashMap<>(); - ageStructure.put("18-24", "EXAMPLE__AGE_18_24_SHARE"); - ageStructure.put("25-34", "EXAMPLE__AGE_25_34_SHARE"); - ageStructure.put("35-44", "EXAMPLE__AGE_35_44_SHARE"); - ageStructure.put("45-54", "EXAMPLE__AGE_45_54_SHARE"); - ageStructure.put("55_plus", "EXAMPLE__AGE_55_PLUS_SHARE"); + Map targetAudienceData = request.getTargetAudience(); + if (targetAudienceData != null && targetAudienceData.containsKey("ageRanges")) { + @SuppressWarnings("unchecked") + List ageRanges = (List) targetAudienceData.get("ageRanges"); + if (ageRanges != null && !ageRanges.isEmpty()) { + // Используем только те возрастные группы, которые указаны в запросе + for (String ageRange : ageRanges) { + // Нормализуем формат ключа: "55+" -> "55_plus", остальные оставляем как есть + String normalizedKey = ageRange.replace("+", "_plus"); + // Формируем плейсхолдер для примера + String placeholderKey = normalizedKey.replace("-", "_").toUpperCase(); + ageStructure.put(normalizedKey, "EXAMPLE__AGE_" + placeholderKey + "_SHARE"); + } + } else { + // Если возрастные группы не указаны, используем дефолтный пример + ageStructure.put("25-34", "EXAMPLE__AGE_25_34_SHARE"); + ageStructure.put("35-44", "EXAMPLE__AGE_35_44_SHARE"); + } + } else { + // Если targetAudience не указан, используем дефолтный пример + ageStructure.put("25-34", "EXAMPLE__AGE_25_34_SHARE"); + ageStructure.put("35-44", "EXAMPLE__AGE_35_44_SHARE"); + } targetAudience.setAgeStructure(ageStructure); targetAudience.setConclusion("EXAMPLE__TARGET_AUDIENCE_CONCLUSION"); sections.setTargetAudience(targetAudience);