136 lines
8.3 KiB
Markdown
136 lines
8.3 KiB
Markdown
Проект: Доработка модуля генерации отчётов в существующем Java/Spring Boot бэкенде.
|
||
|
||
1. Общее описание
|
||
Целью является создание "Агента-синтезатора" внутри бэкенда. Этот агент будет использовать существующий сервис OllamaAnalyticsService для взаимодействия с локально развёрнутой языковой моделью Ollama. Задача агента — принять "сырые" тезисы (learnings) от сервиса deep-research и с помощью Ollama сгенерировать из них связный, структурированный и стилистически выверенный текстовый отчёт в формате Markdown.
|
||
|
||
2. Требования к реализации
|
||
2.1. Создание нового сервиса: ReportSynthesisService
|
||
|
||
Необходимо создать новый Spring-сервис (@Service).
|
||
|
||
Этот сервис будет зависеть от уже существующего OllamaAnalyticsService и должен инжектировать его (@Autowired).
|
||
|
||
Сервис должен иметь один публичный метод:
|
||
|
||
Java
|
||
|
||
public Mono<String> synthesizeReport(String originalQuery, List<String> learnings, String lang)
|
||
Вход:
|
||
|
||
originalQuery: Исходная тема исследования.
|
||
|
||
learnings: Список "сырых" тезисов от deep-research.
|
||
|
||
lang: Целевой язык отчёта.
|
||
|
||
Выход: Mono<String>, содержащий готовый отчёт в формате Markdown.
|
||
|
||
2.2. Логика работы ReportSynthesisService
|
||
|
||
Метод synthesizeReport получает на вход данные.
|
||
|
||
Он объединяет список learnings в единый блок текста, где каждый тезис начинается с новой строки (например, с помощью String.join("\n- ", learnings)).
|
||
|
||
Он формирует промпт (инструкцию) для модели Ollama (см. п. 2.3).
|
||
|
||
Ключевой шаг: Он вызывает метод generateWithInstruction из вашего существующего OllamaAnalyticsService:
|
||
|
||
Java
|
||
|
||
// Преобразуем асинхронный вызов в Mono
|
||
Mono<String> synthesizedReportMono = Mono.fromCallable(() ->
|
||
ollamaAnalyticsService.generateWithInstruction(aggregatedLearnings, prompt)
|
||
);
|
||
Метод возвращает synthesizedReportMono.
|
||
|
||
2.3. Разработка промпта для Ollama
|
||
|
||
Промпт — это сердце качественного результата. Он должен быть чётким и структурированным. Этот промпт будет реализован как форматированная строка внутри ReportSynthesisService.
|
||
|
||
Пример промпта для Ollama (модели типа Llama3, Gemma):
|
||
|
||
Ты — профессиональный AI-аналитик. Твоя задача — написать подробный и структурированный отчёт на основе предоставленных данных. Не добавляй никаких предисловий или комментариев от себя, просто верни готовый отчёт.
|
||
|
||
### Тема исследования:
|
||
|
||
${originalQuery}
|
||
|
||
### Собранные данные (тезисы):
|
||
|
||
- ${learnings_as_string}
|
||
|
||
### Твоя задача:
|
||
|
||
1. **Язык:** Напиши отчёт полностью на ${lang} языке.
|
||
2. **Структура:** Отчёт должен иметь чёткую структуру: введение, основная часть с несколькими разделами и подразделами, и заключение.
|
||
3. **Форматирование:** Используй Markdown. Для главных заголовков разделов используй `##`, для подзаголовков — `###`. Для списков используй `-`.
|
||
4. **Стиль:** Преврати разрозненные тезисы в единую, связную и хорошо написанную статью. Не просто перечисляй факты, а анализируй и обобщай их.
|
||
5. **Ограничение:** Используй только ту информацию, которая дана в собранных данных. Не придумывай ничего от себя.
|
||
|
||
Начинай отчёт с главного заголовка.
|
||
2.4. Интеграция в существующий асинхронный процесс
|
||
|
||
Необходимо модифицировать ваш существующий метод в контроллере, который запускает весь процесс. Вместо вызова OpenAI, теперь будет вызываться ваш новый ReportSynthesisService.
|
||
|
||
Примерная схема интеграции (на основе вашего кода):
|
||
|
||
Java
|
||
|
||
// ...
|
||
// Инжектируем новый сервис
|
||
@Autowired
|
||
private ReportSynthesisService reportSynthesisService;
|
||
|
||
// ... в вашем методе generateResearchReport
|
||
deepResearchService.generateReportAsync(deepRequest)
|
||
.publishOn(Schedulers.boundedElastic())
|
||
.flatMap(researchResponse -> { // Используем flatMap для асинхронной цепочки
|
||
if (researchResponse == null || researchResponse.getLearnings() == null || researchResponse.getLearnings().isEmpty()) {
|
||
return Mono.error(new RuntimeException("Empty research response content"));
|
||
}
|
||
// НОВЫЙ ШАГ: Вызываем наш сервис синтеза с Ollama
|
||
return reportSynthesisService.synthesizeReport(
|
||
request.getQuery(),
|
||
researchResponse.getLearnings(),
|
||
request.getLang()
|
||
).map(synthesizedText -> new SynthesizedReport(researchResponse, synthesizedText)); // Объединяем результаты
|
||
})
|
||
.map(synthesizedReport -> {
|
||
// Здесь мы используем synthesizedText для генерации PDF
|
||
// Этот код остаётся почти без изменений
|
||
byte[] pdfBytes = researchPdfService.generatePdfReport(
|
||
request.getQuery(),
|
||
synthesizedReport.getOriginalResponse(),
|
||
synthesizedReport.getSynthesizedMarkdown() // Передаем новый красивый текст от Ollama
|
||
);
|
||
String filename = "research*report*" + System.currentTimeMillis() + ".pdf";
|
||
saveResearchReportToHistory(request, filename, pdfBytes, synthesizedReport.getOriginalResponse());
|
||
return true;
|
||
})
|
||
.doOnError(e -> {
|
||
System.err.println("Error in full research chain: " + e.getMessage());
|
||
})
|
||
.subscribe();
|
||
// ...
|
||
Вам также понадобится немного изменить researchPdfService.generatePdfReport, чтобы он принимал новый синтезированный Markdown-текст в качестве основного контента.
|
||
|
||
3. Конфигурация
|
||
Вся конфигурация для Ollama уже есть в вашем OllamaAnalyticsService. Новых настроек добавлять не нужно.
|
||
|
||
Важная рекомендация: Убедитесь, что в вашем application.properties (или yml) для ollama.model указана достаточно мощная модель, способная следовать сложным инструкциям. Модель gemma3:1b слишком слабая для такой задачи. Рекомендуется использовать:
|
||
|
||
llama3:8b-instruct (хороший баланс)
|
||
|
||
gemma:7b-it
|
||
|
||
mistral или mixtral
|
||
|
||
Пример:
|
||
|
||
Properties
|
||
|
||
ollama.host=http://185.35.223.45:11434
|
||
ollama.model=llama3:8b-instruct
|
||
ollama.timeoutMs=180000 # Увеличьте таймаут до 3 минут, т.к. генерация отчёта - долгая задача 4. Ожидаемый результат
|
||
После реализации бэкенд будет использовать ваш собственный, локально развёрнутый Ollama для создания высококачественных, структурированных PDF-отчётов, полностью контролируя весь процесс и не завися от внешних платных API для синтеза текста.
|