La pregunta previa: ¿hace falta un agente?
La mayoría de los fallos en sistemas agénticos nacen de la misma decisión: usar un agente donde bastaba algo más simple. Un agente cambia previsibilidad por flexibilidad, y esa flexibilidad se paga en coste, latencia, dificultad de prueba y superficie de ataque. Antes de diseñar nada conviene situar el problema en el espectro de autonomía.
Se avanza un escalón solo cuando el anterior no basta.
| Flujo de trabajo | Agente | |
|---|---|---|
| Quién decide los pasos | El código | El modelo |
| Previsibilidad | Alta | Menor |
| Coste y latencia | Bajos y estables | Más altos y variables |
| Pruebas | Casos deterministas por paso | Hay que evaluar trayectorias |
| Encaja con | Tareas con pasos conocidos | Tareas abiertas cuyo camino no se puede prever |
La regla de oficio: empezar por lo más simple que funcione. Una llamada bien construida; si no basta, un flujo fijo de varias llamadas; si el camino no se puede conocer de antemano, un agente; y solo si un agente no abarca el problema, varios.
Anatomía de un agente
Un agente es, en esencia, un modelo llamado en bucle con herramientas, un historial y una condición de parada.
Vuelve al modelo hasta que responda, agote los pasos o el presupuesto.
En cada vuelta el modelo recibe el objetivo, las herramientas disponibles y todo lo ocurrido hasta ahora; decide entre responder o pedir una herramienta; el código la ejecuta y añade el resultado al historial. Todo lo demás (memoria, planificación, supervisores) son variaciones sobre este bucle. Las condiciones de parada no son opcionales: respuesta final, número máximo de pasos, tiempo máximo y presupuesto máximo de gasto.
Patrones de flujo de trabajo
Estos patrones no necesitan que el modelo decida el camino: lo fija el código y el modelo se limita a cada paso.
Encadenado y enrutado
Encadenado
Enrutado
El encadenado descompone una tarea en pasos fijos donde la salida de uno alimenta al siguiente, con comprobaciones intermedias. Se usa cuando la tarea es separable y cada paso, más sencillo, sale mejor que la tarea completa. El enrutado clasifica la petición y la dirige a un tratamiento especializado; permite usar un modelo barato para clasificar y uno capaz solo donde hace falta.
Paralelización
A la vez
Dos variantes: sectorización (subtareas independientes a la vez, por ejemplo revisar un documento desde varios criterios) y votación (la misma tarea varias veces para ganar confianza). Reduce latencia o aumenta fiabilidad, pero multiplica el coste.
Orquestador y trabajadores
En paralelo
Se parece a la paralelización, pero las subtareas no se conocen de antemano: el orquestador las decide según la entrada (por ejemplo, qué ficheros hay que modificar) y los trabajadores las ejecutan. El punto débil es la síntesis: combinar resultados dispares es difícil.
Evaluador y optimizador
Si no cumple, devuelve su crítica al generador. Con máximo de vueltas.
Un modelo genera y otro (o el mismo con otro rol) critica con un criterio explícito. Funciona cuando existe un criterio claro de calidad y la iteración realmente mejora el resultado. Sin un máximo de vueltas puede iterar eternamente.
Patrones de agente
ReAct
Si falta información, vuelve a razonar. Siempre con un límite de pasos.
El agente alterna razonamiento, acción y observación. Es el patrón por defecto para tareas de exploración donde cada resultado condiciona el siguiente paso. Su riesgo es el bucle y el coste imprevisible; por eso lleva límites.
Planificar y ejecutar
Si un paso falla o cambia el contexto, vuelve al planificador.
Primero un plan completo, luego ejecución paso a paso, con replanificación solo cuando algo cambia. Frente a ReAct, da más previsibilidad y permite usar un modelo pequeño para ejecutar; a cambio, un plan inicial malo arrastra todo lo demás.
Reflexión
Útil con un criterio comprobable, como tests o un esquema. Con límite de vueltas.
El agente revisa su resultado antes de darlo por válido. Es eficaz cuando la comprobación es objetiva (pasan los tests, el JSON cumple el esquema). Cuando el criterio es subjetivo, el modelo tiende a aprobar su propio trabajo.
Patrones multiagente
Supervisor
Cada especialista devuelve el control al supervisor.
Un agente central reparte el trabajo entre especialistas y decide cuándo termina. Da control y trazabilidad; el supervisor es cuello de botella y punto único de fallo.
Jerárquico
Trabajadores
Supervisores de equipo coordinados por un supervisor general. Es apropiado cuando hay muchos especialistas y un solo supervisor se saturaría; cada nivel añade latencia y coste.
Enjambre y traspasos (handoffs)
Hay que evitar ciclos infinitos entre agentes.
No hay jefe: cada agente conoce a quién puede ceder el control y lo hace cuando la conversación sale de su especialidad. Encaja en atención al cliente y flujos conversacionales. Exige evitar ciclos y decidir qué contexto viaja en cada traspaso.
Cuándo usar cada patrón
| Patrón | Encaja cuando | Riesgo principal |
|---|---|---|
| Encadenado | Pasos fijos y secuenciales | Rigidez |
| Enrutado | Tipos de petición con tratamientos distintos | Clasificar mal |
| Paralelización | Subtareas independientes o necesidad de votar | Coste multiplicado |
| Orquestador y trabajadores | Subtareas no previsibles de antemano | Síntesis difícil |
| Evaluador y optimizador | Existe un criterio claro de calidad | Iteraciones sin fin |
| ReAct | Exploración con herramientas | Bucles y coste imprevisible |
| Planificar y ejecutar | Tareas largas con estructura | Plan inicial erróneo |
| Reflexión | Criterio de calidad comprobable | Autoaprobación |
| Supervisor | Especialidades claras | Cuello de botella |
| Jerárquico | Muchos especialistas | Latencia y coste |
| Traspasos | Conversación que cambia de dominio | Ciclos de cesión |
Herramientas: la interfaz del agente
Un agente vale lo que valgan sus herramientas. Una herramienta es una función con un nombre, una descripción y un esquema; el modelo elige por la descripción, así que la descripción es parte del diseño.
from pydantic import BaseModel, Field
class CrearIncidencia(BaseModel):
"""Crea una incidencia en el gestor. Usar solo tras confirmar el resumen con el usuario."""
titulo: str = Field(description="Resumen en una frase, máximo 100 caracteres")
prioridad: str = Field(description="Una de: baja, media, alta")
clave_idempotencia: str = Field(description="Identificador único de esta petición")
def crear_incidencia(args: CrearIncidencia) -> dict:
if args.prioridad not in {"baja", "media", "alta"}:
return {"error": "prioridad no válida", "validas": ["baja", "media", "alta"]}
return {"id": "INC-001", "estado": "creada"}Principios de diseño:
- Nombres y verbos claros.
buscar_clienteycrear_incidencia, noprocesarniherramienta1. - Descripción con cuándo usarla y cuándo no. Y con ejemplos de entrada si el formato no es obvio.
- Esquemas estrictos. Tipos, valores permitidos y descripción de cada campo; se valida siempre en código.
- Errores útiles. Un fallo debe devolver qué ha ocurrido y qué hacer, como en el ejemplo. Un mensaje opaco provoca reintentos inútiles.
- Pocas herramientas, bien separadas. Muchas herramientas parecidas confunden al modelo.
- Idempotencia. Repetir la llamada (por un reintento) no debe duplicar el efecto; se usan claves de idempotencia.
- Mínimo privilegio. Cada agente tiene solo las herramientas que necesita y con los permisos mínimos.
- Lectura separada de escritura. Las acciones con efecto reciben más controles y, si son irreversibles, aprobación humana.
- Resultados acotados. Devolver solo lo necesario; un resultado enorme llena el contexto.
Estado, grafos y persistencia
Un agente de varios pasos necesita un estado explícito: mensajes, datos intermedios, decisiones tomadas. Modelarlo como un grafo (nodos que leen y escriben el estado, aristas que deciden el siguiente paso) hace el flujo visible, comprobable y reanudable.
Nodos del grafo
Las aristas condicionales deciden el siguiente nodo según el estado.
Lo que permite
Un grafo mínimo con LangGraph:
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
class Estado(TypedDict):
pregunta: str
contexto: str
respuesta: str
def buscar(estado: Estado) -> dict:
return {"contexto": "fragmentos recuperados"}
def responder(estado: Estado) -> dict:
return {"respuesta": "texto generado a partir del contexto"}
grafo = StateGraph(Estado)
grafo.add_node("buscar", buscar)
grafo.add_node("responder", responder)
grafo.add_edge(START, "buscar")
grafo.add_edge("buscar", "responder")
grafo.add_edge("responder", END)
aplicacion = grafo.compile()Al compilar con un checkpointer se guarda el estado tras cada paso. Eso habilita tres capacidades valiosas: reanudar tras un fallo sin repetir el trabajo, pausar a la espera de una persona y viajar en el tiempo (volver a un punto anterior, corregir y bifurcar la ejecución para depurar). En producción el checkpointer se apoya en una base de datos persistente, no en memoria.
Humano en el bucle
Para acciones irreversibles (pagar, borrar, enviar, publicar) el agente propone y una persona decide. Técnicamente se implementa con una interrupción: el grafo guarda su estado, se detiene y espera. Quien revisa debe ver la acción exacta con sus argumentos, no un resumen, y poder aprobar, editar o rechazar. La aprobación se diseña como un paso del flujo, no como un aviso que se pueda ignorar.
Ingeniería de contexto
El rendimiento de un agente depende de qué hay en la ventana en cada paso, y eso se diseña activamente.
Se selecciona
Se recorta
- Seleccionar: instrucciones estables al principio, solo las herramientas que el paso necesita, memoria relevante recuperada en lugar de volcada.
- Recortar: los resultados largos de herramientas se resumen o se guardan fuera con una referencia.
- Compactar: el historial antiguo se condensa para liberar espacio sin perder las decisiones tomadas.
- Aislar: los subagentes trabajan con contexto limpio y devuelven un resumen, no su historial completo.
Un contexto lleno de ruido produce agentes que se dispersan, repiten pasos o ignoran instrucciones.
Sistemas multiagente: cuándo compensan
Dividir en varios agentes no es una mejora automática. Introduce coordinación, más tokens, más latencia y más puntos de fallo.
| Compensa cuando | No compensa cuando |
|---|---|
| Las subtareas son realmente independientes y se pueden paralelizar | Todos los pasos dependen del anterior |
| Hay especialidades con herramientas y contexto muy distintos | Un solo agente con buenas herramientas lo resuelve |
| Un solo contexto se desbordaría | La tarea cabe cómodamente en una ventana |
| Se quiere aislar permisos por zona | El coste de coordinar supera el beneficio |
Si se opta por varios, hay que decidir cómo se coordinan (un supervisor, traspasos o un estado compartido), qué comparten (memoria común con espacios por agente o solo mensajes) y cuánto cuesta (cada agente consume tokens propios). Los mensajes entre agentes deben ser estructurados y acotados; el texto libre acumula ruido.
Seguridad
Inyección de instrucciones
Un agente lee contenido y actúa según lo que entiende. Si ese contenido contiene órdenes, el modelo puede obedecerlas. La inyección directa viene del propio usuario; la indirecta se esconde en datos que el agente procesa: una web, un correo, un documento, la respuesta de una herramienta. No existe un filtro de texto que la elimine de forma fiable.
La combinación peligrosa
Si un agente reúne a la vez
El riesgo grave aparece cuando un mismo agente reúne a la vez datos privados, contenido no fiable y capacidad de actuar hacia fuera. Con las tres, un texto ajeno puede ordenar al agente que recopile información y la envíe. La defensa es estructural: eliminar al menos una de las tres en cada agente.
Zonas de confianza
Zona sin confianza
Frontera
Zona de confianza
Se separa quien lee contenido no fiable (sin herramientas de acción) de quien actúa (sin acceso a contenido ajeno). Entre ambos solo viajan datos estructurados y validados.
Guardias deterministas
Lo que no puede fallar no se le confía al modelo. Las guardias son código: validan la entrada antes de llegar al modelo, comprueban cada llamada a herramienta contra listas de permitidos y esquemas, y verifican la salida antes de entregarla (formatos, datos personales, destinos permitidos).
Contención
- Sandbox para cualquier ejecución de código o de comandos, sin red ni secretos por defecto.
- Credenciales con alcance mínimo, de vida corta y fuera del contexto del modelo.
- Límites de pasos, tiempo, llamadas por herramienta y gasto.
- Listas de destinos permitidos para peticiones salientes.
- Registro de cada acción para poder reconstruir lo ocurrido.
Evaluación
| Nivel | Qué mide | Cómo |
|---|---|---|
| Resultado final | Si la tarea quedó resuelta | Comprobaciones automáticas o revisión humana |
| Trayectoria | Si el camino fue razonable | Comparar herramientas llamadas y su orden con las esperadas |
| Paso a paso | Si cada decisión fue correcta | Casos aislados: dado este estado, ¿qué herramienta y con qué argumentos? |
| Coste y latencia | Llamadas, tokens y tiempo | Métricas por ejecución |
Un modelo como juez permite evaluar aspectos que no se pueden comprobar con código, pero tiene límites: sesgo hacia respuestas largas y hacia su propio estilo, sensibilidad al orden y falta de fiabilidad en criterios vagos. Se usa con rúbricas concretas y se calibra contra una muestra revisada por una persona.
La lógica del agente debe poder probarse con modelos falsos: un modelo de pega que devuelve respuestas y llamadas a herramientas predefinidas. Así las pruebas son deterministas, rápidas y de coste cero, y comprueban lo que de verdad es código propio: el enrutado, los límites, el manejo de errores y las guardias.
class ModeloFalso:
def __init__(self, respuestas):
self.respuestas = iter(respuestas)
def invoke(self, mensajes):
return next(self.respuestas)
def test_agente_respeta_el_limite_de_pasos():
modelo = ModeloFalso([{"tool": "buscar", "args": {}}] * 50)
resultado = ejecutar_agente(modelo, max_pasos=5)
assert resultado.motivo_parada == "limite_de_pasos"Observabilidad y trazas
Un agente que falla no deja una línea de error: deja una conversación. Sin trazas completas (cada llamada al modelo, cada herramienta con sus argumentos y resultado, tokens, latencia, coste) depurar es adivinar. Herramientas como LangSmith, Langfuse o Arize Phoenix muestran la ejecución como árbol. Buenas prácticas: un identificador de traza que atraviese todo, etiquetas por versión de prompt y de modelo, y convertir cada fallo real en un caso del conjunto de evaluación.
Despliegue
Un agente puede tardar minutos; no cabe en una petición HTTP síncrona. El patrón habitual es una API que valida y encola, un trabajador que ejecuta el grafo con checkpoints y un canal de streaming de eventos para informar del progreso. Hay que diseñar para el fallo: reintentos con espera creciente, idempotencia en las herramientas con efecto, reanudación desde el último checkpoint y un estado visible para el usuario. Para procesos largos y duraderos, motores como Temporal ofrecen reanudación y reintentos de serie.
Lista de comprobación
- Se ha comprobado que un flujo fijo no basta.
- Cada herramienta tiene nombre claro, descripción útil y esquema estricto.
- Las herramientas con efecto son idempotentes y tienen permisos mínimos.
- Hay límite de pasos, tiempo y gasto.
- El estado se persiste y la ejecución se puede reanudar.
- Las acciones irreversibles requieren aprobación.
- Ningún agente reúne datos privados, contenido no fiable y capacidad de actuar hacia fuera.
- Hay guardias deterministas de entrada y de salida.
- Existe un conjunto de evaluación y pruebas con modelos falsos.
- Cada ejecución queda trazada.
Antipatrones
| Antipatrón | Consecuencia | Alternativa |
|---|---|---|
| Un agente donde bastaba un flujo | Coste y fallos innecesarios | Subir de autonomía solo si hace falta |
| Todas las herramientas «por si acaso» | El modelo duda y el riesgo crece | Mínimo privilegio por agente |
| Confiar en el prompt para la seguridad | Se elude con una inyección | Guardias en código y permisos |
| Sin límite de pasos ni de gasto | Bucles y facturas inesperadas | Límites explícitos |
| Herramientas con errores opacos | Reintentos inútiles | Errores que explican y orientan |
| Historial completo en cada turno | Contexto saturado | Ingeniería de contexto |
| Multiagente por moda | Coordinación sin beneficio | Un agente bien equipado |
| Probar solo a mano | Regresiones invisibles | Evaluación y modelos falsos |
| Acciones irreversibles sin revisión | Daños sin vuelta atrás | Humano en el bucle |
| Agente sin trazas | Fallos imposibles de reproducir | Observabilidad desde el inicio |