Диагностика падений CI-сборок: правила, RAG и локальная LLM
Java-система для анализа падений CI: детерминированно разбирает логи, достаёт заметки по траблшутингу из PGVector через Ollama и только потом просит LLM рассуждать.
CI-сервер отлично умеет одно: сообщить, что сборка сломалась. А вот то, что волнует инженеров, когда продакшен-пайплайн покраснел, он объясняет редко. Почему сломалось? Где сам дефект? Что нужно поменять? И может ли накопленный инженерный опыт сделать диагноз точнее?
Речь пойдёт об анализаторе падений, построенном вокруг этих вопросов. Вставить сырой лог в LLM и спросить, что пошло не так, — не тот план. Дизайн сочетает детерминированную инженерную логику, семантический поиск, генерацию с дополнением выборкой (RAG) и рассуждения модели. Разработка идёт поэтапно: сначала анализ логов, потом агенты, понимающие репозиторий, потом автоматические исправления.
Что выдаёт система
Получив упавшую сборку, анализатор прогоняет лог через пайплайн, вот он в общих чертах.
CI/CD Build
|
v
Build Log
|
v
Log Preprocessing
|
v
Deterministic Analysis
|
+------------------+
| |
v v
Structured Data RAG
| |
| v
| Relevant Knowledge
| |
+--------+---------+
|
v
LLM Reasoning
|
v
Build Diagnosis
Результат структурирован. В нём указаны тип сбоя, упавший компонент, место в исходниках, стектрейс, корневая причина, рекомендуемое исправление и уровень уверенности.
Сначала факты, потом модель
Самое первое архитектурное решение касалось порядка. Наивная версия отдаёт лог прямо модели.
Build Log
|
v
LLM
|
v
Root Cause
Изъян тут серьёзный. Модели приходится выводить всё сама из лога, в котором может быть полно шума. Анализатор же сначала вытаскивает всё, что можно установить наверняка. Возьмём лог с маркером падения сборки, NullPointerException при валидации платежа и фреймом стека, указывающим на класс платёжного сервиса.
BUILD FAILURE
java.lang.NullPointerException:
Cannot invoke "PaymentValidator.validate()"
because "validator" is null
at com.example.payment.PaymentService.process(
PaymentService.java:47)
Детерминированный парсер может считать отсюда статус, категорию ошибки, а также точный файл и строку.
{
"status": "FAILED",
"errorType": "RUNTIME_ERROR",
"sourceLocation": "PaymentService.java:47",
"stackTrace": "..."
}
Никаких догадок, всё взято прямо из лога. Получив эти факты, LLM может сосредоточиться на их интерпретации, а не на базовом парсинге. Отсюда и руководящая формула.
Deterministic Analysis
+
RAG Knowledge
+
LLM Reasoning
=
Context-Aware Diagnosis
Добавляем выборку
Следующий вопрос был в том, можно ли подтянуть релевантные инженерные знания до того, как модель возьмётся что-то диагностировать. Здесь и появляется RAG. Мы написали небольшую базу знаний с заметками по траблшутингу, по одному Markdown-файлу на тему: NullPointerException, IllegalArgumentException, внедрение зависимостей в Spring и так далее.
knowledge/
├── null-pointer-exceptions.md
├── illegal-argument-exceptions.md
├── spring-dependency-injection.md
├── database-connection-failures.md
├── kafka-failures.md
└── maven-build-failures.md
В каждой заметке описаны типичные паттерны сбоев, причины, шаги диагностики и рекомендуемые решения. Загрузка превращает Markdown-файлы в векторы, по которым можно искать.
Markdown Documents
|
v
Document Loader
|
v
Chunking
|
v
Embedding Model
|
v
PostgreSQL + PGVector
Во время запроса сбой переформулируется в семантический запрос и сопоставляется с этими векторами.
Build Failure
|
v
Create Semantic Query
|
v
Embedding Model
|
v
PGVector Similarity Search
|
v
Relevant Knowledge
То, что вернулось, может служить контекстом для модели.
Намеренно локальный стек
Первая версия задумывалась локальной и скромной. Она работает на PostgreSQL с расширением PGVector, Ollama и LangChain4j. PostgreSQL живёт в Docker-контейнере. Эмбеддинги делает модель, которую локально раздаёт Ollama.
nomic-embed-text
Каждый эмбеддинг — длинный числовой вектор.
768-dimensional embeddings
Раз знания превращаются в векторы, искать по ним можно по смысловой близости, а не по ключевым словам.
Первое доказательство, что выборка работает
В раннем тесте использовался запрос обычным языком с описанием бага.
PaymentValidator is null when processing a payment
Запрос превратили в эмбеддинг и отправили в PGVector. В ответ пришла заметка про NullPointerException со сходством примерно такого уровня.
0.8006
Вернувшийся текст объяснял, что NullPointerException возникает, когда код обращается к ссылке на объект, в которой лежит null. Нашёлся и пример с внедрением в Spring: сервисный класс с полем, которое так и не связывается.
@Service
public class PaymentService {
private PaymentValidator validator; public void process(Payment payment) {
validator.validate(payment);
}
}
Вместе с ним пришёл совет перейти на внедрение через конструктор. Этап был важен, потому что система больше не просто хранила документы. Она находила нужные знания по смыслу запроса.
Чанки вместо целых документов
Поначалу каждый Markdown-файл превращался в один эмбеддинг. Для proof of concept это нормально, для продакшена — нет. Задуманный поток режет большие документы на чанки поменьше.
Large Document
|
v
Chunks
|
+----> Embedding
|
+----> Embedding
|
+----> Embedding
|
+----> Embedding
|
v
PGVector
С чанками выборка возвращает самые релевантные куски знаний, а не целый файл. Сейчас пробуем рекурсивное разбиение с перекрытием, чтобы важный контекст не обрезался на границах чанков.
Стек по частям
- Java 21 со Spring Boot — обычный бэкенд с REST-эндпоинтами.
- LangChain4j связывает Java-приложение с моделью эмбеддингов, LLM и векторным хранилищем.
- Llama 3.2 через Ollama работает локально, так что проект не зависит целиком от облачного API.
- Модель nomic-embed-text, тоже через Ollama, превращает документы и запросы в векторы.
- PostgreSQL с PGVector хранит эмбеддинги и отвечает на поиск по сходству.
- Docker держит PostgreSQL и PGVector во время локальной разработки.
Что уже работает
Обнаружение покрывает ошибки компиляции, проблемы с зависимостями Maven, падения автотестов и сбои во время выполнения, причём для NullPointerException и IllegalArgumentException есть отдельный анализ. Извлечение умеет находить компоненты, места в исходниках, стектрейсы и чистить логи от ANSI-цветов.
На стороне рассуждений есть структурированные ответы LLM, анализ корневых причин, советы по исправлению и рекомендации с учётом уверенности.
Слой знаний включает базу знаний в Markdown, загрузку, разбиение на чанки, локальную генерацию эмбеддингов, хранение в PostgreSQL и PGVector, поиск по сходству и выборку нужных материалов по траблшутингу.
Подключаем выборку к модели
Следующий этап — связать найденные знания с LLM. Целевой поток начинается со сбоя и заканчивается рекомендацией.
Build Failure
|
v
Rule-Based Analysis
|
v
Structured Data
|
v
RAG Retrieval
|
v
Relevant Knowledge
|
v
Llama 3.2
|
v
Context-Aware Diagnosis
Вместо того чтобы задавать модели открытый вопрос о сборке,
"What is wrong with this build?"
анализатор передаст ей одним пакетом сбой, тип ошибки и найденные знания.
Build failure
+
Error type
+
Component
+
Source location
+
Stack trace
+
Retrieved engineering knowledge
Затем модель просят рассуждать в этом контексте.
К инженерному агенту
RAG — лишь один этап более длинного замысла. Сегодня путь короткий.
Build Log
↓
Diagnosis
Итоговая форма куда богаче.
Build Failure
|
v
Diagnosis
|
v
Repository Analysis
|
v
Find Relevant Code
|
v
Generate Fix
|
v
Create Git Branch
|
v
Create Pull Request
|
v
Run CI
|
+------ Failed ------+
| |
| v
| Investigate
| |
+--------------------+
|
+------ Passed ------> Successful Fix
Здесь появляется инженерный ИИ-агент, который мог бы пользоваться инструментами для изучения репозитория, операций с Git, поиска по базе знаний, общения с Jenkins, запуска сборок и тестов и открытия pull request'ов. Ключевое отличие в том, что такой агент не просто выдаёт ответ. Он исследует, действует, смотрит на результат и пробует снова.
Выводы на сегодня
Полезная ИИ-инженерия — это больше, чем вызов LLM. У рабочей системы несколько слоёв.
Software Engineering
+
Data
+
Retrieval
+
Models
+
Tools
+
Validation
Детерминированный слой важен. Важна и выборка, и качество базы знаний. Но важнее всего возможность проверить рекомендацию, которую сгенерировал ИИ. Именно поэтому система строится шаг за шагом, а не прыжком сразу к автономному агенту.
Для Java-бэкендера такой проект выводит инженерные вопросы на поверхность: не как вызвать API LLM, а как встроить ИИ в реальный процесс разработки. Он охватывает интеграцию LLM, эмбеддинги, векторные базы, RAG, семантический поиск, ИИ-помощь в разработке, агентные процессы, автоматическое исправление и интеграцию с CI/CD. Работа продолжается, но траектория уже ясна.
Build Logs
↓
Deterministic Analysis
↓
LLM Reasoning
↓
Embeddings
↓
PGVector
↓
Semantic Retrieval
↓
RAG
↓
Repository Intelligence
↓
AI Agent
↓
Automated Remediation
Эта лестница от логов к диагнозу, а потом и к действию, задаёт направление дальнейшей работы.