.
This commit is contained in:
@@ -1441,16 +1441,32 @@ public class MarketingAnalysisService {
|
||||
"делай глубокие сравнения нашего бренда с конкурентами (используй данные из competitorAnalysis и comparisonTable). ");
|
||||
promptBuilder.append("Каждое утверждение должно быть подкреплено конкретными данными из JSON.\n\n");
|
||||
|
||||
promptBuilder.append("ТРЕБОВАНИЯ К ОБЪЕМУ ОТЧЕТА:\n");
|
||||
promptBuilder.append("1. Напиши подробный отчет объемом не менее 2500-3000 слов\n");
|
||||
promptBuilder.append(
|
||||
"2. КРИТИЧНО: Каждый из 11 разделов (I-XI) должен содержать минимум 5-6 развернутых абзацев с глубокой аналитикой\n");
|
||||
promptBuilder
|
||||
.append("3. Каждый абзац должен быть содержательным и развернутым, не менее 4-5 предложений\n\n");
|
||||
|
||||
promptBuilder.append("ТРЕБОВАНИЯ К ОТЧЕТУ:\n");
|
||||
promptBuilder.append("1. Используй деловой стиль письма, убедительный для владельца бизнеса\n");
|
||||
promptBuilder.append("2. Добавляй аналитические инсайты и практические выводы\n");
|
||||
promptBuilder.append("3. Структурируй текст по разделам с римскими цифрами (I, II, III и т.д.)\n");
|
||||
promptBuilder.append("4. Используй данные ТОЛЬКО из переданного JSON, не выдумывай цифры\n");
|
||||
promptBuilder.append("5. Пиши конкретно и практично, избегай общих фраз\n");
|
||||
promptBuilder.append(
|
||||
"4. КРИТИЧНО: Каждый раздел (I-XI) должен содержать минимум 3-4 детальных абзаца с глубокой аналитикой\n");
|
||||
promptBuilder.append("5. Используй данные ТОЛЬКО из переданного JSON, не выдумывай цифры\n");
|
||||
promptBuilder.append("6. Пиши конкретно и практично, избегай общих фраз\n");
|
||||
"6. Обязательно используй данные из полей product, businessNiche, goal, regions для контекста анализа\n\n");
|
||||
|
||||
promptBuilder.append("АНАЛИТИЧЕСКАЯ ГЛУБИНА:\n");
|
||||
promptBuilder.append("1. Используй \"Метод Case Study\" и \"Бенчмаркинг\" для глубокого анализа\n");
|
||||
promptBuilder
|
||||
.append("2. НЕ просто перечисляй данные из JSON, а ИНТЕРПРЕТИРУЙ их с практическими выводами\n");
|
||||
promptBuilder.append(
|
||||
"7. Обязательно используй данные из полей product, businessNiche, goal, regions для контекста анализа\n\n");
|
||||
"3. Приводи конкретные примеры: если сезонность падает в июле, объясни, как именно это влияет на кэшфлоу бизнеса и предложи конкретные акции для перекрытия кассового разрыва\n");
|
||||
promptBuilder.append(
|
||||
"4. Каждое утверждение должно быть подкреплено конкретными данными из JSON с объяснением их бизнес-значения\n");
|
||||
promptBuilder.append(
|
||||
"5. Делай глубокие сравнения нашего бренда с конкурентами (используй данные из competitorAnalysis и comparisonTable)\n\n");
|
||||
|
||||
promptBuilder.append("ОБЯЗАТЕЛЬНАЯ СТРУКТУРА ОТЧЕТА:\n");
|
||||
promptBuilder.append("I. Краткое резюме (общий обзор бизнеса и ключевые выводы)\n");
|
||||
@@ -1471,19 +1487,37 @@ public class MarketingAnalysisService {
|
||||
promptBuilder.append("X. Рекомендации по контенту (типы контента для разных целей)\n");
|
||||
promptBuilder.append("XI. Итоговая стратегия: что делать в первую очередь (конкретный план действий)\n\n");
|
||||
|
||||
promptBuilder.append("КРИТИЧНО - ПЛЕЙСХОЛДЕРЫ ГРАФИКОВ:\n");
|
||||
promptBuilder.append("КРИТИЧНО - ПРИНУДИТЕЛЬНАЯ ВСТАВКА ГРАФИКОВ:\n");
|
||||
promptBuilder.append(
|
||||
"Обязательно расставь ВСЕ плейсхолдеры графиков: [[CHART_SEASONALITY]], [[CHART_AUDIENCE]], ");
|
||||
promptBuilder.append("[[CHART_COMPETITORS]], [[CHART_SWOT]], [[CHART_CHANNELS]], [[CHART_FUNNEL]]. ");
|
||||
promptBuilder.append("Каждый плейсхолдер должен быть на отдельной строке в соответствующем разделе, ");
|
||||
promptBuilder.append("как указано в структуре выше. НЕ пропускай ни одного плейсхолдера!\n\n");
|
||||
"1. Ты ОБЯЗАН вставить каждый плейсхолдер [[CHART_...]] в контекст обсуждения соответствующего раздела\n");
|
||||
promptBuilder.append(
|
||||
"2. Обязательно расставь ВСЕ плейсхолдеры графиков: [[CHART_SEASONALITY]], [[CHART_AUDIENCE]], ");
|
||||
promptBuilder.append("[[CHART_COMPETITORS]], [[CHART_SWOT]], [[CHART_CHANNELS]], [[CHART_FUNNEL]]\n");
|
||||
promptBuilder.append(
|
||||
"3. Каждый плейсхолдер должен быть на отдельной строке в соответствующем разделе, как указано в структуре выше\n");
|
||||
promptBuilder.append(
|
||||
"4. ПЕРЕД каждым графиком напиши вводный абзац (2-3 предложения), начинающийся с: \"Анализ данных на графике ниже показывает...\"\n");
|
||||
promptBuilder.append(
|
||||
"5. ПОСЛЕ каждого графика напиши интерпретацию данных (2-3 предложения), начинающуюся с: \"Из этих показателей следует, что...\"\n");
|
||||
promptBuilder.append(
|
||||
"6. НЕ пропускай ни одного плейсхолдера! Каждый график должен быть встроен в повествование раздела\n\n");
|
||||
|
||||
promptBuilder.append("ТЕМЫ ДЛЯ РАСШИРЕНИЯ В РАЗДЕЛАХ:\n");
|
||||
promptBuilder.append(
|
||||
"1. В разделе VI (SWOT-анализ): обязательно проведи анализ пересечений - объясни, как сильные стороны помогут использовать возможности, и как слабые стороны могут помешать противостоять угрозам\n");
|
||||
promptBuilder.append(
|
||||
"2. В разделе IV (Анализ целевой аудитории): для каждого сегмента опиши \"путь клиента\" (Customer Journey Map) - этапы от первого контакта до покупки и лояльности\n");
|
||||
promptBuilder.append(
|
||||
"3. В разделе V (Анализ конкурентов): проведи детальный разбор их контент-воронок - какие типы контента они используют на каждом этапе воронки, какие каналы задействуют, какие месседжи доносят\n\n");
|
||||
|
||||
promptBuilder.append("ВАЖНО:\n");
|
||||
promptBuilder.append("- Плейсхолдеры [[CHART_...]] вставляй ТОЧНО как указано выше, на отдельной строке\n");
|
||||
promptBuilder.append("- Начинай сразу с 'I. Краткое резюме', без вступлений\n");
|
||||
promptBuilder.append(
|
||||
"- НЕ добавляй секции 'Целевая аудитория', 'Рекомендации', 'Стратегия' отдельно в конце — они уже включены в структуру\n");
|
||||
promptBuilder.append("- Пиши так, чтобы отчет был полезен для принятия бизнес-решений\n\n");
|
||||
promptBuilder.append("- Пиши так, чтобы отчет был полезен для принятия бизнес-решений\n");
|
||||
promptBuilder.append(
|
||||
"- Цель: получить полноценное маркетинговое исследование на 15+ страниц с глубокой аналитикой и визуализациями\n\n");
|
||||
|
||||
promptBuilder.append("JSON ДАННЫЕ:\n");
|
||||
promptBuilder.append(jsonString);
|
||||
@@ -2860,6 +2894,36 @@ public class MarketingAnalysisService {
|
||||
logger.debug("replaceChartPlaceholders: Adding missing charts to sections");
|
||||
result = addMissingChartsToSections(result, chartsData, placeholderMap, processedPlaceholders);
|
||||
|
||||
// Финальная очистка: удаляем все оставшиеся не замененные плейсхолдеры
|
||||
// (если AI вернул их, но данных в chartsData нет)
|
||||
java.util.regex.Pattern remainingPlaceholderPattern = java.util.regex.Pattern.compile("\\[\\[CHART_\\w+\\]\\]");
|
||||
java.util.regex.Matcher matcher = remainingPlaceholderPattern.matcher(result);
|
||||
Set<String> remainingPlaceholders = new HashSet<>();
|
||||
while (matcher.find()) {
|
||||
String placeholder = matcher.group();
|
||||
remainingPlaceholders.add(placeholder);
|
||||
}
|
||||
|
||||
for (String placeholder : remainingPlaceholders) {
|
||||
// Проверяем, есть ли данные для этого плейсхолдера
|
||||
boolean hasData = false;
|
||||
ChartMapping mapping = placeholderMap.get(placeholder);
|
||||
if (mapping != null) {
|
||||
Object chartData = chartsData.get(mapping.dataKey);
|
||||
if (!isEmptyChartData(chartData)) {
|
||||
hasData = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasData) {
|
||||
logger.warn("replaceChartPlaceholders: Removing unprocessed placeholder without data: {}", placeholder);
|
||||
// Удаляем плейсхолдер с окружающими пустыми строками
|
||||
String placeholderWithNewlines = "(\\r?\\n)?\\s*" + java.util.regex.Pattern.quote(placeholder)
|
||||
+ "\\s*(\\r?\\n)?";
|
||||
result = result.replaceAll(placeholderWithNewlines, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Финальная очистка: удаляем множественные пустые строки (более 2 подряд)
|
||||
result = result.replaceAll("(\\r?\\n){3,}", "\n\n");
|
||||
|
||||
@@ -2918,21 +2982,38 @@ public class MarketingAnalysisService {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ищем раздел в тексте
|
||||
// Ищем раздел в тексте: сначала по markdown заголовкам, затем по римским цифрам
|
||||
java.util.regex.Pattern pattern = java.util.regex.Pattern
|
||||
.compile("(##+\\s*" + sectionPattern + "[^#]*)", java.util.regex.Pattern.CASE_INSENSITIVE);
|
||||
java.util.regex.Matcher matcher = pattern.matcher(result);
|
||||
boolean found = matcher.find();
|
||||
|
||||
if (matcher.find()) {
|
||||
// Если не нашли по markdown, ищем по римским цифрам (I., II., III. и т.д.)
|
||||
if (!found) {
|
||||
// Паттерн для поиска раздела по римским цифрам: I. Название, II. Название и
|
||||
// т.д.
|
||||
pattern = java.util.regex.Pattern
|
||||
.compile("(^|\\n)\\s*" + sectionPattern + "[^\\n]*(\\n|$)",
|
||||
java.util.regex.Pattern.CASE_INSENSITIVE | java.util.regex.Pattern.MULTILINE);
|
||||
matcher = pattern.matcher(result);
|
||||
found = matcher.find();
|
||||
}
|
||||
|
||||
if (found) {
|
||||
try {
|
||||
String jsonStr = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(chartData);
|
||||
String jsonBlock = "\n\n```json:" + mapping.chartKey + "\n" + jsonStr + "\n```\n";
|
||||
// Вставляем после найденного раздела, перед следующим разделом или концом
|
||||
// текста
|
||||
int insertPos = matcher.end();
|
||||
|
||||
// Ищем конец текущего раздела (начало следующего раздела или конец текста)
|
||||
String remainingText = result.substring(insertPos);
|
||||
java.util.regex.Pattern nextSectionPattern = java.util.regex.Pattern.compile("^##+\\s*[IVX]+\\.");
|
||||
|
||||
// Паттерн для поиска следующего раздела: может быть markdown заголовок или
|
||||
// римская цифра
|
||||
java.util.regex.Pattern nextSectionPattern = java.util.regex.Pattern
|
||||
.compile("^(##+\\s*[IVX]+\\.|\\s*[IVX]+\\.\\s+[А-Я])", java.util.regex.Pattern.MULTILINE);
|
||||
java.util.regex.Matcher nextSectionMatcher = nextSectionPattern.matcher(remainingText);
|
||||
|
||||
if (nextSectionMatcher.find()) {
|
||||
@@ -2940,12 +3021,29 @@ public class MarketingAnalysisService {
|
||||
int nextSectionPos = insertPos + nextSectionMatcher.start();
|
||||
result = result.substring(0, nextSectionPos) + jsonBlock + result.substring(nextSectionPos);
|
||||
} else {
|
||||
// Вставляем в конец текста
|
||||
result = result + jsonBlock;
|
||||
// Ищем конец текущего раздела по содержимому (до следующего раздела с римской
|
||||
// цифрой)
|
||||
// Ищем следующий раздел по паттерну: начало строки, римская цифра, точка,
|
||||
// пробел, заглавная буква
|
||||
java.util.regex.Pattern romanNumeralPattern = java.util.regex.Pattern
|
||||
.compile("\\n\\s*[IVX]+\\.\\s+[А-Я]", java.util.regex.Pattern.MULTILINE);
|
||||
java.util.regex.Matcher romanMatcher = romanNumeralPattern.matcher(remainingText);
|
||||
|
||||
if (romanMatcher.find()) {
|
||||
int nextSectionPos = insertPos + romanMatcher.start();
|
||||
result = result.substring(0, nextSectionPos) + jsonBlock + result.substring(nextSectionPos);
|
||||
} else {
|
||||
// Вставляем в конец текста
|
||||
result = result + jsonBlock;
|
||||
}
|
||||
}
|
||||
logger.debug("addMissingChartsToSections: Added chart {} to section matching pattern: {}",
|
||||
mapping.dataKey, sectionPattern);
|
||||
} catch (JsonProcessingException e) {
|
||||
logger.warn("Failed to serialize chart data for fallback {}: {}", mapping.dataKey, e.getMessage());
|
||||
}
|
||||
} else {
|
||||
logger.debug("addMissingChartsToSections: Section not found for pattern: {}", sectionPattern);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -264,7 +264,7 @@ public class OpenAIAnalyticsService {
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("model", modelName);
|
||||
requestBody.put("messages", List.of(message));
|
||||
requestBody.put("max_tokens", 2000);
|
||||
requestBody.put("max_tokens", 12000); // Увеличено для поддержки отчетов объемом 2500-3000 слов
|
||||
requestBody.put("temperature", 0.3);
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user