17°
Portada del artículo: AWS dice que su harness de agentes ahorra 28% de tokens. Medí 1,82x más caro en local, 88% más barato en la nube
AgentesLangGraphAWSBenchmarksLLM

AWS dice que su harness de agentes ahorra 28% de tokens. Medí 1,82x más caro en local, 88% más barato en la nube

Probé Strands Harness, el runtime de agentes de AWS, contra un LangGraph pelado, mismos 3 tools, mismo gatekeeper, misma conversación de 20 turnos. Contra un Qwen3.8-27B local de 16k de contexto gastó 1,82 veces más tokens. Contra el mismo trabajo en un modelo cloud, con la temperatura por fin igualada entre los dos, gastó un 88,8% menos. Y tres auditorías adversariales tuvieron que corregir mis propios errores en el camino: 11 herramientas de más, un límite de turnos que mentía, y un doble conteo de caché que inflaba el primer número 4 veces.

Efrain Garay 23 de septiembre de 2026

Reproduciendo el resumen

Quería probar si la nueva gestión de contexto de AWS, Strands Harness, resuelve algo real. La tenía a mano por casualidad: ya había roto y rearmado un chat en Amazon Bedrock AgentCore, con LangGraph, un gatekeeper determinístico y una conversación adversarial de 64 turnos ya grabada. El plan parecía simple: cambiar solo el harness, dejar todo lo demás igual, medir tokens.

No fue simple. Terminé auditando mi propio experimento tres veces, y las dos primeras conclusiones que iba a publicar eran falsas.

En 61 segundos: los 28% que promete AWS, el 1,82x más caro que medí en local, el giro a 88,8% más barato en la nube con la temperatura igualada, y los 918.648 tokens de Claude Code como tercer harness.Verlo en el visualizador de reels →

Lo que Strands Harness promete, y lo que no toca

Strands Harness es un SDK aparte de AgentCore (pip install strands-harness), anunciado el 21 de septiembre con una cifra concreta: 28% menos consumo de tokens frente a otros harnesses, logrado truncando salidas de herramientas sobre 1.500 tokens y resumiendo automático al 85% de la ventana de contexto.

Mi primera idea fue probarlo contra el jailbreak que rompió mi chat de AgentCore: si la gestión de contexto es mejor, ¿resiste mejor una conversación adversarial larga? Revisé el código antes de perder tiempo: el fix real de ese incidente vive en scope.py, un clasificador por regex que corre antes de que el mensaje toque el modelo. De los 64 turnos de esa conversación, 9 llegan a ser un SITE que efectivamente invoca al LLM; los otros 55 reciben una respuesta fija sin gastar un token. Lo que frena el jailbreak es el clasificador, no la ventana de contexto: la gestión de contexto de cualquier harness es irrelevante para eso.

Cambié de pregunta: tomé una conversación real de 20 turnos (no adversarial, del mismo banco de evaluación del chat: alguien que saluda, pregunta cosas sueltas, pide un resumen, cuenta que “el sitio tiene solo 2 artículos” cuando tiene 41), el mismo system_prompt, las mismas 3 herramientas (list_posts, search_posts, read_post) y el mismo gatekeeper. Lo corrí contra dos backends: un Qwen3.8-27B servido local con llama.cpp (16.384 tokens de ventana real) y MiniMax-M3 en la nube, el modelo que usa el chat en producción.

La matriz: dos harnesses, dos backends, la misma conversaciónMismo system prompt, mismas 3 herramientas, mismo gatekeeper, los mismos 20 turnos.
conversación real · 20 turnos3 tools · 1 gatekeeperLangGraphcreate_react_agentStrands Harnesscontext_manager=autoQwen3.8-27B local · 16k ctx181.833 tokensMiniMax-M3 · cloud103.952 tokensQwen3.8-27B local · 16k ctx331.118 tokens1,82xMiniMax-M3 · cloud11.618 tokenstemperatura igualada

Contra el modelo local, Strands gastó 1,82 veces más que un ReAct pelado. Contra el modelo cloud, con la temperatura por fin igualada, gastó una fracción de lo que gastó LangGraph — el punto de cache de Anthropic que Strands inserta y LangGraph no.

Primera corrida: los dos chocan igual. Era mentira.

La primera vez, LangGraph y Strands parecían morir en el mismo punto contra el modelo local, con el mismo tipo de error de ventana excedida. Iba a publicar eso. Le pedí a dos revisores adversariales (Codex, y un segundo modelo por separado) que auditaran el experimento antes de escribir una palabra más, porque esta misma semana ya había encontrado números inventados en otro post de este sitio y no quería repetirlo con datos propios.

Encontraron tres errores míos, no de Strands:

  1. create_harness() agrega 11 herramientas de más por defecto (shell, archivos, tareas en segundo plano, sub-agentes), encima de las 3 que sí pedí: 14 en total por request. builtin_tools=[] solo apaga las de coding-agent; los plugins de sistema y el propio contrato del harness quedan encima igual. Eso infla cada request sin que se note.
  2. Un límite artificial de limits={"turns": 8} que yo había puesto para frenar un loop, devolvía stop_reason="limit_turns" en silencio. Mi script contaba esos turnos como completados cuando en realidad se habían cortado a la mitad.
  3. LangGraph, en la corrida original, en realidad completó los 20 turnos sin chocar. El error que vi primero venía de una corrida anterior y distinta que confundí con esta.

Corregí los tres: saqué el contrato del harness (system_prompt=SYSTEM_PROMPT en vez de instructions, que salta el preámbulo por defecto), apagué los plugins de sistema (builtin_plugins=[]), saqué el límite de turnos, y agregué detección real de truncamiento (finish_reason/stop_reason) en los dos lados, no solo en uno.

Segunda corrida: 4,1 veces más caro. También era mentira, a medias.

Con esas correcciones, Strands completó los 20 turnos contra el modelo local sin chocar. Buena noticia. Pero el total, 752.659 tokens contra los 181.833 de LangGraph, daba 4,14 veces más caro. Mandé esto a auditar de nuevo, con los números nuevos.

Encontraron un cuarto error, este más sutil: el proveedor de llama.cpp de Strands reporta inputTokens como el prompt completo, y por separado expone cacheReadInputTokens como un subconjunto informativo de ese mismo total, no algo adicional. Mi script sumaba los dos. El propio SDK de Strands ya trae la función correcta para esto (_total_prompt_tokens, que decide según el proveedor si el caché va adentro o aparte), y yo la había reinventado mal.

Con la contabilidad corregida (la función real del SDK, no la mía), el número bajó a 331.118 tokens: 1,82 veces más que LangGraph, no 4,1. Sigue siendo una diferencia real y grande, solo que menos de la mitad de lo que iba a publicar.

Tokens totales por la misma conversación de 20 turnostokens totales · menos es mejor
  1. LangGraph · Qwen local181.833
  2. Strands · Qwen local331.118
  3. LangGraph · MiniMax cloud103.952
  4. Strands · MiniMax cloud11.618

Qwen3.8-27B Q3_K_M local (llama.cpp, ventana 16.384) y MiniMax-M3 cloud. Mismo system prompt, mismas 3 herramientas, mismo gatekeeper, misma conversación de 20 turnos. Contabilidad final tras 3 rondas de auditoría adversarial.

Contra el modelo local: la ventana de 16k explica buena parte

Ninguno de los dos harnesses chocó esta vez, pero los dos terminaron cerca del límite. LangGraph acumuló hasta 15.314 tokens de contexto en el turno 19 (93% de la ventana), sin ninguna gestión de contexto: simplemente porque mi baseline descarta los resultados de herramientas entre turnos y solo conserva texto humano/asistente, una política de memoria mucho más agresiva que la de Strands, que retiene y compacta.

Strands, con su context_manager="auto" activo, llegó a 16.370 de 16.384 (99,9%) en el turno 18, y su propio resumidor automático nunca se disparó a tiempo: el umbral es 85% de utilización, pero ese cálculo cuenta solo los mensajes, no el system prompt ni las especificaciones de herramientas, así que subestima cuánto ocupa de verdad.

Cuánto tarda cada harness en gastar su total, turno a turnoContra el mismo Qwen3.8-27B local. Los 20 turnos, escalados a la velocidad de lectura.
LangGraph181,833 s
Strands Harness331,118 s

En miles de tokens acumulados. Strands termina con 1,82 veces más, aun sin chocar contra la ventana.

La misma conversación, el mismo modelo, la misma ventana de 16.384 tokens. Ninguno choca esta vez; lo que separa a los dos es cuánto gasta cada uno para llegar al mismo lugar.

Parte de esa diferencia tiene una causa concreta y verificable: contando las herramientas que de verdad recibe el modelo en cada request (con una sonda directa contra el servidor), Strands manda 6 especificaciones de herramienta por llamada, no 3: además de list_posts/search_posts/read_post, el ContextOffloader del propio harness agrega retrieve_offloaded_content, el gestor de contexto del SDK agrega retrieve_context, y el manejo de tareas en segundo plano agrega una tercera. Medido en el servidor real: 6 herramientas pesan 2.549 tokens de encabezado por request; 3 pesan 1.324. Multiplicado por los 38 ciclos internos que Strands corrió en total durante la conversación, esa sola diferencia explica una parte importante del 1,82x. No pude sacar esas tres herramientas sin desarmar la pieza que estaba probando: son justamente el mecanismo de gestión de contexto.

Contra el modelo cloud: el resultado se invierte

Acá está el giro que casi no llego a medir bien. La primera corrida contra MiniMax daba a Strands un 12% más caro que LangGraph, cerca del empate. Pero esa corrida tenía una asimetría que no había cerrado: LangGraph manda temperature=0.2 explícito; el AnthropicModel de Strands, al pasarle el mismo parámetro, hacía fallar al SDK de Anthropic instalado (AsyncMessages.stream() got an unexpected keyword argument 'temperature', un choque de versión, no un bug de Strands). Lo dejé sin ese parámetro y seguí, marcándolo como una brecha abierta.

Un segundo revisor encontró la salida: el SDK de Anthropic sí acepta extra_body, y Strands reenvía params completo al request. Con params={"extra_body": {"temperature": 0.2}}, la llamada funciona igual que LangGraph.

Con la temperatura por fin igualada, el resultado no se acercó: se invirtió del todo. Strands pasó de 116.472 a 11.618 tokens, un 88,8% menos que los 103.952 de LangGraph.

anuncio de Strands Harness · 21-sep-2026 · contra mi banco de pruebas
1/ 1se sostienen al medirlas
Reducción de tokens vs. otro harnessse sostiene
dicen 28% menos

88,8% menosMedido contra un LangGraph con la misma conversación de 20 turnos, MiniMax-M3, temperatura igualada.

AWS compara contra Claude Code y Codex, no contra LangGraph, así que la comparación no es idéntica. Aun así, el número medido en mi propio banco supera por mucho lo publicado, en la dirección contraria a lo que había medido primero contra el modelo local.

No tengo una causa tan verificable como la del lado local, pero sí una hipótesis con apoyo real en el código: el proveedor de Anthropic de Strands inserta explícitamente puntos de corte de caché de prompt (_manage_cache_points en su código fuente) en cada request. La implementación de LangChain que usé para LangGraph no hace eso por su cuenta. En una conversación de 20 turnos que repite el mismo prompt de sistema y buena parte del historial en cada llamada, un punto de caché bien puesto ahorra reprocesar exactamente lo que más pesa. No verifiqué esto turno a turno con los contadores de caché de Anthropic por separado; lo dejo dicho como hipótesis, no como hecho medido.

Un tercer dato: el harness que ya usas todos los días

Antes de cerrar, agregué un tercer punto que no había planeado: Claude Code, la misma CLI con la que escribo estos posts, apuntada a MiniMax-M3 con cc-minimax (una de mis herramientas de línea de comandos, que solo cambia las variables de entorno del modelo). No es un script mío: es software de producción real, así que no necesitó una ronda de auditoría como las anteriores, solo una verificación puntual contra el transcript crudo de la sesión.

Misma conversación de 20 turnos, mismo gatekeeper, el mismo SYSTEM_PROMPT agregado con --append-system-prompt (sin reemplazar el propio de Claude Code, que sigue ahí), mismo modelo. La diferencia real: en vez de mis 3 herramientas de juguete, Claude Code exploró el repositorio del sitio con sus propias herramientas nativas (terminó usando Bash además de Read/Grep, porque --allowedTools con --permission-mode bypassPermissions autoriza sin preguntar pero no restringe cuáles hay disponibles).

918.648 tokens totales, 0,87 dólares reales según la propia CLI. Casi 9 veces lo que gastó LangGraph con el mismo modelo, y 79 veces lo que gastó Strands Harness con la temperatura igualada.

Los tres harnesses contra el mismo modelo cloud (MiniMax-M3)tokens totales · menos es mejor
  1. Strands Harness11.618
  2. LangGraph103.952
  3. Claude Code (cc-minimax)918.648

Misma conversación de 20 turnos, mismo gatekeeper, mismo system prompt. Claude Code verificado contra el transcript crudo de la sesión, sin doble conteo.

Nada de esto dice que Claude Code sea ineficiente en lo que hace: es un agente de propósito general con acceso a todo un sistema de archivos y un juego de herramientas mucho más grande, pensado para tareas de código, no para responder tres preguntas sobre un blog. El punto es justamente ese: usar un martillo de producción para clavar un alfiler cuesta como un martillo, no como un alfiler. El harness correcto depende completamente de la tarea, y ningún anuncio de marketing de ninguno de los tres te lo va a decir con esta claridad.

Lo que me llevo

Que un framework “ahorre tokens” o no depende brutalmente de contra qué lo midas. Contra un modelo local con ventana chica y sin caché de servidor propio, Strands paga la sobrecarga de su propio andamiaje (tres herramientas más, un contrato de sistema más largo, un resumidor que no dispara a tiempo) y sale 1,82 veces más caro que la opción sin nada. Contra un modelo cloud con caché de verdad, esa misma sobrecarga se diluye y lo que queda es un ahorro real, más grande que el que promete el propio anuncio.

Y lo otro que me llevo es sobre mí, no sobre Strands: la primera conclusión que iba a publicar era falsa, y la segunda también, a medias. Las dos veces el error estaba en mi propio código, nunca en el sistema que estaba midiendo. Si esto pasó con un experimento que diseñé con cuidado y auditó dos veces, la lección real apunta a otro lado: medir el rendimiento de un framework ajeno, con sus valores por defecto escondidos (herramientas que no pediste, contratos de sistema que no viste, una función de conteo que hay que usar tal cual viene, no reinventar), es más difícil de lo que parece, y publicar sin auditar es exactamente cómo se cuelan los números que no son.

Cómo reproducirlo

Los cuatro scripts (run_langgraph.py, run_strands.py, run_langgraph_minimax.py, run_strands_minimax.py), el módulo compartido con el gatekeeper real (scope.py del chat en producción) y los cuatro logs crudos de la corrida final están documentados en el repositorio de este sitio. El servidor local es llama-server (llama.cpp) sirviendo un Qwen3.8-27B Q3_K_M con -c 16384; el modelo cloud es MiniMax-M3 por su endpoint compatible con Anthropic.

Fuentes

Comentarios

Todavía no hay comentarios. El primero es tuyo.

Se revisa antes de publicarse. El correo no se guarda ni aparece en ninguna parte.