Inicio / Artículos

Siete patrones de bucles de agente y cuándo falla cada uno: ReAct, Maker-Checker y más

Un catálogo práctico de diseños de bucles de agente, desde planificar-actuar-verificar hasta la escalada humana, con indicaciones sobre cuándo es adecuado cada uno, cuándo evitarlo y cómo elegir.

Editor: REACTAPP.TOP9 min de lectura1958 palabras

Fuentes: Agentic Loops & Design Patterns

Nadie debería juzgar a un agente por lo inteligente que suene. Lo importante es su comportamiento cuando hay mucho tráfico, cuando algo se rompe o cuando llega un auditor a hacer preguntas. Es en esas situaciones donde los patrones de diseño de bucles superan a cualquier instrucción individual. Los bucles funcionan mejor cuando se tratan como primitivas arquitectónicas: cada uno aborda una clase específica de problemas, concebe compromisos conocidos y debe elegirse intencionadamente. Los patrones que se presentan a continuación incluyen ejemplos al estilo de Claude y las situaciones en las que se debe evitar cada uno.

1. Planificar, actuar, verificar

Este es el patrón fundamental. El agente planifica un pequeño paso, realiza una acción y luego verifica si dicha acción lo acercó a un objetivo claramente definido. Logra un equilibrio entre flexibilidad y control.

// Pseudo-code: Node/TypeScript orchestrating Claude as an agent

async function planActVerifyLoop(ticket) {
  let iteration = 0;
  const maxIterations = 5;

  while (iteration < maxIterations) {
    iteration++;

    // 1. PLAN: ask Claude for the next step
    const plan = await claude.chat({
      model: "claude-3-opus",
      messages: [
        {
          role: "user",
          content: `You are a coding agent working on ticket ${ticket.id}.
Goal: Make tests pass for this ticket without changing public APIs.

Current context:
${ticket.description}
${ticket.latestFailureLog}

What is the single most useful next action?`
        }
      ]
    });

    // 2. ACT: execute the suggested action if it's in the allowed action space
    const action = parseAction(plan);
    const result = await executeAction(action); // run tests, edit file, etc.

    // 3. VERIFY: use tests as verification
    const verification = await runTests(ticket.testSuite);

    if (verification.allPassing) {
      return { status: "done", iterations: iteration };
    }

    // Attach the failure output back into ticket context
    ticket.latestFailureLog = verification.failureOutput;
  }

  return { status: "budget_exhausted" };
}

En el boceto, Claude se encarga de la microplanificación de la siguiente acción, mientras que el bucle en sí realiza la verificación a través de pruebas y impone un límite de iteraciones. Esa división evita que el sistema declare éxito solo porque el modelo se siente seguro.

Los casos adecuados son tareas de ingeniería de múltiples pasos con progreso medible, scripts de implementación y cambios de configuración que deben llegar a un estado funcional verificado, así como herramientas internas en las que se pueden integrar controles estrictos como pruebas o validación de esquemas.

Los casos inadecuados incluyen trabajos puramente creativos como textos de marketing o escritura extensa, decisiones subjetivas donde la verificación solo serviría para reflejar el gusto personal, y flujos en tiempo real que no pueden permitirse varias iteraciones en la ruta crítica.

2. ReAct (razonar, luego actuar)

ReAct entrelaza breves ráfagas de pensamiento con acciones individuales. En lugar de planificar todo el recorrido de antemano, el agente estudia el estado más reciente, selecciona una herramienta, la utiliza y luego vuelve a pensar. Eso lo convierte en una opción natural para trabajos exploratorios o de diagnóstico, donde cada nueva observación puede modificar legítimamente el plan.

# Pseudo-code: Python coordinator orchestrating Claude tool calls

def react_loop(incident):
    iteration = 0
    max_iterations = 6

    while iteration < max_iterations:
        iteration += 1

        # REASON: Claude decides what to inspect next
        reasoning = claude.chat(
            model="claude-3-opus",
            messages=[
                {
                    "role": "user",
                    "content": f"""
You are an SRE assistant triaging a payment incident.

Incident summary:
{incident.summary}

Recent metrics:
{incident.latest_metrics}

Logs snippet:
{incident.logs_snippet}

Decide one next diagnostic action from:
- CHECK_METRICS
- CHECK_LOGS
- CHECK_DB_HEALTH
- SUMMARIZE_FINDINGS_AND_RECOMMEND_ACTION

Explain your reasoning briefly and output JSON with 'action' and 'target'.
"""
                }
            ]
        )

        action = parse_json(reasoning)
        if action["action"] == "SUMMARIZE_FINDINGS_AND_RECOMMEND_ACTION":
            return claude.chat(... )  # final summary + recommended steps

        # ACT: run the selected diagnostic
        observation = run_diagnostic(action, incident)

        # Update incident state for the next reasoning step
        incident.update_with_observation(observation)

Cada paso de diagnóstico tiene un propósito claro, y Claude recibe un contexto actualizado después de cada acción.

Úsalo para el diagnóstico de incidentes y el análisis de causas raíz, en flujos de trabajo de soporte complejos donde cada respuesta determina qué inspeccionar a continuación, y en investigaciones de seguridad en las que la evidencia llega de forma fragmentada e incompleta.

Omite este proceso cuando el plan es estable y la secuencia de acciones obvia, como en el procesamiento de documentos al estilo ETL, cuando el rendimiento es lo suficientemente alto como para que el razonamiento paso a paso resulte demasiado costoso, o cuando el abanico de acciones es tan reducido que la deliberación adicional no aporta nada.

3. Reflexionar y revisar

Aquí se siguen dos fases, la generación y la crítica, seguidas de un paso de revisión. Esto constituye una opción por defecto razonable para todo aquello que necesita ser revisado antes de llegar a un cliente, un regulador o un sistema de producción. El punto importante es la separación: el crítico puede ser otra instrucción, un modelo más económico o incluso un motor de reglas.

async function reflectReviseLoop(draftInput: string) {
  // 1. GENERATE
  const draft = await claude.chat({
    model: "claude-3-opus",
    messages: [
      {
        role: "user",
        content: `
Write a customer-facing email explaining a declined payment due to suspected fraud.
Constraints:
- empathetic but clear
- no admission of fault
- no promises about future approvals

Context:
${draftInput}
`
      }
    ]
  });

  // 2. CRITIQUE (using a cheaper model as checker)
  const critique = await claude.chat({
    model: "claude-3-haiku",
    messages: [
      {
        role: "user",
        content: `
You are a compliance checker.

Review the following email for:
- policy violations
- misleading statements
- over-commitments

Output a JSON with:
- issues: list of strings
- safe: boolean

Email:
${draft.content}
`
      }
    ]
  });

  const review = JSON.parse(critique.content);

  if (review.safe) {
    return draft.content;
  }

  // 3. REVISE
  const revised = await claude.chat({
    model: "claude-3-opus",
    messages: [
      {
        role: "user",
        content: `
You wrote this email:
${draft.content}

Compliance issues:
${review.issues.join("\n")}

Rewrite the email to resolve all issues while preserving intent.
`
      }
    ]
  });

  return revised.content;
}

Este patrón es adecuado para los mensajes de los clientes sobre temas sensibles como fraudes, devoluciones de pagos o cambios regulatorios, los análisis posteriores a incidentes que puedan ser auditados, y cualquier contenido en el que el cumplimiento de las políticas tenga prioridad sobre el estilo.

No es adecuado para sesiones internas de lluvia de ideas de bajo riesgo, donde las críticas generan fricción y no ofrecen verdadera seguridad; ni para respuestas muy breves en chats donde realizar una segunda revisión es excesivo; tampoco para flujos críticos en cuanto a latencia, como los mensajes durante una transacción, donde solo se puede enviar un mensaje.

4. Redactar, probar, corregir

Los equipos de ingeniería suelen preferir este ciclo, ya que el resultado es objetivo y está fuera del modelo: el conjunto de pruebas pasa o queda en rojo. En cada ronda, el agente propone un cambio, ejecuta las pruebas y verifica qué falló. Luego corrige los problemas y repite el proceso, deteniéndose una vez que el sistema esté en un estado conocido como correcto o se alcance el límite de iteraciones.

async function draftTestFixLoop(issue: Issue) {
  const maxIterations = 4;
  let iteration = 0;

  while (iteration < maxIterations) {
    iteration++;

    // DRAFT: Claude proposes code changes
    const patch = await claude.chat({
      model: "claude-3-opus",
      messages: [
        {
          role: "user",
          content: `
You are an autonomous coding agent.

Ticket:
${issue.title}
${issue.body}

Current failing tests:
${issue.failingTests}

Propose a minimal patch as a diff that makes tests pass
without changing public APIs.`
        }
      ]
    });

    applyPatchToGitRepo(patch.content);

    // TEST: run CI locally or via API
    const testResult = await runCi(issue.branchName);

    if (testResult.success) {
      // FIX_DONE: open a PR with the diff
      await openPullRequest(issue, patch.content);
      return { status: "done" };
    }

    // FEEDBACK: update failingTests for next iteration
    issue.failingTests = testResult.failureSummary;
  }

  return { status: "needs_human_review" };
}

Funciona con tickets de bajo riesgo y bien delimitados, marcados como listos para ser atendidos por un agente, en herramientas internas donde revertir cambios es sencillo, y en rutas no críticas que pueden soportar la latencia y el costo.

Evítelo para cualquier cambio que haga pasar dinero directamente por las rutas de transacciones principales, para cambios en esquemas, migraciones y refactores transversales, así como para lógicas reguladas donde una edición automática podría violar las políticas establecidas.

5. Crítico y creador

Un agente crea el cambio y otro lo verifica. En pagos, esto se asemeja al sistema de control de creador-verificador ya utilizado en operaciones de alto riesgo, lo que facilita su implementación en los equipos de riesgos y cumplimiento.

Es adecuado para cualquier situación en la que ya se requiera una segunda opinión, donde la automatización afecte sistemas relacionados con la producción como la configuración, el enrutamiento o las notificaciones, y para informes o paneles de control de gran importancia que lean los directivos.

No es adecuado para tareas triviales en las que la revisión adicional cuesta más de lo que protege, procesos sensibles a la latencia que deben responder en un solo paso, ni para pipelines donde reglas débiles obligarían al verificador a aceptar cualquier cosa que parezca lo suficientemente buena.

6. Reintentar con memoria

La mayoría de los bucles más simples simplemente intentan de nuevo. Un bucle de reintentos con memoria registra los estados de fallo y los utiliza para evitar repetir el mismo error.

def retry_with_memory_loop(task, max_attempts=3):
    failures = []

    for attempt in range(1, max_attempts + 1):
        # Ask Claude to consider past failures before deciding the next move
        decision = claude.chat(
            model="claude-3-opus",
            messages=[
                {
                    "role": "user",
                    "content": f"""
You are handling a task with a flaky external API.

Task:
{task.description}

Past failures:
{failures}

Decide whether to:
- RETRY_API
- FALLBACK_TO_CACHE
- ESCALATE_TO_HUMAN

Explain briefly and output JSON: {{ "choice": "...", "reason": "..." }}
"""
                }
            ]
        )

        choice = parse_json(decision)

        if choice["choice"] == "RETRY_API":
            result = call_api(task)
        elif choice["choice"] == "FALLBACK_TO_CACHE":
            result = use_cache(task)
        else:
            return {"status": "escalated", "failures": failures}

        if result.success:
            return {"status": "success", "attempts": attempt}

        failures.append(result.error_summary)

    return {"status": "max_attempts_exhausted", "failures": failures}

Los objetivos adecuados son las integraciones inestables, las APIs con límites de velocidad y los sistemas externos fuera de su control, el análisis de documentos con formato inconsistente, y los flujos de trabajo no críticos en los que son aceptables las intentonas repetidas.

Los inadecuados son los flujos de transacciones principales donde una repetición podría duplicar efectos, las operaciones que no son idempotentes y por lo tanto no se sabe si una repetición es segura, y los escenarios en los que el fallo debe ser inmediato y visible, sin intentarse repetir de forma silenciosa.

7. Escalación humana

Este es el patrón de protección para todo aquello que involucre dinero real, clientes reales o exposición regulatoria. El ciclo funciona por sí solo hasta cierto punto y luego transfiere la responsabilidad de manera ordenada una vez que disminuye la confianza o aumenta el riesgo.

async function triageLoop(ticket: Ticket) {
  const autoActions = ["LABEL", "ROUTE_TO_QUEUE", "REQUEST_MORE_INFO"];

  const decision = await claude.chat({
    model: "claude-3-opus",
    messages: [
      {
        role: "user",
        content: `
You are a support triage agent in a payments company.

Ticket:
${ticket.body}

Decide one of:
- LABEL (low risk)
- ROUTE_TO_QUEUE (medium risk)
- ESCALATE_TO_HUMAN (high risk / unclear)

Return JSON with:
- choice
- risk_level
- rationale
`
      }
    ]
  });

  const choice = JSON.parse(decision.content);

  if (choice.choice === "ESCALATE_TO_HUMAN") {
    await createHumanTask(ticket, choice.rationale);
    return { status: "escalated" };
  }

  // LABEL or ROUTE_TO_QUEUE are automated but bounded
  await applyAutomatedTriage(ticket, choice);
  return { status: "auto_treated" };
}

Úselo para cualquier automatización que pueda mejorar la experiencia del cliente o su percepción de equidad, en flujos donde la automatización parcial ayuda pero la autonomía total es arriesgada, y en implementaciones iniciales que necesitan aprender antes de que se relajen los controles.

Omitálo en tareas completamente internas y de bajo riesgo donde los humanos solo añadirían ruido, en trabajos por lotes de área administrativa donde la revisión humana no puede escalar, y en situaciones donde la escalada sobrecargaría a las personas sin mejorar los resultados.

Elegir en la práctica

Cuando aparece una nueva oportunidad para un agente, “¿cómo debo guiarlo?” no es la primera pregunta adecuada. Cuatro mejores son:

  1. ¿Qué señal verifica el resultado: pruebas, esquemas, reglas, métricas o juicio humano?
  2. ¿Cuántos pasos requiere realistamente la tarea?
  3. ¿Qué sucede cuando el sistema falla?
  4. ¿Quién se ve afectado si comete errores?

Las respuestas apuntan a un patrón:

  • Una verificación binaria estricta sugiere el proceso de borrador, prueba y corrección.
  • Un enfoque exploratorio sugiere el método ReAct.
  • Las preocupaciones relacionadas con la política y el tono sugieren reflexionar y revisar, o adoptar enfoques de crítica y construcción.
  • Un entorno inestable requiere intentos repetidos teniendo en cuenta la memoria.
  • Un alto riesgo implica incorporar desde el principio una escalada humana en la gestión del problema.

La ingeniería de bucles resulta ser menos sobre una orquestación inteligente y más sobre un pensamiento disciplinado acerca de los sistemas.