volver al blog

Planificar primero, ejecutar después: el patrón plan-and-execute

agentesia-generativaarquitectura

El agente que se olvida de a dónde iba

Le pides a un agente que migre tres endpoints, actualice los tests y abra una pull request. Hace bien los dos primeros pasos. En el cuarto se distrae con un error de lint, lo arregla, descubre otro y, veinte llamadas después, ya no recuerda que quedaba un endpoint por migrar.

No es un fallo de inteligencia. Es un fallo de estructura. En el patrón ReAct el modelo decide la siguiente acción mirando solo lo que tiene delante. Para tareas cortas funciona. Para tareas largas, el objetivo original queda enterrado bajo el ruido de las observaciones.

El patrón plan-and-execute ataca ese problema de raíz: primero se escribe el plan completo y después se ejecuta. Son dos fases con responsabilidades distintas.

Cómo funciona

El flujo tiene tres piezas:

  1. Planificador. Una llamada al modelo que recibe el objetivo y devuelve una lista de pasos, cada uno con su criterio de éxito.
  2. Ejecutor. Un bucle que toma un paso, lo resuelve con herramientas (aquí sí puede ser un mini ReAct) y devuelve un resultado.
  3. Replanificador. Tras cada paso, comprueba si el plan sigue siendo válido. Si no, lo corrige.

La clave es que el plan vive fuera de la conversación. Es un dato estructurado, no un párrafo perdido en el historial. El ejecutor ve el paso actual y un resumen de lo hecho, no cuarenta turnos de observaciones intermedias.

Un esqueleto en Python

Sin frameworks, para que se vea la mecánica:

from dataclasses import dataclass, field
from typing import Literal

Estado = Literal["pendiente", "hecho", "fallido"]

@dataclass
class Paso:
    id: int
    descripcion: str
    criterio: str                 # cómo sabemos que está terminado
    estado: Estado = "pendiente"
    resultado: str = ""

@dataclass
class Plan:
    objetivo: str
    pasos: list[Paso] = field(default_factory=list)

def planificar(llm, objetivo: str) -> Plan:
    # Salida estructurada: lista de pasos con criterio de éxito.
    # Un plan sin criterios es una lista de deseos.
    pasos = llm.estructurado(
        f"Divide el objetivo en pasos verificables: {objetivo}",
        esquema=list[Paso],
    )
    return Plan(objetivo, pasos)

def ejecutar(llm, plan: Plan, herramientas, max_replanes: int = 2):
    replanes = 0
    while any(p.estado == "pendiente" for p in plan.pasos):
        paso = next(p for p in plan.pasos if p.estado == "pendiente")

        # El ejecutor solo ve el paso actual y un resumen de lo hecho.
        contexto = resumen(plan)
        paso.resultado = bucle_react(llm, paso, contexto, herramientas)
        paso.estado = "hecho" if cumple(llm, paso) else "fallido"

        # Replanificar solo cuando algo falla; no tras cada paso.
        if paso.estado == "fallido":
            if replanes >= max_replanes:
                raise RuntimeError(f"Plan abandonado en el paso {paso.id}")
            plan = replanificar(llm, plan, paso)
            replanes += 1
    return plan

Dos decisiones merecen atención. La primera: max_replanes. Sin tope, un plan que no funciona se reescribe indefinidamente, y cada versión cuesta tokens. La segunda: el criterio de éxito por paso. Sin él, “hecho” significa “el modelo dice que ha terminado”, que es la peor definición posible.

ReAct frente a plan-and-execute

AspectoReActPlan-and-execute
DecisiónUna acción cada vezPlan completo y después pasos
Contexto del ejecutorTodo el historialPaso actual y resumen
Tareas cortas e inciertasMejor ajusteSobrecoste de planificar
Tareas largas con estructuraSe pierde el objetivoMejor ajuste
Coste por tareaCrece con cada turnoUna llamada extra, contexto menor
AuditoríaHay que leer la trazaEl plan es legible de antemano
Reacción a lo inesperadoInmediataDepende del replanificador

No es una cuestión de cuál es mejor. Es una cuestión de cuánta incertidumbre tiene la tarea. Si el siguiente paso depende por completo del resultado del anterior (explorar una base de código desconocida, depurar un fallo raro), planificar de antemano es adivinar. Si la tarea tiene forma conocida (migrar, generar un informe, procesar un lote), el plan ahorra desvíos.

Lo que se gana y lo que se paga

A favor:

  • Menos deriva. El objetivo no se diluye, porque está en una estructura y no en la conversación.
  • Contexto más pequeño. El ejecutor trabaja con una ventana corta, lo que abarata cada llamada y reduce el ruido.
  • Plan revisable. Puedes mostrar el plan a una persona antes de ejecutarlo. Para acciones con efectos reales, esa pausa vale oro.
  • Modelos distintos por fase. Un modelo potente planifica una vez; uno pequeño y barato ejecuta los pasos mecánicos.

En contra:

  • Planes frágiles. El plan se escribe sin ver los resultados. Si el paso dos invalida el paso cinco, hace falta replanificar.
  • Latencia inicial. Hay una llamada más antes de la primera acción.
  • Falsa seguridad. Un plan bien redactado parece más fiable de lo que es. Un paso vago sigue siendo un paso vago.

Cómo adoptarlo sin reescribir tu agente

No hace falta tirar tu bucle actual. Un camino incremental:

  1. Empieza por el plan visible. Pide al agente que escriba los pasos antes de actuar y guárdalos como dato. Todavía no ejecutes nada distinto.
  2. Añade criterios de éxito. Cada paso con una comprobación que no dependa de la opinión del modelo: un test que pasa, un fichero que existe, un campo no vacío.
  3. Limita el contexto del ejecutor. Pasa el paso actual y un resumen, no el historial entero. Mide si baja el coste por tarea.
  4. Pon tope a la replanificación. Dos o tres intentos. Si no basta, que escale a una persona.
  5. Evalúa por trayectoria. Comprueba si el agente siguió el plan y si el plan era razonable, no solo si el resultado final es correcto. Lo explicaba en evaluar agentes por trayectoria.

Para llevarte

Plan-and-execute no hace más listo al modelo. Hace más difícil que se despiste. Es una decisión de arquitectura: pagas una llamada y un poco de rigidez a cambio de contexto limpio, auditoría y costes predecibles.

Úsalo cuando la tarea tenga forma conocida y varios pasos. Evítalo cuando cada paso dependa por completo del anterior. Y, pase lo que pase, escribe el criterio de éxito de cada paso: un plan sin criterios es solo una lista bien formateada.