Un agente es una forma de control de flujo. Todo lo demás —las herramientas, el sandbox, los guardrails— es consecuencia de esa decisión de flujo. Si entendés el mecanismo, podés predecir dónde falla, y eso alcanza para decidir arquitectura sin entrar en la discusión de si algo "es un agente de verdad".
El loop, literalmente
En "Building effective agents", Erik Schluntz y Barry Zhang definen workflows como "systems where LLMs and tools are orchestrated through predefined code paths" y agents como "systems where LLMs dynamically direct their own processes and tool usage, maintaining control over how they accomplish tasks". La diferencia mecánica es una sola línea de código: quién elige la próxima llamada.
Un agente es un while alrededor de la API:
messages = [{"role": "user", "content": tarea}]
while True:
resp = client.messages.create(
model="claude-opus-5",
max_tokens=16000,
tools=TOOLS,
messages=messages,
)
messages.append({"role": "assistant", "content": resp.content})
if resp.stop_reason != "tool_use":
break
resultados = [ejecutar(b) for b in resp.content if b.type == "tool_use"]
messages.append({"role": "user", "content": resultados})
Tres detalles del bloque son load-bearing. max_tokens es obligatorio: sin él la request vuelve 400, así que el loop no arranca. El append del mensaje del asistente va antes del corte, de modo que el turno final también queda en el historial. Y el corte es stop_reason != "tool_use", no stop_reason == "end_turn": un max_tokens o un refusal traen cero bloques tool_use, y con el corte por end_turn caerían al camino de herramientas, armarían un resultados vacío y appendearían un mensaje de usuario sin contenido, que la API rechaza. Con herramientas server-side agregás una rama para pause_turn, que se reanuda reenviando el turno en vez de terminar.
Ahora mirá qué hace cada parte. El modelo emite un bloque tool_use con un nombre y un objeto de argumentos; ejecutar es tarea del harness. Tu código —la función ejecutar— decide si eso corre, con qué permisos, en qué sandbox, y qué texto vuelve como tool_result. El modelo ve ese texto en el context window y muestrea la próxima acción condicionado a todo lo anterior. Es el patrón que formaliza ReAct: la traza se construye en runtime y no existe antes de correr.
Un workflow es el mismo grafo con las aristas escritas a mano: clasificás, después extraés, después validás. Las llamadas al modelo están adentro de tu if. En el agente, tu if está adentro del loop del modelo.
De ahí sale la propiedad que ordena todo lo demás. En un workflow, el conjunto de secuencias posibles es finito y lo escribiste vos. En un agente, cada turno multiplica las ramas por la cantidad de herramientas disponibles: el espacio de trazas de N turnos es del orden de |herramientas|^N. No lo enumeraste nunca, y no lo vas a enumerar.
El costo del error cambia de lugar
Un workflow falla donde vos lo escribiste. El paso 3 tira excepción, mirás el paso 3. La cobertura de tests es tratable porque los caminos son enumerables, y cuando algo se rompe, el stack trace apunta a una línea que existe en tu repo.
Un agente falla donde no lo previste. Y su modo de falla característico es la acción plausible, ejecutada con éxito, sobre el objeto equivocado. El rm que corrió perfecto en el directorio que no era. El UPDATE sin WHERE que la base aceptó feliz.
El blast radius de un agente es el conjunto de acciones irreversibles que sus herramientas hacen alcanzables.
Por eso la pregunta de diseño que rinde es qué tan reversible es cada cosa que el agente puede hacer, medida acción por acción. Y por eso el conteo de herramientas dice poco: un agente con veinte herramientas de solo lectura tiene blast radius vacío, porque ninguna de sus acciones alcanzables es irreversible. Uno con dos herramientas, donde una escribe en producción, no.
Los cuatro criterios
La documentación de Anthropic ordena la decisión en cuatro preguntas. Si alguna da que no, conviene quedarse un tier más abajo.
| Criterio | La pregunta | Qué la responde |
|---|---|---|
| Complejidad | ¿Es multi-paso y difícil de especificar de antemano? | Intentar escribir el pipeline. Si te sale, no hacía falta el agente. |
| Valor | ¿El resultado justifica más costo y más latencia? | El loop hace N llamadas donde el workflow hacía una. |
| Viabilidad | ¿El modelo es capaz en este tipo de tarea? | Una eval, no una intuición. |
| Costo del error | ¿Los errores se detectan y se revierten? | Tests, code review, rollback. |
Los tres primeros hablan de la tarea y del modelo. El cuarto habla de tu infraestructura, y es el único que podés cambiar vos esta semana. Un agente sobre un repo con tests y git es viable porque el error se detecta en CI y se revierte con un checkout. El mismo agente sobre un sistema sin rollback resuelve el mismo problema con un perfil de riesgo distinto. Ese criterio es también la razón de que el mismo agente sea buena idea en staging y mala en producción sin que cambie una línea de su prompt.
Los cuatro tiers
De más simple a más complejo:
- Una sola llamada a la API. Prompt, tal vez tools, una respuesta.
- Un workflow con lógica en tu código. Vos escribís el orden; el modelo llena los huecos.
- Un agente con tus propias herramientas. Vos corrés el loop y definís el harness.
- Un agente gestionado. El proveedor corre el loop y hospeda el sandbox.
La recomendación explícita es empezar por el más simple que resuelva el problema. La razón es la de siempre: cada tier que subís agrega estados que no enumeraste. El tier 2 te da determinismo de orden. El tier 3 te da control total del punto de ejecución, que es lo que vas a querer cuando las acciones sean caras de revertir. El tier 4 te saca la operación del sandbox de encima y te la saca también de las manos.
Bash contra herramientas dedicadas
Acá el eje se vuelve concreto. Una herramienta bash le da al modelo alcance máximo: cualquier binario del sistema, composición con pipes, todo el ecosistema de Unix sin que vos escribas un wrapper. CodeAct mide esa ganancia: expresar acciones como código ejecutable habilita composición, control de flujo y bucles dentro de una sola acción. El costo está del lado del harness. Lo que te llega es esto:
{ "name": "bash", "input": { "command": "find . -name '*.tmp' -delete" } }
Un string opaco. Para saber si eso borra tres archivos o el árbol entero tenés que parsear shell —sustitución de comandos, variables, redirecciones, alias— y el shell es Turing-completo. No hay un gate confiable sobre un string arbitrario.
Promover la acción a herramienta dedicada te cambia el objeto que interceptás:
{
name: "delete_files",
description: "Borra archivos del working tree. Usala cuando la tarea " +
"pide eliminar archivos concretos que ya identificaste. Para descubrir " +
"qué borrar, usá Glob primero: esta herramienta no acepta patrones.",
input_schema: {
type: "object",
properties: {
paths: { type: "array", items: { type: "string" },
description: "Rutas absolutas. Sin globs." }
},
required: ["paths"]
}
}
Ahora el harness tiene argumentos tipados antes de ejecutar nada, y con eso puede hacer cuatro cosas que sobre un string no puede:
- Gate. Pedir confirmación mostrando la lista exacta de rutas.
- Precondición. Hacer cumplir invariantes del estado: que el archivo no cambió desde la última lectura del modelo, que la ruta cae adentro del workspace. Bash puede hashear un archivo, pero no puede hacer cumplir el invariante sobre la acción que viene.
- Render. Dibujar UI propia —un diff, una tabla— en vez de volcar stdout.
- Paralelismo. Saber cuáles llamadas son independientes y correrlas juntas, porque la firma te dice qué toca cada una. Sobre bash, un
grepparalelizable y ungit pushque no lo es tienen la misma forma, así que hay que serializar todo.
La regla práctica: empezar con bash por alcance, y promover a herramienta dedicada lo que necesite gate, render, auditoría o paralelismo.
El criterio de corte es la reversibilidad
Lo difícil de revertir se promueve, y se promueve con un gate específico. Esta tabla es el artefacto operativo del artículo: inventariás las acciones que el agente puede tomar, las ordenás por reversibilidad, y cada fila sale con su destino y su gate escritos.
| Acción | Reversible | Dónde vive | Gate del harness |
|---|---|---|---|
ls, grep, cat | Sí, trivialmente | bash | Ninguno |
| Leer un archivo grande | Sí, pero pesa en contexto | dedicada | Ninguno; paginado y truncado en el retorno |
| Editar un archivo versionado | Sí, con git | dedicada | Precondición verificada por el harness (el archivo no cambió desde la última lectura) más diff renderizado |
git push --force | Difícil | dedicada | Confirmación humana con el rango de commits a la vista |
| DDL o escritura en producción | No | dedicada | Confirmación humana, o directamente fuera del agente |
La última columna es la que convierte la tabla en implementación. "Dedicada" sin gate escrito es solo un cambio de firma.
La descripción es el prompt de la herramienta
La descripción es el factor que más influye en que el modelo use bien una herramienta, y la falla más común es sub-describir. "Borra archivos" es cierto y no sirve: no dice cuándo llamarla, qué formato aceptan los argumentos, ni qué hacer cuando falla.
Conviene ser prescriptivo sobre cuándo llamarla, no solo sobre qué hace. "Writing effective tools for agents — with agents", del equipo de ingeniería de Anthropic, lo plantea como escribirle a alguien que entra al equipo: hacé explícito el contexto implícito, usá nombres de parámetro sin ambigüedad —user_id antes que user—, agrupá familias de herramientas con un prefijo común, y devolvé respuestas con paginación o truncado en vez de volcar todo. Ese mismo texto es el que le dice al modelo cuándo elegir la herramienta dedicada en vez de resolverlo por bash. Si la descripción no lo dice, el modelo va a hacer lo obvio y tu punto de intercepción queda sin usar.
Skills: revelación progresiva y su factura
Una skill es una carpeta con un SKILL.md que empaqueta instrucciones y archivos para una tarea. El mecanismo es la revelación progresiva, y la documentación de Agent Skills de Anthropic (platform.claude.com, secciones overview y best practices) publica sus umbrales, así que la factura se puede imprimir en vez de afirmarla:
| Capa | Cuánto pesa | Cuándo se carga |
|---|---|---|
Metadata: name + description | ~100 tokens por skill | Siempre, en cada request |
Cuerpo del SKILL.md | Bajo 5k tokens | Cuando la tarea dispara la skill |
| Archivos referenciados | Cero | Solo cuando el modelo los abre |
Los topes duros van con eso: la description admite hasta 1024 caracteres, el cuerpo se mantiene bajo 500 líneas, y las referencias van a un solo nivel de profundidad desde el SKILL.md.
La primera fila es la que factura. Con veinte skills instaladas, la metadata suma unos 2.000 tokens que se pagan en todos los pedidos, use el modelo alguna o ninguna. Enumerar quince frases de disparo casi sinónimas engorda esa fila y además generaliza peor que nombrar dos o tres categorías de intención: el modelo hace matching semántico, no lookup de strings.
El límite de anidación tiene un mecanismo detrás que conviene conocer, porque falla en silencio. Cuando una referencia está a más de un nivel, el modelo previsualiza el archivo anidado en vez de leerlo completo. La skill dispara, el modelo trabaja, y lo hace con información parcial sin que nada lo avise.
Grados de libertad
La especificidad de una instrucción tiene que coincidir con la fragilidad de lo que describe.
| Naturaleza de la tarea | Qué escribir |
|---|---|
| Decisión de juicio, campo abierto | Heurísticas en prosa, criterios, contraejemplos |
| Operación frágil, una sola secuencia segura | El comando exacto, verbatim |
Un guion paso a paso para una decisión de juicio sobre-restringe: el modelo sigue el guion cuando el caso no encaja. Prosa vaga para una operación frágil sub-restringe: el modelo improvisa una variante que rompe. La mayoría de las skills malas fallan por el lado equivocado en cada mitad.
Dos cosas envejecieron mal en esa línea. Los prompts y skills escritos para modelos anteriores suelen ser demasiado prescriptivos para los actuales y reducen la calidad; enunciar el objetivo y las restricciones rinde más que enumerar los pasos. Y pedir explícitamente que el modelo verifique su trabajo hoy produce sobre-verificación: el comportamiento ya viene de fábrica, así que la instrucción se volvió contraproducente. Si querés garantías de calidad, van en el harness y en las evals —eso lo tratamos en "Evals: si no puede bloquear un deploy, no es una eval"—, no en una frase del prompt.
Herramientas
Cada una de estas hace visible una parte distinta del eje. Todas son gratis.
- Claude Agent SDK (Python) — El harness de Claude Code expuesto como librería: el loop ya construido, con ejecución de herramientas, permisos, subagentes y compactación de contexto. La referencia para ver que un harness serio tiene más piezas que un
whilecon untry, y para leer el sistema de permisos como la capa que decide si bash entra o no. - OpenAI Agents SDK — El mismo loop con otro vocabulario: lo que acá llamamos harness aparece repartido entre runner y guardrails. El tracing incorporado es el argumento visual de por qué observar un loop es distinto de observar un pipeline.
- LangGraph — El lado pipeline: el flujo como grafo de nodos y aristas, con estado explícito, checkpoints y human-in-the-loop. El mismo framework te deja escribir el orden a mano o dejar que un nodo decida el próximo salto, así que el código hace visible dónde exactamente cedés el control.
- Pydantic AI — La herramienta dedicada como contrato: el esquema es documentación para el modelo y validación en runtime al mismo tiempo, así que una llamada mal formada falla en el borde y no adentro de tu sistema.
- smolagents — La implementación práctica de CodeAct: el agente actúa escribiendo Python en vez de emitir llamadas JSON. Incluye ejecución en sandbox, que es justo la pieza que hay que agregar cuando elegís el camino de bash o intérprete.
- Model Context Protocol — El protocolo que estandariza cómo se le exponen herramientas, recursos y prompts a un agente. Mueve la pregunta de qué herramienta le doy a cómo se descubren y se gobiernan, y deja ver que el harness solo controla lo que está declarado: una herramienta MCP se lista y se audita; un comando adentro de bash, no.
Cómo decidir
El orden que se sostiene es de abajo hacia arriba.
- Escribí el pipeline. Si te sale, terminaste: tenés determinismo de orden y caminos enumerables por el precio de un
if. - Si no te sale porque no sabés de antemano cuántos pasos son ni en qué orden, pasá los cuatro criterios. Prestá atención al cuarto, porque es el único que podés arreglar vos: tests, review y rollback cambian el perfil de riesgo sin tocar el prompt.
- Inventariá las acciones alcanzables. No las herramientas: las acciones. Una herramienta bash aporta el conjunto entero de binarios del sistema, y eso es lo que hay que listar.
- Ordenalas por reversibilidad, con la tabla de más arriba. La columna que importa es la última.
- Promové las de abajo. Cada una sale con dos cosas escritas: la descripción, para que el modelo la elija en vez de resolverlo por bash, y el gate, para que un humano la confirme.
Un agente bien construido se mide por el undo: cada acción que puede tomar tiene vuelta atrás, y las que no la tienen pasan por un humano antes de correr. Si podés llenar la última columna de esa tabla para todas las filas, tenés un agente. Si hay filas en blanco, todavía tenés un pipeline con ambiciones.
Keep going
Reading
- Building effective agents — Erik Schluntz y Barry Zhang. La fuente de la distinción workflow/agent y de los patrones intermedios que muestran que hay escalera, no interruptor.
- ReAct: Synergizing Reasoning and Acting in Language Models — El paper que formaliza el loop: el resultado de cada herramienta reentra al contexto y condiciona la próxima decisión.
- Executable Code Actions Elicit Better LLM Agents (CodeAct) — Mide acciones como código ejecutable contra llamadas JSON a herramientas predefinidas. La evidencia dura del eje bash contra dedicada.
- Writing effective tools for agents — with agents — Cómo se escribe una herramienta que el modelo elija solo: namespacing, retornos con significado, descripciones que funcionan como contrato.
- Effective harnesses for long-running agents — Trata el harness como pieza de ingeniería: límites, permisos, recuperación, compactación. Justifica el cuarto criterio.
- How Stripe Built Kai, its Company-Wide AI Agent, on Deep Agents — El caso con la falla incluida: al pasar de 500 herramientas, la selección por LLM puro dejó de escalar y hubo que reponer estructura determinística adelante.
Videos
- How We Build Effective Agents: Barry Zhang, Anthropic — Quince minutos con el árbol de decisión y los cuatro criterios, dados por uno de los autores del post.
- Building Effective Agents with LangGraph — Los mismos patrones implementados. Ver el grafo explícito al lado del loop hace visible la diferencia.
- Don't Build Agents, Build Skills Instead — Barry Zhang & Mahesh Murag, Anthropic — El contrapunto: mucha capacidad que se construye como agente a medida se resuelve mejor equipando un loop genérico que ya existe.