### **Техническое Задание: Внедрение гибридной 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 getChartDataJson(List learnings)`: Принимает `learnings`, использует **Промпт №1** (для извлечения данных) и возвращает `Mono` со строкой, содержащей JSON-массив. 2. `public Mono 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> chartsMono = openAiChartService.getChartDataJson(aggregatedLearnings) .flatMap(jsonArrayString -> { String cleanJsonArray = extractJsonArray(jsonArrayString); if (cleanJsonArray == null || cleanJsonArray.equals("[]")) { return Mono.just(Collections.emptyList()); // Если данных нет, возвращаем пустой список } try { List chartDataList = objectMapper.readValue(cleanJsonArray, new TypeReference>() {}); // Превращаем список задач в поток (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-приложение.