Saltar al contenido principal
Jordi Izquierdo Montalbán
Lo que practico

Fundamentos

Agentes de IA

Agentes con herramientas acotadas, guardias deterministas y evaluación.

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.

Cuanto más decide el modelo, menos previsible y más caro de controlar.
Flujo de trabajoAgente
Quién decide los pasosEl códigoEl modelo
PrevisibilidadAltaMenor
Coste y latenciaBajos y establesMás altos y variables
PruebasCasos deterministas por pasoHay que evaluar trayectorias
Encaja conTareas con pasos conocidosTareas 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.

Un agente es un modelo llamado en bucle con herramientas y una condición de parada.

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

Dos flujos de trabajo: pasos en serie, o un clasificador que elige el camino.

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

Subtareas independientes a la vez, o la misma tarea varias veces para votar.

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

La tarea se divide en partes que no se conocen de antemano y se resuelven 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

Uno genera, otro critica con un criterio claro, y se itera hasta cumplirlo.

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

El agente razona, actúa y observa en bucle hasta tener la respuesta.

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

Se planifica una vez y se ejecuta; se replanifica solo si algo falla.

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

El agente revisa su propio resultado antes de darlo por bueno.

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

Un agente coordina a varios especialistas y decide a quién toca.

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

Supervisores de equipo coordinados por un supervisor superior.

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)

Sin jefe central: cada agente decide a quién cede el control.

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ónEncaja cuandoRiesgo principal
EncadenadoPasos fijos y secuencialesRigidez
EnrutadoTipos de petición con tratamientos distintosClasificar mal
ParalelizaciónSubtareas independientes o necesidad de votarCoste multiplicado
Orquestador y trabajadoresSubtareas no previsibles de antemanoSíntesis difícil
Evaluador y optimizadorExiste un criterio claro de calidadIteraciones sin fin
ReActExploración con herramientasBucles y coste imprevisible
Planificar y ejecutarTareas largas con estructuraPlan inicial erróneo
ReflexiónCriterio de calidad comprobableAutoaprobación
SupervisorEspecialidades clarasCuello de botella
JerárquicoMuchos especialistasLatencia y coste
TraspasosConversación que cambia de dominioCiclos 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_cliente y crear_incidencia, no procesar ni herramienta1.
  • 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.

Cada nodo lee y escribe un estado compartido; el checkpointer lo guarda en cada paso.

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

El grafo se detiene antes de la acción irreversible y espera una decisión.

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.

En cada paso se decide qué entra en la ventana, no se acumula todo.
  • 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 cuandoNo compensa cuando
Las subtareas son realmente independientes y se pueden paralelizarTodos los pasos dependen del anterior
Hay especialidades con herramientas y contexto muy distintosUn solo agente con buenas herramientas lo resuelve
Un solo contexto se desbordaríaLa tarea cabe cómodamente en una ventana
Se quiere aislar permisos por zonaEl 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

Cuando coinciden las tres, un texto ajeno puede sacar datos privados.

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

Lo que toca contenido ajeno se aísla de lo que puede actuar.

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.

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

Se evalúa el resultado, el camino y cada decisión.
NivelQué mideCómo
Resultado finalSi la tarea quedó resueltaComprobaciones automáticas o revisión humana
TrayectoriaSi el camino fue razonableComparar herramientas llamadas y su orden con las esperadas
Paso a pasoSi cada decisión fue correctaCasos aislados: dado este estado, ¿qué herramienta y con qué argumentos?
Coste y latenciaLlamadas, tokens y tiempoMé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

Las tareas largas no viven en una petición HTTP.

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ónConsecuenciaAlternativa
Un agente donde bastaba un flujoCoste y fallos innecesariosSubir de autonomía solo si hace falta
Todas las herramientas «por si acaso»El modelo duda y el riesgo creceMínimo privilegio por agente
Confiar en el prompt para la seguridadSe elude con una inyecciónGuardias en código y permisos
Sin límite de pasos ni de gastoBucles y facturas inesperadasLímites explícitos
Herramientas con errores opacosReintentos inútilesErrores que explican y orientan
Historial completo en cada turnoContexto saturadoIngeniería de contexto
Multiagente por modaCoordinación sin beneficioUn agente bien equipado
Probar solo a manoRegresiones invisiblesEvaluación y modelos falsos
Acciones irreversibles sin revisiónDaños sin vuelta atrásHumano en el bucle
Agente sin trazasFallos imposibles de reproducirObservabilidad desde el inicio