Головна / Статті / Діагностика падінь CI-збірок: правила, RAG і локальна LLM

Діагностика падінь CI-збірок: правила, RAG і локальна LLM

Java-система для аналізу падінь CI: детерміновано розбирає логи, дістає нотатки з траблшутингу з PGVector через Ollama і лише потім просить LLM міркувати.

1539 слів

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

Ця драбина від логів до діагнозу, а згодом і до дії, задає напрям подальшої роботи.