.
This commit is contained in:
@@ -0,0 +1,112 @@
|
|||||||
|
# Интеграция OpenAI API
|
||||||
|
|
||||||
|
## Обзор
|
||||||
|
|
||||||
|
Создан новый сервис `OpenAIAnalyticsService` для замены `OllamaAnalyticsService`. Новый сервис использует OpenAI API для анализа текста и генерации контента.
|
||||||
|
|
||||||
|
## Основные изменения
|
||||||
|
|
||||||
|
### 1. Новый сервис OpenAIAnalyticsService
|
||||||
|
|
||||||
|
- **Файл**: `src/main/java/kz/konturai/parser/service/OpenAIAnalyticsService.java`
|
||||||
|
- **Функциональность**: Аналогична `OllamaAnalyticsService`, но использует OpenAI API
|
||||||
|
- **Методы**:
|
||||||
|
- `analyzeText(String text)` - анализ текста с извлечением саммари, тегов, тональности и сущностей
|
||||||
|
- `analyzeText(String text, String language)` - анализ с указанием языка
|
||||||
|
- `generateWithInstruction(String text, String instruction)` - генерация текста по инструкции
|
||||||
|
- `generateWithInstruction(String text, String instruction, String language)` - генерация с указанием языка
|
||||||
|
|
||||||
|
### 2. Обновленные сервисы
|
||||||
|
|
||||||
|
Все сервисы, которые использовали `OllamaAnalyticsService`, теперь используют `OpenAIAnalyticsService`:
|
||||||
|
|
||||||
|
- `ReportSynthesisService`
|
||||||
|
- `ReportGenerationService`
|
||||||
|
- `KursivParserService`
|
||||||
|
- `KapitalParserService`
|
||||||
|
- `LsmParserService`
|
||||||
|
- `RbcParserService`
|
||||||
|
- `VedomostiParserService`
|
||||||
|
|
||||||
|
### 3. Конфигурация
|
||||||
|
|
||||||
|
В `application.properties` уже настроены параметры для OpenAI:
|
||||||
|
|
||||||
|
```properties
|
||||||
|
# OpenAI Configuration
|
||||||
|
openai.api.key=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
|
||||||
|
openai.api.url=https://api.openai.com/v1/chat/completions
|
||||||
|
openai.model.name=gpt-4o-mini
|
||||||
|
openai.timeoutMs=90000
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Тестовый контроллер
|
||||||
|
|
||||||
|
Создан `OpenAITestController` для тестирования функциональности:
|
||||||
|
|
||||||
|
- `POST /api/openai/analyze` - анализ текста
|
||||||
|
- `POST /api/openai/generate` - генерация по инструкции
|
||||||
|
- `GET /api/openai/test` - тест подключения
|
||||||
|
|
||||||
|
## Использование
|
||||||
|
|
||||||
|
### Анализ текста
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Autowired
|
||||||
|
private OpenAIAnalyticsService openAIAnalyticsService;
|
||||||
|
|
||||||
|
// Анализ текста на русском языке
|
||||||
|
MarketItem.Analytics analytics = openAIAnalyticsService.analyzeText(text, "ru");
|
||||||
|
|
||||||
|
// Получение саммари
|
||||||
|
String summary = analytics.getSummary();
|
||||||
|
|
||||||
|
// Получение тегов
|
||||||
|
String[] tags = analytics.getTags();
|
||||||
|
|
||||||
|
// Получение тональности
|
||||||
|
String sentiment = analytics.getSentiment();
|
||||||
|
|
||||||
|
// Получение сущностей
|
||||||
|
Map<String, Object> entities = analytics.getEntities();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Генерация текста
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Генерация отчета
|
||||||
|
String instruction = "Напиши краткий отчет на основе следующих данных:";
|
||||||
|
String generatedText = openAIAnalyticsService.generateWithInstruction(data, instruction, "ru");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Преимущества OpenAI API
|
||||||
|
|
||||||
|
1. **Высокое качество**: GPT-4o-mini обеспечивает более качественный анализ и генерацию текста
|
||||||
|
2. **Надежность**: Стабильная работа API без необходимости локального сервера
|
||||||
|
3. **Масштабируемость**: Легко масштабируется под нагрузку
|
||||||
|
4. **Многоязычность**: Отличная поддержка русского и английского языков
|
||||||
|
5. **Консистентность**: Более предсказуемые результаты
|
||||||
|
|
||||||
|
## Миграция
|
||||||
|
|
||||||
|
Все существующие вызовы `OllamaAnalyticsService` автоматически заменены на `OpenAIAnalyticsService`. API остается совместимым, поэтому дополнительных изменений в коде не требуется.
|
||||||
|
|
||||||
|
## Тестирование
|
||||||
|
|
||||||
|
Для тестирования нового сервиса:
|
||||||
|
|
||||||
|
1. Запустите приложение
|
||||||
|
2. Откройте `GET /api/openai/test` для проверки подключения
|
||||||
|
3. Используйте `POST /api/openai/analyze` для анализа текста
|
||||||
|
4. Используйте `POST /api/openai/generate` для генерации контента
|
||||||
|
|
||||||
|
## Настройка
|
||||||
|
|
||||||
|
Убедитесь, что в `application.properties` указан корректный API ключ OpenAI:
|
||||||
|
|
||||||
|
```properties
|
||||||
|
openai.api.key=your-openai-api-key-here
|
||||||
|
```
|
||||||
|
|
||||||
|
Модель по умолчанию: `gpt-4o-mini` (можно изменить в настройках).
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package kz.konturai.parser.controller;
|
||||||
|
|
||||||
|
import kz.konturai.parser.model.MarketItem;
|
||||||
|
import kz.konturai.parser.service.OpenAIAnalyticsService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/openai")
|
||||||
|
public class OpenAITestController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OpenAIAnalyticsService openAIAnalyticsService;
|
||||||
|
|
||||||
|
@PostMapping("/analyze")
|
||||||
|
public MarketItem.Analytics analyzeText(@RequestParam String text,
|
||||||
|
@RequestParam(defaultValue = "ru") String language) {
|
||||||
|
return openAIAnalyticsService.analyzeText(text, language);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/generate")
|
||||||
|
public String generateWithInstruction(@RequestParam String text,
|
||||||
|
@RequestParam String instruction,
|
||||||
|
@RequestParam(defaultValue = "ru") String language) {
|
||||||
|
return openAIAnalyticsService.generateWithInstruction(text, instruction, language);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/test")
|
||||||
|
public String testConnection() {
|
||||||
|
String testText = "Это тестовая новость о развитии технологий в Казахстане. Компания Kaspi Bank объявила о новых инвестициях в цифровые сервисы.";
|
||||||
|
MarketItem.Analytics analytics = openAIAnalyticsService.analyzeText(testText);
|
||||||
|
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
result.append("OpenAI Analytics Service Test:\n");
|
||||||
|
result.append("Summary: ").append(analytics.getSummary()).append("\n");
|
||||||
|
result.append("Sentiment: ").append(analytics.getSentiment()).append("\n");
|
||||||
|
result.append("Tags: ").append(String.join(", ", analytics.getTags())).append("\n");
|
||||||
|
result.append("Entities: ").append(analytics.getEntities()).append("\n");
|
||||||
|
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,7 +33,7 @@ public class KapitalParserService implements ParserService {
|
|||||||
private MarketItemRepository marketItemRepository;
|
private MarketItemRepository marketItemRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private OllamaAnalyticsService analyticsService;
|
private OpenAIAnalyticsService analyticsService;
|
||||||
|
|
||||||
@Value("${rss.kapital.url}")
|
@Value("${rss.kapital.url}")
|
||||||
private String rssFeedUrl;
|
private String rssFeedUrl;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public class KursivParserService implements ParserService {
|
|||||||
private MarketItemRepository marketItemRepository;
|
private MarketItemRepository marketItemRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private OllamaAnalyticsService analyticsService;
|
private OpenAIAnalyticsService analyticsService;
|
||||||
|
|
||||||
@Value("${rss.feed.url}")
|
@Value("${rss.feed.url}")
|
||||||
private String rssFeedUrl;
|
private String rssFeedUrl;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public class LsmParserService implements ParserService {
|
|||||||
private MarketItemRepository marketItemRepository;
|
private MarketItemRepository marketItemRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private OllamaAnalyticsService analyticsService;
|
private OpenAIAnalyticsService analyticsService;
|
||||||
|
|
||||||
@Value("${rss.lsm.url}")
|
@Value("${rss.lsm.url}")
|
||||||
private String rssFeedUrl;
|
private String rssFeedUrl;
|
||||||
|
|||||||
@@ -0,0 +1,217 @@
|
|||||||
|
package kz.konturai.parser.service;
|
||||||
|
|
||||||
|
import kz.konturai.parser.model.MarketItem;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import org.springframework.core.ParameterizedTypeReference;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import reactor.netty.http.client.HttpClient;
|
||||||
|
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class OpenAIAnalyticsService {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(OpenAIAnalyticsService.class);
|
||||||
|
|
||||||
|
private final WebClient webClient;
|
||||||
|
|
||||||
|
@Value("${openai.model.name:gpt-4o-mini}")
|
||||||
|
private String modelName;
|
||||||
|
|
||||||
|
@Value("${openai.timeoutMs:90000}")
|
||||||
|
private long timeoutMs;
|
||||||
|
|
||||||
|
@Value("${openai.api.key}")
|
||||||
|
private String apiKey;
|
||||||
|
|
||||||
|
public OpenAIAnalyticsService(@Value("${openai.api.url:https://api.openai.com/v1/chat/completions}") String openaiUrl) {
|
||||||
|
HttpClient httpClient = HttpClient.create()
|
||||||
|
.compress(true)
|
||||||
|
.responseTimeout(Duration.ofMillis(timeoutMs));
|
||||||
|
|
||||||
|
this.webClient = WebClient.builder()
|
||||||
|
.baseUrl(openaiUrl)
|
||||||
|
.clientConnector(new ReactorClientHttpConnector(httpClient))
|
||||||
|
.defaultHeader("Authorization", "Bearer " + apiKey)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MarketItem.Analytics analyzeText(String text) {
|
||||||
|
return analyzeText(text, "ru");
|
||||||
|
}
|
||||||
|
|
||||||
|
public MarketItem.Analytics analyzeText(String text, String language) {
|
||||||
|
MarketItem.Analytics analytics = new MarketItem.Analytics();
|
||||||
|
boolean isRussian = "ru".equalsIgnoreCase(language) || "russian".equalsIgnoreCase(language);
|
||||||
|
|
||||||
|
try {
|
||||||
|
String summaryPrompt = isRussian
|
||||||
|
? "Напиши краткое саммари следующей новостной статьи на русском языке. Ответ должен содержать только саммари из 3-4 предложений, без лишних вступлений. Статья: "
|
||||||
|
: "Write a brief summary of the following news article in English. The response should contain only a summary of 3-4 sentences, without any unnecessary introductions. Article: ";
|
||||||
|
String summary = generate(text, summaryPrompt);
|
||||||
|
analytics.setSummary(summary);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("Failed to get summary from OpenAI: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String tagsPrompt = isRussian
|
||||||
|
? "Извлеки 5-7 ключевых слов или тегов из текста новостной статьи. В ответе дай только список тегов через запятую, без нумерации и заголовков. Статья: "
|
||||||
|
: "Extract 5-7 key words or tags from the news article text. In your response, provide only a list of tags separated by commas, without numbering or headers. Article: ";
|
||||||
|
String tagsCsv = generate(text, tagsPrompt);
|
||||||
|
String[] tags = tagsCsv == null ? new String[] {} : tagsCsv.replace("\n", " ").split("\\s*,\\s*");
|
||||||
|
analytics.setTags(tags);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("Failed to get tags from OpenAI: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String sentimentPrompt = isRussian
|
||||||
|
? "Определи тональность текста новостной статьи. В ответе дай только одно слово латиницей: positive, negative или neutral. Статья: "
|
||||||
|
: "Determine the sentiment of the news article text. In your response, provide only one word in English: positive, negative, or neutral. Article: ";
|
||||||
|
String sentiment = generate(text, sentimentPrompt);
|
||||||
|
if (sentiment != null) {
|
||||||
|
sentiment = sentiment.trim().toLowerCase();
|
||||||
|
}
|
||||||
|
analytics.setSentiment(sentiment);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("Failed to get sentiment from OpenAI: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String entitiesPrompt = isRussian
|
||||||
|
? "Извлеки из текста имена людей, названия компаний и географические локации. В ответе дай только JSON объект следующей структуры: {\"persons\": [], \"companies\": [], \"locations\": []}. Статья: "
|
||||||
|
: "Extract from the text names of people, company names, and geographic locations. In your response, provide only a JSON object with the following structure: {\"persons\": [], \"companies\": [], \"locations\": []}. Article: ";
|
||||||
|
String entitiesJson = generate(text, entitiesPrompt);
|
||||||
|
Map<String, Object> entities = new HashMap<>();
|
||||||
|
if (entitiesJson != null && entitiesJson.trim().startsWith("{")) {
|
||||||
|
try {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
entities = mapper.readValue(entitiesJson, new TypeReference<Map<String, Object>>() {
|
||||||
|
});
|
||||||
|
} catch (Exception parseEx) {
|
||||||
|
entities.put("raw", entitiesJson);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
analytics.setEntities(entities);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("Failed to get entities from OpenAI: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return analytics;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generate(String text, String instructionPrefix) {
|
||||||
|
String prompt = instructionPrefix + text;
|
||||||
|
|
||||||
|
Map<String, Object> message = new HashMap<>();
|
||||||
|
message.put("role", "user");
|
||||||
|
message.put("content", prompt);
|
||||||
|
|
||||||
|
Map<String, Object> requestBody = new HashMap<>();
|
||||||
|
requestBody.put("model", modelName);
|
||||||
|
requestBody.put("messages", List.of(message));
|
||||||
|
requestBody.put("max_tokens", 1000);
|
||||||
|
requestBody.put("temperature", 0.3);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Map<String, Object> response = this.webClient.post()
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.bodyValue(requestBody)
|
||||||
|
.retrieve()
|
||||||
|
.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
|
||||||
|
})
|
||||||
|
.onErrorResume(err -> {
|
||||||
|
logger.warn("OpenAI request failed: {}", err.getMessage());
|
||||||
|
return Mono.empty();
|
||||||
|
})
|
||||||
|
.block(Duration.ofMillis(timeoutMs));
|
||||||
|
|
||||||
|
if (response == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Map<String, Object>> choices = (List<Map<String, Object>>) response.get("choices");
|
||||||
|
if (choices != null && !choices.isEmpty()) {
|
||||||
|
Map<String, Object> firstChoice = choices.get(0);
|
||||||
|
Map<String, Object> messageObj = (Map<String, Object>) firstChoice.get("message");
|
||||||
|
if (messageObj != null) {
|
||||||
|
Object content = messageObj.get("content");
|
||||||
|
return content == null ? null : String.valueOf(content).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("Error calling OpenAI generate: {}", e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generateWithInstruction(String text, String instruction) {
|
||||||
|
return generateWithInstruction(text, instruction, "ru");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generateWithInstruction(String text, String instruction, String language) {
|
||||||
|
String prompt = instruction + "\n\n" + text;
|
||||||
|
|
||||||
|
Map<String, Object> message = new HashMap<>();
|
||||||
|
message.put("role", "user");
|
||||||
|
message.put("content", prompt);
|
||||||
|
|
||||||
|
Map<String, Object> requestBody = new HashMap<>();
|
||||||
|
requestBody.put("model", modelName);
|
||||||
|
requestBody.put("messages", List.of(message));
|
||||||
|
requestBody.put("max_tokens", 2000);
|
||||||
|
requestBody.put("temperature", 0.3);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Map<String, Object> response = this.webClient.post()
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.bodyValue(requestBody)
|
||||||
|
.retrieve()
|
||||||
|
.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
|
||||||
|
})
|
||||||
|
.onErrorResume(err -> {
|
||||||
|
logger.warn("OpenAI request failed: {}", err.getMessage());
|
||||||
|
return Mono.empty();
|
||||||
|
})
|
||||||
|
.block(Duration.ofMillis(timeoutMs));
|
||||||
|
|
||||||
|
if (response == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Map<String, Object>> choices = (List<Map<String, Object>>) response.get("choices");
|
||||||
|
if (choices != null && !choices.isEmpty()) {
|
||||||
|
Map<String, Object> firstChoice = choices.get(0);
|
||||||
|
Map<String, Object> messageObj = (Map<String, Object>) firstChoice.get("message");
|
||||||
|
if (messageObj != null) {
|
||||||
|
Object content = messageObj.get("content");
|
||||||
|
String result = content == null ? null : String.valueOf(content).trim();
|
||||||
|
System.out.println("OpenAI response: " + result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("Error calling OpenAI generate (generic): {}", e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,7 +33,7 @@ public class RbcParserService implements ParserService {
|
|||||||
private MarketItemRepository marketItemRepository;
|
private MarketItemRepository marketItemRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private OllamaAnalyticsService analyticsService;
|
private OpenAIAnalyticsService analyticsService;
|
||||||
|
|
||||||
@Value("${rss.rbc.url}")
|
@Value("${rss.rbc.url}")
|
||||||
private String rssFeedUrl;
|
private String rssFeedUrl;
|
||||||
|
|||||||
@@ -82,18 +82,18 @@ public class ReportGenerationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final MarketItemRepository marketItemRepository;
|
private final MarketItemRepository marketItemRepository;
|
||||||
private final OllamaAnalyticsService ollamaAnalyticsService;
|
private final OpenAIAnalyticsService openAIAnalyticsService;
|
||||||
private final ReportHistoryRepository reportHistoryRepository;
|
private final ReportHistoryRepository reportHistoryRepository;
|
||||||
private final MinIOService minIOService;
|
private final MinIOService minIOService;
|
||||||
private final EmailService emailService;
|
private final EmailService emailService;
|
||||||
|
|
||||||
public ReportGenerationService(MarketItemRepository marketItemRepository,
|
public ReportGenerationService(MarketItemRepository marketItemRepository,
|
||||||
OllamaAnalyticsService ollamaAnalyticsService,
|
OpenAIAnalyticsService openAIAnalyticsService,
|
||||||
ReportHistoryRepository reportHistoryRepository,
|
ReportHistoryRepository reportHistoryRepository,
|
||||||
MinIOService minIOService,
|
MinIOService minIOService,
|
||||||
EmailService emailService) {
|
EmailService emailService) {
|
||||||
this.marketItemRepository = marketItemRepository;
|
this.marketItemRepository = marketItemRepository;
|
||||||
this.ollamaAnalyticsService = ollamaAnalyticsService;
|
this.openAIAnalyticsService = openAIAnalyticsService;
|
||||||
this.reportHistoryRepository = reportHistoryRepository;
|
this.reportHistoryRepository = reportHistoryRepository;
|
||||||
this.minIOService = minIOService;
|
this.minIOService = minIOService;
|
||||||
this.emailService = emailService;
|
this.emailService = emailService;
|
||||||
@@ -131,7 +131,7 @@ public class ReportGenerationService {
|
|||||||
+ "Не упоминай, что ты ИИ. Используй русский язык.\n\nКонтекст отчёта:\n" + contextBlock
|
+ "Не упоминай, что ты ИИ. Используй русский язык.\n\nКонтекст отчёта:\n" + contextBlock
|
||||||
+ "\nСводки новостей:\n";
|
+ "\nСводки новостей:\n";
|
||||||
|
|
||||||
String generated = ollamaAnalyticsService.generateWithInstruction(combined, instruction);
|
String generated = openAIAnalyticsService.generateWithInstruction(combined, instruction);
|
||||||
if (generated == null) {
|
if (generated == null) {
|
||||||
generated = "(Не удалось сгенерировать текст отчёта по предоставленным данным.)";
|
generated = "(Не удалось сгенерировать текст отчёта по предоставленным данным.)";
|
||||||
}
|
}
|
||||||
@@ -248,7 +248,7 @@ public class ReportGenerationService {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
String instruction = "На основе этих кратких сводок новостей напиши общую аннотацию на 2-3 абзаца, выделяя ключевые тренды и события.";
|
String instruction = "На основе этих кратких сводок новостей напиши общую аннотацию на 2-3 абзаца, выделяя ключевые тренды и события.";
|
||||||
String res = ollamaAnalyticsService.generateWithInstruction(combined, instruction);
|
String res = openAIAnalyticsService.generateWithInstruction(combined, instruction);
|
||||||
return res == null ? "" : res;
|
return res == null ? "" : res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,16 +257,16 @@ public class ReportGenerationService {
|
|||||||
if (combined.isBlank()) {
|
if (combined.isBlank()) {
|
||||||
return new Sections("", "", "", "");
|
return new Sections("", "", "", "");
|
||||||
}
|
}
|
||||||
String annotation = ollamaAnalyticsService.generateWithInstruction(combined,
|
String annotation = openAIAnalyticsService.generateWithInstruction(combined,
|
||||||
"На основе кратких сводок новостей напиши краткое содержание (2-3 абзаца), выделив ключевые тренды и события. Стиль: деловой, нейтральный.");
|
"На основе кратких сводок новостей напиши краткое содержание (2-3 абзаца), выделив ключевые тренды и события. Стиль: деловой, нейтральный.");
|
||||||
|
|
||||||
String intro = ollamaAnalyticsService.generateWithInstruction(combined,
|
String intro = openAIAnalyticsService.generateWithInstruction(combined,
|
||||||
"Напиши краткое введение к аналитическому отчёту за период. Укажи цель отчёта, источники данных и подход к анализу. Объём: 1 абзац.");
|
"Напиши краткое введение к аналитическому отчёту за период. Укажи цель отчёта, источники данных и подход к анализу. Объём: 1 абзац.");
|
||||||
|
|
||||||
String mainPart = ollamaAnalyticsService.generateWithInstruction(combined,
|
String mainPart = openAIAnalyticsService.generateWithInstruction(combined,
|
||||||
"Сформируй основную часть отчёта: перечисли и кратко объясни 3-5 ключевых тенденций и событий периода, добавь замеченные риски и возможности. Используй маркированные пункты и короткие абзацы.");
|
"Сформируй основную часть отчёта: перечисли и кратко объясни 3-5 ключевых тенденций и событий периода, добавь замеченные риски и возможности. Используй маркированные пункты и короткие абзацы.");
|
||||||
|
|
||||||
String recs = ollamaAnalyticsService.generateWithInstruction(combined,
|
String recs = openAIAnalyticsService.generateWithInstruction(combined,
|
||||||
"На основе сводок сформулируй практические рекомендации (4-6 пунктов) для бизнеса/маркетинга. Формат: маркированный список, краткие и конкретные формулировки.");
|
"На основе сводок сформулируй практические рекомендации (4-6 пунктов) для бизнеса/маркетинга. Формат: маркированный список, краткие и конкретные формулировки.");
|
||||||
|
|
||||||
return new Sections(annotation, intro, mainPart, recs);
|
return new Sections(annotation, intro, mainPart, recs);
|
||||||
|
|||||||
@@ -18,13 +18,13 @@ import java.util.stream.Collectors;
|
|||||||
@Service
|
@Service
|
||||||
public class ReportSynthesisService {
|
public class ReportSynthesisService {
|
||||||
|
|
||||||
private final OllamaAnalyticsService ollamaAnalyticsService;
|
private final OpenAIAnalyticsService openAIAnalyticsService;
|
||||||
private final OpenAiChartService openAiChartService;
|
private final OpenAiChartService openAiChartService;
|
||||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
public ReportSynthesisService(OllamaAnalyticsService ollamaAnalyticsService,
|
public ReportSynthesisService(OpenAIAnalyticsService openAIAnalyticsService,
|
||||||
OpenAiChartService openAiChartService) {
|
OpenAiChartService openAiChartService) {
|
||||||
this.ollamaAnalyticsService = ollamaAnalyticsService;
|
this.openAIAnalyticsService = openAIAnalyticsService;
|
||||||
this.openAiChartService = openAiChartService;
|
this.openAiChartService = openAiChartService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ public class ReportSynthesisService {
|
|||||||
String aggregatedLearnings = formatLearnings(learnings);
|
String aggregatedLearnings = formatLearnings(learnings);
|
||||||
String prompt = buildTextPrompt(originalQuery, aggregatedLearnings, lang);
|
String prompt = buildTextPrompt(originalQuery, aggregatedLearnings, lang);
|
||||||
return Mono
|
return Mono
|
||||||
.fromCallable(() -> ollamaAnalyticsService.generateWithInstruction(aggregatedLearnings, prompt, lang));
|
.fromCallable(() -> openAIAnalyticsService.generateWithInstruction(aggregatedLearnings, prompt, lang));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mono<FinalReportPayload> synthesizeReportAndCharts(String originalQuery, List<String> learnings,
|
public Mono<FinalReportPayload> synthesizeReportAndCharts(String originalQuery, List<String> learnings,
|
||||||
@@ -40,7 +40,7 @@ public class ReportSynthesisService {
|
|||||||
String aggregatedLearnings = formatLearnings(learnings);
|
String aggregatedLearnings = formatLearnings(learnings);
|
||||||
|
|
||||||
Mono<String> textMono = Mono.fromCallable(
|
Mono<String> textMono = Mono.fromCallable(
|
||||||
() -> ollamaAnalyticsService.generateWithInstruction(aggregatedLearnings,
|
() -> openAIAnalyticsService.generateWithInstruction(aggregatedLearnings,
|
||||||
buildTextPrompt(originalQuery, aggregatedLearnings, lang), lang))
|
buildTextPrompt(originalQuery, aggregatedLearnings, lang), lang))
|
||||||
.subscribeOn(Schedulers.boundedElastic());
|
.subscribeOn(Schedulers.boundedElastic());
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public class VedomostiParserService implements ParserService {
|
|||||||
private MarketItemRepository marketItemRepository;
|
private MarketItemRepository marketItemRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private OllamaAnalyticsService analyticsService;
|
private OpenAIAnalyticsService analyticsService;
|
||||||
|
|
||||||
@Value("${rss.vedomosti.url}")
|
@Value("${rss.vedomosti.url}")
|
||||||
private String rssFeedUrl;
|
private String rssFeedUrl;
|
||||||
|
|||||||
Reference in New Issue
Block a user