This commit is contained in:
root
2026-01-07 21:48:29 +05:00
parent 65d110fe40
commit bb1f280e6c
@@ -649,13 +649,28 @@ public class MarketingAnalysisService {
Map<String, Object> 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<String, Object> targetAudienceData = request.getTargetAudience();
if (targetAudienceData != null && targetAudienceData.containsKey("ageRanges")) {
@SuppressWarnings("unchecked")
List<String> ageRanges = (List<String>) 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<String, String> 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<String, Object> targetAudienceData = request.getTargetAudience();
if (targetAudienceData != null && targetAudienceData.containsKey("ageRanges")) {
@SuppressWarnings("unchecked")
List<String> ageRanges = (List<String>) 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);