.
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user