Agentes de IA frente a backends tradicionales: flujo de control, costos y modos de fallo
Compare un backend determinista con un bucle de agente LLM utilizando un ejemplo de reembolso: latencia, costos de tokens, crecimiento del contexto, modos de fallo y dónde encaja mejor un diseño híbrido.
Fuentes: AI Agents vs Traditional Backend Applications: What’s Actually Different?
La ingeniería de backend siempre se ha basado en una propiedad tranquilizadora: el determinismo. Si se escribe un endpoint que recibe un pedido, verifica las existencias en PostgreSQL y cobra mediante Stripe, la misma secuencia se ejecuta cada vez. Con las mismas entradas y el mismo estado de la base de datos, la línea 42 siempre se ejecuta antes que la línea 43.
Luego, la industria comenzó a denominar a los agentes de IA como la siguiente etapa de los sistemas backend. Es fácil descartar esto como mera estrategia de marketing, ya que un agente también parece un servicio dentro de un contenedor que llama a APIs externas y escribe en una base de datos. Sin embargo, por debajo de todo esto, el flujo de control ha cambiado. En un backend clásico, el desarrollador soluciona cada paso, rama y caso límite con antelación. En un agente, el desarrollador proporciona un objetivo y un conjunto de herramientas, y un modelo probabilístico elige los pasos, su orden y el momento en que se completa la tarea. Si tratas a un agente como otro microservicio más, te encontrarás con serios problemas de latencia, fiabilidad, pruebas y facturas de nube.
Flujo fijo versus bucle de razonamiento
Un backend estándar procesa una solicitud a través de un grafo acíclico dirigido que el programador definió de forma fija:
[Request] ──> [Validate Input] ──> [Database Query] ──> [Transform Data] ──> [Response]
│
└── (Invalid) ──> [Return 400]
Cada rama, ya sea if/else, switch o try/catch, se decide de antemano. El código no puede inventar un paso que usted no haya escrito. Una entrada inesperada genera un error o una solución alternativa predefinida.
Un agente no sigue una línea estática. Funciona dentro de un bucle en tiempo de ejecución, generalmente basado en el patrón ReAct, abreviatura de Reason plus Act. El backend transfiere el flujo de control a un modelo de lenguaje grande:
┌────────────────────────┐
▼ │
[Objective] ──> [LLM Evaluates State] │
│ │
Does it need more info? │
├── Yes ──> [Select Tool & Arguments]
│ │
│ [Execute Tool] ────────┘
│ (API, DB, Search)
│
└── No ──> [Return Final Answer]
Un ciclo consta de cuatro partes:
- Se le proporciona al modelo el objetivo, el contexto y las acciones realizadas hasta el momento.
- El modelo verifica si cuenta con suficiente información o necesita una herramienta externa.
- Si necesita datos, emite una solicitud estructurada a una herramienta, como leer un archivo, llamar a una API o ejecutar una consulta.
- El entorno de ejecución procesa la herramienta, coloca los resultados brutos en la ventana de contexto del modelo y reinicia el ciclo.
Lo importante es que el modelo seleccione dinámicamente la secuencia de pasos en tiempo de ejecución.
Ejemplo: reembolso por envío retrasado
Considere una herramienta interna que realiza el reembolso a un cliente cuyo envío se ha demorado.
El método tradicional
En un servicio Express o FastAPI, podría escribirse un manejador que busque el pedido, aplique una regla estricta, emita el reembolso y envíe una notificación:
// 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 };
}
Este código se ejecuta en menos de 100 milisegundos, cuesta casi nada en términos de recursos computacionales y se comporta de manera idéntica cada vez. Sin embargo, también es frágil. Supongamos que un cliente indica que el paquete llegó a tiempo, pero que el repartidor atropelló el buzón. El sistema rechaza el reembolso porque daysDelayed es cero, ya que ninguna de sus reglas predefinidas le permite razonar más allá de ellas.
El enfoque con agentes
Un agente decide qué herramientas utilizar según la explicación del cliente:
// 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.`,
});
Aquí el modelo lee la queja, obtiene el pedido, verifica que el problema es daño a la propiedad y no una cuestión logística de envío, omite la comprobación habitual de reembolso y llama a escalateToHuman. Esa flexibilidad tiene un costo. El proceso requirió tres intentos con el LLM, consumió unos pocos centavos en tokens y tardó 4 segundos, mientras que el operador humano necesitaba 50 milisegundos.
Comparación entre ambos
Casi todas las suposiciones operativas cambian al pasar de un backend determinista a un sistema agente.
- Flujo de control: en un caso el código define los pasos, mientras que en el otro el LLM los elige dentro de un bucle.
- Ruta de ejecución: conocida en tiempo de compilación frente a descubierta en tiempo de ejecución.
- Latencia: entre 10 y 200 ms y predecible en un backend, mientras que en un agente es de 2 a 30 segundos o más y variable, dependiendo de las iteraciones.
El ventanal de contexto como memoria de trabajo
Un backend almacena el estado en variables, claves de Redis o filas de base de datos, y una función solo lee los campos que necesita. Para un agente, la ventana de contexto funciona como memoria de trabajo a corto plazo. Cada vez que se ejecuta una herramienta, su salida se serializa en texto, generalmente en formato JSON, y se agrega al historial de solicitudes. Un ciclo típico evoluciona de la siguiente manera:
- Paso 1: la solicitud del usuario, de unos 50 tokens.
- Paso 2: la solicitud de llamada a herramienta del modelo, de unos 40 tokens.
- Paso 3: el resultado de la herramienta, que añade aproximadamente 500 tokens.
- Paso 4: el modelo analiza la solicitud más extensa y realiza una segunda llamada, que añade unos 80 tokens.
- Paso 5: llega el segundo resultado, sumando unos 1,200 tokens.
La solicitud crece con cada ciclo. Dado que tanto el precio como la latencia aumentan según el tamaño de la entrada, el quinto paso es notablemente más lento y costoso que el primero.
Un error común en la generación de contenido es devolver filas completas de la base de datos o cargas enormes de API al agente. Si una herramienta searchUsers() inserta 50 perfiles completos en el contexto, pronto se agotará el tiempo de razonamiento efectivo del modelo. Diseñe las herramientas para agentes como APIs públicas: recorte y sanee las salidas para que contengan únicamente los campos que el modelo necesita para tomar su próxima decisión.
Por qué es más difícil mantener en funcionamiento a los agentes
Un backend defectuoso le proporciona un rastro de errores. Puede reproducir la misma carga localmente, inspeccionar las variables y corregir el error. Un agente falla de forma probabilística.
Bucles interminables. Supongamos que el agente llama a una API y recibe un error de validación sutil como invalid_date_format. Vuelve a intentarlo con una cadena de fecha ligeramente diferente y luego lo intenta nuevamente. Al no haber un límite estricto en las iteraciones (maxSteps), puede gastar cientos de dólares en créditos de API discutiendo con un validador.
Argumentos de herramientas inventados. Los esquemas y la validación brindan seguridad en cuanto al tipo de código en tiempo de ejecución. Un LLM puede generar JSON válido que cumpla con su esquema el 98% de las veces, pero ocasionalmente incluye un parámetro imaginario. Lo que se espera:
// Expected:
{ "customerId": "cust_123", "amount": 50 }
Lo que el modelo emite a veces a alta temperatura:
// What the model occasionally outputs under high temperature:
{ "user_id": "cust_123", "refund_value": "$50.00" }
Si el entorno de ejecución no aplica un análisis estricto del esquema con herramientas como Zod o Pydantic antes de ejecutar la función real, su backend se colapsará.
Ruteo impredecible. Una consulta que ayer requería dos pasos puede necesitar cuatro hoy debido a que el usuario la formuló de manera ligeramente diferente. Eso hace difícil cumplir con los acuerdos de nivel de servicio en cuanto al tiempo de respuesta.
Elegir la arquitectura adecuada
Los agentes atraen tanta atención que los desarrolladores intentan construir sistemas completos con ellos, lo cual es un error costoso.
Manténgase con código de backend convencional cuando:
- La lógica empresarial es conocida y estable, como en la facturación, autenticación, operaciones CRUD y cálculos de carritos.
- La latencia debe ser baja, como en flujos de pago, autocompletado de búsquedas y mensajería en tiempo real.
- Los errores conllevan riesgos de cumplimiento o financieros, como en libros contables, registros médicos y aplicación de permisos.
Busque a un agente cuando:
- La ruta hacia una solución es ambigua, por ejemplo encontrar un vuelo con una escala de tres horas, comidas vegetarianas y un precio inferior a 800 dólares.
- Los datos de entrada son desordenados y no estructurados, como facturas en PDF variadas, correos electrónicos sin formato fijo o informes de errores de los clientes.
- El sistema debe coordinar herramientas dispares sin una API compartida, por ejemplo navegar por sitios externos, explorar documentación que se encuentra en el camino o clasificar problemas entre varios productos.
El diseño híbrido que funciona
En las aplicaciones reales, el diseño más fiable rara vez es uno basado únicamente en agentes. Se trata de un backend determinista con un agente ubicado dentro de unos límites controlados:
[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]
Mantenga la enrutación, autorización, persistencia y la validación crítica de negocios en código determinista. Recurra al agente únicamente para las subtareas que realmente necesitan razonamiento al estilo humano y una orquestación flexible de herramientas.
Conclusión
Un agente no es mejor que un backend tradicional; se trata de un paradigma computacional diferente. Los backends priorizan la previsibilidad, la velocidad y el bajo costo al seguir instrucciones explícitas, mientras que los agentes favorecen la adaptabilidad y la resolución de problemas al permitir que un LLM elija la ruta de ejecución en un bucle de razonamiento.
Por lo tanto, antes de desarrollar su próxima función, omita la pregunta de si un agente podría hacerlo, ya que con suficientes tokens probablemente pueda. Pregúntese si ya conoce los pasos necesarios para resolver el problema. Si es así, escriba el código usted mismo y reserve los agentes para los problemas abiertos que el código ordinario nunca fue lo suficientemente flexible como para manejar.