Strona główna / Artykuły

Agenci AI kontra tradycyjne backendi: przepływ sterowania, koszty i możliwe błędy

Porównaj deterministyczny backend z pętlą agenta LLM przy użyciu przykładu zwrotu pieniędzy: opóźnienia, koszty tokenów, wzrost kontekstu, możliwe błędy oraz sytuacje, w których hybrydowy projekt sprawdzi się najlepiej.

Wydawca: REACTAPP.TOP9 min czytania1966 słów

Źródła: AI Agents vs Traditional Backend Applications: What’s Actually Different?

Inżynieria backendu od dawna opiera się na jednej uspokajającej cechy: determinizmie. Napisz endpoint, który przyjmuje zamówienie, sprawdza stan zapasów w PostgreSQL i pobiera opłatę z karty przez Stripe – ta sama sekwencja jest wykonywana za każdym razem. Przy tych samych danych wejściowych i tym samym stanie bazy danych linia 42 zawsze jest wykonywana przed linią 43.

Następnie branża zaczęła nazywać agenty AI następnym etapem systemów backendowych. Łatwo odrzucić to jako element marketingu, ponieważ agent przypomina również usługę w kontenerze, która wywołuje zewnętrzne API i zapisuje dane do bazy danych. Jednak w rzeczywistości przepływ sterowania uległ zmianie. W klasycznym backendzie programista z góry ustala każdy krok, gałąź i przypadek graniczny. W przypadku agenta programista dostarcza cel oraz zestaw narzędzi, a model probabilistyczny decyduje o krokach, ich kolejności oraz momencie zakończenia zadania. Traktując agenta jak kolejną mikrousługę, napotkasz poważne problemy z opóźnieniami, niezawodnością, testowaniem oraz rachunkami za chmurę.

Kanał stały versus pętla rozumowania

Standardowy backend obsługuje żądanie za pomocą grafu skierowanego acyklicznego, który programista zdefiniował w sposób sztywny:

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

Każda gałąź, czy to if/else, switch czy try/catch, jest określana z góry. Kod nie może stworzyć kroku, którego nie napisałeś. Nieoczekiwany wprowadzony danych powoduje błąd lub z góry zdefiniowane rozwiązanie awaryjne.

Agent nie postępuje według sztywnej sekwencji. Działa w pętli w czasie wykonywania, często zbudowanej według wzorca ReAct, skrótu od Reason plus Act. Serwer przekazuje kontrolę nad przepływem zadania modelowi językowemu o dużej mocy obliczeniowej:

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

Jedna iteracja pętli składa się z czterech części:

  • Model otrzymuje cel, kontekst oraz dotychczas podjęte działania.
  • Pyta on, czy ma wystarczająco dużo informacji, czy potrzebuje narzędzia z zewnątrz.
  • Jeśli potrzebuje danych, wysyła strukturyzowany wywołanie narzędzia, takie jak odczyt pliku, wezwanie API lub wykonanie zapytania.
  • Twoje środowisko wykonuje to narzędzie, umieszcza surowy wynik w oknie kontekstowym modelu i ponownie uruchamia pętlę.

Ważne jest to, że model dynamicznie wybiera sekwencję kroków w trakcie działania.

Przykład: zwrot pieniędzy za opóźnioną dostawę

Rozważmy wewnętrzne narzędzie, które zwraca pieniądze klientowi, którego dostawa się opóźniła.

Tradycyjny sposób

W usłudze Express lub FastAPI można napisać obsługę, która wyszukuje zamówienie, stosuje ścisłe zasady, przeprowadza zwrot pieniędzy i wysyła powiadomienie:

// 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 };
}

Ten kod kończy wykonywanie się w mniej niż 100 milisekund, nie kosztuje prawie nic w zakresie obliczeń i zachowuje się identycznie za każdym razem. Jest jednak kruchy. Załóżmy, że klient pisze, iż paczka dotarła na czas, ale kierowca przejechał nad skrzynką pocztową. System odrzuca zwrot pieniędzy, ponieważ daysDelayed wynosi zero, gdyż żadna z jego sztywno zdefiniowanych zasad nie pozwala mu na rozważanie sytuacji poza nimi.

Sposób agenta

Agent decyduje, jakie narzędzia użyć, na podstawie wyjaśnienia klienta:

// 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.`,
});

Tutaj model czyta skargę, pobiera zamówienie, stwierdza, że problem dotyczy uszkodzenia mienia, a nie logistyki dostawy, pomija standardową weryfikację zwrotu pieniędzy i wywołuje funkcję escalateToHuman. Ta elastyczność ma swoją cenę – proces wymagał trzech prób zadania zapytania do LLM, zużył kilka centów w postaci tokenów i trwał 4 sekundy, podczas gdy obsługa potrzebowała zaledwie 50 milisekund.

Jak one się porównują

Prawie każde założenie operacyjne zmienia się, gdy przechodzi się od deterministycznego backendu do systemu opartego na agentach.

  • Przepływ sterowania: w jednym przypadku kod określa kroki, natomiast w drugim LLM wybiera je w ramach pętli.
  • Ścieżka wykonywania: w przypadku backendu jest znana już podczas kompilacji, natomiast w systemie agentowym zostaje odkryta dopiero podczas wykonywania.
  • Latencja: u backendu wynosi około 10 do 200 ms i jest przewidywalna, natomiast u agenta wynosi od 2 do 30 sekund lub więcej i jest zmienna, w zależności od liczby iteracji.
  • Tryby awarii: błędy składniowe, nieobsłużone wyjątki oraz wartości null w backendach, argumenty wynikające z halucynacji, pętle nieskończone oraz pominięte ograniczenia u agentów.
  • Testowanie: testy jednostkowe, integracyjne i end-to-end z jednej strony, LLM-as-a-judge, benchmarki oceniające oraz twierdzenia semantyczne z drugiej.
  • Stan: dane transakcyjne w formacie SQL lub NoSQL w porównaniu z historią rozmowy, ograniczeniami okna kontekstowego oraz pamięcią roboczą.
  • Koszt: zużycie CPU i pamięci, które są stałe i niskie, w porównaniu z tokenami wejściowymi i wyjściowymi przy każdej iteracji pętli, które mogą szybko rosnąć.
  • Okno kontekstowe jako pamięć robocza

    Tło systemu przechowuje stan w zmiennych, kluczach Redis lub wierszach bazy danych, a funkcja odczytuje tylko te pola, które są jej potrzebne. Dla agenta okno kontekstowe stanowi krótkoterminową pamięć roboczą. Za każdym razem, gdy uruchamiana jest narzędzie, jego wynik jest serializowany do tekstu, zwykle w formacie JSON, i dodawany do historii zapytań. Typowy przebieg wygląda w ten sposób:

    • Krok 1: zapytanie użytkownika, około 50 tokenów.
    • Krok 2: żądanie wywołania narzędzia przez model, około 40 tokenów.
    • Krok 3: wynik narzędzia, dodający około 500 tokenów.
    • Krok 4: model analizuje rozszerzone zapytanie i wysyła drugie wywołanie, dodając około 80 tokenów.
    • Krok 5: przychodzi drugi wynik, dodając około 1200 tokenów.

    Zapytanie powiększa się z każdym cyklem. Ponieważ zarówno cena, jak i opóźnienie zmieniają się w zależności od wielkości wejścia, piąty krok jest wyraźnie wolniejszy i droższy od pierwszego.

    Częstym błędem w implementacji jest zwracanie do agenta całych wierszy bazodanowych lub ogromnych obciążeń API. Jeśli narzędzie searchUsers() wypełni kontekst 50 kompletnymi profilami, szybko wyczerpie się okno efektywnego rozumowania modelu. Narzędzia dla agentów należy projektować jak publiczne API: filtrować i oczyszczać wyniki, aby zawierały jedynie te pola, których model potrzebuje do podjęcia kolejnej decyzji.

    Dlaczego trudniej jest utrzymywać agenty w działaniu

    Błąd w backendzie daje nam ślad błędu. Możemy ponownie wykonać ten sam zapytanie lokalnie, sprawdzić zmienne i naprawić błąd. Agent natomiast zawodzi w sposób probabilistyczny.

    Nieskończone pętle. Załóżmy, że agent wywołuje API i otrzymuje subtelny błąd walidacji, np. invalid_date_format. Spróbuje ponownie z nieco innym łańcuchem daty, a potem jeszcze raz. Bez sztywnego ograniczenia liczby iteracji (maxSteps) może zużyć setki dolarów na kredyty API, walcząc z mechanizmem walidacji.

    Sfabrykowane argumenty narzędzi. Schematy i mechanizmy walidacji zapewniają zwykłemu kodowi bezpieczeństwo typów w czasie wykonywania. LLM może w 98% przypadków generować poprawny JSON zgodny z Twoim schematem, ale czasami dodać wyimaginowany parametr. To, czego można się spodziewać:

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

    To, co model czasami wytwarza przy wysokiej temperaturze:

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

    Jeśli środowisko wykonywania nie stosuje ścisłego analizowania schematu za pomocą narzędzi takich jak Zod lub Pydantic przed uruchomieniem rzeczywistej funkcji, twój backend się zawali.

    Nieprzewidywalne routowanie. Zapytanie, które wczoraj wymagało dwóch kroków, może dziś wymagać czterech, ponieważ użytkownik sformułował je nieco inaczej. To utrudnia dotrzymanie obietnic dotyczących czasu odpowiedzi.

    Wybór odpowiedniej architektury

    Agenty przyciągają tak wiele uwagi, że programiści próbują budować z nich całe systemy, co jest kosztownym błędem.

    Trzymaj się zwykłego kodu backendu, gdy:

    • Logika biznesowa jest znana i stabilna, jak w przypadku fakturowania, autoryzacji, operacji CRUD oraz obliczeń koszyka zakupowego.
    • Opóźnienia muszą być niskie, np. w procesach płatności, autodopasowywaniu wyników wyszukiwania i komunikacji w czasie rzeczywistym.
    • Błędy niosą ze sobą ryzyko związane z przepisami lub finansami, jak w przypadku ksiąg rachunkowych, kart medycznych i egzekwowania uprawnień.
  • Koszt za zapytanie ma znaczenie w skali dużych obciążeń, takich jak przetwarzanie milionów zdarzeń na godzinę.
  • Zwróć się do agenta w następujących sytuacjach:

    • Droga do rozwiązania jest niejasna, na przykład znalezienie lotu z trzogodzinnym przesiadką, dań wegetariańskich i ceną poniżej 800 dolarów.
    • Dane wejściowe są nieuporządkowane i brakujące struktury, jak różnorodne faktury w formacie PDF, e-maile o dowolnej strukturze lub raporty błędów od klientów.
    • System musi koordynować różne narzędzia bez wspólnej API, na przykład przeglądanie stron zewnętrznych, analiza dokumentacji znalezionej po drodze lub sortowanie problemów pomiędzy kilkoma produktami.

    Hybrydowy projekt, który działa

    W rzeczywistych aplikacjach najbardziej niezawodnym rozwiązaniem rzadko jest czysty agent. Jest to deterministyczny backend z agentem umieszczonym w kontrolowanych granicach:

    [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]
    

    Zachowaj routowanie, autoryzację, przechowywanie danych oraz kluczowe weryfikacje biznesowe w kodzie deterministycznym. Korzystaj z agenta tylko wtedy, gdy konkretna podzadanie naprawdę wymaga rozumowania w stylu ludzkim oraz swobodnej orkiestracji narzędzi.

    Główny wniosek

    Agent nie jest lepszy od tradycyjnego backendu – to zupełnie inny paradygmat obliczeniowy. Backendy preferują przewidywalność, szybkość i niskie koszty poprzez realizację wyraźnych instrukcji, natomiast agenci skupiają się na adaptowalności i rozwiązywaniu problemów, pozwalając modelowi językowemu wybrać ścieżkę wykonania w ramach pętli rozumowania.

    Zanim zaczniesz pracować nad kolejną funkcją, pomijaj pytanie, czy agent mógłby to zrobić – przy wystarczającej liczbie tokenów prawdopodobnie tak. Zamiast tego zastanów się, czy już znasz kroki potrzebne do rozwiązania problemu. Jeśli tak, napisz kod samodzielnie i zachowaj agenty na problemy otwarte, których zwykły kod nigdy nie był wystarczająco elastyczny, by je rozwiązać.

    Pozycje pokrewne

  • Jeszcze nie używasz agentów AI? Przewodnik w pięć minut, jak zacząć — Dlaczego agenci przewyższają chatboty, plus szybkie wskazówki dotyczące konfiguracji dla Claude Cowork, Claude Code, aplikacji Codex i Codex CLI, w tym informacje o kosztach, ograniczeniach oraz przykładowy proces pracy.
  • Notatki praktyczne: Utrzymywanie synchronizacji typesów frontendu i backendu z Typescriptem — Szczegółowy przewodnik po notatkach praktycznych dotyczących synchronizacji typesów frontendu i backendu z Typescriptem: kontrakty, sprawdzania oraz gotowe fragmenty kodu dla zespołów wdrażających ten wzorzec.
  • MCP, RAG i LLM: Co każda warstwa faktycznie robi w twojej aplikacji — Proste objaśnienie tego, co MCP, RAG oraz LLM dodają do aplikacji, którą już potrafisz stworzyć, oraz kiedy standaryzowany protokół narzędziowy jest warty dodatkowej warstwy.