Inicio / Artículos

Agentes Harness, Loop y Graph: Tres capas detrás de los agentes de IA fiables

Por qué los agentes poco fiables suelen carecer de un elemento clave: el arnés alrededor del modelo, un bucle basado en evidencias y un grafo explícito, junto con patrones de código y una lista de verificación para la depuración.

Editor: REACTAPP.TOP15 min de lectura3454 palabras

Fuentes: Harness, Loop, and Graph: What I Learned Building the Same AI Agent Three Different Ways

Nombres que parecen familiares pero no se entienden

Las frases “agent harness engineering” y “loop engineering” aparecen con tanta frecuencia en publicaciones sociales y artículos de blogs que dan la impresión de ser comprensibles. La familiaridad es engañosa, y un pequeño fallo suele revelarlo.

Imagínese un agente de investigación cuya tarea es buscar artículos de arXiv, resumirlos y elaborar un resumen semanal. Tres pruebas funcionan bien. Luego, una llamada a una API con límite de velocidad falla sin dejar rastro; el código no se da cuenta de nada, y el agente comienza a inventar resúmenes de artículos que nunca abrió. El modelo no tiene la culpa, al igual que la instrucción dada. Lo que falta es alguna forma en que el código circundante pueda distinguir entre un artículo que realmente se leyó y una fabricación verosímil. Esa brecha existe, tiene un nombre, y los ajustes en las instrucciones no la solucionan.

Crear el mismo agente tres veces de forma separada hace que las diferencias sean concretas: un proceso ajustado para el entorno, otro para los bucles de retroalimentación y un tercero para el control explícito del flujo de trabajo. Este campo tiene solo unos dieciocho meses de existencia en su forma actual, por lo que la denominación aún es objeto de debate. Sin embargo, las distinciones subyacentes son reales y se corresponden con decisiones de ingeniería reales. Posteriormente, un agente defectuoso debería indicar en qué de las tres capas se debe reparar primero.

Por sí solo, un modelo de lenguaje simplemente traduce texto a texto. La memoria entre llamadas solo existe si se proporciona. El acceso a archivos, los comandos de shell y las llamadas a API requieren infraestructura que alguien debe construir, y el modelo no puede verificar sus propias respuestas sin una verificación externa. Quienes desarrollan agentes en 2024 se dieron cuenta de esto cuando una demostración sin problemas fallaba ante la primera acción inesperada del usuario.

La industria ha respondido dejando de tratar “el agente” como un único elemento. Se trata de tres problemas de ingeniería separables que se superponen sobre el modelo.

LAYER            QUESTION IT ANSWERS                         FAILS AS
----------------------------------------------------------------------
Harness           What can the agent see and touch?          Missing tools,
                   (environment, tools, memory,               lost state, blown
                   permissions, execution limits)             budgets, leaks
----------------------------------------------------------------------
Loop               How does one attempt turn into a           Confident wrong
                   correct one? (retry, verify, stop)          answers, infinite
                                                                spinning, silent
                                                                failure
----------------------------------------------------------------------
Graph              What is the actual shape of the work?       Wrong step order,
                   (sequence, branches, parallel paths,         no recovery path,
                   human checkpoints)                           no visibility
----------------------------------------------------------------------

Es más fácil comprender cada capa al crear su versión más pequeña, romperla y luego leer los documentos de investigación y del framework para entender por qué falló.

El arnés: lo que el agente puede ver y tocar

La definición más clara aparece en una nota técnica de LangChain que reduce la idea a una ecuación: agente igual a modelo más arnés. El modelo proporciona el razonamiento. El arnés suministra todo lo que ese razonamiento necesita para interactuar con el mundo real, es decir, herramientas, memoria, contexto, permisos y las restricciones que impiden que haga algo costoso o peligroso.

Un modelo mental común y inicial de “proporcionar herramientas a un agente” consiste en escribir unas pocas funciones en Python y describirlas en el prompt del sistema. Eso está bien para una demostración, pero no funciona en tareas que superen una ventana de contexto, ni en llamadas a herramientas que fallen a las 2 a.m. sin que nadie las supervise. En la práctica, este enfoque acaba teniendo seis responsabilidades distintas.

  • La inyección de contexto determina qué ve el modelo antes de cada paso de razonamiento: el mensaje del usuario, pero también documentos recuperados, conversaciones anteriores, el estado del proyecto y las políticas organizacionales. Es fácil sobrecorregir; incluir todo un árbol de archivos y el historial de git en cada llamada empeora la precisión, ya que el modelo se ve abrumado por tokens irrelevantes, y un contexto más reducido y seleccionado suele ser mejor que uno más grande e indiscriminado.
  • Las acciones convierten una decisión en un efecto en el mundo, ya sea una solicitud API, un comando de shell, una escritura en una base de datos o un clic en el navegador. Aquí interviene el Protocolo de Contexto del Modelo, el cual ha experimentado muchos cambios este año. Comenzó a finales de 2024 como una forma estándar para que los agentes encontraran e invocaran herramientas, evitando a los equipos la necesidad de crear integraciones personalizadas para cada API. Para la especificación de julio de 2026 ya contaba con un núcleo sin estado que funcionaba detrás de balanceadores de carga normales sin sesiones persistentes, una extensión Tasks de AWS para tareas que superan el alcance de una sola solicitud, y una autorización más estricta basada en OAuth y OpenID Connect. La fuente indica más de diez mil servidores en producción públicos y descargas de SDK superiores a 97 millones al mes. Hoy en día, construir una solución sobre una capa de herramientas que no sea MCP o similar requiere una justificación sólida.
  • La persistencia se refiere a todo aquello que debe sobrevivir a un reinicio, una caída o el cierre de una ventana de contexto completa, como puntos de control, estado de sesión, memoria vectorial e historial de Git. El primer agente de investigación no conservaba nada de esto, por lo que la interrupción de la conexión eliminaba cuarenta minutos de progreso. Escribir un pequeño punto de control en JSON después de cada subtarea completada contribuyó más a la fiabilidad que cualquier modificación en las instrucciones.
  • El control de ejecución agrupa reglas de intentos repetidos, tiempos de espera, presupuestos de tokens y gastos, enrutamiento de modelos, límites para los subagentes generados y los pasos en los que una persona debe dar su aprobación. Sin él, un agente atrapado en un ciclo de intentos fallidos durante toda la noche puede hacer que un trabajo se alargue indefinidamente.
  • La seguridad y la gobernanza abarcan el acceso con mínimo privilegio a las herramientas, el manejo de secretos, el aislamiento en entornos sandbox, los registros de auditoría y las listas de permisos. El código escrito por el modelo siempre debe ejecutarse en un entorno sandbox. Se capturó a un agente de programación a punto de ejecutar rm -rf en una carpeta que erróneamente consideró como un artefacto de compilación. No hubo mala intención, solo un error combinado con permisos para causar daños.
  • La observabilidad se refiere a los rastros, los registros de llamadas a herramientas, el uso de tokens, la latencia y los resultados de evaluación. Parece algo aburrido hasta que un agente comienza a comportarse de manera extraña en producción y no hay registro de lo que realmente hizo en comparación con lo que se suponía.

A continuación se muestra un framework mínimo pero funcional en Python. Es impulsado por un modelo local de Ollama, por lo que no se requiere clave API, y cambiar a un modelo alojado solo implica una línea de código.

import json
import subprocess
import requests
from datetime import datetime
OLLAMA_URL = "http://localhost:11434/api/chat"
MODEL = "llama3.1"
# --- Action surface: the only two things this agent is allowed to do ---
def read_file(path: str) -> str:
    with open(path, "r") as f:
        return f.read()[:4000]  # cap it, don't blow the context window
def run_tests() -> str:
    result = subprocess.run(
        ["pytest", "-q"], capture_output=True, text=True, timeout=60
    )
    return result.stdout[-2000:] + result.stderr[-2000:]
TOOLS = {"read_file": read_file, "run_tests": run_tests}
TOOL_SCHEMA = [
    {
        "type": "function",
        "function": {
            "name": "read_file",
            "description": "Read a file from the project",
            "parameters": {
                "type": "object",
                "properties": {"path": {"type": "string"}},
                "required": ["path"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "run_tests",
            "description": "Run the project's pytest suite",
            "parameters": {"type": "object", "properties": {}},
        },
    },
]
# --- Persistence: cheap but real ---
def checkpoint(state: dict, path: str = "checkpoint.json"):
    with open(path, "w") as f:
        json.dump({**state, "saved_at": datetime.utcnow().isoformat()}, f)
# --- Execution control: hard ceilings, not suggestions ---
MAX_TOOL_CALLS = 8
def harness_run(user_task: str):
    messages = [{"role": "user", "content": user_task}]
    calls_made = 0
    while calls_made < MAX_TOOL_CALLS:
        response = requests.post(
            OLLAMA_URL,
            json={"model": MODEL, "messages": messages, "tools": TOOL_SCHEMA, "stream": False},
        ).json()
        msg = response["message"]
        messages.append(msg)
        if not msg.get("tool_calls"):
            return msg["content"]  # model is done, no more tools needed
        for call in msg["tool_calls"]:
            name = call["function"]["name"]
            args = call["function"].get("arguments", {})
            if name not in TOOLS:
                result = f"error: tool {name} is not permitted"
            else:
                result = TOOLS[name](**args)
            messages.append({"role": "tool", "content": str(result)})
            calls_made += 1
            checkpoint({"messages": messages, "calls_made": calls_made})
    return "stopped: hit the tool call ceiling before finishing"

Nada aquí es sofisticado, y esa es la lección: el harness funciona como un registro cuidadoso alrededor de un modelo que no lleva registros. Para un modelo alojado, reemplace el bloque requests.post por una llamada al SDK de OpenAI o Anthropic. El esquema de la herramienta y el sistema de guardado de puntos de control permanecen sin cambios, ya que no dependen de quién responde.

Las quejas de que el modelo se ha vuelto más tonto casi siempre se deben al harness. Puede que la herramienta haya devuelto datos obsoletos, el contexto esté saturado de historial irrelevante, o las permisos sean demasiado amplios o demasiado restrictivos, lo que hace que el agente intente hacer algo para lo que nunca tuvo autorización. Revise el harness antes de editar los prompts o cambiar de modelo, porque allí suele estar el problema.

El ciclo: cómo un intento se convierte en uno bueno

Si el arnés decide qué puede tocar el agente, el bucle decide cómo pasar de un primer intento a un buen resultado. Es la capa que más a menudo se subestima, ya que “simplemente déjelo intentarlo de nuevo” suena trivial hasta que el agente realmente lo hace.

El fallo clásico es un bucle autocorregible que, después de que el modelo responde, le pregunta si está seguro de que la respuesta es correcta. La respuesta es siempre sí. Un modelo que califica su propio trabajo sin ninguna señal externa casi siempre se considera exitoso, porque no hay nada contra lo que pueda fallar. Este es un problema bien documentado y no un error aislado: un bucle sin evidencia contra la cual fallar siempre creerá que ha tenido éxito.

La solución consiste en dejar de pedirle al modelo que se evalúe por sí mismo y asignarle a el bucle un árbitro externo: un conjunto de pruebas con resultados de aprobado o rechazado, un validador de esquemas, un compilador, una regla de linting, o una segunda búsqueda independiente que respalde o contradiga la primera respuesta. Los bucles basados en evidencias observan el estado del mundo, mientras que los basados en confianza se centran en cómo se siente el modelo.

La técnica de Ralph Wiggum y por qué funciona un bucle simple

Un patrón llamado “Ralph Wiggum” ganó popularidad después de que el ingeniero Geoffrey Huntley lo describiera en 2025. En esencia, es sorprendentemente sencillo: un bucle de bash envía repetidamente el mismo prompt a un agente de programación hasta que la tarea se completa, y el progreso se registra en archivos y commits de git en lugar de en el recuerdo del modelo del chat.

#!/bin/bash
# ralph.sh - crude but surprisingly effective
while true; do
  claude -p "$(cat PROMPT.md)" --dangerously-skip-permissions
  if git diff --quiet HEAD~1 HEAD -- STATUS.md && grep -q "DONE" STATUS.md; then
    echo "Task complete."
    break
  fi
  sleep 5
done

Parece que no debería funcionar, y tras una pequeña refactorización resulta que su crudeza es precisamente su ventaja. Al descartar el contexto conversacional en cada paso e imponer el progreso en los archivos de historial y estado de Git, se evita la degradación del contexto, y se impide que el agente se convenza de que ya ha terminado algo que en realidad no ha hecho. No es elegante. Es una solución basada en evidencias por casualidad, ya que Git y el sistema de archivos no mienten para halagar al modelo.

Aquí hay un pequeño bucle basado en evidencias del mismo tipo, nuevamente ejecutable localmente contra Ollama, que considera que una prueba fallida es el único significado aceptable de “todavía no está hecho”.

def evidence_loop(task_description: str, max_attempts: int = 5):
    attempt = 0
    history = []
    while attempt < max_attempts:
        attempt += 1
        code = generate_patch(task_description, history)   # calls the model
        apply_patch(code)
        test_output = run_tests()                          # external evidence
        passed = "failed" not in test_output.lower()
        history.append({
            "attempt": attempt,
            "patch": code,
            "test_output": test_output,
            "passed": passed,
        })
        if passed:
            return {"status": "success", "attempts": attempt}
        # feed the *actual failure*, not a vague "try again"
        task_description = (
            f"{task_description}\n\nPrevious attempt failed with:\n{test_output}\n"
            "Fix the specific failure above."
        )
    return {"status": "gave_up", "attempts": attempt, "history": history}

Mire la última línea antes del retorno, porque es la que contiene la idea clave. El bucle no dice “intente con más fuerza”. En su lugar, proporciona al modelo el fallo real del test, ya que son las pruebas externas concretas las que hacen que el segundo intento sea mejor que el primero, y no un nuevo intento aleatorio. En la mayoría de los agentes, el mayor aumento en la fiabilidad se logra al reemplazar la pregunta “¿Esto parece correcto?” por una verificación externa real, sea del tipo que sea y que se adapte a la tarea.

También es esencial saber cuándo detenerse. Un bucle sin límite estricto en cuanto a intentos, costos o tiempo real no constituye un agente sólido; es más bien un error listo para causar problemas.

El grafo: cuando la tarea tiene ramas reales

Un arnés proporciona el soporte para el cuerpo del agente y un bucle le permite mejorar intento tras intento, pero ninguno de los dos describe una tarea con forma de árbol: hacer esto, luego elegir una de tres acciones según el resultado, esperar la aprobación de un humano y, finalmente, ejecutar dos verificaciones en paralelo y combinarlas.

Eso es ingeniería de grafos. Suele ser el aspecto al que la gente se resiste, ya que un bucle parece suficiente durante mucho tiempo, pero deja de serlo cuando el trabajo no consiste en una tarea continua hacia una sola prueba de aprobación. Los bucles son buenos solo para una cosa: intentar, verificar y revisar hasta que se cumpla una condición. Son inadecuados cuando el siguiente paso depende de cuál de varios resultados ocurrió, cuando dos subtareas pueden realizarse simultáneamente, o cuando alguien debe aprobarlo antes de continuar. Incluir todo eso en una sola instrucción y esperar que el modelo lo gestione funcionó para algunos equipos, pero es frágil. Una vez que aparecen las ramificaciones, el flujo de control debe estar en una estructura explícita, no en lo que el modelo improvise en cada turno.

La ingeniería de grafos hace que el flujo de trabajo sea explícito. Los nodos representan pasos como una llamada a un agente, una llamada a una herramienta, un validador o un punto de control humano, mientras que las aristas son las condiciones que permiten pasar la ejecución de un nodo a otro. Al observar el grafo se puede ver con exactitud qué ruta siguió una ejecución, lo cual es de gran importancia cuando ocurre un incidente en producción a medianoche y es necesario saber qué realmente sucedió en lugar de suponerlo.

LangGraph es la opción habitual en este caso. Representa un flujo de trabajo como un grafo dirigido, transfiere estados tipados de nodo en nodo, utiliza aristas condicionales para crear ramificaciones y cuenta con funcionalidades de puntos de control integradas, lo que permite que una ejecución se detenga, reinicie o vuelva a un estado anterior.

from langgraph.graph import StateGraph, END
from typing import TypedDict

class AgentState(TypedDict):
    task: str
    draft: str
    review_passed: bool
    attempts: int
def draft_node(state: AgentState) -> AgentState:
    state["draft"] = generate_draft(state["task"])
    return state
def review_node(state: AgentState) -> AgentState:
    state["review_passed"] = run_validator(state["draft"])
    state["attempts"] += 1
    return state
def needs_human(state: AgentState) -> str:
    if state["review_passed"]:
        return "approved"
    if state["attempts"] >= 3:
        return "escalate"   # branch: give up automating, ask a human
    return "retry"
graph = StateGraph(AgentState)
graph.add_node("draft", draft_node)
graph.add_node("review", review_node)
graph.set_entry_point("draft")
graph.add_edge("draft", "review")
graph.add_conditional_edges(
    "review",
    needs_human,
    {"approved": END, "retry": "draft", "escalate": "human_approval"},
)

Un bucle simple tiene dificultades para explicar con claridad qué hace esto: una verificación que genera tres resultados (finalizado, repetir, detenerse y consultar a una persona), cada uno llevando a un destino diferente. El camino de escalada es importante; marca la diferencia entre un agente que se rinde en silencio tras el tercer fallo y uno que proporciona al humano todo el historial de los intentos realizados.

AutoGen de Microsoft añadió algo similar este año, llamado GraphFlow, por la misma razón. Varios agentes o flujos de trabajo ramificados hacen que un bucle implícito sea difícil de seguir, mientras que un grafo puede inspeccionarse directamente.

Para determinar dónde brilla realmente cada framework, en lugar de confiar en las páginas de marketing, se probaron cuatro en un único proyecto pequeño: un pipeline de revisión de documentos que incluía una rama de validación y una puerta de aprobación humana.

FRAMEWORK          ORCHESTRATION MODEL         STRONGEST AT               STATE / PERSISTENCE
--------------------------------------------------------------------------------------------
LangGraph           Directed graph, typed        Complex branching,        Built-in checkpointing,
                     state, conditional edges     long-running workflows,   time-travel debugging
                                                   human-in-the-loop
--------------------------------------------------------------------------------------------
CrewAI               Role-based "crew" of         Fast prototyping,         Sequential task output
                      agents, sequential or        multi-agent brainstorm   passing, lighter state
                      hierarchical process         style collaboration      model
--------------------------------------------------------------------------------------------
OpenAI Agents SDK     Explicit agent handoffs      Minimal-friction start   Ephemeral context
                                                    inside the OpenAI        variables by default
                                                    ecosystem
--------------------------------------------------------------------------------------------
AutoGen / GraphFlow   Multi-agent conversation     Agent-to-agent           Conversation history
                       plus optional graph layer   negotiation, graph-      plus graph state
                       for explicit control        based orchestration
--------------------------------------------------------------------------------------------

La conclusión es práctica. Si el flujo de trabajo cabe en una página como una lista plana de pasos, no se necesita un marco de grafos, y un bucle bien estructurado dentro de un mecanismo sólido cumple la función con menos código que mantener. Una vez que se dibuja el flujo de trabajo y este tiene ramificaciones, un marco de grafos comienza a valerse por sí mismo de inmediato, principalmente al ahorrar tiempo en la depuración, y no por algo que haga en tiempo de ejecución.

Cómo se superponen las tres capas

Estos no son enfoques rivales. Se superponen verticalmente, y así es como se ve la estructura una vez que se ha utilizado las tres.

+--------------------------------------------------------------+
|  GRAPH   - decides which node runs next, where branches      |
|            are, where a human has to approve                 |
+--------------------------------------------------------------+
|  LOOP    - inside a given node, drives attempt -> verify ->  |
|            revise until the evidence says stop               |
+--------------------------------------------------------------+
|  HARNESS - underneath both, provides the tools, memory,      |
|            permissions, and limits that make any of it       |
|            possible to execute safely                        |
+--------------------------------------------------------------+
|  MODEL   - the reasoning engine none of the above exists     |
|            without                                           |
+--------------------------------------------------------------+

Dentro de un flujo de trabajo de LangGraph, un nodo de borrador podría ejecutar un bucle basado en evidencias varias veces antes de pasar el control de vuelta al grafo. Ese bucle interno solo funciona porque la estructura subyacente permite que el modelo realice pruebas, lea archivos y recuerde sus intentos anteriores. Si se elimina cualquier capa, el resto deja de funcionar, por lo que hacer que un “agente” monolítico sea más complejo siempre llegaría a un punto de estancamiento.

Pensar en tres capas hace que la depuración sea notablemente más rápida. Esta es la lista de verificación que se debe seguir, en orden, antes de modificar un prompt.

SYMPTOM                                    LIKELY LAYER TO CHECK FIRST
----------------------------------------------------------------------
Agent doesn't know something it should      Harness -> context injection
Agent "can't" do something it should be     Harness -> action surface /
  able to do                                 permissions
Agent forgets earlier progress after a      Harness -> persistence
  restart or long run
Costs or run time spiral unexpectedly       Harness -> execution control
Agent confidently reports success on        Loop -> swap confidence checks
  work that's actually wrong                 for external evidence
Agent retries the same mistake              Loop -> is it seeing the actual
  repeatedly without improving                failure, or just "try again"?
Agent takes the wrong path when a task      Graph -> is the branching logic
  has more than one possible outcome          explicit, or implicit in a
                                               single prompt?
No human ever gets a chance to catch a      Graph -> is there an approval
  bad decision before it ships                node, or is it all automatic?

Una tabla como esta habría evitado pasar todo un fin de semana culpando al prompt por algo que en realidad era la falta de un punto de control.

Preguntas abiertas

Algunas cuestiones siguen sin resolverse, y sería incorrecto concluir con más certeza de la que permiten las pruebas.

En primer lugar, el vocabulario sigue evolucionando. La “ingeniería de harness” apenas existía a gran escala antes de este año, y dentro de otro año podría integrarse en la categoría más amplia de “ingeniería de agentes”, donde “harness”, “loop” y “graph” pasarían a ser subcategorías y no disciplinas en las que las personas se especialicen.

En segundo lugar, el enfoque de Ralph Wiggum parece ligeramente defectuoso incluso cuando funciona. Darle a un modelo una sola instrucción en un bucle repetitivo y depender del historial de Git como su única memoria se asemeja a un parche aplicado sobre un arnés que carece de verdadera persistencia. Funciona bien para refactores pequeños y de alcance limitado, pero no se debe confiar en él para tareas que involucren ramas reales o consecuencias significativas.

En tercer lugar, los frameworks de grafos podrían seguir cambiando. La función de guardado de estados de LangGraph es excelente, pero los equipos adoptan un framework de grafos completo para un flujo de tres pasos que nunca se bifurca, simplemente porque está de moda. Un flujo de trabajo que es una línea recta puede seguir siéndolo.

Hay una afirmación que resiste todo esto: un agente poco fiable casi nunca se soluciona con una instrucción mejor. La falla se encuentra casi siempre en uno de los tres niveles, y las preguntas anteriores ayudan a identificar cuál es.

Comenzar a desarrollar el agente de investigación hoy mismo significaría construir primero el sistema de soporte y tratarlo como una infraestructura básica y resistente, en lugar de algo secundario. Desde el primer día se implementaría un ciclo basado en evidencias en lugar de confiar en la autoevaluación del modelo, ya que ese cambio por sí solo corrige más errores que cualquier otra cosa. Un marco gráfico esperaría hasta que se pudiera representar el flujo de trabajo en papel con una división visible.

El modelo ha captado toda la atención durante tres años, pero la fiabilidad en producción proviene de la ingeniería que lo rodea. Ese trabajo es mucho menos llamativo y mucho más manejable que la afirmación de que el modelo no es lo suficientemente inteligente. La mayoría de las veces sí lo es. Simplemente no se le ha proporcionado el entorno, los comentarios o la información necesaria para demostrarlo.

Leer más

  • Agentes Gestionados por Configuración: Crea un único agente en tiempo de ejecución y especialízalo rápidamente — Por qué las empresas deben crear un agente base y modificarlo mediante configuraciones externas: catálogos, registros de habilidades, gobernanza, pruebas, LangGraph y modos de fallo.
  • Utiliza, Bucle y Gráfico: Tres capas anidadas en un sistema de agente de programación — Los síntomas de fallo indican la capa que debe corregirse. Cómo se anidan el uso de herramientas, los bucles y los gráficos, dónde es útil el uso de múltiples agentes, qué no pueden garantizar las verificaciones adversarias, y una lista de verificación.
  • Notas prácticas: MCP 2026: El protocolo creció aquí: esto es la IA en producción — Guía paso a paso de las notas prácticas: MCP 2026: El protocolo creció aquí: esto es la IA en producción: contratos, verificaciones y espacios para código listo para usar para los equipos que implementan este patrón.