Sistemas agénticos: qué son y por qué casi nunca los necesitas
Todo el mundo quiere construir agentes. En cada conferencia, en cada demo y en cada hilo de redes sociales aparece la misma promesa: dale un objetivo a un LLM, dale acceso a un puñado de herramientas y déjalo trabajar solo. Los frameworks compiten entre sí por ofrecer más autonomía: menos código, más orquestación automática, un agente que planifica, ejecuta y se corrige a sí mismo sin que nadie tenga que supervisar cada paso.
Y hay que reconocerlo: las demos son impresionantes. Ver a un agente leer un ticket, escribir código, correr los tests, revisar el resultado y volver a intentarlo cuando algo falla produce una sensación real de progreso. Es fácil salir de una demo así convencido de que más autonomía siempre es mejor ingeniería.
Pero hay una pregunta que casi nadie se hace antes de empezar a construir: mi problema, ¿de verdad pedía un agente? La mayoría de las veces la respuesta es no. La arquitectura agéntica se elige porque está de moda, porque el framework la hace fácil de armar, o porque suena más sofisticado que un prompt bien escrito con un poco de código determinista alrededor.
Esa es la tesis de este post: la autonomía que hace atractivo a un agente es exactamente lo que lo hace caro, lento y difícil de depurar. No es que los sistemas agénticos sean malos. Es que casi siempre son la respuesta equivocada para el problema que tienes enfrente.
Qué es realmente un sistema agéntico
Antes de seguir criticando hay que definir el término con precisión, porque casi nadie lo hace. Decir que un sistema agéntico es “IA que hace cosas” o “un LLM con acceso a internet” no dice nada útil. Con esa definición, cualquier script que llama a una API calificaría como agente, y evidentemente no es eso lo que la gente tiene en mente cuando habla de sistemas agénticos.
La definición precisa es más estrecha: un sistema agéntico es un LLM dentro de un loop. En cada vuelta del loop el modelo observa el resultado de la acción anterior, decide qué herramienta llamar a continuación y decide también cuándo detenerse. No hay una secuencia de pasos escrita de antemano: el modelo vuelve a evaluar la situación en cada iteración y elige el siguiente movimiento con la información que tiene disponible en ese momento.
Lo que define a un sistema agéntico no son las herramientas ni el LLM en sí: es quién decide el flujo de control. En el software tradicional esa decisión la toma el desarrollador, por adelantado, con un
if o un for. En un sistema agéntico esa decisión la toma el modelo, en tiempo de ejecución, y puede decidir distinto la próxima vez aunque el input sea prácticamente el mismo.Para que ese loop funcione hacen falta tres ingredientes, ni uno más ni uno menos:
- Herramientas que el modelo puede invocar: funciones, APIs, comandos de shell, cualquier cosa que le dé la capacidad de actuar sobre el mundo.
- Un loop que toma el resultado de cada herramienta y se lo devuelve al modelo como parte del contexto para la siguiente decisión.
- Un criterio de parada que también pertenece al modelo: es él quien decide cuándo el trabajo está terminado, no una condición fija que el desarrollador programó de antemano.
Ese ciclo se ve así:
El ciclo agéntico: el modelo decide cuándo parar
La escalera de complejidad
Entre una llamada directa a un LLM y un agente autónomo no hay un salto: hay una escalera de cuatro escalones. Cada escalón resuelve un poco más que el anterior, pero también cuesta más y, sobre todo, te quita una decisión de las manos. Vale la pena recorrerla completa antes de decidir en cuál conviene quedarse.
1. Prompt directo
Es la forma más simple: una llamada, una entrada y una salida. No hay memoria entre llamadas ni pasos intermedios. Tú controlas absolutamente todo, desde el prompt hasta qué se hace con la respuesta.
Basta con esto cuando la tarea es una transformación de un solo paso: clasificar un texto, resumirlo, extraer un dato puntual o reescribirlo con otro tono. Si el trabajo cabe completo en una sola llamada, agregar cualquier capa adicional es puro costo sin beneficio.
2. Cadena de prompts
La salida de una llamada se convierte en la entrada de la siguiente. El flujo sigue siendo tuyo: está escrito en código, con un orden fijo de pasos que no cambia de una ejecución a otra.
Basta con esto cuando los pasos son conocidos de antemano y siempre ocurren en el mismo orden: extraer datos y luego resumirlos, traducir y luego dar formato, generar un borrador y luego revisarlo. El modelo aporta capacidad de lenguaje en cada paso, pero la secuencia la decidiste tú al escribir el código.
3. Workflow con ramas
Aquí el modelo empieza a aportar juicio: clasifica una entrada o evalúa una condición, y tú decides el camino a partir de ese resultado con un
match o un if. El modelo elige entre opciones, pero las opciones y las reglas que conectan una con otra siguen siendo tuyas.Basta con esto en la enorme mayoría de los casos reales. Cubre situaciones donde el camino depende de una decisión, pero esa decisión se puede enumerar de antemano: si el ticket es de facturación va por un camino, si es de soporte técnico va por otro. El control sigue en tu código; el modelo solo informa cuál rama tomar.
4. Agente
En este escalón el modelo decide el camino completo: elige qué herramienta usar en cada paso y decide también cuándo el trabajo está terminado. Ya no hay un
if ni un match escrito por ti esperando su turno: la ejecución se ramifica en tiempo real según lo que el modelo va observando.Es necesario solo cuando los pasos no se pueden enumerar de antemano, es decir, cuando ni siquiera tú podrías escribir con anticipación la lista de ramas posibles. Eso ocurre con menos frecuencia de la que el entusiasmo por los agentes sugiere.
Cuatro escalones: cada uno te quita una decisión
Cada escalón que subes añade una decisión que dejas de controlar. Esa es la moneda con la que se paga la autonomía, y conviene saber, escalón por escalón, cuánto se está gastando antes de subir el siguiente.
Los cuatro costos ocultos
Las demos de agentes nunca muestran la factura, el reloj, la bitácora de reproducción ni el postmortem. Esos cuatro documentos aparecen todos después de la demo, ya en producción, y son los que terminan definiendo si la decisión de subir el último escalón fue acertada. Lo que sigue no es teoría: es lo que me tocó ver de cerca al construir con agentes.
1. El costo económico
Un agente no hace una llamada al modelo: hace las que necesite. Cada vuelta del loop reenvía todo el contexto acumulado hasta ese momento, así que el costo no crece de forma lineal con el número de pasos, crece más rápido que eso. Lo viví de primera mano: un loop de razonamiento que se enreda, que duda entre dos caminos o que vuelve a intentar algo que ya había intentado, puede inflar el costo de una sola petición mucho más allá de lo que uno anticiparía al diseñarla.
Lo incómodo no es solo el monto: es la incertidumbre. Con una llamada directa a un LLM sé de antemano, con bastante precisión, cuánto va a costar. Con un agente solo lo sé después de que terminó, y a veces después de que terminó mal.
El prompt caching mitiga buena parte de ese reenvío de contexto, pero no elimina el costo de las vueltas adicionales: cada turno sigue siendo una llamada nueva que hay que pagar.
En el siguiente post desgloso esa factura con más detalle y muestro seis palancas concretas para reducirla.
2. La latencia
Cada vuelta del loop es un viaje de ida y vuelta al modelo. Observar, decidir, actuar: eso no es gratis en tiempo, y cuando el agente necesita varios turnos de razonamiento antes de dar una respuesta útil, esos turnos se sienten. Vi esa espera de cerca: son segundos reales de un usuario mirando una pantalla que no cambia, para una tarea que un workflow con ramas fijas habría resuelto en una sola llamada.
Lo frustrante es que la espera no siempre corresponde a más valor entregado. A veces el agente da varias vueltas para llegar exactamente al mismo resultado que un camino fijo habría dado de inmediato. La autonomía no compró nada ahí; solo compró tiempo de espera.
3. El no determinismo
El mismo input puede tomar rutas distintas en ejecuciones distintas y producir resultados distintos. Eso rompe algo básico del trabajo de ingeniería con el que todos crecimos: no puedes reproducir un fallo que no sabes cómo reproducir, y no puedes escribir una prueba estable sobre un camino que cambia de una corrida a la siguiente.
Me pasó exactamente eso: un caso que fallaba una vez y funcionaba bien la siguiente, con el mismo input y sin ningún cambio de código de por medio. La pregunta “¿esto se puede reproducir?” dejó de tener una respuesta simple, y con eso también dejó de tenerla la pregunta de si ya estaba arreglado.
4. La observabilidad
Cuando un agente falla en producción, la pregunta que importa no es “qué devolvió” sino “por qué eligió ese camino”. Sin instrumentar cada decisión del loop, esa pregunta no tiene respuesta. Y el rastro que sí queda suele ser texto libre del modelo explicando su propio razonamiento después del hecho, que no es lo mismo que un registro confiable de lo que realmente ocurrió.
Terminé necesitando instrumentar el loop paso a paso, algo que con un workflow de ramas fijas jamás hizo falta: ahí el camino ya estaba escrito, y saber qué pasó era tan simple como leer el código. Con un agente, entender un fallo se convierte en una investigación aparte.
| Costo | Cuándo aparece | Qué lo mitiga |
|---|---|---|
| Económico | Cuando el loop se enreda y reenvía contexto acumulado en cada vuelta | Bajar un escalón: fijar el número de pasos en vez de dejar que el modelo decida cuántos dar |
| Latencia | En cada turno adicional de razonamiento antes de responder | Bajar un escalón: reemplazar turnos de decisión por una rama fija que resuelve en una sola llamada |
| No determinismo | Cuando el mismo input toma rutas distintas en ejecuciones distintas | Bajar un escalón: dejar el camino escrito en código en vez de que lo decida el modelo |
| Observabilidad | Cuando falla en producción y hay que reconstruir por qué el modelo eligió ese camino | Bajar un escalón: con un camino fijo, saber qué pasó es tan simple como leer el código |
El mismo problema, dos veces
La tarea es concreta y aburrida, como la mayoría de tareas reales. Llega un ticket de soporte, hay que clasificarlo y enrutarlo al equipo correcto. Primero la versión agéntica, que es la que se construye por defecto.
agentic_router.py
from anthropic import Anthropic
client = Anthropic()
TOOLS = [
{
"name": "search_knowledge_base",
"description": "Search internal docs for context about a ticket",
"input_schema": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
},
},
{
"name": "route_ticket",
"description": "Send the ticket to a team queue",
"input_schema": {
"type": "object",
"properties": {
"team": {"type": "string"},
"priority": {"type": "string"},
},
"required": ["team", "priority"],
},
},
]
def run_agent(ticket: str, max_turns: int = 10) -> str:
messages = [{"role": "user", "content": f"Route this ticket: {ticket}"}]
for _ in range(max_turns):
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
tools=TOOLS,
messages=messages,
)
if response.stop_reason != "tool_use":
return response.content[0].text
messages.append({"role": "assistant", "content": response.content})
results = []
for block in response.content:
if block.type == "tool_use":
output = execute_tool(block.name, block.input)
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
messages.append({"role": "user", "content": results})
raise RuntimeError("agent did not converge")Fíjate en lo que no se ve. No hay forma de saber de antemano cuántas vueltas dará el loop, ni qué herramientas usará, ni en qué orden. El
max_turns está ahí porque en algún momento no convergió, y el RuntimeError es la confesión de que el flujo puede no terminar.fixed_router.py
from anthropic import Anthropic
client = Anthropic()
TEAMS = {"billing", "infra", "account", "other"}
PROMPT = """Classify this support ticket into exactly one team:
billing, infra, account, or other.
Answer with the team name only.
Ticket: {ticket}"""
def route(ticket: str) -> str:
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=16,
messages=[{"role": "user", "content": PROMPT.format(ticket=ticket)}],
)
team = response.content[0].text.strip().lower()
if team not in TEAMS:
team = "other"
return send_to_queue(team, ticket)Una llamada, un modelo más pequeño y barato, un límite de dieciséis tokens de salida (la respuesta real ocupa uno o dos), una validación explícita contra un conjunto cerrado y un fallback determinista. Si falla, falla en un lugar que puedes señalar con el dedo.
| Dimensión | Agente | Workflow fijo |
|---|---|---|
| Llamadas al modelo | Variable, de 1 a 10 | Exactamente 1 |
| Modelo | Uno grande, porque tiene que razonar | Uno pequeño, porque solo clasifica |
| Rutas posibles | Combinatorias | Cuatro |
| Latencia | Varios turnos secuenciales, cada uno un viaje de ida y vuelta al modelo | Una sola llamada |
| Ruta reproducible | No | Sí, fijada por el código |
| Qué haces cuando falla | Leer el rastro del razonamiento | Mirar una línea |
La segunda versión hace menos. No busca contexto ni asigna prioridad. La pregunta correcta no es cuál es más capaz, sino si de verdad necesitabas esa capacidad extra.
Cuándo un agente sí vale la pena
Nada de lo anterior significa que los agentes no sirvan. Significa que sirven para una clase específica de problema, y esa clase es más estrecha de lo que sugiere el entusiasmo actual. Hay al menos tres condiciones que, cuando se cumplen juntas, inclinan la balanza a favor del agente.
- Cuando los pasos no se pueden enumerar de antemano, porque dependen de lo que se encuentre en el camino.
- Cuando el espacio de búsqueda es abierto y explorar es precisamente el trabajo.
- Cuando el costo de una iteración extra es bajo y nadie está esperando en tiempo real.
Esto lo he visto de cerca con los agentes de código, y ahí el patrón se nota con claridad. En un repositorio que no conozco, el agente gana con facilidad: no tengo el conocimiento necesario para trazar la ruta de antemano, así que dejar que explore, lea archivos, pruebe una hipótesis y la descarte es más barato que intentar planificar a ciegas.
En cambio, cuando ya sé qué archivo tocar y qué línea cambiar, lanzar un agente para eso cuesta más tokens y más tiempo que hacerlo directo. Ahí no hay nada que explorar: ya sé exactamente qué hacer, y pedirle al modelo que lo redescubra por su cuenta es pagar por una búsqueda que no hacía falta.
El mismo agente, el mismo día, gana en una tarea y pierde en la otra. La diferencia no está en la herramienta, está en si el trabajo pedía explorar o ya sabía a dónde ir.
La guía de decisión
Antes de subir al último escalón conviene hacerse cuatro preguntas. Ninguna exige un análisis profundo: bastan para descartar la mayoría de los casos donde un agente sería la respuesta equivocada.
- ¿Puedes enumerar los pasos de antemano? Si la respuesta es sí, no necesitas un agente: necesitas un workflow.
- ¿Importa la latencia para tu usuario? Si alguien está esperando frente a una pantalla, cada vuelta del loop se la cobras a esa persona.
- ¿Necesitas reproducir un fallo? Si la respuesta es sí, el no determinismo del agente es un problema, no un detalle.
- ¿El costo por ejecución tiene techo? Si no lo tiene, un solo loop enredado puede costar un múltiplo impredecible de una petición normal.
La regla práctica
Empieza en el escalón más bajo que funcione y sube solo cuando el problema te obligue. Subir es fácil y siempre puedes hacerlo después. Bajar, en cambio, significa desmontar algo que ya está en producción y que nadie entiende del todo.
La pregunta no es si puedes construir un agente, es si tu problema pedía uno.