Java-інтеграція AI-агента: як побудувати фічу, яку захоче бізнес і зрозуміє розробник
Привіт! Мене звати Поліна Сергієнко, я Senior Java Software Engineer у Levi9. У цій статті ділюся досвідом інтеграції AI у Java-продакшн — без PhD і тотальної перебудови системи.
Якщо ви Java-розробник, то, швидше за все, вже чули фразу «а давайте зробимо AI-фічу». Наче звучить просто, але на практиці — немає
Наприклад, онлайн-магазин кілька років тому це такий флоу: клієнт питає про доставку, оператор вручну шукає відповідь, черга росте, всі нервують. Сьогодні AI-чатбот самостійно може розрулити
У цей момент якась бізнес-команда каже розробникам: «А давайте зробимо AI-фічу». Просто ж? Тільки у реальності ситуація така:
ML-досвіду немає;- команда — Java-розробники, а не Data Science-гуру;
- система живе в продакшні роками й працює стабільно (тому страшно чіпати);
- «прикрутити» нову технологію абияк не вийде — доведеться інтегрувати.
І ось тут гарна новина — власну модель будувати не доведеться. Є безліч готових інструментів — LLM API та open-source рішень. І так, ними реально користуватися без спеціальних знань.
Java-екосистема дозволяє інтегрувати AI в enterprise-проєкти без тотальних перебудов. Найзручніше винести AI-функціонал в окремий шар чи сервіс. Тоді:
- основна бізнес-логіка залишається недоторканною (що працює — краще не чіпати);
- AI-частина ізольована й зрозуміла;
- можна додавати й покращувати фічі поступово, без ризику «зламати все».
AI у системі? Цілком реально.
Постановка задачі
Онлайн-магазин захотів інтегрувати AI у свій застосунок. Бізнес-очікування виглядали дуже прямолінійно: менеджери повинні мати змогу вводити звичайні фрази на кшталт «Покажи товари зі знижкою в категорії електроніки» — і отримувати релевантну вибірку продуктів.
При цьому одразу з’явилися обмеження:
- жодних SQL-заклять для менеджерів;
- мінімум витрат на імплементацію: без епопейних редизайнів архітектури та без залучення цілої
ML-команди.
Існуюча логіка
Система працювала на Java (Spring Boot + PostgreSQL) і вже мала REST-ендпоїнт для пошуку, який виконував запити до БД. Проблема в тому, що цей ендпоїнт був занадто вузьким:
- пошук — тільки в одній категорії;
- параметри — тільки чіткі значення;
- жодних «більше/менше», часткових збігів чи складних умов.
Іншими словами, він працював чітко, але без жодної гнучкості.
При цьому ендпоїнт повертав структурований респонс, який уже споживав фронтенд. І тут була принципова вимога: навіть якщо дані будуть тягнутися через AI, формат відповіді має залишитися тим самим. Це гарантує стабільність клієнтської частини та зворотну сумісність.
Схема БД:

Ендпоїнт: POST /products.
Request body:
{
"category": "8cacd6cf-37db-4b75-9219-88808ff59e0e",
"params": [
{
"id": "c2a19482-d216-4e6a-abd2-e2443c4f8cfc",
"values": [
"12",
"24",
"36"
]
},
{
"id": "585489b8-f64a-439e-9e5f-2c51fabe8355",
"values": [
"сірий"
]
}
]
}
Response body:
[
{
"id": "ddddddd1-dddd-dddd-dddd-dddddddddddd",
"name": "Smartphone",
"categoryId": "11111111-1111-1111-1111-111111111111",
"price": 699.99,
"availableAmount": 100,
"createdAt": "2025-06-22T16:58:51.042661Z",
"updatedAt": "2025-06-22T16:58:51.042661Z"
}
]
Рішення
Під час брейншторму з’явилися дві основні ідеї, як реалізувати AI-функціональність:
1. Генерація SQL-запиту за допомогою AI
Суть підходу: передавати текстовий запит користувача в AI-модель і отримувати у відповідь готовий SQL-запит, який можна виконати безпосередньо на базі даних.
Плюси: максимальна гнучкість — можна побудувати довільні запити з умовами, фільтрами, діапазонами тощо.
2. Генерація DTO-об’єкта з тексту
Інший варіант — навчити AI одразу формувати готовий DTO на основі текстового опису користувача. Плюси: існуюча бізнес-логіка лишається недоторканною, а користувачі отримують свободу у формуванні запитів природною мовою.
Загальна архітектура. Для реалізації ми обрали хмарну
Загальна схема виглядає наступним чином:

Першим кроком для обох випадків було створення AI-агента з системним запитом (system prompt), який формулює контекст і правила поведінки моделі. Ми описали, яку саме задачу має виконувати агент, які дані він повинен аналізувати, і що має повертати у відповідь.
Цей промпт формувався ітеративно. Після кожної серії тестів ми помічали, які інструкції модель інтерпретує неоднозначно або де виникають помилки, і поступово уточнювали та розширювали інструкції. Таким чином з кожною ітерацією агент краще «розумів» нашу систему і запити користувачів.
Наступним спільним кроком було додавання тулів. Розглянемо в деталях роботу над двома варіантами нижче.
Генерація SQL-запиту за допомогою AI
Ми вирішили не описувати вручну структуру бази даних у промпті. Натомість дали агенту можливість самостійно звертатися до БД і отримувати потрібну інформацію про структуру.
Для цього реалізували набір спеціалізованих тулів, які роблять службові запити до PostgreSQL:
- Tool для отримання списку всіх таблиць у схемі.
- Tool для отримання списку полів конкретної таблиці.
Такий підхід дав кілька переваг:
- завжди актуальна інформація про структуру БД (жодних «застарілих промптів» після змін у схемі);
- агент не перевантажений зайвим контекстом — додаткові дані підтягуються лише тоді, коли вони реально потрібні;
- у system message ми залишили тільки справді неочевидні деталі про структуру даних.
Приклади промптів і коду можна взяти тут:
github.com/...linaucc/ai-sql-generation
Проблеми на етапі тестування
Одне з перших питань звучало просто: чи варто щоразу ходити в базу, щоб підтягнути структуру таблиць? Відповідь швидко знайшлася: ні. Схема змінюється рідко, а навантаження зростає. Тому ми додали кешування службових запитів — і одразу розвантажили БД.
Далі з’ясувалося, що модель іноді «економить кроки» і будує SQL напряму. У результаті з’являлися вигадані поля або синтаксичні помилки. Вирішення тут одне: чітко прописати правила, коли саме слід звертатися до допоміжних інструментів і що саме вони мають повертати.
Ще один момент стосувався формату відповіді. Запит міг виглядати правильним, але не відповідати нашому DTO. Щоб цього уникнути, ми зробили окремий механізм, який підказує обов’язковий набір полів для кожної сутності.
На рівні агента довелося відмовитися від автоматичного режиму: він викликав усе підряд, що тільки було в контексті. Ми перейшли на EXPLICIT, де самі визначаємо, які інструменти доступні, яка саме модель використовується і чи є пам’ять. Це дало контроль і прибрало хаос.
І навіть після цього траплялися сюрпризи: іноді AI генерував SQL, який PostgreSQL відкидав ще на етапі парсингу. Щоб обійтись без ручних виправлень, ми додали два механізми: пам’ять для збереження контексту та retry-логіку, яка дає моделі шанс спробувати ще раз з урахуванням конкретної помилки.
Приклади:
User input: Please show me all products with weight between 50 and 70 kg.

Починали ми з максимально простих задач. Просили показати всі продукти вагою від 50 до 70 кг, і загалом кверя згенерувалася робоча. АІ зрозумів найскладнішу частину в базі — це зберігання значень характеристики (у нас це varchar-колонка, яка може містити як цифри
User input: I want to receive list of Electronics products with weight between 0.1 and 0.4 tons.

Складність тут полягає в тому, що вага в БД зберігається в кілограмах. Нам важливо було виконати переведення з одиниць виміру користувача до наших.
І у нас це вийшло за допомогою додавання ще одного тула, який ходить в БД і просить одиницю виміру за назвою характеристики. Єдиний нюанс — тули мають завжди щось повертати, бо NULL вважається не валідним значенням.
User input: I want to receive list of Electronics products with voltage suitable for US and power 60W.

Наступний запит ми трохи ускладнили: попросили згенерувати список продуктів з вольтажем, що відповідає стандартам у США. У нашій базі даних інформації про вольтаж у різних країнах не було, але ШІ самостійно знайшов потрібні значення й додав їх у підсумковий запит.
Саме такого типу завдання виглядають найбільш доречними для використання ШІ.
User input: Please show me all books where author is a woman.

В результаті АІ зробила кверю з пошуком за параметром author gender, якого не було в базі.
Інсайти: генерація SQL-запитів
- Якість промптів та описів інструментів критично важлива. Що зрозуміліше вони сформульовані, то менше шансів, що модель «забуде» викликати потрібний інструмент.
- Виклики треба логувати. Найпростіше — увімкнути debug-логи в LangChain, і тоді буде видно, коли й з якими параметрами викликався кожен інструмент. Це дуже полегшує аналіз.
- Результати роботи інструментів можуть зберігатися в пам’яті й не викликатися повторно. Але є й зворотний випадок: якщо пам’яті замало, а інструментів треба кілька, агент може «зациклитися» і викликати їх по колу без фінального результату.
- Важливо пам’ятати: однакові запити не завжди повертають однакові результати. Тут допомагає параметр temperature, який визначає рівень «творчості» моделі. Низьке значення робить відповіді більш передбачуваними — що зручно для генерації коду чи SQL. Високе — додає імпровізації, але важче отримати повторюваний результат, наприклад, після retry.
- Усі згенеровані SQL-запити треба перевіряти. Найбезпечніше запускати їх на read-репліці чи спеціально підготовленій базі з обмеженим пулом даних. Ще один варіант — робити санітизацію.
Інсайти: генерація DTO
Для цього підходу ми зробили кілька допоміжних інструментів:
- два прості — щоб із назви категорії чи параметра отримати відповідний ID (бо наш ендпоїнт працює з ID, а користувачі у тексті їх не вказують);
- ще один — для формування структури об’єкта, який потрібен у відповіді.
Приклади запитів і згенерованих об’єктів ми супроводжували коментарями.
Що помітили під час роботи:
- Варіант виявився більш передбачуваним, бо базувався на нашій існуючій бізнес-логіці.
- Помилок для користувача значно менше. Були кейси, коли DTO згенерувався некоректно, але юзер просто не отримував нічого. Це можна сприймати і як мінус, і як плюс.
- Ми були впевнені, що виконуються лише SELECT-операції, отже, не потрібно додатково «чистити» запити.
- Оцінювати відповіді моделі в цьому випадку було набагато простіше.
- Недолік — менша гнучкість. Можна отримати тільки те, що вже передбачено нашою бізнес-логікою.
Порівняня обох варіантів, що можна використати:

На нашу думку, у продакшн-рішеннях із реальними користувачами безпечніше й стабільніше працювати з генерацією DTO. До того ж він дозволяє контролювати перформанс, адже запускаються перевірені нами запити.
Генерацію SQL доцільно використовувати для внутрішніх потреб, коли важлива максимальна гнучкість, а ризики прийнятні.
Що варто пам’ятати
AI — це потужний сучасний інструмент, який можна відносно швидко інтегрувати в існуючі системи, значно розширюючи вже написану логіку. Водночас важливо пам’ятати про неповну передбачуваність результатів, питання безпеки та вартість інтеграції, яка може зростати залежно від інтенсивності використання.
Сподобалась стаття авторки? Підписуйтесь на її акаунт вгорі сторінки, щоб отримувати сповіщення про нові публікації на пошту.
10 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів