104 lines
7.5 KiB
Markdown
104 lines
7.5 KiB
Markdown
### **Техническое Задание: Внедрение гибридной AI-модели (OpenAI для диаграмм, Ollama для текста)**
|
||
|
||
#### **1. Общее описание**
|
||
|
||
Целью является доработка AI-агента для использования гибридной модели генерации. Основной синтез текстового отчёта по-прежнему будет выполняться с помощью локального сервиса **Ollama**. Для опциональной и более сложной задачи — извлечения структурированных данных и генерации диаграмм — будет использоваться API **OpenAI**.
|
||
|
||
Этот подход позволяет использовать сильные стороны каждой модели:
|
||
|
||
- **Ollama:** Экономичная и быстрая генерация основного текста отчёта.
|
||
- **OpenAI:** Высокая точность в следовании инструкциям для генерации идеально отформатированного JSON и SVG-кода для диаграмм.
|
||
|
||
#### **2. Обновлённая схема работы**
|
||
|
||
1. **Сбор данных:** Бэкенд получает `learnings` от `deep-research` API.
|
||
2. **Параллельный запуск двух AI-задач:**
|
||
- **Ветка A (Текст -\> Ollama):** `learnings` отправляются в ваш существующий `OllamaAnalyticsService` для синтеза основного текста отчёта в Markdown.
|
||
- **Ветка B (Диаграммы -\> OpenAI):** `learnings` отправляются в **новый сервис** (`OpenAiChartService`), который делает два последовательных вызова к **API OpenAI**:
|
||
1. Извлечь из текста данные для диаграмм в формате **JSON**.
|
||
2. Превратить этот JSON в **SVG-код** диаграммы.
|
||
3. **Конвертация SVG:** SVG-код, полученный от OpenAI, конвертируется в PNG-изображение на стороне Java.
|
||
4. **Финальная сборка PDF:** `ResearchPdfService` объединяет текст отчёта (от Ollama) и изображения диаграмм (от OpenAI) в единый PDF-файл.
|
||
|
||
#### **3. Требования к реализации**
|
||
|
||
**3.1. Конфигурация**
|
||
|
||
В ваш файл `application.properties` (или `application.yml`) необходимо добавить API-ключ для OpenAI:
|
||
|
||
```properties
|
||
# application.properties
|
||
|
||
# ... ваши существующие настройки Ollama ...
|
||
ollama.model=llama3:8b-instruct
|
||
|
||
# --- НОВЫЕ НАСТРОЙКИ ДЛЯ OPENAI ---
|
||
openai.api.key=sk-...(ваш API ключ от OpenAI)...
|
||
openai.api.url=https://api.openai.com/v1/chat/completions
|
||
openai.model.name=gpt-4-turbo # Рекомендуемая модель для работы с JSON и SVG
|
||
```
|
||
|
||
**3.2. Создание нового сервиса: `OpenAiChartService`**
|
||
|
||
Необходимо создать новый Spring-сервис, отвечающий за взаимодействие с OpenAI.
|
||
|
||
- **`WebClient`:** Сервис должен содержать собственный `WebClient`, настроенный на `openai.api.url` и автоматически добавляющий заголовок `Authorization: Bearer ${openai.api.key}` ко всем запросам.
|
||
- **Методы:**
|
||
1. `public Mono<String> getChartDataJson(List<String> learnings)`: Принимает `learnings`, использует **Промпт №1** (для извлечения данных) и возвращает `Mono` со строкой, содержащей JSON-массив.
|
||
2. `public Mono<String> getChartSvg(String jsonData)`: Принимает JSON с данными для одной диаграммы, использует **Промпт №2** (для генерации SVG) и возвращает `Mono` со строкой SVG-кода.
|
||
|
||
**3.3. Модификация оркестратора: `ReportSynthesisService`**
|
||
|
||
Этот сервис теперь будет управлять вызовами к обоим AI-сервисам.
|
||
|
||
- **Зависимости:** `ReportSynthesisService` теперь должен инжектировать (`@Autowired`) и `OllamaAnalyticsService`, и новый `OpenAiChartService`.
|
||
- **Метод `synthesizeReportAndCharts`:**
|
||
- **Ветка текста (`textMono`)** по-прежнему вызывает `ollamaAnalyticsService` для генерации отчёта.
|
||
- **Ветка диаграмм (`chartsMono`)** теперь будет вызывать `openAiChartService`.
|
||
|
||
**Пример обновлённой логики для `chartsMono`:**
|
||
|
||
```java
|
||
// ... внутри ReportSynthesisService ...
|
||
|
||
// Ветка B: Генерация диаграмм через OpenAI
|
||
Mono<List<byte[]>> chartsMono = openAiChartService.getChartDataJson(aggregatedLearnings)
|
||
.flatMap(jsonArrayString -> {
|
||
String cleanJsonArray = extractJsonArray(jsonArrayString);
|
||
if (cleanJsonArray == null || cleanJsonArray.equals("[]")) {
|
||
return Mono.just(Collections.emptyList()); // Если данных нет, возвращаем пустой список
|
||
}
|
||
|
||
try {
|
||
List<ChartData> chartDataList = objectMapper.readValue(cleanJsonArray, new TypeReference<List<ChartData>>() {});
|
||
|
||
// Превращаем список задач в поток (Flux) и выполняем их последовательно
|
||
return Flux.fromIterable(chartDataList)
|
||
.concatMap(chartData ->
|
||
openAiChartService.getChartSvg(objectMapper.writeValueAsString(chartData))
|
||
)
|
||
.map(this::extractSvgCode)
|
||
.map(SvgToPngConverter::convert)
|
||
.filter(Objects::nonNull)
|
||
.collectList();
|
||
|
||
} catch (IOException e) {
|
||
return Mono.error(e);
|
||
}
|
||
});
|
||
```
|
||
|
||
**3.4. Промпты для OpenAI**
|
||
|
||
Промпты остаются теми же, что и в предыдущем ТЗ, так как модели OpenAI отлично их понимают.
|
||
|
||
- **Промпт №1 (Извлечение данных):** Просит вернуть **JSON-массив** объектов с данными для диаграмм или пустой массив `[]`, если данных нет.
|
||
- **Промпт №2 (Генерация SVG):** Просит на основе одного JSON-объекта вернуть **только SVG-код**.
|
||
|
||
#### **4. План действий**
|
||
|
||
1. **Добавить `openai.api.key`** в ваш `application.properties`.
|
||
2. **Создать новый класс `OpenAiChartService.java`**. Он будет похож на `OllamaAnalyticsService`, но настроен для работы с API OpenAI (другой URL, заголовок авторизации, другая структура JSON-запроса).
|
||
3. **Обновить `ReportSynthesisService.java`**, чтобы он использовал `OllamaAnalyticsService` для текста и `OpenAiChartService` для диаграмм, как показано в примере выше.
|
||
4. **Перезапустить** ваше Java-приложение.
|