8.5 KiB
Техническое Задание: Опциональное добавление диаграмм в PDF-отчёты
- Общее описание Целью является улучшение существующих PDF-отчётов путём опционального добавления в них диаграмм и графиков. Основной процесс генерации текстового отчёта на основе learnings остаётся без изменений. Параллельно с этим, AI-агент будет пытаться найти в "сырых" данных (learnings) числовую информацию, подходящую для визуализации.
Если такие данные найдены, агент сгенерирует диаграмму и вставит её в PDF. Если данных нет, будет сгенерирован обычный текстовый отчёт без диаграмм. Процесс не должен прерываться, если сгенерировать диаграмму невозможно.
- Обновлённая схема работы Процесс будет состоять из двух параллельных веток, которые затем объединяются:
Сбор данных: Бэкенд вызывает deep-research API и получает learnings и visitedUrls.
Запускаются два асинхронных вызова к Ollama:
Ветка A (Синтез текста - основная): Все learnings отправляются в Ollama с промптом на написание красивого, структурированного текстового отчёта в формате Markdown. (Этот шаг остаётся без изменений).
Ветка B (Поиск данных для диаграммы - опциональная): Все learnings отправляются в Ollama с новым, специальным промптом для поиска числовых данных и возврата их в виде JSON.
Обработка Ветки B (если успешно):
Если Ollama вернул валидный JSON с данными, делается третий вызов к Ollama с промптом на генерацию SVG-кода этой диаграммы.
Shutterstock
- Полученный SVG-код конвертируется в изображение (PNG) на стороне Java. Финальная сборка PDF:
ResearchPdfService получает обязательный текстовый отчёт из Ветки А, опциональное изображение диаграммы из Ветки B и список visitedUrls.
Он собирает всё это в единый PDF-документ и возвращает пользователю.
- Требования к реализации 3.1. Модификация сервиса синтеза (например, ReportSynthesisService)
Этот сервис теперь будет оркестрировать несколько асинхронных вызовов.
Основной метод synthesizeReportAndCharts будет возвращать объект, содержащий и текст, и изображения, например Mono.
Java
class FinalReportPayload { String markdownContent; List<byte[]> chartImages; // ... } Внутри этого метода будут асинхронно запускаться два вызова к Ollama:
generateTextReport(learnings) - для получения текста.
generateChartData(learnings) - для получения JSON для диаграммы.
3.2. Промпт-инжиниринг для диаграмм (Ветка B)
Шаг 1: Извлечение данных в JSON
Задача: Найти в тексте данные для визуализации.
Промпт:
"Ты — AI-аналитик данных. Внимательно проанализируй следующий текст. Если найдёшь в нём числовые данные, которые можно представить в виде простой столбчатой или круговой диаграммы (например, рост по годам, доли рынка, сравнение показателей), верни ТОЛЬКО один JSON-объект со структурой: {"chartType": "bar" или "pie", "title": "Название диаграммы", "labels": ["Метка 1", "Метка 2"], "data": [число1, число2]}. Если подходящих данных нет, верни пустой JSON-объект {}. Текст для анализа:\n---\n${learnings_as_string}\n---"
Шаг 2: Генерация SVG из JSON
Задача: Превратить JSON с данными в SVG-код.
Промпт:
"Ты — эксперт по визуализации данных. На основе следующего JSON, сгенерируй полный и валидный SVG-код для диаграммы. SVG должен быть стильным и читаемым, с подписями на русском языке. Не добавляй никаких комментариев, верни ТОЛЬКО SVG-код. JSON с данными:\n---\n${json_from_previous_step}\n---"
3.3. Конвертация SVG в изображение
Этот модуль остаётся без изменений. Используется библиотека Apache Batik для преобразования SVG-строки в byte[] PNG-изображения.
Зависимость в pom.xml:
XML
org.apache.xmlgraphics batik-transcoder 1.17 Вспомогательный класс-конвертер остаётся тем же.3.4. Модификация PDF-сервиса (ResearchPdfService)
Сервис должен быть готов к тому, что диаграмм может и не быть.
Измените метод generatePdfReport, чтобы он принимал список изображений, который может быть пустым:
Java
public byte[] generatePdfReport(String query, String markdownContent, List visitedUrls, List<byte[]> chartImages) Внутри метода добавьте проверку: если список chartImages не пустой, вставляйте изображения в документ.
Java
// ... после вставки основного текста отчёта ...
if (chartImages != null && !chartImages.isEmpty()) { document.newPage(); document.add(new Paragraph("Визуализация данных", headingFont));
for (byte[] imageData : chartImages) {
try {
Image chartImage = Image.getInstance(imageData);
// Масштабируем, чтобы поместилось на страницу
float scaler = ((document.getPageSize().getWidth() - document.leftMargin() - document.rightMargin()) / chartImage.getWidth()) * 80; // 80% ширины
chartImage.scalePercent(scaler);
chartImage.setAlignment(Element.ALIGN_CENTER);
document.add(new Paragraph(" ")); // Отступ
document.add(chartImage);
} catch (Exception e) {
// Логируем ошибку, но не прерываем генерацию PDF
System.err.println("Не удалось добавить изображение диаграммы в PDF: " + e.getMessage());
}
}
} 4. План действий Добавить зависимость Apache Batik в pom.xml.
Реализовать асинхронную логику в ReportSynthesisService, которая параллельно запрашивает у Ollama:
Основной текст отчёта.
JSON с данными для диаграммы.
Добавить в тот же сервис условную логику: если JSON получен, сделать ещё один запрос к Ollama за SVG-кодом.
Реализовать SVG-конвертер (или добавить его как util-класс).
Обновить ResearchPdfService, чтобы он мог принимать и вставлять список изображений, если он не пуст.