¿Quién debería poseer el bucle de agentes: herramientas en tiempo de ejecución vs frameworks compuestos?
Un marco de toma de decisiones para crear agentes de IA que se basa en una única pregunta: quién es el responsable del bucle de ejecución, al comparar las herramientas Node harness con LangGraph, CrewAI y LangChain.
Fuentes: Who Owns the Agent Loop? Node.js Harnesses vs. Python Frameworks (LangGraph, CrewAI, LangChain)
A simple vista, las herramientas para crear agentes de IA parecen intercambiables. Cada una te proporciona un modelo, una forma de llamar a las herramientas y algún mecanismo que mantiene el modelo funcionando hasta que se completa una tarea; con cualquiera de ellas puedes tener un agente operativo en tan solo una tarde. La diferencia entre ellas permanece oculta hasta que pasas de la fase de demostración a la producción, y entonces resulta ser fundamental: ¿quién es dueño del ciclo que impulsa al agente? Un grupo de herramientas te entrega un entorno de ejecución y se retira; el otro te ofrece abstracciones y te pide que las combines tú mismo. Este artículo traza con precisión la línea entre estas dos filosofías y luego te ofrece una forma de decidir a qué lado pertenece un proyecto dado. El objetivo no es coronar a un ganador, sino hacer explícita la elección a tomar.
Por un lado están los entornos de ejecución: sistemas que gestionan completamente el ciclo de envío de solicitudes, ejecución de herramientas, procesamiento de resultados y repetición del ciclo. Usted no escribe ese ciclo; lo configura y lo extiende. Por el otro lado se encuentran frameworks como LangGraph, CrewAI y LangChain, que le proporcionan componentes explícitos como grafos, equipos y cadenas, y esperan que usted conecte manualmente el flujo de control. Aunque esto se presenta como una comparación entre Node y Python, el lenguaje es solo un efecto secundario. La verdadera variable es cuánta de esta infraestructura desea controlar usted mismo.
Dos definiciones que resisten la presión
Defina claramente las dos palabras antes de utilizarlas. Un harness es un entorno de ejecución que se hace cargo del bucle del agente. Realiza el ciclo en su lugar: envía la solicitud, recopila las llamadas a herramientas que el modelo desea, las ejecuta, devuelve los resultados y repite hasta que el modelo deje de solicitar algo. También tiende a abarcar los problemas complejos circundantes, como el manejo de contexto y memoria, la compresión del historial a medida que se llena el presupuesto de tokens, los intentos repetidos y la persistencia de sesiones. Usted lo dirige a través de la configuración, es decir, el prompt del sistema, la elección del modelo, las herramientas permitidas y los límites de uso, así como mediante ganchos y puntos de extensión, nunca escribiendo manualmente el bucle. Un harness en este ámbito, Pi, un entorno de ejecución basado en Node y TypeScript, resume su filosofía en términos de primitivas en lugar de características: adapta el entorno de ejecución a su flujo de trabajo en lugar de forzar su flujo de trabajo para que se ajuste a él.
Un framework, o SDK, es en realidad un conjunto de abstracciones explícitas que se componen en código. LangGraph ofrece un grafo de nodos conectados por aristas. CrewAI proporciona grupos de agentes que participan en juegos de rol. LangChain ofrece cadenas. En cada uno de ellos, usted es el autor del flujo de control, y la biblioteca solo suministra las partes necesarias y le permite conectarlas. La distinción que realmente importa es esta: en un framework el bucle, la estructura del grafo o la organización de los agentes está expuesto y le pertenece a usted, mientras que en un runtime el bucle pertenece al propio sistema operativo.
Se trata de un espectro y no de una opción binaria. En un extremo, usted dirige el runtime mediante configuraciones y complementos, mientras que él se encarga del ciclo; en el otro extremo, usted crea cada nodo, arista, grupo de agentes o cadena, y el bucle es literalmente su propio código.
HARNESS you → [config + hooks] → ╔═══ runtime owns the loop ═══╗ → result
║ prompt → tools → results → ↺║
FRAMEWORK you author every node / edge / crew / chain → the loop IS your code
Tenga en cuenta ese eje, ya que todo lo que sigue depende de la posición del herramienta en él.
El enfoque del arnés: dejar la iteración al entorno de ejecución
Pi es un arnés mínimo, nativo de Node, organizado como una pila en capas, y solo se utiliza la capa que se necesita. En la parte inferior está una abstracción de proveedor que abarca a muchos proveedores de modelos; en el medio se encuentra la iteración del agente y la distribución de herramientas; por encima de eso hay un entorno de ejecución completo con persistencia de sesiones; y en la superficie se encuentra una interfaz de usuario en terminal. Para la mayoría de los ingenieros, la capa intermedia, la iteración, es la más interesante. Iniciar un agente implica describir cuál debe ser y luego transferirle el control. Un pequeño agente de investigación construido sobre la API de sesiones se ve así.
import { getModel } from "@mariozechner/pi-ai";
import { createAgentSession, SessionManager } from "@mariozechner/pi-coding-agent";
const { session } = await createAgentSession({
model: getModel("anthropic", "claude-sonnet-4-6"),
cwd: process.cwd(),
sessionManager: SessionManager.open("./session.jsonl"),
tools: ["read", "grep", "bash", "search_docs"], // built-ins + your custom tools, by name
customTools: [searchDocsTool], // your typed tools
});
// You steer the runtime with config, not loop code:
session.agent.state.systemPrompt = "You are a research assistant. Always cite sources.";
// A single call runs the entire prompt → tools → results → repeat cycle:
await session.prompt("Summarize the latest on agent frameworks and cite sources.");
Observe lo que falta. No hay bucle while, ni rama que diga “si el modelo devolvió llamadas a herramientas, ejecútalas y llama al modelo nuevamente”, ni un sistema de contabilidad de tokens para decidir cuándo se debe compactar el historial. El tiempo de ejecución se encarga de todo eso. Todo lo que proporcionaste fue un modelo, las herramientas que puede utilizar y una instrucción de sistema, seguida de una única llamada al método prompt. Una herramienta personalizada es igualmente un objeto pequeño y tipado: un nombre, una descripción dirigida al modelo, un esquema para sus parámetros y una función de ejecución.
const searchDocsTool = {
name: "search_docs",
description: "Search the internal docs and return matching passages.",
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
async execute(_id: string, params: { query: string }, signal?: AbortSignal) {
const passages = await search(params.query, signal);
return { content: [{ type: "text", text: passages }], details: {} };
},
};
Hasta ahora esto parece ser solo una conveniencia. La complejidad se hace evidente en el momento en que es necesario cambiar el comportamiento del bucle, ya que no se puede reescribir un bucle del cual no se es dueño. En su lugar, se accede a él a través de eventos y ganchos. Suscribirse a eventos permite observar su ejecución e intervenir, por ejemplo contando las iteraciones y deteniéndolo una vez se alcanza un límite.
let turns = 0;
const MAX_TURNS = 20;
const unsubscribe = session.agent.subscribe((event) => {
if (event.type === "turn_end" && ++turns >= MAX_TURNS) {
session.agent.abort(); // pull the cord on a loop you didn't write
}
});
La misma estructura se aplica para establecer un techo de costos: se acumulan los costos por mensaje a medida que llegan y se interrumpe su procesamiento una vez se supera un umbral. El tiempo de ejecución sigue haciendo que el bucle funcione mientras se observa su progreso y se decide cuándo detenerlo. Los ganchos van un paso más allá, permitiendo interceptar una decisión antes de que tenga efecto, de forma invisible para el modelo. Un gancho antes de una llamada a la herramienta puede inspeccionar una llamada pendiente y rechazarla.
session.agent.beforeToolCall = async (ctx) => {
if (ctx.toolCall.name === "bash" && isDangerous(ctx.args.command)) {
return { block: true, reason: "blocked by policy" };
}
return undefined; // allow
};
El modelo “pidió” ejecutar una orden de shell; el complemento rechazó silenciosamente la solicitud y devolvió un motivo, y el bucle continuó como si nada hubiera pasado. Modificaste el comportamiento del agente en torno a un bucle que nunca creaste tú. Ese es el trato que ofrece un framework: cedes la capacidad de reescribir el flujo de control y, a cambio, obtienes rápidamente un agente funcional, valores predeterminados sólidos para las partes realmente difíciles como la gestión de contexto, la compactación y la persistencia, además de un conjunto organizado de mecanismos para adaptar el comportamiento, configuraciones, eventos y complementos. Es una excelente oferta precisamente cuando las opiniones del entorno de ejecución coinciden con las tuyas.
El enfoque del framework: compone el flujo de control tú mismo
El ecosistema de Python apostó por lo contrario. En lugar de un entorno de ejecución que controle el bucle, proporciona abstracciones que tú mismo debes combinar, y tres de ellas son las más utilizadas.
LangChain: componer una cadena
LangChain es el más antiguo y el de mayor alcance de los tres. Comenzó como una forma de concatenar llamadas a modelos LLM y se convirtió en una plataforma extensa: más de mil integraciones, una interfaz que no tiene en cuenta qué modelo está detrás de ella, y un producto relacionado para la observabilidad. Para los agentes ofrece un bucle de llamadas a herramientas al estilo ReAct, y su enfoque de composición conecta un componente con el siguiente. Su gran ventaja es su alcance: si existe un modelo, un almacén de vectores o una herramienta externa, probablemente también exista una integración de LangChain para ello. Su desventaja es el precio que se paga por esa versatilidad. Las capas profundas de abstracción pueden convertir la depuración en una tarea complicada, y como es estadoless por defecto, la durabilidad debe implementarse de forma adicional en lugar de ser algo inherente. LangChain es adecuado para flujos de trabajo estándar, en su mayoría lineales y con un solo agente, donde lo que más se valora es la amplitud de las integraciones.
LangGraph: modelado de una máquina de estados
LangGraph hace una apuesta contundente y firme: los sistemas de agentes son máquinas de estados, y se debe controlar cada transición. Se modela al agente como un grafo cuyos nodos representan pasos y cuyas aristas son las transiciones, todo ello operando sobre un objeto de estado tipado.
from langgraph.graph import StateGraph, MessagesState, START, END
def call_model(state: MessagesState):
return {"messages": [llm.invoke(state["messages"])]}
graph = StateGraph(MessagesState)
graph.add_node(call_model)
graph.add_edge(START, "call_model")
graph.add_edge("call_model", END)
app = graph.compile()
app.invoke({"messages": [{"role": "user", "content": "hi"}]})
Mientras que un marco oculta el bucle, LangGraph lo convierte en el artefacto que usted mismo crea. La ramificación condicional se expresa de manera explícita.
graph.add_conditional_edges("call_model", should_continue, {"tools": "tools", "end": END})
Esa función de continuar o detenerse es precisamente la rama que el framework ejecuta por usted tras una sola llamada de comando. Aquí la tiene: visible, probable y versionable. La recompensa por esta mayor verbosidad es la característica distintiva de LangGraph: el control de puntos de verificación nativo en cada transición. Dado que el estado se escribe en disco a medida que avanza el grafo, se obtiene una ejecución capaz de sobrevivir a los reinicios, la recuperación tras fallos, la posibilidad de rebobinar e inspeccionar estados anteriores, y pausas que esperan a una intervención humana durante la ejecución. Los costos son una curva de aprendizaje pronunciada y un diseño deliberadamente poco adecuado para el despliegue sin servidor. LangGraph es ideal para flujos de trabajo de larga duración, cíclicos, resistentes a fallos y con estado.
CrewAI: formando un equipo
CrewAI sostiene que la mayoría de los problemas multiagente se descomponen fácilmente en equipos de especialistas. Cada agente está definido por un rol, un objetivo y una historia de fondo, y luego se reúnen en una tripulación a la que se le asignan tareas.
from crewai import Agent, Task, Crew
researcher = Agent(role="Researcher", goal="Find key insights on {topic}",
backstory="A meticulous analyst.")
writer = Agent(role="Writer", goal="Summarize the research clearly",
backstory="A concise technical writer.")
research = Task(description="Research {topic}.", expected_output="3 insights.", agent=researcher)
summary = Task(description="Write a summary.", expected_output="A short summary.", agent=writer)
Crew(agents=[researcher, writer], tasks=[research, summary]).kickoff(inputs={"topic": "agent frameworks"})
El modelo mental es intuitivo y el camino hacia un pipeline multiagente funcional es corto, por lo que goza de una amplia adopción en las empresas. Por otro lado, la abstracción basada en roles y objetivos resulta conveniente hasta que surge algún problema, momento en el cual se convierte en una caja negra; la delegación entre agentes puede generar bucles; y es necesario implementar mecanismos propios para evitar costos descontrolados. CrewAI es adecuado para sistemas multiagente basados en roles y para el prototipado rápido. Lo que une a estos tres aspectos es que usted mismo diseña el flujo de control. Eso representa tanto un costo —más código, más decisiones, más tareas de mantenimiento— como una ventaja, ya que puede ver, probar y reconfigurar cada transición. La influencia de Python en el ámbito de los datos y el aprendizaje automático es un verdadero impulso en este contexto, ya que los agentes operan junto a las bibliotecas y cuadernos de notas con los que ya trabaja.
Las dimensiones en las que realmente se toma la decisión
Alineados uno al lado del otro, estos enfoques se separan a lo largo de un pequeño número de ejes, y algunos de ellos soportan la mayor parte de la carga en la práctica.
La propiedad del bucle de ejecución es el eje principal; todo lo demás deriva de él. Un framework se queda con el bucle para sí mismo, mientras que otro te lo entrega a ti; LangGraph se encarga del extremo donde tú escribes todo, Pi se ocupa del extremo donde el sistema ejecuta y escribe todo, y los demás se distribuyen entre ambos.
La durabilidad es la ventaja decisiva de LangGraph. Al guardar un punto de control en cada transición, se puede recuperar el estado tras una caída y reproducirlo prácticamente sin esfuerzo adicional. La persistencia de sesiones que ofrecen los frameworks y los tipos de memoria proporcionados por CrewAI son útiles, pero ninguno de ellos equivale a un estado que se pueda reproducir de forma duradera. Cuando un proceso se detiene a mitad de una ejecución, un grafo con punto de control continúa desde la última transición, mientras que un registro de sesión generalmente solo permite reconstruir, a posteriori, lo que ocurrió.
Orquestar varios agentes está integrado en CrewAI a través de roles, y en LangGraph mediante subgrafos. Los frameworks logran esto por otros medios, ya sea a través de subagentes, extensiones o lógica de coordinación que se proporcione.
El ecosistema lingüístico circundante tiende a decidir de forma silenciosa. Elegir Python lleva al agente hacia el ámbito de los datos y el trabajo con machine learning, junto a tus dataframes, código de entrenamiento y cuadernos de notas. Elegir TypeScript lo dirige hacia la web y los entornos periféricos, junto a tu interfaz API, tus tipos frontend compartidos y cualquier lugar donde despliegues funciones serverless. Esa atracción gravitacional suele superar a cualquier característica individual en una tabla de comparación, y es conveniente ser sincero sobre el mundo en el que ya opera tu equipo.
Un último matiz para mantener la comparación honesta: el patrón de Node con herramientas auxiliares y Python con frameworks es una tendencia, no una regla. Existen entornos de ejecución de tipo herramienta auxiliar escritos en Python y bibliotecas de orquestación de tipo gráfico escritas en TypeScript. Decide según el paradigma y tus limitaciones reales, nunca basándote únicamente en el lenguaje indicado en el paquete.
Un trabajo, visto desde cada lado
Imagínese una tarea sencilla y común: un agente que obtiene algo mediante una herramienta y luego genera un resumen de ello. Lo que cada paradigma hace que enfrentemos desde el principio es la parte reveladora del proceso. Bajo este enfoque, casi todo el trabajo consiste en la configuración. Una vez declarada una herramienta de búsqueda y una instrucción para el sistema, se realiza una sola llamada y el entorno de ejecución asume el control, invocando al modelo, verificando que este desea realizar la búsqueda, ejecutándola, devolviendo los resultados, permitiendo al modelo compilar el resumen y finalizando por sí mismo. Este enfoque, en efecto, le plantea una sola pregunta: ¿qué capacidades debería tener este agente?
Bajo un marco como LangGraph, el trabajo se reduce casi por completo a la estructura. Se especifica el objeto de estado, un nodo que llama al modelo, un nodo que ejecuta la herramienta y un borde condicional que decide entre volver al modelo o finalizar. Hasta que ese grafo se dibuja por completo, no se ejecuta nada en absoluto. LangGraph le pidió algo diferente: ¿cuál es exactamente el flujo de control?
La idea clave es que ninguna de las versiones resulta difícil; la tarea es sencilla en ambas. Lo que las diferencia no es el esfuerzo, sino lo que debe decidirse de antemano y qué puede inspeccionarse o recuperarse posteriormente. Un marco de trabajo hace que la primera pregunta desaparezca y le proporciona rápidamente un agente organizado. Un framework, en cambio, se niega a que esa pregunta desaparezca y lo compensa con un flujo que permite rastrear una transición a la vez cuando el sistema falla en un momento inoportuno. Este equilibrio cobra aún más importancia a medida que el sistema crece. Cuando se está desarrollando un asistente de programación o un agente integrado en una aplicación, lo importante es la capacidad del sistema, y el marco de trabajo supone un alivio. Cuando se está creando un flujo de trabajo que debe ejecutarse durante mucho tiempo, soportar caídas y resistir auditorías, lo importante es el flujo de control, y cada línea adicional del grafo explícito se justifica por sí misma.
Ajustar la herramienta al bucle que debes poseer
Las heurísticas se derivan directamente de todo lo anterior. Elige un arnés cuando la prioridad sea un agente rápido y eficiente, cuando las opciones integradas del entorno ya se adapten a tus necesidades, cuando el objetivo sea un agente de programación o de herramientas para desarrolladores, o cuando vayas a integrar un agente dentro de una aplicación TypeScript y necesites seguridad tipológica y proximidad al frontend. Elige LangGraph cuando sea inaceptable perder avances, en casos de flujos de trabajo de larga duración o cíclicos que deben sobrevivir a caídas, que requieren un control preciso sobre el estado y cada transición, o que deben pausarse por intervención humana. Elige CrewAI cuando el problema realmente se pueda descomponer en un conjunto de roles especializados y quieras establecer rápidamente una pipeline multiagente. Elige LangChain cuando la amplia gama de integraciones disponibles para un flujo convencional de un solo agente que utiliza herramientas sea más importante que cualquier otro factor, especialmente dentro de una base de código en Python ya existente. Y, una vez más
Con sensibilidad, los límites son flexibles: decida sobre el paradigma y las restricciones, no sobre qué lenguaje se imprime en la lata.La pregunta desde el principio fue el bucle
El debate nunca fue realmente entre Node y Python, ni entre un paquete y su competidor. Se trataba de si el entorno de ejecución debía manejar el bucle del agente o si usted mismo debería hacerlo. Un harness sacrifica la libertad de reescribir el flujo a cambio de lograr rápidamente un agente funcional y modificar su comportamiento mediante configuraciones, eventos y ganchos, todo aplicado a un bucle que usted nunca creó. Un framework intercambia velocidad por comandos explícitos, durabilidad y capacidad de orquestación, y le cobra el costo continuo de escribir y mantener ese flujo. Para la mayoría de los equipos, una postura inicial sensata es utilizar un harness para prototipos, la creación de agentes y cualquier componente integrado en un producto, donde lo importante es la rapidez; pasar a LangGraph una vez que la persistencia del estado se convierta en un requisito esencial, y recurrir a CrewAI cuando el trabajo realmente requiera un equipo de especialistas. Dado que estas herramientas siguen absorbiendo las mejores ideas unas de otras, la habilidad que perdura no es la fluidez en una sola de ellas.
API, pero la decisión consiste en observar al agente que tienes frente a ti y determinar quién debería ser el propietario de su bucle.