Головна / Статті

AI-агенти проти традиційних бекендів: поток керування, витрати та способи збоїв

Порівняйте детерміністичний бекенд з циклом агента LLM на прикладі повернення грошей: затримки, витрати на токени, зростання контексту, способи збоїв та ситуації, де найкраще підходить гібридний дизайн.

Видавець: REACTAPP.TOP9 хв читання1966 слів

Джерела: AI Agents vs Traditional Backend Applications: What’s Actually Different?

Інженерія бекенду протягом тривалого часу спирається на одну важливу властивість — детермінізм. Якщо написати ендпоїнт, який приймає замовлення, перевіряє наявність товару в PostgreSQL та здійснює оплату карткою через Stripe, то ця сама послідовність виконуватиметься щоразу. За однакових вхідних даних та однакового стану бази даних рядок 42 завжди виконується перед рядком 43.

Тоді індустрія почала називати AI-агентів наступним етапом бекенд-систем. Легко проігнорувати це як маркетинговий трюк, адже агент також виглядає як сервіс у контейнері, який викликає зовнішні API та записує дані у базу даних. Однак насправді логіка керування змінилась. У класичному бекенді розробник заздалегідь вирішує кожен крок, гілку та межовий випадок. У агенті розробник надає мету та набір інструментів, а стохастична модель вибирає кроки, їхній порядок та момент завершення роботи. Якщо ставитися до агента як до ще одного мікросервісу, виникнуть серйозні проблеми з запізненнями, надійністю, тестуванням та витратами на хмарні послуги.

Фіксований потік обробки vs цикл міркувань

Стандартний бекенд обробляє запит через орієнтований ациклічний граф, який програміст закодував вручну:

[Request] ──> [Validate Input] ──> [Database Query] ──> [Transform Data] ──> [Response]
                     │
                     └── (Invalid) ──> [Return 400]

Кожен варіант розвитку справи — чи то if/else, switch чи try/catch — визначається заздалегідь. Код не може створити крок, якого ви не написали. Неочікуваний вхідний даний призводить до помилки або до використання заздалегідь визначеного альтернативного рішення.

Агент не дотримується жорстко визначеної послідовності дій. Він функціонує в циклі під час виконання, який часто будується за шаблоном ReAct — скороченням від Reason plus Act. Бекенд передає керування потоком дій великій мовній моделі:

┌────────────────────────┐
                       ▼                        │
[Objective] ──> [LLM Evaluates State]           │
                       │                        │
             Does it need more info?            │
             ├── Yes ──> [Select Tool & Arguments]
             │                  │
             │           [Execute Tool] ────────┘
             │           (API, DB, Search)
             │
             └── No  ──> [Return Final Answer]

Одна ітерація циклу складається з чотирьох етапів:

  • Моделі надаються мета, контекст та дії, вже виконані до цього моменту.
  • Вона перевіряє, чи є у неї достатньо інформації, чи потрібен зовнішній інструмент.
  • Якщо їй потрібні дані, вона формує структурований запит до інструменту — наприклад, для читання файлу, виклику API або виконання запиту.
  • Ваша система виконує цей інструмент, поміщає необроблені результати у вікно контексту моделі та знову запускає цикл.

Важливо те, що модель динамічно обирає послідовність кроків під час виконання.

Приклад: повернення грошей за затримку доставки

Розглянемо внутрішній інструмент, який повертає гроші клієнту у разі затримки доставки.

Традиційний підхід

У сервісі Express або FastAPI ви можете написати обробник, який знаходить замовлення, застосовує суворе правило, здійснює повернення грошей та надсилає сповіщення:

// Traditional Backend: Strict, deterministic logic
async function handleRefund(orderId: string, customerReason: string) {
  const order = await db.orders.findById(orderId);
  if (!order) {
    throw new NotFoundError("Order not found");
  }
  // Strict business rule written by an engineer
  const isEligible = order.status === "DELAYED" && order.daysDelayed > 5;

  if (!isEligible) {
    return { status: "rejected", reason: "Delay does not meet refund criteria." };
  }
  const refund = await paymentGateway.refund(order.paymentIntentId);
  await db.orders.update(orderId, { status: "REFUNDED" });
  await emailService.sendRefundNotice(order.customerEmail);
  return { status: "success", refundId: refund.id };
}

Цей код виконується менш ніж за 100 мілісекунд, коштує майже нічого з точки зору обчислень та поводиться однаково щоразу. Водночас він є крихким. Припустимо, клієнт пише, що посилка прибула вчасно, але кур’єр переехав поштову скриньку. Система відхиляє запит на повернення грошей, оскільки значення daysDelayed дорівнює нулю, адже жодне з її жорстко закодованих правил не дозволяє їй робити висновки поза межами цих правил.

Підхід агента

Агент вирішує, які інструменти використати, на основі пояснень клієнта:

// Agent Loop: The model decides which tools to call
import { generateText, tool } from "ai";
import { z } from "zod";
const tools = {
  fetchOrder: tool({
    description: "Fetch order details and shipping history",
    parameters: z.object({ orderId: z.string() }),
    execute: async ({ orderId }) => db.orders.findById(orderId),
  }),
  issueRefund: tool({
    description: "Issue a monetary refund to the customer",
    parameters: z.object({
      paymentIntentId: z.string(),
      amount: z.number(),
      reason: z.string()
    }),
    execute: async (args) => paymentGateway.refund(args.paymentIntentId, args.amount),
  }),
  escalateToHuman: tool({
    description: "Escalate edge cases or physical damage to a support manager",
    parameters: z.object({ ticketSummary: z.string() }),
    execute: async ({ ticketSummary }) => supportDesk.createEscalation(ticketSummary),
  })
};
// The execution is governed by an evaluation loop
const response = await generateText({
  model: yourConfiguredLLM,
  tools,
  maxSteps: 5, // Prevents infinite execution loops
  prompt: `Customer Issue: "The package arrived on time, but the driver ran over my mailbox. Order #1234."
           Evaluate policy guidelines and take appropriate action.`,
});

Тут модель читає скаргу, отримує інформацію про замовлення, бачить, що проблема полягає у пошкодженні товару, а не у логістиці доставки, пропускає звичайну перевірку повернення грошей та викликає функцію escalateToHuman. Така гнучкість має свою ціну. Для виконання цього процесу знадобилося три запуски моделі, було витрачено кілька центів у вигляді токенів, і час виконання становив 4 секунди, тоді як оператору вистачало 50 мілісекунд.

Як вони порівнюються

Майже кожна операційна припущення змінюється, коли переходять від детермінованого бекенду до агентської системи.

  • Потік керування: у першому випадку код визначає кроки, тоді як у другому LLM обирає їх у межах циклу.
  • Шлях виконання: у детермінованому бекенді він відомий на момент компіляції, тоді як у агентській системі виявляється під час виконання.
  • Затримка: у детермінованому бекенді вона становить приблизно 10–200 мс та є прогнозованою, тоді як у агентській системі — від 2 до 30 секунд або більше, і є змінною залежно від кількості ітерацій.
  • Способи збою: синтаксичні помилки, некеровані винятки та значення null у бекендах, а також хибні аргументи, нескінченні цикли та недотримання обмежень у агентах.
  • Тестування: з одного боку — модульні, інтеграційні та кінцево-кінцеві перевірки, з іншого — використання LLM як судді, оцінювальні бенчмарки та семантичні перевірки.
  • Стан: транзакційні дані SQL чи NoSQL проти історії розмов, обмежень вікна контексту та робочої пам’яті.
  • Витрати: CPU та пам’ять, які є стабільними та недорогими, проти кількості токенів вхідних та вихідних даних за кожну ітерацію циклу, яка може швидко зростати.
  • Вікно контексту як робоча пам’ять

    Бекенд зберігає стан у змінних, ключах Redis або рядах бази даних, а функція читає лише ті поля, які їй потрібні. Для агента вікно контексту є короткостроковою робочою пам’яттю. Кожного разу, коли запускається інструмент, його результат серіалізується у текст, зазвичай у форматі JSON, та додається до історії запитів. Типовий процес виконання розвивається так:

    • Крок 1: запит користувача, приблизно 50 токенів.
    • Крок 2: запит моделі до інструменту, приблизно 40 токенів.
    • Крок 3: результат інструменту, що додає приблизно 500 токенів.
    • Крок 4: модель аналізує розширений запит та здійснює другий виклик, що додає близько 80 токенів.
    • Крок 5: надходить другий результат, що додає приблизно 1 200 токенів.

    Обсяг запиту зростає з кожним циклом. Оскільки і вартість, і затримка залежать від розміру вхідних даних, п’ятий крок є значно повільнішим та дорожчим за перший.

    Частою помилкою під час роботи є надсилання агенту цілих рядків бази даних або величезних об’ємів даних через API. Якщо інструмент searchUsers() виводить у контекст 50 повних профілів, це швидко призведе до вичерпання ефективного часу на обробку інформації моделлю. Розробляйте інструменти для агентів як публічні API: обробляйте та очищуйте результати так, щоб вони містили лише ті поля, які потрібні моделі для наступного рішення.

    Чому агентів важче підтримувати у робочому стані

    При збої серверної частини ви отримуєте стек-трейс. Ви повторно виконуєте той самий набір даних локально, перевіряєте змінні та виправляєте помилку. Агенти ж збиваються з роботи імовірнісно.

    Нескінченні цикли. Припустимо, агент викликає API та отримує непомітну помилку перевірки, наприклад invalid_date_format. Він пробує знову, використовуючи трохи інший формат дати, а потім знову. Оскільки немає жорсткого ліміту кількості ітерацій (maxSteps), він може витратити сотні доларів на API-кредити, намагаючись подолати перешкоди під час перевірки.

    Вигадані аргументи інструментів. Схеми та механізми перевірки забезпечують типову безпеку коду під час виконання. Штучний інтелект може 98% часу генерувати коректний JSON, що відповідає вашій схемі, але іноді додає уявний параметр. Ось що ви можете очікувати:

    // Expected:
    { "customerId": "cust_123", "amount": 50 }
    

    Що іноді виводить модель при високій температурі:

    // What the model occasionally outputs under high temperature:
    { "user_id": "cust_123", "refund_value": "$50.00" }
    

    Якщо середовище виконання не застосовує суворого аналізу схеми за допомогою Zod чи Pydantic перед запуском фактичної функції, ваш бекенд зламається.

    Непередбачуване маршрутизування. Запит, який вчора вимагав двох кроків, сьогодні може потребувати чотирьох через невеликі зміни у формулюванні користувачем. Це ускладнює дотримання обіцянок щодо часу відповіді.

    Вибір правильної архітектури

    Ейєнти привертають стільки уваги, що розробники намагаються будувати цілі системи на їхній основі, що є дорогою помилкою.

    Залишайтеся зі звичайним кодом бекенду, коли:

    • Бізнес-логіка є відомою та стабільною, як у випадку з формуванням рахунків-фактур, автентифікацією, операціями CRUD та обчисленням вартості кошика.
    • Необхідна низька затримка, як у процесах оплати, автодоповненні пошуку та реальному часі повідомлень.
    • Помилки можуть призвести до проблем зі відповідністю стандартам чи фінансових ризиків, як у веденні бухгалтерського обліку, медичних записах та контролі дозволів.
  • Витрати за запит мають велике значення при великих обсягах обробки, наприклад при обробці мільйонів подій на годину.
  • Звертайтеся до агента, коли:

    • Шлях до рішення є неоднозначним, наприклад пошук рейсу з трьохгодинною пересадкою, вегетаріанськими стравами та ціною до 800 доларів.
    • Дані є неструктурованими та хаотичними, як-от різноманітні PDF-рахунки, електронні листи у вільній формі чи звіти клієнтів про проблеми.
    • Система мусить координувати різні інструменти без спільного API, наприклад переглядати зовнішні сайти, вивчати документацію, знайдену по дорозі, чи сортувати проблеми між кількома продуктами.

    Гібридний дизайн, який працює

    У реальних застосунках найнадійнішим дизайном рідко є суто агентський підхід. Це детерміністичний бекенд із агентом, розміщеним у контрольованих межах:

    [Incoming Request] ──> [Traditional API Gateway] ──> Authentication / Rate Limiting
                                  │
                                  ▼
                          [Standard Business Logic] ──> Fast DB Reads / Validation
                                  │
                                  ▼
                        [Targeted Agent Boundary]  ──> Handles unstructured task
                                  │                    (Restricted to 3 safe tools)
                                  ▼
                          [Standard Business Logic] ──> Validates agent output
                                  │
                                  ▼
    [Final Response] <─── [Structured DB Write]
    

    Зберігайте механізми маршрутизації, авторизації, зберігання даних та критичної перевірки бізнес-логіки у детермінованому коді. Звертайтеся до агента лише для підзавдань, які справді потребують міркувань у стилі людини та гнучкої організації роботи інструментів.

    Висновок

    Агент не кращий за традиційний бекенд – це просто інший парадигматичний підхід до обробки даних. Бекенди надають перевагу прогнозованості, швидкості та низьким витратам завдяки дотриманню чітких інструкцій, тоді як агенти сприяють адаптивності та вирішенню проблем, дозволяючи ШІ обирати шлях виконання у процесі міркувань.

    Тож перед розробкою наступної функції не ставте питання про те, чи може це зробити агент – адже за наявності достатньої кількості токенів він, ймовірно, зможе. Замість цього запитайте себе, чи вже знаєте ви кроки, необхідні для вирішення проблеми. Якщо так, напишіть код самостійно та залиште агентів для відкритих проблем, які звичайний код недостатньо гнучкий, щоб вирішити.