<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: AWS Español</title>
    <description>The latest articles on DEV Community by AWS Español (@aws-espanol).</description>
    <link>https://dev.to/aws-espanol</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F7402%2F04f86f58-db61-410f-8eda-06b0c052f17f.jpeg</url>
      <title>DEV Community: AWS Español</title>
      <link>https://dev.to/aws-espanol</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aws-espanol"/>
    <language>en</language>
    <item>
      <title>Cómo Prevenir Loops de Razonamiento en Agentes de IA y No Desperdiciar Tokens</title>
      <dc:creator>Elizabeth Fuentes L</dc:creator>
      <pubDate>Thu, 28 May 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/aws-espanol/como-prevenir-loops-de-razonamiento-en-agentes-de-ia-y-no-desperdiciar-tokens-5gmj</link>
      <guid>https://dev.to/aws-espanol/como-prevenir-loops-de-razonamiento-en-agentes-de-ia-y-no-desperdiciar-tokens-5gmj</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Los loops de razonamiento en agentes de IA&lt;/strong&gt; ocurren cuando un agente llama a la misma herramienta repetidamente sin hacer progreso, convencido de que un intento más producirá la respuesta perfecta. El agente desperdicia tokens, tiempo y dinero sin entregar un resultado. Este post muestra cómo detectar y bloquear llamadas repetidas, validado con una demo donde herramientas ambiguas causaron 14 llamadas vs estados SUCCESS claros que se detuvieron en 2.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Esta demo usa &lt;a href="https://strandsagents.com/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Agents&lt;/a&gt;. Los patrones (debounce hooks, estados claros de herramientas y límites de llamadas) son independientes del framework y aplican a cualquier agente que soporte hooks de ciclo de vida, incluyendo LangGraph, AutoGen y CrewAI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Código funcional:&lt;/strong&gt; &lt;a href="https://github.com/aws-samples/sample-why-agents-fail/tree/main/stop-ai-agents-wasting-tokens/03-reasoning-loops-demo" rel="noopener noreferrer"&gt;github.com/aws-samples/sample-why-agents-fail&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Serie: Por Qué Fallan los Agentes de IA
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/aws-samples/sample-why-agents-fail/tree/main/stop-ai-agents-wasting-tokens/01-context-overflow-demo" rel="noopener noreferrer"&gt;Desbordamiento de Ventana de Contexto&lt;/a&gt;&lt;/strong&gt; — Patrón de Puntero de Memoria para datos grandes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/aws-samples/sample-why-agents-fail/tree/main/stop-ai-agents-wasting-tokens/02-mcp-timeout-demo" rel="noopener noreferrer"&gt;Herramientas MCP Que Nunca Responden&lt;/a&gt;&lt;/strong&gt; — Patrón asíncrono para APIs externas lentas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loops de Razonamiento en Agentes de IA&lt;/strong&gt; (este post) — Detectar y bloquear llamadas repetidas a herramientas&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  El Problema: Agentes Que Piensan Demasiado
&lt;/h2&gt;

&lt;p&gt;Los loops de razonamiento en agentes de IA ocurren cuando un agente llama a la misma herramienta repetidamente sin hacer progreso, desperdiciando tokens y tiempo sin entregar un resultado. Los agentes de IA no solo fallan dando respuestas incorrectas; fallan al nunca terminar. Las investigaciones muestran que los agentes quedan atrapados en loops de razonamiento donde llaman a la misma herramienta repetidamente, convencidos de que "un paso más" producirá la respuesta perfecta.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://the-decoder.com/language-models-can-overthink-and-get-stuck-in-endless-thought-loops/" rel="noopener noreferrer"&gt;The Decoder (Jan 2025)&lt;/a&gt; encontró que incluso con poder de cómputo ilimitado, pensar demasiado lleva a decisiones pobres. La comprensión incompleta del mundo causa errores compuestos. Cada paso de razonamiento adicional empeora las cosas, no las mejora.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://particula.tech/blog/ai-agent-loops-reasoning-steps-optimization" rel="noopener noreferrer"&gt;Particula (Jul 2025)&lt;/a&gt; (observación comunitaria) documentó un caso extremo: un agente ejecutó &lt;strong&gt;847 pasos de razonamiento&lt;/strong&gt; a &lt;strong&gt;$47 por minuto&lt;/strong&gt; y nunca entregó una respuesta final. Siguió refinando lógica, cuestionando conclusiones y solicitando más datos en un ciclo sin fin.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codieshub.com/for-ai/prevent-agent-loops-costs" rel="noopener noreferrer"&gt;CodiesHub (Dec 2025)&lt;/a&gt; (observación comunitaria) identifica las causas raíz:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Objetivos poco claros&lt;/strong&gt; — el agente no sabe cuándo está completa la tarea&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retroalimentación ambigua de herramientas&lt;/strong&gt; — las herramientas no devuelven estados claros de éxito/fallo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sin criterios de parada&lt;/strong&gt; — sin límites duros en iteraciones o tiempo&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Por Qué Ocurren los Loops: Retroalimentación Ambigua de Herramientas
&lt;/h2&gt;

&lt;p&gt;La retroalimentación ambigua de herramientas ocurre cuando las herramientas devuelven resultados parciales o sugieren "puede haber más datos disponibles" sin estados terminales claros, causando que los agentes reintenten la misma llamada. Las herramientas que devuelven resultados parciales o sugieren "puede haber más datos disponibles" hacen que los agentes reintenten:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_flights&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Busca vuelos bajo un precio máximo.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;prices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;matching&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;prices&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;max_price&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# El problema: "Puede haber más resultados disponibles" señala al LLM que reintente
&lt;/span&gt;    &lt;span class="c1"&gt;# El agente interpreta esto como "Debo buscar de nuevo para encontrar una mejor oferta"
&lt;/span&gt;    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Se encontraron &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matching&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; vuelos bajo $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;max_price&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(de &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prices&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; verificados). &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Nota: Puede haber más resultados disponibles. Los precios cambian frecuentemente.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Esa "Nota: Puede haber más resultados disponibles" dispara el loop. El agente lo ve y piensa: "Tal vez si busco de nuevo, encontraré una mejor oferta." Reintenta con los mismos parámetros, obtiene resultados similares, y el ciclo continúa.&lt;/p&gt;
&lt;h2&gt;
  
  
  Solución 1: Debounce Hook con Strands
&lt;/h2&gt;

&lt;p&gt;Los &lt;a href="https://strandsagents.com/docs/user-guide/concepts/agents/hooks/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Hooks&lt;/a&gt; interceptan el ciclo de vida del agente en cualquier punto. Un Debounce Hook usa &lt;a href="https://strandsagents.com/docs/user-guide/concepts/agents/hooks/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;&lt;code&gt;BeforeToolCallEvent&lt;/code&gt;&lt;/a&gt; para detectar llamadas duplicadas antes de que se ejecuten:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.hooks&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HookProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BeforeToolCallEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BeforeInvocationEvent&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DebounceHook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HookProvider&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;       &lt;span class="c1"&gt;# Rastrea pares (tool_name, input)
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;window_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;window_size&lt;/span&gt;  &lt;span class="c1"&gt;# Tamaño de ventana deslizante para detección de duplicados
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blocked_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register_hooks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# BeforeInvocationEvent se dispara una vez al inicio de cada llamada agent.invoke()
&lt;/span&gt;        &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BeforeInvocationEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# BeforeToolCallEvent se dispara antes de cada ejecución de herramienta — aquí interceptamos
&lt;/span&gt;        &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BeforeToolCallEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_duplicate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Limpia el historial al inicio de cada invocación para que los límites no se mezclen entre llamadas
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_duplicate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Construye una huella digital del nombre de herramienta + entradas exactas
&lt;/span&gt;        &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
        &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_history&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;window_size&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# cancel_tool es una API nativa de Strands que bloquea la ejecución y devuelve este mensaje al LLM
&lt;/span&gt;            &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel_tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BLOCKED: Llamada duplicada detectada&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blocked_count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;search_flights&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;hooks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;DebounceHook&lt;/span&gt;&lt;span class="p"&gt;()])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;El hook rastrea las últimas 3 llamadas a herramientas. Si la misma herramienta con los mismos parámetros aparece dos veces, el tercer intento se bloquea vía &lt;code&gt;event.cancel_tool&lt;/code&gt;, una API nativa de Strands que bloquea la ejecución de herramientas y devuelve un mensaje de error al LLM.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4v3joj5k8bwwcdovbgc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4v3joj5k8bwwcdovbgc.jpg" alt="Diagrama de flujo mostrando cómo DebounceHook intercepta llamadas a herramientas, verifica una ventana deslizante para duplicados y bloquea llamadas repetidas" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Solución 2: Estados SUCCESS/FAILED Claros
&lt;/h2&gt;

&lt;p&gt;Las herramientas que devuelven estados terminales explícitos ayudan a los agentes a saber cuándo detenerse:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;book_hotel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hotel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;guest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Reserva una habitación de hotel. Devuelve SUCCESS o FAILED claro.

    Returns:
        SUCCESS: Reserva confirmada con ID
        FAILED: Reserva fallida con razón
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HT&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;99999&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;350&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SUCCESS: Reserva &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; confirmada — &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;guest&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; en &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;hotel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;nights&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; noches, $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;nights&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; total&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FAILED: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;hotel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; completamente reservado&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Cuando el agente recibe &lt;code&gt;"SUCCESS: Reserva HT79265 confirmada"&lt;/code&gt;, sabe que la tarea está hecha. Sin ambigüedad, sin llamadas extra.&lt;/p&gt;
&lt;h2&gt;
  
  
  Solución 3: Límites Duros con LimitToolCounts
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://codieshub.com/for-ai/prevent-agent-loops-costs" rel="noopener noreferrer"&gt;CodiesHub&lt;/a&gt; recomienda: "Iteraciones, tokens, tiempo, gasto son no negociables." Strands proporciona &lt;a href="https://strandsagents.com/docs/user-guide/concepts/agents/hooks/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;&lt;code&gt;LimitToolCounts&lt;/code&gt;&lt;/a&gt; en el Hooks Cookbook, un hook que limita llamadas a herramientas por invocación:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.hooks&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HookProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BeforeToolCallEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BeforeInvocationEvent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Lock&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LimitToolCounts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HookProvider&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Limita llamadas a herramientas por invocación. Del Strands Hooks Cookbook.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_tool_counts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="c1"&gt;# Presupuestos de llamadas por herramienta: {"search_flights": 2} significa máximo 2 búsquedas por invocación
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_tool_counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max_tool_counts&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Thread-safe para llamadas concurrentes a herramientas en escenarios Swarm
&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register_hooks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BeforeInvocationEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reset_counts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BeforeToolCallEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intercept_tool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reset_counts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Reinicia por invocación para que los límites apliquen por tarea, no por vida del agente
&lt;/span&gt;        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_lock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;intercept_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;tool_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_lock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;max_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_tool_counts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_counts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;max_count&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;max_count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="c1"&gt;# Techo duro: bloquea la llamada y dice al LLM explícitamente que se detenga
&lt;/span&gt;                &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel_tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Límite de herramienta &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; alcanzado. NO LLAMAR MÁS.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Aplica un límite duro de 2 búsquedas de vuelos por tarea de reserva — previene costos desbocados
&lt;/span&gt;&lt;span class="n"&gt;limit_hook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LimitToolCounts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_tool_counts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_flights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;search_flights&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;hooks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;limit_hook&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Incluso si el agente quiere buscar 10 veces, está limitado a 2. Techo duro, costos predecibles.&lt;/p&gt;
&lt;h2&gt;
  
  
  Resultados de la Demo
&lt;/h2&gt;

&lt;p&gt;Probamos con un agente de reserva de viajes que busca vuelos y hoteles:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Escenario&lt;/th&gt;
&lt;th&gt;Llamadas a Herramientas&lt;/th&gt;
&lt;th&gt;Tiempo&lt;/th&gt;
&lt;th&gt;Resultado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Retroalimentación Ambigua&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;21s&lt;/td&gt;
&lt;td&gt;El agente reintentó orgánicamente — "los precios pueden cambiar" causó loops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DebounceHook&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;15s&lt;/td&gt;
&lt;td&gt;Redujo reintentos pero alguna variación en parámetros&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Estados SUCCESS Claros&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4s&lt;/td&gt;
&lt;td&gt;El agente se detuvo inmediatamente después de SUCCESS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LimitToolCounts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6 (2 bloqueadas)&lt;/td&gt;
&lt;td&gt;6s&lt;/td&gt;
&lt;td&gt;Techo duro aplicado — sin desborde&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;El contraste es dramático: &lt;strong&gt;14 llamadas con herramientas ambiguas vs 2 llamadas con estados SUCCESS claros&lt;/strong&gt;. Eso es una diferencia de 7x causada puramente por el diseño de retroalimentación de herramientas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F549teet1ds9cr7ipp1z2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F549teet1ds9cr7ipp1z2.png" alt="Gráfico de barras comparando llamadas a herramientas en estrategias de retroalimentación ambigua, DebounceHook, estados SUCCESS claros y LimitToolCounts" width="799" height="434"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Cuándo Usar Cada Solución
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;DebounceHook&lt;/strong&gt; — previene llamadas duplicadas con parámetros idénticos. Úsalo cuando las herramientas son idempotentes y reintentar con la misma entrada es desperdicio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Estados SUCCESS/FAILED claros&lt;/strong&gt; — la solución más simple. Diseña herramientas para devolver estados terminales explícitos. El agente sabe cuándo detenerse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LimitToolCounts&lt;/strong&gt; — techo duro en llamadas a herramientas por invocación. Úsalo en producción para prevenir costos desbocados independientemente del diseño de herramientas. Del &lt;a href="https://strandsagents.com/docs/user-guide/concepts/agents/hooks/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Hooks Cookbook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Los tres juntos&lt;/strong&gt; — defensa en profundidad. Estados claros previenen la mayoría de loops, debounce atrapa duplicados, y límites duros garantizan ejecución acotada.&lt;/p&gt;
&lt;h2&gt;
  
  
  Pruébalo Tú Mismo
&lt;/h2&gt;

&lt;p&gt;Necesitas &lt;a href="https://python.org/downloads" rel="noopener noreferrer"&gt;Python 3.9+&lt;/a&gt;, &lt;a href="https://docs.astral.sh/uv/" rel="noopener noreferrer"&gt;uv&lt;/a&gt;, y una &lt;a href="https://platform.openai.com/api-keys" rel="noopener noreferrer"&gt;clave API de OpenAI&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/aws-samples/sample-why-agents-fail
&lt;span class="nb"&gt;cd &lt;/span&gt;sample-why-agents-fail/stop-ai-agents-wasting-tokens/03-reasoning-loops-demo
uv venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; uv pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"tu-clave-aquí"&lt;/span&gt;

uv run python test_reasoning_loops.py   &lt;span class="c"&gt;# Ejecuta los 4 escenarios&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;O abre &lt;code&gt;test_reasoning_loops.ipynb&lt;/code&gt; en &lt;a href="https://jupyter.org/" rel="noopener noreferrer"&gt;Jupyter&lt;/a&gt;, &lt;a href="https://jupyterlab.readthedocs.io/" rel="noopener noreferrer"&gt;JupyterLab&lt;/a&gt;, VS Code, o tu entorno de notebook preferido.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusiones Clave
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;La retroalimentación ambigua de herramientas causa loops orgánicos&lt;/strong&gt; — "puede haber más resultados disponibles" hace que los agentes reintenten&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;14 llamadas vs 2 llamadas&lt;/strong&gt; — estados SUCCESS claros reducen llamadas en 7x en nuestra demo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Los hooks interceptan antes de la ejecución&lt;/strong&gt; — &lt;code&gt;BeforeToolCallEvent.cancel_tool&lt;/code&gt; bloquea la llamada antes de que la herramienta se ejecute. El &lt;code&gt;DebounceHook&lt;/code&gt; son ~30 líneas de código&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Los límites duros son obligatorios&lt;/strong&gt; — cada agente necesita topes en iteraciones, tiempo y gasto&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Se documentaron 847 pasos a $47/min&lt;/strong&gt; (Particula, observación comunitaria) — agentes sin límites queman dinero sin entregar respuestas&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Preguntas Frecuentes
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ¿Por qué los agentes de IA repiten la misma llamada a herramienta?
&lt;/h3&gt;

&lt;p&gt;Los agentes repiten llamadas a herramientas cuando las respuestas de herramientas contienen retroalimentación ambigua como "puede haber más resultados disponibles" o "los precios cambian frecuentemente." El LLM interpreta estas señales como una razón para reintentar, esperando resultados diferentes o mejores. Sin estados terminales claros (SUCCESS/FAILED), el agente no tiene forma de saber que la tarea está completa.&lt;/p&gt;
&lt;h3&gt;
  
  
  ¿Qué es un DebounceHook y cómo previene loops de razonamiento?
&lt;/h3&gt;

&lt;p&gt;Un DebounceHook rastrea llamadas recientes a herramientas en una ventana deslizante. Cuando la misma herramienta se llama con parámetros idénticos más que un umbral establecido (típicamente 2 veces dentro de una ventana de 3), el hook bloquea la llamada usando &lt;code&gt;event.cancel_tool&lt;/code&gt; antes de que la herramienta se ejecute. El LLM recibe un mensaje "BLOCKED: Llamada duplicada" y debe intentar un enfoque diferente. En Strands Agents, esto son aproximadamente 30 líneas de código usando la API de &lt;code&gt;HookProvider&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  ¿Cómo reducen los estados SUCCESS/FAILED claros las llamadas a herramientas?
&lt;/h3&gt;

&lt;p&gt;Cuando una herramienta devuelve "SUCCESS: Reserva HT79265 confirmada," el LLM reconoce que la tarea está completa y deja de llamar a esa herramienta. Respuestas ambiguas como "Se encontraron 2 vuelos, puede haber más disponibles" carecen de esta señal, causando que el agente reintente. En nuestra demo, estados claros redujeron las llamadas a herramientas de 14 a 2, una mejora de 7x.&lt;/p&gt;
&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Investigación
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://the-decoder.com/language-models-can-overthink-and-get-stuck-in-endless-thought-loops/" rel="noopener noreferrer"&gt;Language models can overthink&lt;/a&gt; — The Decoder, Jan 2025&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://particula.tech/blog/ai-agent-loops-reasoning-steps-optimization" rel="noopener noreferrer"&gt;How many reasoning steps do AI agents need&lt;/a&gt; — Particula (observación comunitaria), Jul 2025&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://codieshub.com/for-ai/prevent-agent-loops-costs" rel="noopener noreferrer"&gt;How to Prevent Infinite Loops and Spiraling Costs&lt;/a&gt; — CodiesHub (observación comunitaria), Dec 2025&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Implementación
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/concepts/agents/hooks/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Hooks&lt;/a&gt; — Lifecycle event interception and tool cancellation&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Gracias!&lt;/p&gt;

&lt;p&gt;🇻🇪🇨🇱 &lt;a href="https://dev.to/elizabethfuentes12"&gt;Dev.to&lt;/a&gt; &lt;a href="https://www.linkedin.com/in/lizfue/" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt; &lt;a href="https://github.com/elizabethfuentes12/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; &lt;a href="https://twitter.com/elizabethfue12" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; &lt;a href="https://www.instagram.com/elifue.tech" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt; &lt;a href="https://www.youtube.com/channel/UCr0Gnc-t30m4xyrvsQpNp2Q" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag__user ltag__user__id__717518"&gt;
    &lt;a href="/elizabethfuentes12" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=150,height=150,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F717518%2Fb550b165-b8b9-405d-acfb-e5dc846765b0.png" alt="elizabethfuentes12 image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/elizabethfuentes12"&gt;Elizabeth Fuentes L&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/elizabethfuentes12"&gt;I help developers build production-ready AI applications through hands-on tutorials and open-source projects.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>ai</category>
      <category>tutorial</category>
      <category>python</category>
      <category>programming</category>
    </item>
    <item>
      <title>CLI vs MCP: guía para agentes en producción</title>
      <dc:creator>ricardoceci</dc:creator>
      <pubDate>Wed, 27 May 2026 13:49:46 +0000</pubDate>
      <link>https://dev.to/aws-espanol/cli-vs-mcp-guia-para-agentes-en-produccion-2dkc</link>
      <guid>https://dev.to/aws-espanol/cli-vs-mcp-guia-para-agentes-en-produccion-2dkc</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Una de las preguntas más interesantes que me hicieron en la última clase de mi curso "Strands Agents + AgentCore: De Cero a Agentes en Producción".&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ayer, en medio de la clase, llegó la pregunta:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Ricardo, estoy viendo en Twitter y LinkedIn una pelea entre CLI y MCP para tools. ¿Cuál usamos? ¿Es verdad que MCP se come el contexto?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;La pregunta no es trivial. Tiene impacto directo en costos, latencia, confiabilidad y arquitectura de cualquier agente que llevemos a producción. La respuesta corta: no hay una respuesta única. Hay un &lt;em&gt;framework de decisión&lt;/em&gt; que la comunidad fue construyendo en los últimos meses.&lt;/p&gt;

&lt;p&gt;En este post te cuento qué está pasando, por qué se generó el debate, y cómo decidir en cada caso.&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Qué son MCP y CLI?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  MCP (Model Context Protocol)
&lt;/h3&gt;

&lt;p&gt;Lo lanzó &lt;strong&gt;Anthropic en noviembre de 2024&lt;/strong&gt; como un estándar abierto para conectar agentes a herramientas externas (GitHub, Slack, bases de datos, lo que sea). La promesa fue clara: el &lt;em&gt;"USB-C de la IA"&lt;/em&gt;, un protocolo único para que cualquier modelo hable con cualquier herramienta sin reinventar la integración cada vez.&lt;/p&gt;

&lt;p&gt;A mayo de 2026, MCP es &lt;strong&gt;el estándar de facto&lt;/strong&gt;. La Linux Foundation lo gobierna a través de la Agentic AI Foundation, hay más de 177,000 tools registradas y casi 100 millones de descargas mensuales del SDK.&lt;/p&gt;

&lt;h3&gt;
  
  
  CLI (Command-Line Interface)
&lt;/h3&gt;

&lt;p&gt;La interfaz de línea de comandos existe desde &lt;strong&gt;1971 en Unix&lt;/strong&gt;. Son los comandos de toda la vida: &lt;code&gt;git&lt;/code&gt;, &lt;code&gt;gh&lt;/code&gt;, &lt;code&gt;kubectl&lt;/code&gt;, &lt;code&gt;docker&lt;/code&gt;, &lt;code&gt;aws&lt;/code&gt;, &lt;code&gt;ffmpeg&lt;/code&gt;. No es tecnología nueva. Es la interfaz más veterana del desarrollo de software, y eso, sorprendentemente, &lt;strong&gt;resultó ser una ventaja enorme para los modelos de lenguaje grandes (LLMs)&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  El problema que detonó el debate: MCP devora contexto
&lt;/h2&gt;

&lt;p&gt;A fines de 2025 y principios de 2026, los developers que pusieron MCP en producción empezaron a notar algo grave: &lt;strong&gt;MCP consume una cantidad enorme de tokens antes de que el agente haga una sola cosa útil&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  El ejemplo concreto
&lt;/h3&gt;

&lt;p&gt;Cuando conectás el servidor MCP de GitHub, este inyecta el esquema completo de herramientas en la ventana de contexto del modelo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"create_issue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Create a new issue..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"inputSchema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;se&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;repite&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;para&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;~&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;herramientas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;más&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El servidor MCP de GitHub tiene del orden de &lt;strong&gt;93 herramientas&lt;/strong&gt;, lo que se traduce en &lt;strong&gt;~55,000 tokens consumidos solo en definiciones&lt;/strong&gt;, antes de que el agente reciba el primer prompt útil.&lt;/p&gt;

&lt;p&gt;Con 3 servidores MCP conectados (GitHub + Slack + tu base de datos), podés llegar a consumir &lt;strong&gt;el 70%+ de una ventana de contexto de 200K tokens solo en metadata&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Anthropic mismo lo reconoció
&lt;/h3&gt;

&lt;p&gt;En noviembre de 2025, Adam Jones y Conor Kelly del equipo de Engineering de Anthropic publicaron &lt;a href="https://www.anthropic.com/engineering/code-execution-with-mcp" rel="noopener noreferrer"&gt;&lt;em&gt;"Code execution with MCP: Building more efficient agents"&lt;/em&gt;&lt;/a&gt;, donde reconocen el problema explícitamente:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"As MCP usage scales, there are two common patterns that can increase agent cost and latency: tool definitions overload the context window, and intermediate tool results consume additional tokens."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cuando los creadores del protocolo te dicen &lt;em&gt;"sí, hay un problema"&lt;/em&gt;, no es polémica de Twitter. Es una corrección de arquitectura real.&lt;/p&gt;




&lt;h2&gt;
  
  
  La chispa: Peter Steinberger y OpenClaw
&lt;/h2&gt;

&lt;p&gt;El debate explotó en febrero de 2026 cuando &lt;strong&gt;Peter Steinberger&lt;/strong&gt;, creador de &lt;a href="https://github.com/openclaw/openclaw" rel="noopener noreferrer"&gt;OpenClaw&lt;/a&gt; (el agente open source que pasó de 0 a 190,000 stars de GitHub en pocas semanas y terminó con Steinberger fichado por OpenAI), tiró una frase que se hizo viral:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"MCP was a mistake. Bash is better."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Steinberger no estaba haciendo &lt;em&gt;clickbait&lt;/em&gt;. Su tesis era pragmática: los LLMs ya saben usar bash de memoria porque los entrenaron con miles de millones de líneas de scripts, Stack Overflow y man pages. &lt;strong&gt;No hace falta enseñarles un protocolo nuevo cuando ya hablan el viejo a la perfección.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Disclaimer importante:&lt;/strong&gt; OpenClaw también fue protagonista de uno de los desastres de seguridad más sonados del 2026 (ClawHavoc Attack, registrado como Common Vulnerabilities and Exposures CVE-2026-25253, miles de instancias expuestas en internet). El argumento técnico de Steinberger sobre CLI vs MCP es sólido, pero su modelo de seguridad fue criticado duramente por Cisco, Gartner y Meta. Es importante separar las dos cosas.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Las tres voces que estructuraron el debate
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. David Zhang (Duet) y el "trilemma"
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/dzhng" rel="noopener noreferrer"&gt;David Zhang&lt;/a&gt;, construyendo Duet, describió el dilema imposible que enfrentó al integrar MCP, incluso después de haber resuelto OAuth y client registration dinámico:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cargar todo al inicio&lt;/strong&gt; → perdés memoria de trabajo para razonamiento.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limitar integraciones&lt;/strong&gt; → el agente solo habla con pocos servicios.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cargar herramientas dinámicamente&lt;/strong&gt; → agregás latencia y middleware complejo.
Lo bautizó el &lt;em&gt;"trilemma de MCP"&lt;/em&gt;. Su decisión: sacó MCP completamente y adoptó CLI + ejecución de código.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2. Cobus Greyling y la tesis del "puente que ya existe"
&lt;/h3&gt;

&lt;p&gt;En &lt;a href="https://cobusgreyling.medium.com/replace-mcp-with-cli-the-best-ai-agent-interface-already-exists-bcbb8094cff8" rel="noopener noreferrer"&gt;&lt;em&gt;Replace MCP With CLI&lt;/em&gt;&lt;/a&gt; (Feb 2026), Greyling escribió la frase que mejor sintetiza el argumento pro-CLI:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Con MCP, construís el puente hacia la herramienta. Con CLI, el puente ya existe."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Todo servicio serio ya tiene su CLI: AWS, GCP, Azure, GitHub, Stripe, Twilio, Kubernetes. Son production-grade, los mantienen los proveedores, y los modelos los conocen sin que les expliques nada.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Anthropic y el contraataque: &lt;em&gt;Code Execution with MCP&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Anthropic no se quedó callada. En noviembre 2025 publicó una solución intermedia muy elegante: en lugar de pedirle al modelo que llame tools una por una vía MCP, &lt;strong&gt;el agente escribe código corto&lt;/strong&gt; que llama esas tools por debajo en un sandbox.&lt;/p&gt;

&lt;p&gt;El resultado, según sus propios benchmarks, es una reducción de &lt;strong&gt;hasta el 98% de tokens consumidos&lt;/strong&gt; (pasaron de 150,000 tokens a 2,000 tokens en su ejemplo de prueba).&lt;/p&gt;

&lt;p&gt;Hay implementaciones en producción que ya validaron ese resultado: &lt;strong&gt;70,000 → 800 tokens&lt;/strong&gt; (~98% de reducción) en agentes reales sobre GitHub.&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Por qué CLI funciona tan bien para LLMs?
&lt;/h2&gt;

&lt;p&gt;Cuatro razones técnicas que vale la pena entender:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Los LLMs ya saben usar CLI "de memoria"
&lt;/h3&gt;

&lt;p&gt;El modelo no necesita que le expliques qué hace &lt;code&gt;git log --oneline -10&lt;/code&gt;. Lo vio millones de veces en su entrenamiento. Con MCP, cada esquema es nuevo para el modelo y tiene que interpretarlo &lt;em&gt;en runtime&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# CLI: el modelo ya sabe esto&lt;/span&gt;
docker ps &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s2"&gt;"status=running"&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"{{.Names}}: {{.Status}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;MCP:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;el&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;modelo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;recibe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;esto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tiene&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;que&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;interpretarlo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"list_containers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inputSchema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"status_filter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"enum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"running"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stopped"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"all"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"format_fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. CLI tiene "divulgación progresiva" gratis
&lt;/h3&gt;

&lt;p&gt;Un agente con CLI puede ir descubriendo herramientas a medida que las necesita:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--help&lt;/span&gt;                          &lt;span class="c"&gt;# ¿qué servicios hay?&lt;/span&gt;
aws s3 &lt;span class="nt"&gt;--help&lt;/span&gt;                       &lt;span class="c"&gt;# ¿qué puedo hacer con S3?&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;--help&lt;/span&gt;                    &lt;span class="c"&gt;# ¿cómo se usa cp exactamente?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cada llamada consume pocos tokens y solo cuando el agente realmente necesita esa información. MCP, por contraste, &lt;strong&gt;carga todo el esquema antes del primer mensaje&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Composabilidad tipo Unix
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Pipeline real que un agente puede armar solo&lt;/span&gt;
aws ec2 describe-instances &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Reservations[].Instances[?State.Name==`running`].InstanceId'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text | &lt;span class="se"&gt;\&lt;/span&gt;
  xargs &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; aws cloudwatch get-metric-statistics &lt;span class="nt"&gt;--instance-id&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hacer esto vía MCP requiere coordinar múltiples llamadas estructuradas, parsear resultados intermedios, mantener estado. Bash lo hace en una línea.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Cero overhead de protocolo
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CLI&lt;/strong&gt;: &lt;code&gt;genera comando → ejecuta → lee output&lt;/code&gt;. Directo.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  - &lt;strong&gt;MCP&lt;/strong&gt;: &lt;code&gt;negocia capacidades → carga esquemas → construye llamada → servidor ejecuta → wrap del resultado → parse&lt;/code&gt;. Cada paso suma tokens.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  La comparación honesta: ¿cuándo gana cada uno?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criterio&lt;/th&gt;
&lt;th&gt;CLI ✅&lt;/th&gt;
&lt;th&gt;MCP ✅&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Eficiencia de tokens&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐⭐&lt;/td&gt;
&lt;td&gt;⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Herramientas locales (git, docker, kubectl)&lt;/td&gt;
&lt;td&gt;Ideal&lt;/td&gt;
&lt;td&gt;Innecesario&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Datos externos en tiempo real (Salesforce, Notion)&lt;/td&gt;
&lt;td&gt;Limitado&lt;/td&gt;
&lt;td&gt;Ideal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ecosistemas multi-agente / multi-tenant&lt;/td&gt;
&lt;td&gt;Difícil&lt;/td&gt;
&lt;td&gt;Diseñado para eso&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth compleja (OAuth multi-tenant)&lt;/td&gt;
&lt;td&gt;Limitado&lt;/td&gt;
&lt;td&gt;Nativo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Herramientas sin API (ffmpeg, pandoc, jq)&lt;/td&gt;
&lt;td&gt;Única opción&lt;/td&gt;
&lt;td&gt;No aplica&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confiabilidad en tareas complejas&lt;/td&gt;
&lt;td&gt;Alta (local)&lt;/td&gt;
&lt;td&gt;Variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prototipado rápido&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐⭐&lt;/td&gt;
&lt;td&gt;⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integraciones empresariales gobernadas&lt;/td&gt;
&lt;td&gt;Limitado&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  El framework de decisión que se puede implementar
&lt;/h2&gt;

&lt;p&gt;Este es el árbol de decisión que valdria la pena utilizar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. ¿Existe un CLI maduro para esta tarea y el modelo ya lo conoce?
       ↓ SÍ → USÁ CLI
       ↓ NO → ¿Necesitás datos externos en tiempo real
                vía API de terceros con OAuth?
                    ↓ SÍ → USÁ MCP (o llamada directa a la API)
                    ↓ NO → ¿Es operación multi-agente / multi-tenant
                            con permisos granulares por servidor?
                                ↓ SÍ → USÁ MCP
                                ↓ NO → Considerá construir un CLI interno
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un consejo adicional: si vas a usar MCP, considerá seriamente el patrón &lt;strong&gt;Code Execution&lt;/strong&gt; que propone Anthropic. Reduce el consumo de contexto al mínimo y podés seguir disfrutando del ecosistema MCP sin pagar el costo de cargar todos los esquemas upfront.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lo que realmente está pasando: no es una guerra, es una corrección
&lt;/h2&gt;

&lt;p&gt;Mi lectura, después de haber investigado un poco mas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔴 &lt;strong&gt;MCP fue sobrevendido&lt;/strong&gt; como solución universal en 2025 → resultó costoso para muchas tareas cotidianas.&lt;/li&gt;
&lt;li&gt;🟢 &lt;strong&gt;CLI fue subestimado&lt;/strong&gt; por ser "viejo" → resultó perfecto para agentes porque los LLMs ya lo conocen.&lt;/li&gt;
&lt;li&gt;🟡 &lt;strong&gt;El futuro es híbrido&lt;/strong&gt;: CLI para operaciones locales y determinísticas, MCP para datos externos y multi-tenant, Code Execution para pipelines complejos.
La frase viral &lt;em&gt;"MCP is dead"&lt;/em&gt; es más una corrección de hype que una muerte real. &lt;strong&gt;Lo que murió fue la idea de que MCP tiene que ser el único puente entre los agentes y el mundo exterior.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ¿Qué hacemos en el curso?
&lt;/h2&gt;

&lt;p&gt;En &lt;em&gt;"Strands Agents + AgentCore: De Cero a Agentes en Producción"&lt;/em&gt; construimos un Corporate Travel Agent con &lt;strong&gt;enfoque híbrido&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CLI / SDK directo&lt;/strong&gt;: para operaciones locales, herramientas internas, y APIs simples (como Open-Meteo para clima).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP&lt;/strong&gt;: cuando necesitamos un protocolo gobernado para integraciones empresariales (Duffel para reservas con OAuth).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB SDK nativo&lt;/strong&gt;: porque para state management no tiene sentido envolver todo en MCP.
La regla mental que me funciona: &lt;strong&gt;MCP es como Kubernetes. Fenomenal cuando lo necesitás, sobredimensionado cuando no&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Seguilo Aqui: &lt;a href="https://www.ricardoceci.dev" rel="noopener noreferrer"&gt;https://www.ricardoceci.dev&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿MCP está muerto?
&lt;/h3&gt;

&lt;p&gt;No. MCP es el estándar de facto, gobernado por la Linux Foundation, con casi 100 millones de descargas mensuales. Lo que murió es la idea de que MCP debe ser el &lt;strong&gt;único&lt;/strong&gt; puente entre agentes y herramientas. Para muchos casos cotidianos, CLI es más eficiente.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cuándo conviene MCP sobre CLI?
&lt;/h3&gt;

&lt;p&gt;Tres casos claros: (1) integraciones empresariales con OAuth multi-tenant, (2) ecosistemas multi-agente que necesitan gobierno centralizado, (3) servicios externos sin CLI maduro. Para todo lo demás (git, docker, kubectl, aws, manipulación local), CLI gana en tokens y latencia.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Es seguro darle bash a un agente?
&lt;/h3&gt;

&lt;p&gt;Es la pregunta correcta. La respuesta es: depende del sandbox. Steinberger demostró el peligro con el incidente ClawHavoc en OpenClaw. La práctica recomendada es ejecutar el agente en contenedores con permisos restringidos, whitelist de comandos, y revisión humana para operaciones destructivas.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿El patrón Code Execution reemplaza a MCP?
&lt;/h3&gt;

&lt;p&gt;No, lo complementa. Code Execution te permite usar MCP servers existentes pero cargar solo las tools necesarias bajo demanda y procesar resultados intermedios sin pasarlos por la ventana de contexto. Es lo mejor de ambos mundos para integraciones complejas.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cómo afecta esto a Strands Agents, LangChain, CrewAI?
&lt;/h3&gt;

&lt;p&gt;Todos los frameworks principales soportan ambos patrones. La decisión es por herramienta, no por framework. En Strands Agents podés mezclar &lt;code&gt;Agent(tools=[tu_funcion_python, mcp_client.tools])&lt;/code&gt; sin problema.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recursos para profundizar
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📄 &lt;a href="https://www.anthropic.com/engineering/code-execution-with-mcp" rel="noopener noreferrer"&gt;Code execution with MCP — Anthropic Engineering&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📄 &lt;a href="https://cobusgreyling.medium.com/replace-mcp-with-cli-the-best-ai-agent-interface-already-exists-bcbb8094cff8" rel="noopener noreferrer"&gt;Replace MCP With CLI — Cobus Greyling&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📄 &lt;a href="https://dev.to/apideck/your-mcp-server-is-eating-your-context-window-theres-a-simpler-way-315b"&gt;Your MCP Server Is Eating Your Context Window — Apideck (DEV Community)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📦 &lt;a href="https://github.com/openclaw/openclaw" rel="noopener noreferrer"&gt;OpenClaw en GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;¿Tu agente está sufriendo de context overflow? ¿Qué patrón estás usando vos? Dejame un comentario, me interesa saber cómo está resolviendo cada uno este trade-off en producción.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>mcp</category>
      <category>aws</category>
    </item>
    <item>
      <title>Cómo Evaluar Agentes IA: Tutorial de LLM-as-Judge</title>
      <dc:creator>Elizabeth Fuentes L</dc:creator>
      <pubDate>Tue, 26 May 2026 19:03:38 +0000</pubDate>
      <link>https://dev.to/aws-espanol/como-evaluar-agentes-ia-tutorial-de-llm-as-judge-392g</link>
      <guid>https://dev.to/aws-espanol/como-evaluar-agentes-ia-tutorial-de-llm-as-judge-392g</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Evalúa la calidad de agentes IA con LLM-as-Judge y análisis de trayectorias. Detecta fallos silenciosos, tokens desperdiciados y alucinaciones antes de producción. Tutorial en Python con código.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Tu agente IA acaba de devolver "BA117 a las 7PM ($450)" - respuesta correcta, calificación 5 estrellas. Lo que no viste: hizo 3 llamadas API innecesarias y alucinó una verificación de precio. &lt;strong&gt;Las métricas tradicionales de pasa/falla calificaron esto como "perfecto".&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Este es el problema de los fallos silenciosos. Los agentes IA devuelven respuestas plausibles mientras realizan llamadas API innecesarias, alucina hechos, o siguen caminos de razonamiento inseguros. Las métricas binarias no detectan nada de esto.&lt;/p&gt;

&lt;p&gt;Este artículo cubre las dos técnicas fundamentales de evaluación que todo agente necesita: &lt;strong&gt;LLM-as-Judge&lt;/strong&gt; para calidad de salida y &lt;strong&gt;Evaluación de Trayectorias&lt;/strong&gt; (el camino paso a paso que toma un agente) para calidad de proceso. Estas forman la base para detectar alucinaciones, evaluar el uso de herramientas, alineación de seguridad y optimización de costos - temas cubiertos en posts posteriores de esta serie.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Por qué Strands Agents?&lt;/strong&gt; Usamos Strands para los ejemplos de código porque proporciona captura automática de trayectorias mediante hooks y un SDK de evaluación dedicado (&lt;code&gt;strands-agents-evals&lt;/code&gt;), facilitando demostrar estos patrones. Las técnicas de evaluación mostradas aquí aplican a cualquier framework de agentes - LangGraph, AutoGen, o implementaciones personalizadas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sobre el código:&lt;/strong&gt; Todos los ejemplos provienen del repositorio &lt;a href="https://github.com/elizabethfuentes12/how-to-evaluate-ai-agents-sample-for-aws" rel="noopener noreferrer"&gt;how-to-evaluate-ai-agents-sample-for-aws&lt;/a&gt; - notebooks Jupyter ejecutables con Strands Agents y AWS Bedrock. Cada notebook es autocontenido con explicaciones y ejemplos funcionales.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lo que aprenderás:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cómo implementar evaluación LLM-as-Judge con rúbricas explícitas (configuración en 5 min)&lt;/li&gt;
&lt;li&gt;Por qué la evaluación de trayectorias detecta fallos que las métricas de solo salida no capturan&lt;/li&gt;
&lt;li&gt;Ejemplos de código en Python usando Strands Agents en AWS Bedrock&lt;/li&gt;
&lt;li&gt;Cómo usar los evaluadores integrados de Amazon Bedrock AgentCore para producción&lt;/li&gt;
&lt;li&gt;Investigación más reciente de abril de 2026 (WindowsWorld, D3-Gym, framework CARE)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://github.com/elizabethfuentes12/how-to-evaluate-ai-agents-sample-for-aws" rel="noopener noreferrer"&gt;Ver todos los ejemplos de código en GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tiempo estimado de lectura: 9 minutos&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Por Qué Strands Agents Para Evaluar Agentes IA?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Strands Agents&lt;/strong&gt; proporciona el kit de herramientas de evaluación más completo para agentes IA en producción - combinando captura automática de trayectorias, SDK de evaluación dedicado e integración con AWS Bedrock en un solo framework.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ventajas clave para evaluación:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SDK de evaluación dedicado&lt;/strong&gt; (&lt;code&gt;strands-agents-evals&lt;/code&gt;) con evaluadores integrados para calidad de salida y puntuación de trayectorias&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organización de suites de pruebas&lt;/strong&gt; - clases &lt;code&gt;Experiment&lt;/code&gt; y &lt;code&gt;Case&lt;/code&gt; para ejecutar múltiples escenarios de prueba con generación automática de reportes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Captura automática de trayectorias&lt;/strong&gt; mediante hooks (&lt;code&gt;HookProvider&lt;/code&gt;) - cada llamada a herramienta se registra con estado de éxito/fallo, sin instrumentación manual&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nativo en AWS Bedrock&lt;/strong&gt; - funciona perfectamente con Claude, Llama y Mistral mediante perfiles de inferencia multi-región, eliminando gestión de claves API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibilidad de modelos&lt;/strong&gt; - los evaluadores pueden usar cualquier modelo (GPT-4o, Claude Sonnet, etc.) independiente del modelo del agente&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visualización integrada&lt;/strong&gt; - &lt;code&gt;reports[0].display()&lt;/code&gt; muestra resultados formateados instantáneamente, perfecto para notebooks Jupyter&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Puntuación ponderada&lt;/strong&gt; - combina múltiples evaluadores (ej., 60% calidad de salida + 40% trayectoria) para evaluación completa&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenTelemetry integrado&lt;/strong&gt; - trazas distribuidas automáticas compatibles con Datadog, Honeycomb y otras plataformas de observabilidad&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Por Qué Fallan Las Métricas Binarias
&lt;/h2&gt;

&lt;p&gt;Considera estos dos agentes respondiendo "Encuentra vuelos de NYC a Londres":&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Agente A&lt;/th&gt;
&lt;th&gt;Agente B&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Respuesta&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"BA117 a las 7PM ($450), DL1 a las 9:30PM ($520)"&lt;/td&gt;
&lt;td&gt;"BA117 a las 7PM ($450), DL1 a las 9:30PM ($520)"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Llamadas a Herramientas&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;search_flights("NYC", "London")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;search_flights("NYC", "London")&lt;/code&gt;&lt;br&gt;&lt;code&gt;get_currency_exchange()&lt;/code&gt;&lt;br&gt;&lt;code&gt;search_flights("NYC", "London")&lt;/code&gt; (duplicado)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pasa/Falla&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Pasa&lt;/td&gt;
&lt;td&gt;✅ Pasa&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Ambos producen la respuesta correcta. La puntuación pasa/falla los califica por igual. Pero el Agente B desperdició tokens en una herramienta irrelevante y una llamada duplicada. &lt;strong&gt;La evaluación de trayectorias detecta esto. La evaluación de solo salida no.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F445eko90kw2q6wmvi5h8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F445eko90kw2q6wmvi5h8.png" alt="Diagrama del pipeline de evaluación LLM-as-Judge de agentes IA: la salida del agente fluye a través del LLM juez con rúbrica para producir puntuación 0-1 con razonamiento, comparado con evaluación binaria legada de pasa/falla" width="800" height="619"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Cómo Funciona la Evaluación LLM-as-Judge?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;LLM-as-Judge usa un modelo de lenguaje grande para puntuar salidas de agentes contra criterios definidos, reemplazando la revisión manual. Proporciona puntuaciones continuas (0.0-1.0) con explicaciones, a diferencia del pasa/falla binario. La investigación muestra que rúbricas explícitas con umbrales de puntuación (0.8-1.0 = excelente, 0.5-0.7 = adecuado) producen evaluación consistente y reproducible a escala.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Paper:&lt;/strong&gt; &lt;a href="https://arxiv.org/abs/2603.00077" rel="noopener noreferrer"&gt;Autorubric&lt;/a&gt; (Marzo 2026)&lt;/p&gt;

&lt;h3&gt;
  
  
  El Problema con Prompts Vagos
&lt;/h3&gt;

&lt;p&gt;La mayoría de los jueces LLM usan prompts vagos como "¿Es esta una buena respuesta?" Esto produce puntuaciones impredecibles porque el juez decide qué significa "buena". La investigación muestra que rúbricas vagas conducen a &lt;strong&gt;sesgo de posición&lt;/strong&gt; (preferir la primera opción) y &lt;strong&gt;sesgo de verbosidad&lt;/strong&gt; (preferir respuestas más largas).&lt;/p&gt;

&lt;h3&gt;
  
  
  La Solución: Criterios de Puntuación Explícitos
&lt;/h3&gt;

&lt;p&gt;Define umbrales exactos de puntuación en tu rúbrica:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_evals&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Experiment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Case&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_evals.evaluators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OutputEvaluator&lt;/span&gt;

&lt;span class="c1"&gt;# Define explicit scoring criteria
&lt;/span&gt;&lt;span class="n"&gt;evaluator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OutputEvaluator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;rubric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rate the travel agent response on a 0 to 1 scale:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- 0.8-1.0: Lists specific flights with airline, flight number, times, and price&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- 0.5-0.7: Provides some useful information but missing key details&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- 0.2-0.4: Vague response without actionable information&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- 0.0-0.1: Contains fabricated information or is completely unhelpful&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Or use AWS Bedrock: us.anthropic.claude-sonnet-4-20250514-v1:0
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create test cases
&lt;/span&gt;&lt;span class="n"&gt;cases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;good&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find flights NYC to London&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
         &lt;span class="n"&gt;expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Specific flights with details&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nc"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vague&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find flights NYC to London&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Specific flights with details&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Run evaluation
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;good&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BA117 at 7PM ($450), DL1 at 9:30PM ($520)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;There are several flights available. Prices vary.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;experiment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Experiment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;evaluators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;evaluator&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;reports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;experiment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_evaluations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;display&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Salida:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;good:  Score 0.95 - Lists specific flights with all required details
vague: Score 0.30 - Missing specific details about airlines and times
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Rúbricas Vagas vs Específicas: Una Comparación
&lt;/h3&gt;

&lt;p&gt;El &lt;a href="https://arxiv.org/abs/2603.00077" rel="noopener noreferrer"&gt;paper Autorubric&lt;/a&gt; muestra que la calidad de la rúbrica impacta directamente la confiabilidad de las puntuaciones. Pruébalo tú mismo:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Vague rubric (produces unreliable scores)
&lt;/span&gt;&lt;span class="n"&gt;vague_evaluator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OutputEvaluator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;rubric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Is this a good response?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Specific rubric (produces reliable scores)
&lt;/span&gt;&lt;span class="n"&gt;specific_evaluator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OutputEvaluator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;rubric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rate 0-1:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.8-1.0: Lists specific flights with airline, number, times, price&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.5-0.7: Some useful info but missing key details&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.2-0.4: Vague without actionable information&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0-0.1: Contains fabricated information&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Compare on 3 test cases: good, mediocre, hallucinated
&lt;/span&gt;&lt;span class="n"&gt;responses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;good&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BA117 at 7PM ($450), DL1 at 9:30PM ($520), VS001 at 11PM ($480)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mediocre&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;There are several flights available. Prices vary.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hallucinated&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Take AeroFast Premium with our award-winning service.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Resultados:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Vague rubric&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;good: 0.70 | mediocre: 0.50 | hallucinated: 0.60  (spread&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.20)&lt;/span&gt;

&lt;span class="na"&gt;Specific rubric&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;good: 0.90 | mediocre: 0.30 | hallucinated: 0.10  (spread&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.80)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;La rúbrica específica produce &lt;strong&gt;4x más separación de puntuaciones&lt;/strong&gt;, haciendo posible establecer umbrales de calidad significativos.&lt;/p&gt;
&lt;h3&gt;
  
  
  Mezclando Jueces LLM con Verificaciones Determinísticas
&lt;/h3&gt;

&lt;p&gt;Usa jueces LLM para calidad subjetiva y verificaciones determinísticas para requisitos estrictos:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_evals.evaluators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OutputEvaluator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ToolCalled&lt;/span&gt;

&lt;span class="n"&gt;experiment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Experiment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;evaluators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;OutputEvaluator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rubric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;      &lt;span class="c1"&gt;# LLM judge: subjective quality
&lt;/span&gt;        &lt;span class="nc"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;                 &lt;span class="c1"&gt;# Deterministic: must mention price
&lt;/span&gt;        &lt;span class="nc"&gt;ToolCalled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_flights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;# Deterministic: must search
&lt;/span&gt;    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Por qué esto importa:&lt;/strong&gt; Las verificaciones determinísticas se ejecutan instantáneamente a costo cero. Úsalas para requisitos que pueden verificarse con coincidencia de cadenas (contiene "$", comienza con "Error:", llama a herramienta específica) y jueces LLM para evaluación de calidad que requiere entender contexto.&lt;/p&gt;
&lt;h3&gt;
  
  
  Hallazgos Clave de la Investigación
&lt;/h3&gt;

&lt;p&gt;El &lt;a href="https://arxiv.org/abs/2601.03444" rel="noopener noreferrer"&gt;paper Grading Scale&lt;/a&gt; (Enero 2026) probó escalas de puntuación desde binaria (0/1) hasta 10 puntos y encontró:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Escala 0-5 produce el alineamiento humano-LLM más fuerte&lt;/strong&gt; (correlación de Pearson 0.89)&lt;/li&gt;
&lt;li&gt;Las escalas de 10 puntos introducen ruido sin mejorar precisión&lt;/li&gt;
&lt;li&gt;Las escalas binarias pierden 73% de graduaciones de calidad&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Recomendación:&lt;/strong&gt; Usa una escala 0-5 (mapeada a 0.0-1.0 en código) con criterios explícitos en cada nivel.&lt;/p&gt;


&lt;h2&gt;
  
  
  ¿Qué Es la Evaluación de Trayectorias?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;La evaluación de trayectorias puntúa el camino paso a paso que toma un agente para alcanzar una solución, no solo la respuesta final. Detecta llamadas duplicadas a herramientas, acciones irrelevantes y pasos intermedios inseguros que la evaluación de solo salida no captura. Al capturar la secuencia de invocaciones de herramientas, identifica patrones de razonamiento desperdiciados o peligrosos antes de que lleguen a producción.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Paper:&lt;/strong&gt; &lt;a href="https://arxiv.org/abs/2602.21230" rel="noopener noreferrer"&gt;TRACE&lt;/a&gt; (Febrero 2026)&lt;/p&gt;
&lt;h3&gt;
  
  
  El Problema: La Evaluación de Solo Salida Está Ciega
&lt;/h3&gt;

&lt;p&gt;La evaluación de solo salida ve la respuesta final. No puede detectar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Llamadas duplicadas a herramientas (tokens desperdiciados)&lt;/li&gt;
&lt;li&gt;Llamadas irrelevantes a herramientas (camino de razonamiento incorrecto)&lt;/li&gt;
&lt;li&gt;Pasos intermedios inseguros (violaciones de privacidad, acciones no autorizadas)&lt;/li&gt;
&lt;li&gt;Orden ilógico de herramientas (get_price antes de search_product)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  La Solución: Evalúa el Camino, No Solo el Destino
&lt;/h3&gt;

&lt;p&gt;La evaluación de trayectorias puntúa el &lt;strong&gt;camino paso a paso&lt;/strong&gt; que tomó el agente:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_evals.evaluators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TrajectoryEvaluator&lt;/span&gt;

&lt;span class="n"&gt;traj_eval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TrajectoryEvaluator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;rubric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rate the tool usage trajectory 0-1:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- 0.8-1.0: Only relevant tools called, no duplicates, logical order&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- 0.5-0.7: Mostly correct but minor inefficiency&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- 0.2-0.4: Irrelevant tools called or excessive duplicates&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- 0.0-0.1: Completely wrong tool selection&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Simulate Agent A (efficient) and Agent B (wasteful)
&lt;/span&gt;&lt;span class="n"&gt;efficient_trajectory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_flights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;args&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;origin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NYC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;London&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;args&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;city&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;London&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;wasteful_trajectory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_flights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;args&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;origin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NYC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;London&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_currency_exchange&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;args&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}},&lt;/span&gt;  &lt;span class="c1"&gt;# irrelevant
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_flights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;args&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;origin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NYC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;London&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;  &lt;span class="c1"&gt;# duplicate
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;args&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;city&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;London&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;cases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;efficient&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find flights and weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
         &lt;span class="n"&gt;expected_trajectory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_flights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="nc"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wasteful&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find flights and weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;expected_trajectory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_flights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;traj_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;trajectory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;efficient_trajectory&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;efficient&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;wasteful_trajectory&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BA117 at 7PM, London is 18C&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trajectory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;trajectory&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Experiment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;evaluators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;traj_eval&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;reports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_evaluations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;traj_task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;display&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Salida:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;efficient: Score 0.95 - Clean trajectory, only relevant tools
wasteful:  Score 0.25 - Contains irrelevant tool and duplicate call
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Captura Automática de Trayectorias con Hooks
&lt;/h3&gt;

&lt;p&gt;En producción, no construyes trayectorias manualmente. Usa &lt;strong&gt;Strands hooks&lt;/strong&gt; para capturarlas automáticamente:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.hooks&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HookProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HookRegistry&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.hooks.events&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AfterToolCallEvent&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TrajectoryPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HookProvider&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trajectory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_after_tool_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AfterToolCallEvent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trajectory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;args&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exception&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TrajectoryPlugin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[...],&lt;/span&gt; &lt;span class="n"&gt;hooks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Run the agent
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find flights from NYC to London&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# The hook captured everything automatically
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Trajectory: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trajectory&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Output: [{'name': 'search_flights', 'args': {...}, 'success': True}, ...]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Por qué esto importa:&lt;/strong&gt; Los Strands hooks se ejecutan en &lt;strong&gt;cada llamada a herramienta&lt;/strong&gt; sin configuración. El trazado OpenTelemetry está integrado, dándote trazas distribuidas automáticamente.&lt;/p&gt;


&lt;h2&gt;
  
  
  Investigación Reciente: ¿Qué Hay de Nuevo en Abril de 2026?
&lt;/h2&gt;

&lt;p&gt;Tres papers publicados este mes avanzan la metodología de evaluación:&lt;/p&gt;
&lt;h3&gt;
  
  
  1. D3-Gym: Tareas Científicas Ejecutables
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Paper:&lt;/strong&gt; &lt;a href="https://arxiv.org/abs/2604.27977" rel="noopener noreferrer"&gt;arXiv:2604.27977&lt;/a&gt; (30 de Abril, 2026)&lt;/p&gt;

&lt;p&gt;Publicó 565 tareas científicas con entornos ejecutables. Hallazgo clave: &lt;strong&gt;87.5% de concordancia entre evaluación automatizada y estándares de oro anotados por humanos&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implicación:&lt;/strong&gt; LLM-as-Judge puede igualar la calidad de evaluación humana cuando las rúbricas están bien definidas y la verdad fundamental es verificable.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. WindowsWorld: Benchmark de Agentes GUI
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Paper:&lt;/strong&gt; &lt;a href="https://arxiv.org/abs/2604.27776" rel="noopener noreferrer"&gt;arXiv:2604.27776&lt;/a&gt; (30 de Abril, 2026)&lt;/p&gt;

&lt;p&gt;Probó agentes GUI en 181 tareas profesionales multi-aplicación. Resultado: &lt;strong&gt;&amp;lt;21% tasa de éxito en tareas multi-app&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implicación:&lt;/strong&gt; Incluso los agentes de última generación fallan frecuentemente en tareas complejas de múltiples pasos. La evaluación debe detectar estos fallos antes de producción.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. CARE: Ingeniería Colaborativa de Razonamiento de Agentes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Paper:&lt;/strong&gt; &lt;a href="https://arxiv.org/abs/2604.28043" rel="noopener noreferrer"&gt;arXiv:2604.28043&lt;/a&gt; (30 de Abril, 2026)&lt;/p&gt;

&lt;p&gt;Propone metodología con puertas de etapa con compuertas de verificación en cada etapa de desarrollo. Involucra expertos en la materia, desarrolladores y agentes auxiliares.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implicación:&lt;/strong&gt; La evaluación no es un paso final—debe ocurrir en cada etapa del desarrollo del agente.&lt;/p&gt;


&lt;h2&gt;
  
  
  Amazon Bedrock AgentCore: Evaluación Lista para Producción
&lt;/h2&gt;

&lt;p&gt;Si estás desplegando agentes en producción en AWS, &lt;strong&gt;Amazon Bedrock AgentCore&lt;/strong&gt; proporciona capacidades integradas de evaluación y observabilidad diseñadas específicamente para flujos de trabajo de agentes.&lt;/p&gt;
&lt;h3&gt;
  
  
  Evaluadores Integrados
&lt;/h3&gt;

&lt;p&gt;AgentCore ofrece &lt;strong&gt;13 evaluadores integrados&lt;/strong&gt; que usan LLMs como jueces:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Evaluador&lt;/th&gt;
&lt;th&gt;Lo Que Mide&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Builtin.Helpfulness&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Utilidad y claridad de la respuesta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Builtin.GoalSuccessRate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Si el agente logró el objetivo del usuario&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Builtin.Correctness&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exactitud factual de las respuestas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Builtin.ToolSelection&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Calidad de selección de herramientas/grupos de acción&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Observabilidad
&lt;/h3&gt;

&lt;p&gt;AgentCore proporciona captura de trazas y registro integrados para monitoreo de producción.&lt;/p&gt;
&lt;h3&gt;
  
  
  Cuándo Usar AgentCore vs Strands Evaluation
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Escenario&lt;/th&gt;
&lt;th&gt;Usar AgentCore&lt;/th&gt;
&lt;th&gt;Usar Strands Evals&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Agentes en producción en AWS Bedrock&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (compatible)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Evaluación CI/CD antes de despliegue&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comparación multi-modelo (GPT, Claude, Gemini)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lógica de evaluación personalizada (APIs externas, regex)&lt;/td&gt;
&lt;td&gt;✅ (Lambda)&lt;/td&gt;
&lt;td&gt;✅ (Python)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trazado sin configuración&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⚠️ (requiere hooks)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Recomendación:&lt;/strong&gt; Usa evaluadores integrados de AgentCore para monitoreo de producción y Strands Evals para pruebas pre-despliegue y comparaciones multi-framework.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aprende más:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/agents.html?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Guía de Usuario de Amazon Bedrock Agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/trace-events.html?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Observabilidad y Trazas de Agentes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/agents-test.html?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Probando Bedrock Agents&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Combinando LLM-as-Judge y Evaluación de Trayectorias
&lt;/h2&gt;

&lt;p&gt;La evaluación lista para producción usa &lt;strong&gt;ambas&lt;/strong&gt; técnicas:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Escenario&lt;/th&gt;
&lt;th&gt;Usar LLM-as-Judge&lt;/th&gt;
&lt;th&gt;Usar Eval de Trayectorias&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Agente devuelve respuesta incorrecta&lt;/td&gt;
&lt;td&gt;✅ Lo detecta&lt;/td&gt;
&lt;td&gt;✅ Puede detectar camino ilógico&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agente devuelve respuesta correcta por camino incorrecto&lt;/td&gt;
&lt;td&gt;❌ No lo detecta&lt;/td&gt;
&lt;td&gt;✅ Lo detecta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agente hace paso intermedio inseguro&lt;/td&gt;
&lt;td&gt;❌ No lo detecta&lt;/td&gt;
&lt;td&gt;✅ Lo detecta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Salida del agente no es profesional/grosera&lt;/td&gt;
&lt;td&gt;✅ Lo detecta&lt;/td&gt;
&lt;td&gt;❌ No lo detecta&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Recomendación:&lt;/strong&gt; Ejecuta ambos evaluadores en paralelo. Usa LLM-as-Judge para calidad de salida, evaluación de trayectorias para calidad de proceso.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_evals&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Experiment&lt;/span&gt;

&lt;span class="n"&gt;experiment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Experiment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;evaluators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;output_evaluator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;# Scores output quality
&lt;/span&gt;        &lt;span class="n"&gt;trajectory_evaluator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Scores process quality
&lt;/span&gt;    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;reports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;experiment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_evaluations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Access both scores
&lt;/span&gt;&lt;span class="n"&gt;output_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;overall_score&lt;/span&gt;
&lt;span class="n"&gt;trajectory_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;overall_score&lt;/span&gt;

&lt;span class="c1"&gt;# Combine scores (weighted average)
&lt;/span&gt;&lt;span class="n"&gt;final_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;output_score&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;trajectory_score&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Pruébalo Tú Mismo
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Prerrequisitos:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.10+&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OPENAI_API_KEY&lt;/code&gt; o acceso a AWS Bedrock&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Instalar:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;strands-agents strands-agents-evals boto3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Ejecutar las demos:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/elizabethfuentes12/how-to-evaluate-ai-agents-sample-for-aws.git
&lt;span class="nb"&gt;cd &lt;/span&gt;how-to-evaluate-ai-agents-sample-for-aws

&lt;span class="c"&gt;# LLM-as-Judge demo&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;evaluate-with-llm-judges/01-rubric-based-evaluation
jupyter notebook 01-rubric-based-evaluation.ipynb

&lt;span class="c"&gt;# Trajectory evaluation demo&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ../../evaluate-agent-trajectories/01-trajectory-scoring
jupyter notebook 01-trajectory-scoring.ipynb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Usuarios de AWS Bedrock:&lt;/strong&gt; Reemplaza &lt;code&gt;gpt-4o-mini&lt;/code&gt; con:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.models.bedrock&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BedrockModel&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BedrockModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us.anthropic.claude-sonnet-4-20250514-v1:0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Preguntas Frecuentes
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ¿Cómo elijo entre LLM-as-Judge y verificaciones determinísticas?
&lt;/h3&gt;

&lt;p&gt;Usa verificaciones determinísticas para &lt;strong&gt;requisitos estrictos&lt;/strong&gt; que pueden verificarse con coincidencia de cadenas o regex. Usa LLM-as-Judge para &lt;strong&gt;calidad subjetiva&lt;/strong&gt; que requiere entender el contexto.&lt;/p&gt;

&lt;p&gt;Ejemplo: "Debe mencionar un precio" → verificación determinística. "¿Es la respuesta útil?" → LLM-as-Judge.&lt;/p&gt;
&lt;h3&gt;
  
  
  ¿Qué pasa si mi agente usa más de 50 herramientas? ¿Escala la evaluación de trayectorias?
&lt;/h3&gt;

&lt;p&gt;Sí. La evaluación de trayectorias examina la &lt;strong&gt;secuencia&lt;/strong&gt; de llamadas a herramientas, no detalles individuales de cada herramienta. Una trayectoria de 50 llamadas sigue siendo una sola llamada API al LLM juez.&lt;/p&gt;

&lt;p&gt;Costo por evaluación: ~$0.001-0.003 (GPT-4o-mini) o $0.015-0.045 (Claude Sonnet).&lt;/p&gt;
&lt;h3&gt;
  
  
  ¿Puedo usar evaluación de trayectorias con LangGraph o AutoGen?
&lt;/h3&gt;

&lt;p&gt;Sí. La evaluación de trayectorias solo requiere la lista de llamadas a herramientas como entrada. Captúralas con &lt;code&gt;.get_graph().get_state()&lt;/code&gt; de LangGraph o el historial de mensajes de AutoGen, luego pásalas a &lt;code&gt;TrajectoryEvaluator&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  ¿Con qué frecuencia debo ejecutar evaluaciones?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD:&lt;/strong&gt; Ejecuta en cada commit con una suite pequeña de pruebas (10-20 casos)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Staging:&lt;/strong&gt; Ejecuta suite completa (100-500 casos) antes del despliegue a producción&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Producción:&lt;/strong&gt; Muestrea 1-5% del tráfico en vivo y evalúa de manera asíncrona&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Puntos Clave
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Las métricas binarias pierden 73% de graduaciones de calidad.&lt;/strong&gt; Usa puntuación continua (0.0-1.0) con rúbricas explícitas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;La evaluación de trayectorias detecta problemas que la evaluación de solo salida no capta:&lt;/strong&gt; llamadas duplicadas, herramientas irrelevantes, pasos inseguros.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;La escala 0-5 produce el alineamiento humano-LLM más fuerte&lt;/strong&gt; (0.89 correlación de Pearson). Mapea a 0.0-1.0 en código.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Los hooks de Strands capturan trayectorias automáticamente&lt;/strong&gt; mediante &lt;code&gt;AfterToolCallEvent&lt;/code&gt;. No se necesita instrumentación manual.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Combina ambas técnicas.&lt;/strong&gt; LLM-as-Judge para calidad de salida, evaluación de trayectorias para calidad de proceso.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  ¿Qué Sigue?
&lt;/h2&gt;

&lt;p&gt;Este post cubrió los fundamentos de evaluación - LLM-as-Judge y análisis de trayectorias. Estas técnicas forman la base para patrones de evaluación más profundos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Siguiente en esta serie:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Parte 3: Detectando Fallos de Agentes3-detecting-failures.md)&lt;/strong&gt; - Detección de alucinaciones sin ejemplos previos con métricas LSC, monitoreo de seguridad a nivel de trayectoria y barreras en tiempo real con hooks de Strands&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Parte 4: Métricas de Producción4-production-metrics.md)&lt;/strong&gt; - Compensaciones costo-calidad con índice compuesto KAMI, validación de corrección de herramientas y observabilidad de AWS Bedrock AgentCore&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Todos los ejemplos de código&lt;/strong&gt; están en el &lt;a href="https://github.com/elizabethfuentes12/how-to-evaluate-ai-agents-sample-for-aws" rel="noopener noreferrer"&gt;repositorio de GitHub&lt;/a&gt; con notebooks Jupyter ejecutables.&lt;/p&gt;


&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/abs/2603.00077" rel="noopener noreferrer"&gt;Autorubric: Unifying Rubric-based LLM Evaluation&lt;/a&gt; (Rao &amp;amp; Callison-Burch, Marzo 2026)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/abs/2602.21230" rel="noopener noreferrer"&gt;TRACE: Trajectory-Aware Comprehensive Evaluation&lt;/a&gt; (Febrero 2026)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/abs/2601.03444" rel="noopener noreferrer"&gt;Grading Scale paper&lt;/a&gt; (Enero 2026)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/abs/2604.27977" rel="noopener noreferrer"&gt;D3-Gym: Real-World Verifiable Environments&lt;/a&gt; (30 de Abril, 2026)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/abs/2604.27776" rel="noopener noreferrer"&gt;WindowsWorld: GUI Agent Benchmark&lt;/a&gt; (30 de Abril, 2026)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/abs/2604.28043" rel="noopener noreferrer"&gt;CARE: Collaborative Agent Reasoning&lt;/a&gt; (30 de Abril, 2026)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://strandsagents.com/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Agents Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/strands-agents-evals/" rel="noopener noreferrer"&gt;Strands Evaluation SDK&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Gracias!&lt;/p&gt;

&lt;p&gt;🇻🇪🇨🇱 &lt;a href="https://dev.to/elizabethfuentes12"&gt;Dev.to&lt;/a&gt; &lt;a href="https://www.linkedin.com/in/lizfue/" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt; &lt;a href="https://github.com/elizabethfuentes12/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; &lt;a href="https://twitter.com/elizabethfue12" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; &lt;a href="https://www.instagram.com/elifue.tech" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt; &lt;a href="https://www.youtube.com/channel/UCr0Gnc-t30m4xyrvsQpNp2Q" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag__user ltag__user__id__717518"&gt;
    &lt;a href="/elizabethfuentes12" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=150,height=150,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F717518%2Fb550b165-b8b9-405d-acfb-e5dc846765b0.png" alt="elizabethfuentes12 image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/elizabethfuentes12"&gt;Elizabeth Fuentes L&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/elizabethfuentes12"&gt;I help developers build production-ready AI applications through hands-on tutorials and open-source projects.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;






</description>
      <category>ai</category>
      <category>python</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>De DJ local a DJ con Spotify: tools externos y multi-agente</title>
      <dc:creator>Hazel Saenz</dc:creator>
      <pubDate>Thu, 21 May 2026 17:22:55 +0000</pubDate>
      <link>https://dev.to/aws-espanol/de-dj-local-a-dj-con-spotify-tools-externos-y-multi-agente-lfj</link>
      <guid>https://dev.to/aws-espanol/de-dj-local-a-dj-con-spotify-tools-externos-y-multi-agente-lfj</guid>
      <description>&lt;p&gt;&lt;em&gt;Tu agente ya sabe de música. Ahora va a controlar Spotify, crear playlists reales y delegar a sub-agentes especializados.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;En el &lt;a href="https://community.aws/content/2xFLnDpbRcNM0kkEBfBnIR/como-crear-un-agente-de-ia-desde-cero-open-source-local-y-gratis?trk=a1b2c3d4-spotify-article&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;artículo anterior&lt;/a&gt; construimos un agente DJ desde cero. Cuatro capas: un modelo que habla, herramientas para buscar en una biblioteca local, múltiples tools que el modelo orquesta solo, y memoria para recordar tus gustos entre sesiones.&lt;/p&gt;

&lt;p&gt;Todo local. Todo open source. Todo en tu laptop.&lt;/p&gt;

&lt;p&gt;Pero hay un problema.&lt;/p&gt;

&lt;p&gt;Tu biblioteca local tiene 30 canciones. Spotify tiene &lt;strong&gt;más de 100 millones&lt;/strong&gt;. Tu agente puede recomendar jazz para trabajar, pero no puede &lt;em&gt;reproducir&lt;/em&gt; esa canción en tu parlante. Puede armar una playlist en texto, pero no puede &lt;em&gt;crearla&lt;/em&gt; en tu cuenta.&lt;/p&gt;

&lt;p&gt;Un agente que solo consulta datos locales es útil. Un agente que &lt;strong&gt;controla servicios reales&lt;/strong&gt; es poderoso.&lt;/p&gt;

&lt;p&gt;Y aquí es donde se pone interesante: ¿qué pasa cuando un solo agente no es suficiente? ¿Cuando necesitas un especialista en emociones, otro en eventos, y otro en gustos personales? La respuesta es un patrón que suena complejo pero es elegante: &lt;strong&gt;agent as a tool&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;En este artículo vamos a construir las capas 5 y 6 del DJ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Capa 5&lt;/strong&gt;: Tools que se conectan a una API externa real (Spotify)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capa 6&lt;/strong&gt;: Un agente orquestador que delega a sub-agentes especializados&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El flujo completo se ve así: tú le hablas al agente, el agente razona con Bedrock, invoca tools que llaman a Spotify, y la música suena en tu dispositivo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv23yzzqx12a8zsq3v4nr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv23yzzqx12a8zsq3v4nr.png" alt="Integración del Agente DJ con Spotify, flujo de alto nivel" width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que necesitas para seguir este artículo
&lt;/h2&gt;

&lt;p&gt;No necesitas haber implementado las capas anteriores. Este artículo es autocontenido, puedes clonar el repo y correr las capas 5 y 6 directamente. Pero sí te recomiendo leer el &lt;a href="https://community.aws/content/2xFLnDpbRcNM0kkEBfBnIR/como-crear-un-agente-de-ia-desde-cero-open-source-local-y-gratis?trk=a1b2c3d4-spotify-article&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;artículo anterior&lt;/a&gt; para entender los conceptos de &lt;code&gt;@tool&lt;/code&gt;, agent loop y model-driven que usamos aquí.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/hsaenzG/OpenSource-agents-demo.git
&lt;span class="nb"&gt;cd &lt;/span&gt;OpenSource-agents-demo
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s1"&gt;'strands-agents'&lt;/span&gt; spotipy python-dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lo que necesitas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Python 3.10+&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Una cuenta de Spotify Developer&lt;/strong&gt;, para conectar con la API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS CLI configurado&lt;/strong&gt; con acceso a Amazon Bedrock, porque vamos a usar un modelo en la nube&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;¿Por qué Bedrock y no Ollama? En el artículo anterior usamos &lt;code&gt;llama3.1:8b&lt;/code&gt; corriendo local, y funciona bien con 1-2 tools. Pero las capas 5 y 6 tienen 7-8 herramientas cada una. Para tool-calling confiable con muchas herramientas, necesitas un modelo más capaz. Amazon Bedrock con Nova Pro resuelve eso, y como vimos antes, cambiar de proveedor es cambiar una línea de código gracias a la abstracción del SDK.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capa 5: El DJ controla Spotify
&lt;/h2&gt;

&lt;h3&gt;
  
  
  El concepto: tools que llaman APIs externas
&lt;/h3&gt;

&lt;p&gt;Hasta ahora, nuestros tools eran funciones puras. &lt;code&gt;buscar_canciones()&lt;/code&gt; filtra un JSON local. &lt;code&gt;analizar_energia()&lt;/code&gt; hace cálculos sobre datos en memoria. No salen de tu proceso de Python.&lt;/p&gt;

&lt;p&gt;Pero un &lt;code&gt;@tool&lt;/code&gt; puede hacer &lt;strong&gt;cualquier cosa&lt;/strong&gt; que Python pueda hacer. Incluyendo llamar APIs externas.&lt;/p&gt;

&lt;p&gt;La mecánica es la misma: decoras una función con &lt;code&gt;@tool&lt;/code&gt;, escribes un docstring claro, y el modelo decide cuándo invocarla. La diferencia es que dentro de esa función, en vez de filtrar un JSON, haces un HTTP request a un servicio externo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffmo8f1sypwyv36ihb5ma.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffmo8f1sypwyv36ihb5ma.png" alt="De tools locales a APIs externas" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fíjate: para el modelo, no hay diferencia entre un tool local y uno que llama a Spotify. El modelo ve el tool spec (nombre, descripción, parámetros) y decide si lo necesita. No sabe ni le importa si por dentro es un &lt;code&gt;json.load()&lt;/code&gt; o un &lt;code&gt;requests.get()&lt;/code&gt;. Esa es la elegancia del patrón.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configurar Spotify Developer
&lt;/h3&gt;

&lt;p&gt;Antes del código, necesitas credenciales. Crea una app en el &lt;a href="https://developer.spotify.com/dashboard" rel="noopener noreferrer"&gt;Spotify Developer Dashboard&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click en &lt;strong&gt;Create App&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Nombre: lo que quieras (ej: "DJ Agent")&lt;/li&gt;
&lt;li&gt;Redirect URI: &lt;code&gt;http://127.0.0.1:8000/callback&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Marca &lt;strong&gt;Web API&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Guarda el &lt;strong&gt;Client ID&lt;/strong&gt; y &lt;strong&gt;Client Secret&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Crea un archivo &lt;code&gt;.env&lt;/code&gt; en la raíz del proyecto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SPOTIFY_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;TU-CLIENT-ID
&lt;span class="nv"&gt;SPOTIFY_CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;TU-CLIENT-SECRET
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; La primera vez que ejecutes el script, se abrirá el navegador para autorizar la app con tu cuenta de Spotify. Después, el token se cachea automáticamente y no necesitas volver a autorizar.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  La conexión con Spotify
&lt;/h3&gt;

&lt;p&gt;Usamos &lt;a href="https://spotipy.readthedocs.io/" rel="noopener noreferrer"&gt;spotipy&lt;/a&gt;, una librería de Python que envuelve la Spotify Web API con OAuth2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BedrockModel&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;spotipy&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spotipy.oauth2&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SpotifyOAuth&lt;/span&gt;

&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;sp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spotipy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Spotify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;auth_manager&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;SpotifyOAuth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPOTIFY_CLIENT_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;client_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPOTIFY_CLIENT_SECRET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;redirect_uri&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://127.0.0.1:8000/callback&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;playlist-modify-public,playlist-modify-private,user-library-read,user-top-read,user-modify-playback-state,user-read-playback-state&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;usuario&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅ Conectado a Spotify como: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;display_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El &lt;code&gt;scope&lt;/code&gt; define qué permisos tiene tu app. Necesitamos leer tu biblioteca, crear playlists, y controlar la reproducción. Spotify usa OAuth2, el estándar de la industria para autorización delegada.&lt;/p&gt;

&lt;h3&gt;
  
  
  El primer tool externo: buscar en Spotify
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;buscar_en_spotify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Busca canciones en Spotify por nombre, artista o género.
    SIEMPRE usa esta herramienta cuando el usuario pregunte por canciones o artistas.
    Los resultados son datos REALES y actualizados de Spotify.

    Args:
        query: Texto de búsqueda (ej: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Shakira&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rock alternativo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bad Bunny último&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;)
        limite: Número máximo de resultados (default: 10)
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;resultados&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;track&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limite&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;tracks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resultados&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tracks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;tracks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No encontré canciones en Spotify para: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;canciones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tracks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;canciones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;titulo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;artista&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;artists&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;album&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;album&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uri&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uri&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;duracion_min&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;duration_ms&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canciones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ensure_ascii&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;¿Qué está pasando aquí? La estructura es idéntica a &lt;code&gt;buscar_canciones&lt;/code&gt; del artículo anterior. Misma firma: recibe parámetros, devuelve un string JSON. Mismo decorador &lt;code&gt;@tool&lt;/code&gt;. Mismo docstring descriptivo.&lt;/p&gt;

&lt;p&gt;La diferencia está &lt;strong&gt;dentro&lt;/strong&gt;: en vez de filtrar &lt;code&gt;BIBLIOTECA&lt;/code&gt;, llama a &lt;code&gt;sp.search()&lt;/code&gt; que hace un HTTP GET a &lt;code&gt;https://api.spotify.com/v1/search&lt;/code&gt;. El resultado viene con datos reales: URIs de Spotify, duración exacta, álbum, fecha de lanzamiento.&lt;/p&gt;

&lt;p&gt;Y fíjate en el &lt;code&gt;uri&lt;/code&gt;. Ese &lt;code&gt;spotify:track:xxx&lt;/code&gt; es lo que necesitamos para reproducir o agregar a playlists. Es el identificador único de cada canción en Spotify.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reproducir música: el agente toma acción real
&lt;/h3&gt;

&lt;p&gt;Aquí es donde el agente deja de ser un "recomendador" y se convierte en un &lt;strong&gt;controlador&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reproducir_cancion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nombre_cancion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;artista&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Reproduce una canción en el dispositivo activo de Spotify del usuario.
    Busca la canción por nombre y la reproduce automáticamente.

    Requiere que Spotify esté abierto en algún dispositivo (celular, computadora, etc.).

    Args:
        nombre_cancion: Nombre de la canción a reproducir
        artista: Nombre del artista (opcional, ayuda a encontrar la correcta)
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;dispositivos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;dispositivos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;devices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No hay dispositivos activos de Spotify. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Abre Spotify en tu celular o computadora e intenta de nuevo.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Buscar la canción
&lt;/span&gt;    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;track:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;nombre_cancion&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;artista&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; artist:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;artista&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;resultados&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;track&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tracks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resultados&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tracks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;tracks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No encontré &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;nombre_cancion&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; en Spotify.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;track&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tracks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;device_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dispositivos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;devices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_active&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="n"&gt;dispositivos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;devices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_playback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;device_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;device_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uris&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;track&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uri&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reproduciendo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cancion&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;track&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;artista&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;track&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;artists&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mensaje&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;▶️ Reproduciendo: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;track&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; — &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;track&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;artists&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;ensure_ascii&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto es un tool que &lt;strong&gt;modifica estado en el mundo real&lt;/strong&gt;. Cuando el modelo lo invoca, tu parlante empieza a sonar. No es un mock, no es una simulación. Es la API de Spotify ejecutando &lt;code&gt;PUT /v1/me/player/play&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Crear playlists reales
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;crear_playlist_en_spotify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nombre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;descripcion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;canciones_uris&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Crea una playlist en la cuenta de Spotify del usuario con las canciones indicadas.

    Args:
        nombre: Nombre de la playlist (ej: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Viernes de Rock&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Cena Romántica&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;)
        descripcion: Descripción breve de la playlist
        canciones_uris: Lista de URIs de Spotify o nombres de canciones
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;canciones_uris&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No me diste canciones para agregar a la playlist.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Resolver URIs — si no es una URI válida, buscar la canción
&lt;/span&gt;    &lt;span class="n"&gt;uris_validas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;canciones_uris&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;spotify:track:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;uris_validas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;track&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;tracks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tracks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tracks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;uris_validas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tracks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uri&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;uris_validas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No pude encontrar ninguna de las canciones en Spotify.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Crear la playlist
&lt;/span&gt;    &lt;span class="n"&gt;playlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user_playlist_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nombre&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;public&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descripcion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Agregar canciones (en batches de 100, límite de la API)
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uris_validas&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;playlist_add_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playlist&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;uris_validas&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mensaje&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Playlist &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;nombre&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; creada con &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uris_validas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; canciones&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;playlist&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;external_urls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;spotify&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;ensure_ascii&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fíjate en un detalle importante: el tool acepta tanto URIs (&lt;code&gt;spotify:track:xxx&lt;/code&gt;) como nombres de canciones. Si el modelo pasa nombres en vez de URIs, el tool los resuelve buscando en Spotify. Esto hace al tool más robusto, el modelo no necesita recordar URIs exactas entre llamadas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conocer al usuario: top artistas y canciones
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mis_top_artistas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;periodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;medium_term&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Obtiene los artistas más escuchados del usuario en Spotify.

    Args:
        periodo: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;short_term&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; (último mes), &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;medium_term&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; (6 meses), &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;long_term&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; (siempre)
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;resultados&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_user_top_artists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time_range&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;periodo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;artistas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;resultados&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;artistas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nombre&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;generos&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;genres&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;popularidad&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;popularity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;artistas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ensure_ascii&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mis_top_canciones&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;periodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;medium_term&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Obtiene las canciones más escuchadas del usuario en Spotify.

    Args:
        periodo: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;short_term&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; (último mes), &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;medium_term&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; (6 meses), &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;long_term&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; (siempre)
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;resultados&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_user_top_tracks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time_range&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;periodo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;canciones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;resultados&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;canciones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;titulo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;artista&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;artists&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uri&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uri&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canciones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ensure_ascii&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Estos tools le dan al agente algo que la memoria local no puede: &lt;strong&gt;datos reales de comportamiento&lt;/strong&gt;. No es lo que el usuario &lt;em&gt;dice&lt;/em&gt; que le gusta, es lo que &lt;em&gt;realmente escucha&lt;/em&gt;. Esa diferencia importa cuando armas recomendaciones.&lt;/p&gt;

&lt;h3&gt;
  
  
  El agente completo de la Capa 5
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;modelo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BedrockModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us.amazon.nova-pro-v1:0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;dj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;modelo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Eres un DJ personal conectado a Spotify. Controlas la música del usuario.

    REGLAS:
    1. SIEMPRE usa buscar_en_spotify antes de recomendar música.
    2. NUNCA inventes canciones, artistas o datos.
    3. Para reproducir: usa reproducir_cancion con el nombre.
    4. Para crear playlists: usa crear_playlist_en_spotify con las URIs.
    5. Basa TODAS tus respuestas en datos reales de las herramientas.

    Respondes en español, con onda y buen gusto musical.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;buscar_en_spotify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;crear_playlist_en_spotify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;reproducir_cancion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;mis_top_artistas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;mis_top_canciones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Conversación interactiva
&lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;mensaje&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🎵 Tú: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mensaje&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;salir&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;🎧 DJ: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;dj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mensaje&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora puedes decirle "ponme algo de Daft Punk" y tu parlante empieza a sonar. Puedes decirle "arma una playlist de jazz para cenar" y aparece en tu cuenta de Spotify. Datos reales, acciones reales.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; Necesitas una cuenta premium para tener acceso a la API de Spotify.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  El salto conceptual: de tool local a tool externo
&lt;/h2&gt;

&lt;p&gt;Hagamos una pausa para entender qué acaba de pasar.&lt;/p&gt;

&lt;p&gt;En la Capa 2 del artículo anterior, un tool era esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;buscar_canciones&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;genero&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Busca canciones en la biblioteca local.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;resultados&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;BIBLIOTECA&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;genero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;genero&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resultados&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En la Capa 5, un tool es esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;buscar_en_spotify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Busca canciones en Spotify.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;resultados&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;track&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resultados&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tracks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Misma interfaz. Misma mecánica. Diferente poder.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Para el agente, ambos son iguales: una función que recibe parámetros y devuelve un string. El modelo no sabe (ni necesita saber) si por dentro hay un filtro de lista o un HTTP request con OAuth2.&lt;/p&gt;

&lt;p&gt;Eso significa que puedes conectar tu agente a &lt;strong&gt;cualquier API&lt;/strong&gt; con el mismo patrón:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un &lt;code&gt;@tool&lt;/code&gt; que consulta tu base de datos de producción&lt;/li&gt;
&lt;li&gt;Un &lt;code&gt;@tool&lt;/code&gt; que envía emails via SendGrid&lt;/li&gt;
&lt;li&gt;Un &lt;code&gt;@tool&lt;/code&gt; que crea tickets en Jira&lt;/li&gt;
&lt;li&gt;Un &lt;code&gt;@tool&lt;/code&gt; que despliega código en AWS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El patrón es siempre el mismo: función Python + decorador &lt;code&gt;@tool&lt;/code&gt; + docstring claro = el modelo decide cuándo usarlo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capa 6: Multi-agente: el DJ delega
&lt;/h2&gt;

&lt;h3&gt;
  
  
  El problema: un agente que hace demasiado
&lt;/h3&gt;

&lt;p&gt;La Capa 5 funciona. Pero tiene un system prompt largo, 7-8 tools, y tiene que manejar situaciones muy diferentes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Recomiéndame algo de rock" → necesita conocer tus gustos&lt;/li&gt;
&lt;li&gt;"Arma una playlist de 3 horas para una fiesta" → necesita planificar duración y energía&lt;/li&gt;
&lt;li&gt;"Estoy triste, ponme algo" → necesita entender emociones y mapearlas a música&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Un solo agente &lt;em&gt;puede&lt;/em&gt; hacer todo eso. Pero entre más responsabilidades le das, más largo es el system prompt, más tools tiene que considerar, y más probable es que se confunda.&lt;/p&gt;

&lt;p&gt;La solución no es un agente más grande. Es &lt;strong&gt;varios agentes especializados&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  El concepto: Agent as a Tool
&lt;/h3&gt;

&lt;p&gt;Y aquí viene el patrón más elegante de este artículo.&lt;/p&gt;

&lt;p&gt;¿Recuerdas que un &lt;code&gt;@tool&lt;/code&gt; puede hacer cualquier cosa que Python pueda hacer? Incluyendo... &lt;strong&gt;invocar otro agente&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;consultar_dj_personal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mensaje&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Delega al DJ Personal: experto en gustos musicales y recomendaciones.

    Args:
        mensaje: El mensaje del usuario para el DJ Personal
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;respuesta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dj_personal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mensaje&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;respuesta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eso es todo. Un agente completo, con su propio system prompt, sus propios tools, su propia personalidad, expuesto como un &lt;code&gt;@tool&lt;/code&gt; de otro agente.&lt;/p&gt;

&lt;p&gt;El agente que tiene estos tools se llama &lt;strong&gt;orquestador&lt;/strong&gt;. No busca canciones, no crea playlists. Su único trabajo es entender qué necesita el usuario y decidir a cuál especialista delegarle.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98qb9aglxqgeyh41im4o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98qb9aglxqgeyh41im4o.png" alt="Agent as a Tool Multi-Agente" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Los sub-agentes especializados
&lt;/h3&gt;

&lt;p&gt;Cada sub-agente tiene un rol claro y un conjunto de tools específico:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;modelo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BedrockModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us.amazon.nova-pro-v1:0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;dj_personal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;modelo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Eres un DJ personal experto. Conoces los gustos del usuario.
    SIEMPRE usa buscar_en_spotify antes de recomendar. NUNCA inventes datos.
    Puedes consultar mis_top_artistas y mis_top_canciones para conocer al usuario.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;buscar_en_spotify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;crear_playlist_en_spotify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reproducir_cancion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;mis_top_artistas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mis_top_canciones&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;callback_handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Silenciar output
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;dj_eventos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;modelo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Eres un DJ profesional de eventos. Armas playlists para fiestas,
    bodas, cenas. Verificas que la duración cubra el evento completo.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;buscar_en_spotify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;crear_playlist_en_spotify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reproducir_cancion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;planificar_evento&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;callback_handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;dj_emocional&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;modelo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Eres un DJ empático especializado en emociones y música.
    Primero analizas la emoción, luego buscas música que la acompañe.
    Eres sensible y no juzgas.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;buscar_en_spotify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;crear_playlist_en_spotify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reproducir_cancion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;analizar_emocion&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;callback_handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fíjate en &lt;code&gt;callback_handler=None&lt;/code&gt;. Eso silencia el output de los sub-agentes, solo el orquestador habla con el usuario. Los sub-agentes trabajan en silencio y devuelven su resultado al orquestador.&lt;/p&gt;

&lt;p&gt;Cada sub-agente tiene:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un &lt;strong&gt;system prompt enfocado&lt;/strong&gt; en su especialidad&lt;/li&gt;
&lt;li&gt;Solo los &lt;strong&gt;tools que necesita&lt;/strong&gt; (no todos los disponibles)&lt;/li&gt;
&lt;li&gt;Una &lt;strong&gt;personalidad&lt;/strong&gt; diferente (el emocional es empático, el de eventos es profesional)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Los tools del orquestador: agentes como herramientas
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;consultar_dj_personal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mensaje&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Delega al DJ Personal: experto en gustos musicales y recomendaciones.
    Úsalo cuando el usuario quiera recomendaciones, descubrir música nueva,
    o pida algo basado en sus gustos.

    Args:
        mensaje: El mensaje completo del usuario para el DJ Personal
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;respuesta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dj_personal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mensaje&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;respuesta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;consultar_dj_eventos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mensaje&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Delega al DJ de Eventos: experto en armar playlists para ocasiones específicas.
    Úsalo cuando el usuario mencione un evento, fiesta, boda, cena,
    o pida una playlist con duración específica.

    Args:
        mensaje: El mensaje completo del usuario para el DJ de Eventos
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;respuesta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dj_eventos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mensaje&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;respuesta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;consultar_dj_emocional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mensaje&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Delega al DJ Emocional: experto en música y estados de ánimo.
    Úsalo cuando el usuario exprese cómo se siente o quiera música
    para acompañar un estado de ánimo.

    Args:
        mensaje: El mensaje completo del usuario para el DJ Emocional
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;respuesta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dj_emocional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mensaje&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;respuesta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El docstring de cada tool-agente es clave. Le dice al orquestador &lt;strong&gt;cuándo&lt;/strong&gt; usar cada uno. "Cuando el usuario exprese cómo se siente" → DJ Emocional. "Cuando mencione un evento" → DJ Eventos. El modelo del orquestador lee estos docstrings y decide a quién delegar.&lt;/p&gt;

&lt;h3&gt;
  
  
  El orquestador: el punto de entrada
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;orquestador&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;modelo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Eres el DJ principal. Tu trabajo es entender qué necesita el usuario
    y delegarlo al sub-agente especializado correcto.

    Tienes 3 DJs especializados:
    1. consultar_dj_personal: Recomendaciones basadas en gustos
    2. consultar_dj_eventos: Playlists para eventos con duración específica
    3. consultar_dj_emocional: Música para estados de ánimo

    REGLAS:
    - SIEMPRE delega al sub-agente apropiado.
    - Pasa el mensaje COMPLETO del usuario.
    - Si no estás seguro, usa consultar_dj_personal como default.
    - Presenta la respuesta del sub-agente de forma natural.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;consultar_dj_personal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;consultar_dj_eventos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;consultar_dj_emocional&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;reproducir_cancion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reproducir_playlist&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El orquestador también tiene &lt;code&gt;reproducir_cancion&lt;/code&gt; y &lt;code&gt;reproducir_playlist&lt;/code&gt; directamente. Si el usuario dice "ponme Bohemian Rhapsody", no necesita delegar a nadie, puede reproducir directamente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cómo funciona en la práctica
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🎵 Tú: Estoy triste, ponme algo suave

🎧 DJ: [internamente: invoca consultar_dj_emocional("Estoy triste, ponme algo suave")]
       [DJ Emocional: invoca analizar_emocion("triste")]
       [DJ Emocional: invoca buscar_en_spotify("indie folk acoustic")]
       [DJ Emocional: invoca reproducir_cancion("Skinny Love", "Bon Iver")]

       Entiendo. Te puse "Skinny Love" de Bon Iver — indie folk suave,
       perfecto para este momento. Si quieres, puedo armar una playlist
       completa con ese mood.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El usuario habla con &lt;strong&gt;un solo agente&lt;/strong&gt;. No sabe que detrás hay tres especialistas. No necesita elegir un menú. El orquestador decide, delega, y presenta la respuesta como si fuera suya.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Por qué no un solo agente con todos los tools?
&lt;/h3&gt;

&lt;p&gt;Podrías meter todos los tools en un solo agente con un system prompt gigante. Funcionaría... a veces. Pero:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspecto&lt;/th&gt;
&lt;th&gt;Un solo agente&lt;/th&gt;
&lt;th&gt;Multi-agente&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;System prompt&lt;/td&gt;
&lt;td&gt;Largo, genérico&lt;/td&gt;
&lt;td&gt;Corto, enfocado por especialista&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tools por agente&lt;/td&gt;
&lt;td&gt;10+ (confunde al modelo)&lt;/td&gt;
&lt;td&gt;4-5 por especialista&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personalidad&lt;/td&gt;
&lt;td&gt;Una sola para todo&lt;/td&gt;
&lt;td&gt;Diferente por contexto&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debugging&lt;/td&gt;
&lt;td&gt;Difícil saber qué falló&lt;/td&gt;
&lt;td&gt;Sabes exactamente qué agente falló&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Escalabilidad&lt;/td&gt;
&lt;td&gt;Agregar tools degrada calidad&lt;/td&gt;
&lt;td&gt;Agregas un nuevo sub-agente&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;El patrón multi-agente no es sobre complejidad. Es sobre &lt;strong&gt;separación de responsabilidades&lt;/strong&gt;. El mismo principio que usas en microservicios, aplicado a agentes.&lt;/p&gt;

&lt;h2&gt;
  
  
  El código completo
&lt;/h2&gt;

&lt;p&gt;El código completo de ambas capas está en el repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Capa 5 — Spotify&lt;/span&gt;
python capa5_spotify.py

&lt;span class="c"&gt;# Capa 6 — Multi-agente&lt;/span&gt;
python capa6_multi_agente.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repo: &lt;a href="https://github.com/hsaenzG/OpenSource-agents-demo" rel="noopener noreferrer"&gt;github.com/hsaenzG/OpenSource-agents-demo&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; Sin Spotify configurado, las capas 5-6 funcionan con la biblioteca local como fallback. Verás un aviso &lt;code&gt;⚠️ Spotify no disponible&lt;/code&gt; pero el agente seguirá respondiendo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Lo que aprendiste
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Un &lt;code&gt;@tool&lt;/code&gt; puede hacer &lt;strong&gt;cualquier cosa&lt;/strong&gt; que Python pueda hacer, incluyendo llamar APIs externas con OAuth2, crear recursos en servicios reales, y controlar dispositivos&lt;/li&gt;
&lt;li&gt;Para el modelo, no hay diferencia entre un tool local y uno externo. La interfaz es la misma: función + decorador + docstring&lt;/li&gt;
&lt;li&gt;El patrón &lt;strong&gt;agent as a tool&lt;/strong&gt; permite crear sistemas multi-agente donde un orquestador delega a especialistas&lt;/li&gt;
&lt;li&gt;Los sub-agentes se silencian con &lt;code&gt;callback_handler=None&lt;/code&gt;, solo el orquestador habla con el usuario&lt;/li&gt;
&lt;li&gt;Separar responsabilidades en agentes especializados mejora la calidad de las respuestas y facilita el debugging&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;Con 6 capas, tienes un agente que habla, busca, razona, recuerda, controla servicios externos, y delega a especialistas. Todo con Python, &lt;a href="https://strandsagents.com/?trk=a1b2c3d4-spotify-article&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Agents&lt;/a&gt; y APIs abiertas.&lt;/p&gt;

&lt;p&gt;Si quieres ir más allá:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://strandsagents.com/?trk=a1b2c3d4-spotify-article&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Documentación de Strands Agents&lt;/a&gt; — guías, ejemplos, y API reference&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/concepts/multi-agent/?trk=a1b2c3d4-spotify-article&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Multi-agent patterns en Strands&lt;/a&gt; — swarms, graphs, y más patrones de orquestación&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hsaenzG/OpenSource-agents-demo" rel="noopener noreferrer"&gt;Repo del demo&lt;/a&gt; — el código completo de las 6 capas&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://spotipy.readthedocs.io/" rel="noopener noreferrer"&gt;Spotipy&lt;/a&gt; — la librería de Python para Spotify&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.spotify.com/dashboard" rel="noopener noreferrer"&gt;Spotify Developer Dashboard&lt;/a&gt; — para crear tu app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;¿Te resultó útil este artículo?&lt;/strong&gt; Compártelo con tu equipo o déjame saber en los comentarios qué API te gustaría conectar a tu agente. Y si ya estás construyendo agentes multi-agente o conectando APIs externas, me encantaría escuchar tu experiencia.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>genai</category>
      <category>agents</category>
      <category>espanol</category>
    </item>
    <item>
      <title>Solucionar Timeouts de MCP: Patrón HandleId Asíncrono</title>
      <dc:creator>Elizabeth Fuentes L</dc:creator>
      <pubDate>Thu, 21 May 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/aws-espanol/solucionar-timeouts-de-mcp-patron-handleid-asincrono-3c4m</link>
      <guid>https://dev.to/aws-espanol/solucionar-timeouts-de-mcp-patron-handleid-asincrono-3c4m</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Las herramientas MCP congelan a los agentes de IA cuando las APIs externas son lentas, causando errores 424. El patrón handleId asíncrono devuelve inmediatamente un ID de trabajo y consulta los resultados sin bloquear.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;El timeout de herramienta MCP&lt;/strong&gt; ocurre cuando un agente de IA llama a una herramienta del Protocolo de Contexto de Modelo (MCP) que depende de una API externa lenta. La herramienta bloquea al agente indefinidamente en lugar de devolver un error. El resultado es un error 424 (Failed Dependency) o un flujo de trabajo congelado sin retroalimentación al usuario. Este post muestra el problema con escenarios reales y cómo el patrón handleId asíncrono proporciona respuestas inmediatas.&lt;/p&gt;

&lt;p&gt;Esta demo usa &lt;a href="https://strandsagents.com/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Agents&lt;/a&gt; con &lt;a href="https://strandsagents.com/docs/user-guide/concepts/tools/mcp-tools/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;MCP (Model Context Protocol)&lt;/a&gt;. El patrón asíncrono es independiente del framework y aplica a cualquier agente que llame APIs externas a través de MCP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Código funcional:&lt;/strong&gt; &lt;a href="https://github.com/aws-samples/sample-why-agents-fail/tree/main/stop-ai-agents-wasting-tokens/02-mcp-timeout-demo" rel="noopener noreferrer"&gt;github.com/aws-samples/sample-why-agents-fail&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Serie: Por Qué Fallan los Agentes de IA
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/aws-samples/sample-why-agents-fail/tree/main/stop-ai-agents-wasting-tokens/01-context-overflow-demo" rel="noopener noreferrer"&gt;Desbordamiento de Ventana de Contexto&lt;/a&gt;&lt;/strong&gt; — Patrón de Puntero de Memoria para datos grandes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Herramientas MCP Que Nunca Responden&lt;/strong&gt; (este post) — Patrón asíncrono para APIs externas lentas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/aws-samples/sample-why-agents-fail/tree/main/stop-ai-agents-wasting-tokens/03-reasoning-loops-demo" rel="noopener noreferrer"&gt;Loops de Razonamiento en Agentes de IA&lt;/a&gt;&lt;/strong&gt; — Detectar y bloquear llamadas repetidas a herramientas&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  El Problema: Herramientas MCP Que Nunca Responden
&lt;/h2&gt;

&lt;p&gt;El Protocolo de Contexto de Modelo (MCP) permite a los agentes de IA llamar herramientas externas. Pero cuando esas herramientas dependen de APIs lentas, todo el flujo de trabajo del agente se congela. El agente espera. El usuario espera. No pasa nada.&lt;/p&gt;

&lt;p&gt;Una observación comunitaria de Octopus (&lt;a href="https://octopus.com/blog/mcp-timeout-retry" rel="noopener noreferrer"&gt;Resilient AI Agents With MCP, 2025&lt;/a&gt;) identifica el problema central: a medida que aumentan las integraciones de sistemas externos, también aumenta la probabilidad de fallo. Los sistemas dejan de estar disponibles, responden lentamente o devuelven errores. Los agentes no tienen una estrategia incorporada para manejar esto.&lt;/p&gt;

&lt;p&gt;Los reportes de OpenAI Community confirman el impacto del mundo real:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://community.openai.com/t/call-remote-mcp-server-tool-timed-out-resulting-in-error-424/1364167" rel="noopener noreferrer"&gt;Errores 424&lt;/a&gt; cuando las herramientas MCP tardan demasiado&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://community.openai.com/t/mcp-tool-hangs-indefinitely/1369341" rel="noopener noreferrer"&gt;Estados sin respuesta&lt;/a&gt; donde las solicitudes ni tienen éxito ni fallan&lt;/li&gt;
&lt;li&gt;Herramientas que pasan la validación de handshake pero hacen timeout durante la ejecución&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Por Qué Sucede Esto
&lt;/h2&gt;

&lt;p&gt;MCP espera que las herramientas respondan rápidamente. Cuando una herramienta llama a una API externa lenta.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhpqoi10dvwofqbm2wu9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhpqoi10dvwofqbm2wu9.jpg" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;El protocolo MCP tiene expectativas de timeout implícitas. Si la herramienta no responde dentro de ~7-10 segundos, la conexión puede caerse con un error 424 (Failed Dependency). El agente recibe un error en lugar de datos, y el usuario no obtiene una respuesta útil.&lt;/p&gt;

&lt;p&gt;Tres modos de fallo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;API lenta&lt;/strong&gt; — La herramienta espera 15+ segundos, UX pobre pero eventualmente responde&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API fallida&lt;/strong&gt; — Servicio externo no disponible, error 424 después del timeout&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Estado sin respuesta&lt;/strong&gt; — Solicitud aceptada pero nunca devuelve, requiere reinicio de sesión&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  La Demo: Simulando Escenarios Reales de Timeout
&lt;/h2&gt;

&lt;p&gt;Construimos un servidor MCP que simula estos escenarios del mundo real:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mcp.server&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastMCP&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;

&lt;span class="c1"&gt;# FastMCP es un framework ligero de servidor MCP — las herramientas se registran con @mcp.tool()
&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastMCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Timeout Demo Server&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Línea base: responde en 1s, bien dentro del umbral de timeout implícito de MCP (~7-10s)
&lt;/span&gt;&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fast API - responds in 1 second&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fast_api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fast result for: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Caso problema: retraso de 15s excede timeout de MCP — el agente se congela esperando esto
&lt;/span&gt;&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Slow API - responds in 15 seconds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;slow_api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Simula un servicio externo lento (pipeline de datos, trabajo por lotes)
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Slow result for: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Caso de fallo: retraso de 7s activa el timeout, luego lanza Failed Dependency (424)
&lt;/span&gt;&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failing API - returns 424 after delay&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;failing_api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed Dependency: External service unavailable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  La Solución Async HandleId
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpgtgq97p79untwdxl1ec.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpgtgq97p79untwdxl1ec.png" alt="Comparación de llamada a herramienta MCP síncrona bloqueada por 17.2 segundos versus patrón handleId asíncrono completando en 1.7 segundos" width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En lugar de esperar operaciones lentas, devuelve inmediatamente con un ID de seguimiento:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="c1"&gt;# Almacén de trabajos en memoria: mapea job_id → {status, query, result}
# Para producción, reemplazar con un almacén persistente (Redis, DynamoDB) para durabilidad entre reinicios
&lt;/span&gt;&lt;span class="n"&gt;JOBS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c1"&gt;# El patrón handleId: devuelve un ID de seguimiento inmediatamente en lugar de bloquear
&lt;/span&gt;&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Start a long-running job, returns immediately with job ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_async_job&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;job_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;())[:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# ID corto que el LLM puede pasar en llamadas de seguimiento
&lt;/span&gt;    &lt;span class="n"&gt;JOBS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;processing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Fire-and-forget: el trabajo lento se ejecuta en segundo plano, la herramienta devuelve antes de que termine
&lt;/span&gt;    &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;do_work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;# El agente recibe esto en &amp;lt; 1s — sin timeout, sin UI congelada
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Job started: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Use check_job_status to poll for results.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Endpoint de consulta: el agente llama a esto repetidamente hasta que el estado es "completed"
&lt;/span&gt;&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Check status of a running job&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_job_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JOBS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Job &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;completed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;COMPLETED: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# Devuelve el resultado real al agente
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PROCESSING: Job &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; still running&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# El agente consulta de nuevo después de una breve espera
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Resultados de la Demo
&lt;/h2&gt;

&lt;p&gt;Probamos los cuatro escenarios con un Strands Agent conectado al servidor MCP:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Escenario&lt;/th&gt;
&lt;th&gt;Tiempo de Respuesta&lt;/th&gt;
&lt;th&gt;Experiencia de Usuario&lt;/th&gt;
&lt;th&gt;Hallazgo de Investigación&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Fast API&lt;/strong&gt; (retraso 1s)&lt;/td&gt;
&lt;td&gt;3.2s total&lt;/td&gt;
&lt;td&gt;✅ Buen UX&lt;/td&gt;
&lt;td&gt;Línea base&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Slow API&lt;/strong&gt; (retraso 15s)&lt;/td&gt;
&lt;td&gt;17.8s total&lt;/td&gt;
&lt;td&gt;❌ UX pobre — agente espera&lt;/td&gt;
&lt;td&gt;Octopus: "el agente espera indefinidamente"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Failing API&lt;/strong&gt; (424)&lt;/td&gt;
&lt;td&gt;7.7s total&lt;/td&gt;
&lt;td&gt;❌ Error después de esperar&lt;/td&gt;
&lt;td&gt;OpenAI Community: errores 424&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Patrón asíncrono&lt;/strong&gt; (handleId)&lt;/td&gt;
&lt;td&gt;3.7s total&lt;/td&gt;
&lt;td&gt;✅ Respuesta inmediata&lt;/td&gt;
&lt;td&gt;Solución: "responder ASAP con handleId"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo65kxfg90v71xspu1lfu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo65kxfg90v71xspu1lfu.png" alt="Gráfico de barras comparando tiempos de respuesta de herramientas MCP en escenarios de fast API, slow API, failing API y async handleId" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;El patrón asíncrono transforma una espera de 17.8s en una respuesta inmediata de 3.7s. El agente le dice al usuario "trabajo iniciado" y puede verificar el estado más tarde, sin UI congelada y sin errores de timeout.&lt;/p&gt;
&lt;h2&gt;
  
  
  ¿Por Qué Strands Agents para Integración MCP?
&lt;/h2&gt;

&lt;p&gt;El &lt;a href="https://strandsagents.com/docs/user-guide/concepts/tools/mcp-tools/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;&lt;code&gt;MCPClient&lt;/code&gt;&lt;/a&gt; se conecta a cualquier servidor MCP en dos líneas. El agente descubre herramientas disponibles en tiempo de ejecución a través de &lt;code&gt;list_tools_sync()&lt;/code&gt;, así que no mantienes una lista de herramientas codificada. Cuando el servidor MCP implementa el patrón handleId asíncrono, el agente consulta automáticamente sin código de orquestación adicional.&lt;/p&gt;

&lt;p&gt;Strands soporta múltiples &lt;a href="https://strandsagents.com/docs/user-guide/concepts/model-providers/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;proveedores de modelos&lt;/a&gt; (OpenAI, Amazon Bedrock, Anthropic, Ollama). Los patrones de timeout de MCP mostrados aquí funcionan idénticamente en todos los proveedores.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cuándo Usar Cada Patrón
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Llamada directa&lt;/strong&gt; (herramientas rápidas &amp;lt; 5s):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Búsquedas, cálculos, llamadas pequeñas a API&lt;/li&gt;
&lt;li&gt;Sin riesgo de timeout&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;HandleId asíncrono&lt;/strong&gt; (herramientas lentas &amp;gt; 5s):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Llamadas a API externas con latencia impredecible&lt;/li&gt;
&lt;li&gt;Procesamiento de datos, generación de reportes&lt;/li&gt;
&lt;li&gt;Cualquier operación que pueda exceder el timeout de MCP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Reintento con backoff&lt;/strong&gt; (fallos intermitentes):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Servicios que ocasionalmente fallan pero se recuperan&lt;/li&gt;
&lt;li&gt;Operaciones dependientes de red&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Pruébalo Tú Mismo
&lt;/h2&gt;

&lt;p&gt;Necesitas &lt;a href="https://python.org/downloads" rel="noopener noreferrer"&gt;Python 3.9+&lt;/a&gt;, &lt;a href="https://docs.astral.sh/uv/" rel="noopener noreferrer"&gt;uv&lt;/a&gt;, y una &lt;a href="https://platform.openai.com/api-keys" rel="noopener noreferrer"&gt;clave API de OpenAI&lt;/a&gt;. El servidor MCP se ejecuta localmente como un subproceso, así que no se necesitan servicios externos.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/aws-samples/sample-why-agents-fail
&lt;span class="nb"&gt;cd &lt;/span&gt;sample-why-agents-fail/stop-ai-agents-wasting-tokens/02-mcp-timeout-demo
uv venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; uv pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"tu-clave-aquí"&lt;/span&gt;

uv run python test_mcp_timeout.py   &lt;span class="c"&gt;# Ejecuta los 4 escenarios&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;O abre &lt;code&gt;test_mcp_timeout.ipynb&lt;/code&gt; en &lt;a href="https://jupyter.org/" rel="noopener noreferrer"&gt;Jupyter&lt;/a&gt;, &lt;a href="https://jupyterlab.readthedocs.io/" rel="noopener noreferrer"&gt;JupyterLab&lt;/a&gt;, VS Code, o tu entorno de notebook preferido.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusiones Clave
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Las herramientas MCP hacen timeout silenciosamente&lt;/strong&gt; — errores 424 sin recuperación&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Las APIs lentas congelan todo el agente&lt;/strong&gt; — espera de 17.8s sin retroalimentación&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;El patrón handleId asíncrono lo soluciona&lt;/strong&gt; — respuesta inmediata, consultar por resultados&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diseña para el fallo&lt;/strong&gt; — cada llamada externa puede hacer timeout, planifica en consecuencia&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Preguntas Frecuentes
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ¿Qué causa errores 424 en llamadas a herramientas MCP?
&lt;/h3&gt;

&lt;p&gt;Un error 424 (Failed Dependency) ocurre cuando una herramienta MCP tarda más que el umbral de timeout implícito (típicamente 7-10 segundos) en responder. El protocolo MCP espera que las herramientas devuelvan resultados rápidamente. Cuando una API externa bloquea la herramienta más allá de este umbral, la conexión se cae y el agente recibe un error 424 en lugar de datos.&lt;/p&gt;
&lt;h3&gt;
  
  
  ¿Cuándo debo usar el patrón handleId asíncrono en lugar de una llamada directa a herramienta MCP?
&lt;/h3&gt;

&lt;p&gt;Usa el patrón handleId asíncrono para cualquier herramienta que llame a una API externa con latencia impredecible: procesamiento de datos, generación de reportes, llamadas a servicios de terceros, o cualquier operación que pueda exceder 5 segundos. Para búsquedas rápidas, cálculos y llamadas pequeñas a API por debajo de 5 segundos, las llamadas directas funcionan bien.&lt;/p&gt;
&lt;h3&gt;
  
  
  ¿El patrón handleId asíncrono funciona con cualquier servidor MCP, no solo Strands?
&lt;/h3&gt;

&lt;p&gt;Sí. El patrón handleId asíncrono es un patrón de diseño de servidor MCP, no una característica de framework. Cualquier agente compatible con MCP puede llamar herramientas &lt;code&gt;start_long_job&lt;/code&gt; y &lt;code&gt;check_job_status&lt;/code&gt;. El patrón funciona con OpenAI Agents, integraciones MCP de LangChain, y cualquier cliente que soporte el Protocolo de Contexto de Modelo.&lt;/p&gt;
&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Investigación
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://octopus.com/blog/mcp-timeout-retry" rel="noopener noreferrer"&gt;Resilient AI Agents With MCP: Timeout And Retry Strategies&lt;/a&gt; — Octopus blog (observación comunitaria), May 2025&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://community.openai.com/t/call-remote-mcp-server-tool-timed-out-resulting-in-error-424/1364167" rel="noopener noreferrer"&gt;Call remote MCP server tool timed out, error 424&lt;/a&gt; — OpenAI Community (foro comunitario)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://community.openai.com/t/handling-timeouts-with-long-running-mcp-connectors-vertex-ai-agent/1369341" rel="noopener noreferrer"&gt;Handling Timeouts with Long-Running MCP Connectors&lt;/a&gt; — OpenAI Community (foro comunitario), Dec 2025&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.arsturn.com/blog/no-more-timeouts-how-to-build-long-running-mcp-tools-that-actually-finish-the-job" rel="noopener noreferrer"&gt;Build Timeout-Proof MCP Tools&lt;/a&gt; — Arsturn (observación comunitaria)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Implementación
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/concepts/tools/mcp-tools/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands MCP Tools&lt;/a&gt; — Connect any MCP server&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/concepts/model-providers/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Model Providers&lt;/a&gt; — Swap to Amazon Bedrock, Anthropic, Ollama&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Gracias!&lt;/p&gt;

&lt;p&gt;🇻🇪🇨🇱 &lt;a href="https://dev.to/elizabethfuentes12"&gt;Dev.to&lt;/a&gt; &lt;a href="https://www.linkedin.com/in/lizfue/" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt; &lt;a href="https://github.com/elizabethfuentes12/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; &lt;a href="https://twitter.com/elizabethfue12" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; &lt;a href="https://www.instagram.com/elifue.tech" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt; &lt;a href="https://www.youtube.com/channel/UCr0Gnc-t30m4xyrvsQpNp2Q" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag__user ltag__user__id__717518"&gt;
    &lt;a href="/elizabethfuentes12" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=150,height=150,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F717518%2Fb550b165-b8b9-405d-acfb-e5dc846765b0.png" alt="elizabethfuentes12 image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/elizabethfuentes12"&gt;Elizabeth Fuentes L&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/elizabethfuentes12"&gt;I help developers build production-ready AI applications through hands-on tutorials and open-source projects.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>ai</category>
      <category>tutorial</category>
      <category>python</category>
      <category>programming</category>
    </item>
    <item>
      <title>Migrando zig-lambda-runtime de Zig 0.12 a 0.16 en AWS Lambda</title>
      <dc:creator>olcortesb</dc:creator>
      <pubDate>Mon, 18 May 2026 09:39:16 +0000</pubDate>
      <link>https://dev.to/aws-espanol/migrando-zig-lambda-runtime-de-zig-012-a-016-en-aws-lambda-5b2k</link>
      <guid>https://dev.to/aws-espanol/migrando-zig-lambda-runtime-de-zig-012-a-016-en-aws-lambda-5b2k</guid>
      <description>&lt;p&gt;No soy experto en Zig, pero como me he divertido en esta prueba de concepto! Si han leído alguna vez algún artículo saben que me interesa es probar cómo funciona Lambda en cualquier entorno posible, en esta oportunidad, con un lenguaje de bajo nivel como Zig dentro de AWS Lambda, aprovechando el runtime &lt;code&gt;provided.al2023&lt;/code&gt; y la arquitectura ARM64. Este artículo documenta la migración del fork &lt;a href="https://github.com/softprops/zig-lambda-runtime" rel="noopener noreferrer"&gt;zig-lambda-runtime&lt;/a&gt; que originalmente fue desarrollado por &lt;a href="https://github.com/softprops" rel="noopener noreferrer"&gt;softprops&lt;/a&gt; de Zig 0.12 a 0.16.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué Zig en Lambda
&lt;/h2&gt;

&lt;p&gt;Por estos números...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cold start promedio: ~11ms&lt;/li&gt;
&lt;li&gt;Memoria en promedio: ~10MB&lt;/li&gt;
&lt;li&gt;Duración promedio: 1-2ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Zig compila a un binario estático sin dependencias externas. No necesitas capas, no necesitas Docker. Solo un binario llamado &lt;code&gt;bootstrap&lt;/code&gt; dentro de un zip.&lt;/p&gt;

&lt;p&gt;Aunque necesito hacer más pruebas y compararlo, por ejemplo, con Rust, que es un lenguaje con el mismo concepto de no recolector de basura y alta performance, la verdad que promete que puede ser interesante.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cambios principales en la migración
&lt;/h2&gt;

&lt;p&gt;Y mientras aprendo un poco de Zig aquí los cambios que he hecho, que puede haber más y mejores pero la intención era hacerlo funcionar!&lt;/p&gt;

&lt;h3&gt;
  
  
  build.zig.zon
&lt;/h3&gt;

&lt;p&gt;En 0.12 el nombre del paquete era un string. En 0.16 es un enum literal y se requiere un &lt;code&gt;fingerprint&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="c"&gt;// 0.12&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"lambda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;minimum_zig_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.12.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="c"&gt;// 0.16&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;minimum_zig_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.16.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;fingerprint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0xd39dff828a4fab32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El fingerprint se obtiene omitiendo el campo y dejando que Zig te diga el valor correcto en el primer build.&lt;/p&gt;

&lt;h3&gt;
  
  
  build.zig
&lt;/h3&gt;

&lt;p&gt;La API de build cambió. Antes se usaba &lt;code&gt;createModule&lt;/code&gt; + &lt;code&gt;modules.put&lt;/code&gt;, ahora es &lt;code&gt;addModule&lt;/code&gt; directo. Los ejecutables usan &lt;code&gt;root_module&lt;/code&gt; en vez de &lt;code&gt;root_source_file&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="c"&gt;// 0.16&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;lambda_module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"lambda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;root_source_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"src/lambda.zig"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;link_libc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;exe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addExecutable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"bootstrap"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;root_module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;root_source_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;src&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;optimize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;optimize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;exe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;root_module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addImport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"lambda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lambda_module&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  HTTP Client
&lt;/h3&gt;

&lt;p&gt;El cambio más grande. En 0.12 existía &lt;code&gt;client.fetch()&lt;/code&gt; como método de conveniencia. En 0.16 el ciclo de vida del request es explícito para el polling de invocaciones, aunque &lt;code&gt;fetch&lt;/code&gt; sigue disponible para requests simples como enviar respuestas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="c"&gt;// Polling de invocaciones (explícito)&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;threaded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Threaded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{});&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;threaded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;io&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;allocator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;io&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deinit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;InvalidNextUri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="py"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{});&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deinit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendBodiless&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;header_buf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;u8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receiveHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;header_buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;.&lt;/span&gt;&lt;span class="p"&gt;{});&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allocRemaining&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;unlimited&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="c"&gt;// Enviar respuesta (fetch sigue funcionando)&lt;/span&gt;
&lt;span class="mi"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GeneralPurposeAllocator
&lt;/h3&gt;

&lt;p&gt;Cambio menor en la sintaxis de inicialización:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="c"&gt;// 0.12&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;gpa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GeneralPurposeAllocator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{}){};&lt;/span&gt;

&lt;span class="c"&gt;// 0.16&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;gpa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GeneralPurposeAllocator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Env y manejo de errores
&lt;/h3&gt;

&lt;p&gt;Se reemplazó el &lt;code&gt;.?&lt;/code&gt; (que hace panic si es null) por &lt;code&gt;orelse&lt;/code&gt; para manejo graceful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="c"&gt;// 0.12 - panic si no existe&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;runtime_api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;posix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AWS_LAMBDA_RUNTIME_API"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c"&gt;// 0.16 - retorna error&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;runtime_api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AWS_LAMBDA_RUNTIME_API"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;orelse&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;MissingLambdaEnv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploy
&lt;/h2&gt;

&lt;p&gt;El deploy usa SAM con un template mínimo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Function&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;provided.al2023&lt;/span&gt;
      &lt;span class="na"&gt;Architectures&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;arm64&lt;/span&gt;
      &lt;span class="na"&gt;MemorySize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;128&lt;/span&gt;
      &lt;span class="na"&gt;CodeUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;../lambda.zip"&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;handler&lt;/span&gt;
      &lt;span class="na"&gt;FunctionUrlConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;AuthType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NONE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El flujo completo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build para ARM64 Linux&lt;/span&gt;
zig build apigw-example &lt;span class="nt"&gt;-Dtarget&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;aarch64-linux &lt;span class="nt"&gt;--summary&lt;/span&gt; all

&lt;span class="c"&gt;# Empaquetar&lt;/span&gt;
zip &lt;span class="nt"&gt;-jq&lt;/span&gt; lambda.zip zig-out/bin/bootstrap

&lt;span class="c"&gt;# Deploy&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;infra &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sam deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bugs corregidos en la migración
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;remaining_time_ms&lt;/code&gt; llamaba &lt;code&gt;deadline_ms&lt;/code&gt; como función cuando es un campo, y el orden de la resta estaba invertido.&lt;/li&gt;
&lt;li&gt;Variables indefinidas en el parsing de headers, reemplazadas por tipos nullable.&lt;/li&gt;
&lt;li&gt;Lectura del body del response usando &lt;code&gt;reader().readAllAlloc()&lt;/code&gt; en vez del patrón manual con ArrayList.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Resultado
&lt;/h2&gt;

&lt;p&gt;La Lambda despliega y responde correctamente. El binario compilado para ARM64 es pequeño y los tiempos de respuesta se mantienen en el rango de 1-2ms después de la migración.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;aws lambda get-function-url-config &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--function-name&lt;/span&gt; zig-demo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'FunctionUrl'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;
 &lt;span class="c"&gt;# {"message":"hello world"} &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ejecuté 100 invocaciones con un script de benchmark y estos son los reportes de CloudWatch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPORT RequestId: e6c0c71c-...  Duration: 1.64 ms   Billed Duration: 2 ms   Memory Size: 128 MB  Max Memory Used: 13 MB
REPORT RequestId: 85c863bf-...  Duration: 1.57 ms   Billed Duration: 2 ms   Memory Size: 128 MB  Max Memory Used: 13 MB
REPORT RequestId: 9819ece8-...  Duration: 1.54 ms   Billed Duration: 2 ms   Memory Size: 128 MB  Max Memory Used: 13 MB
REPORT RequestId: 074f600b-...  Duration: 1.63 ms   Billed Duration: 2 ms   Memory Size: 128 MB  Max Memory Used: 13 MB
REPORT RequestId: da687f87-...  Duration: 10.08 ms  Billed Duration: 11 ms  Memory Size: 128 MB  Max Memory Used: 13 MB
REPORT RequestId: c7575544-...  Duration: 1.49 ms   Billed Duration: 2 ms   Memory Size: 128 MB  Max Memory Used: 14 MB
REPORT RequestId: e02e5bb4-...  Duration: 12.16 ms  Billed Duration: 13 ms  Memory Size: 128 MB  Max Memory Used: 14 MB
REPORT RequestId: 158b7657-...  Duration: 1.39 ms   Billed Duration: 2 ms   Memory Size: 128 MB  Max Memory Used: 14 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resumen promediando los 100 invocaciones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Duration típica: ~1.5ms&lt;/li&gt;
&lt;li&gt;Picos ocasionales: 10-12ms (cold starts o micro-pauses del runtime)&lt;/li&gt;
&lt;li&gt;Memoria usada: 13-14 MB de 128 MB asignados&lt;/li&gt;
&lt;li&gt;Billed duration: 2ms en la mayoría de invocaciones&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Posiblemente Zig no es un lenguaje que se esté usando masivamente, pero como ejercicio para entender cómo funciona un custom runtime en Lambda y qué tan lejos se puede llegar en performance, es interesante. La migración de 0.12 a 0.16 no fue trivial por los cambios en la stdlib (especialmente el HTTP client), pero el compilador te guía bastante bien con los errores.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html" rel="noopener noreferrer"&gt;AWS Lambda Runtime API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/softprops/zig-lambda-runtime" rel="noopener noreferrer"&gt;zig-lambda-runtime (softprops)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ziglang.org/download/0.16.0/release-notes.html" rel="noopener noreferrer"&gt;Zig 0.16.0 Release Notes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>zig</category>
    </item>
    <item>
      <title>Cómo Evaluar AI Agents: Comparación de 3 Frameworks</title>
      <dc:creator>Elizabeth Fuentes L</dc:creator>
      <pubDate>Mon, 18 May 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/aws-espanol/como-evaluar-ai-agents-comparacion-de-3-frameworks-2i9e</link>
      <guid>https://dev.to/aws-espanol/como-evaluar-ai-agents-comparacion-de-3-frameworks-2i9e</guid>
      <description>&lt;p&gt;Al evaluar AI agents, la elección del framework determina tus puntajes. Ejecuta pruebas idénticas en Strands, PydanticAI y DeepEval y los números divergen hasta 40%. Esto no es un bug. Es por diseño.&lt;/p&gt;

&lt;p&gt;La mayoría de las comparaciones de frameworks prueban diferentes agents con diferentes rúbricas y lo llaman justo. Esta ejecuta los mismos casos de prueba, mismo modelo de evaluación (Claude Sonnet 4 en Amazon Bedrock), mismos criterios de evaluación en los tres frameworks. La única variable es la API del framework.&lt;/p&gt;

&lt;p&gt;La divergencia revela arquitectura. Strands y PydanticAI envían rúbricas directamente al modelo de evaluación para puntuación transparente. DeepEval usa G-Eval, una técnica respaldada por investigación que descompone la evaluación en pasos chain-of-thought y pondera puntajes con probabilidades de tokens. Diferentes metodologías, diferentes resultados, ambos válidos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lo que aprenderás:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Por qué los puntajes de GEval difieren del prompting directo de rúbricas (es por diseño, no un bug)&lt;/li&gt;
&lt;li&gt;Qué framework funciona mejor para tu stack (AWS vs type-safety vs framework-agnostic)&lt;/li&gt;
&lt;li&gt;Cuándo usar verificaciones determinísticas vs evaluación basada en LLM&lt;/li&gt;
&lt;li&gt;Por qué PydanticAI no puede evaluar listas de herramientas pre-computadas (requisito de OpenTelemetry)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lo que realmente se está comparando:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://strandsagents.com/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Agents&lt;/a&gt;&lt;/strong&gt; = Framework de agents + biblioteca de evaluación (&lt;code&gt;strands-agents-evals&lt;/code&gt;), soporta 12+ proveedores de modelos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://ai.pydantic.dev/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;PydanticAI&lt;/a&gt;&lt;/strong&gt; = Framework de agents + biblioteca de evaluación (&lt;code&gt;pydantic-evals&lt;/code&gt;), soporta múltiples proveedores via Logfire
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.confident-ai.com/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;DeepEval&lt;/a&gt;&lt;/strong&gt; = Framework solo de evaluación (funciona con cualquier agent)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DeepEval no construye agents. Solo los evalúa. Esto lo hace comparable a &lt;code&gt;strands-agents-evals&lt;/code&gt; y &lt;code&gt;pydantic-evals&lt;/code&gt; (las bibliotecas de evaluación), no a los frameworks completos Strands/PydanticAI.&lt;/p&gt;

&lt;p&gt;El panorama de evaluación para AI agents vio más de 45 nuevos papers de investigación en los últimos 6 meses en &lt;a href="https://arxiv.org/" rel="noopener noreferrer"&gt;arXiv (repositorio de preprints de acceso abierto de Cornell University)&lt;/a&gt;, proponiendo nuevas métricas para calidad de trajectory (&lt;a href="https://arxiv.org/abs/2602.21230" rel="noopener noreferrer"&gt;TRACE&lt;/a&gt;), detección de hallucinations (&lt;a href="https://arxiv.org/abs/2601.19918" rel="noopener noreferrer"&gt;LSC&lt;/a&gt;), y compromisos costo-rendimiento (&lt;a href="https://arxiv.org/abs/2511.08042" rel="noopener noreferrer"&gt;KAMI&lt;/a&gt;). Pero cuando se trata de &lt;strong&gt;implementar&lt;/strong&gt; estas evaluaciones, ¿qué framework deberías usar?&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Por qué estos 3 frameworks (y no CrewAI, LangGraph o AutoGen)?
&lt;/h2&gt;

&lt;p&gt;Comparé 8 frameworks de agents por sus capacidades de evaluación. Los frameworks más populares (CrewAI, LangGraph, AutoGen, OpenAI Agents SDK, Google ADK) se enfocan en &lt;strong&gt;construir&lt;/strong&gt; agents, no en evaluarlos. No incluyen bibliotecas dedicadas de evaluación.&lt;/p&gt;

&lt;p&gt;Estos 3 fueron seleccionados porque son los únicos con &lt;strong&gt;SDKs de evaluación dedicados y de código abierto&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Framework&lt;/th&gt;
&lt;th&gt;Biblioteca de Evaluación&lt;/th&gt;
&lt;th&gt;Qué Proporciona&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://strandsagents.com/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;&lt;strong&gt;Strands Agents&lt;/strong&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;strands-agents-evals&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OutputEvaluator, TrajectoryEvaluator, ToolCalled, ActorSimulator, Experiment runner&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://ai.pydantic.dev/evals/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;&lt;strong&gt;PydanticAI&lt;/strong&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pydantic-evals&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;LLMJudge, Datasets tipados con YAML, diffing de reportes, HasMatchingSpan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.confident-ai.com/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;&lt;strong&gt;DeepEval&lt;/strong&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;deepeval&lt;/code&gt; (standalone)&lt;/td&gt;
&lt;td&gt;30+ métricas: GEval, HallucinationMetric, FaithfulnessMetric, ToolCorrectnessMetric&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;¿Qué pasa con los demás?&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Framework&lt;/th&gt;
&lt;th&gt;Por Qué No Está Incluido&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CrewAI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;crewai test&lt;/code&gt; solo soporta OpenAI, proporciona puntuación básica de 1-10. Sin rúbricas, sin evaluación de trajectory, sin detección de hallucinations.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LangGraph&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;La evaluación vive en &lt;strong&gt;LangSmith&lt;/strong&gt; (SaaS de pago), no en el framework de código abierto.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AutoGen&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tiene AutoGen Bench para benchmarking pero no un SDK de evaluación con métricas comparables.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OpenAI Agents SDK&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Proporciona hooks de tracing pero no una biblioteca de evaluación. Combínalo con DeepEval para evaluar.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Google ADK&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tiene CLI &lt;code&gt;adk eval&lt;/code&gt; pero está fuertemente acoplado al ecosistema Gemini.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Si usas CrewAI, LangGraph o AutoGen para &lt;strong&gt;construir&lt;/strong&gt; tu agent, aún necesitas uno de estos 3 frameworks para &lt;strong&gt;evaluarlo&lt;/strong&gt;. DeepEval en particular es framework-agnostic y funciona con cualquier agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzb0x25ugqgmvdpbndbn8.png" alt="Diagram comparing Strands Agents, PydanticAI, and DeepEval evaluation flow showing the same test data flowing through each framework's unique API" width="800" height="495"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  ¿Qué tareas de evaluación estamos ejecutando?
&lt;/h2&gt;

&lt;p&gt;Evaluamos el mismo escenario de agente asistente de viajes en los tres frameworks. El agent responde preguntas de viajeros usando herramientas (buscar vuelos, verificar disponibilidad de hoteles, obtener clima).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Calidad de Salida&lt;/strong&gt; - ¿La respuesta del agent es útil y precisa? (LLM-as-Judge)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Corrección de Herramientas&lt;/strong&gt; - ¿El agent llamó las herramientas correctas con los parámetros correctos?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detección de Hallucinations&lt;/strong&gt; - ¿El agent fabricó información que no está en el contexto?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faithfulness&lt;/strong&gt; - ¿La respuesta está fundamentada en la información recuperada?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Mismos casos de prueba. Mismo modelo de evaluación (&lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/models.html?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Claude&lt;/a&gt; en &lt;a href="https://aws.amazon.com/bedrock/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Amazon Bedrock&lt;/a&gt;). Mismas rúbricas donde sea posible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ronda 1: Calidad de Salida (LLM-as-Judge)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Respuesta rápida:&lt;/strong&gt; Los tres frameworks soportan LLM-as-Judge con rúbricas personalizadas, pero Strands requiere menos líneas (7), PydanticAI ofrece las opciones de configuración más completas (modos de puntuación + aserción), y DeepEval soporta la gama más amplia de criterios personalizados via GEval. Strands y PydanticAI soportan Bedrock nativamente; DeepEval requiere un wrapper personalizado.&lt;/p&gt;

&lt;p&gt;LLM-as-Judge es la técnica de evaluación más fundamental: usar un modelo de lenguaje grande para puntuar si la salida del agent cumple criterios de calidad. Los tres frameworks soportan este patrón, pero la API difiere significativamente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strands Agents (7 líneas)
&lt;/h3&gt;

&lt;p&gt;Strands usa &lt;code&gt;OutputEvaluator&lt;/code&gt; con una rúbrica personalizada, haciéndolo la opción más concisa para LLM-as-Judge básico:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_evals&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Experiment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Case&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_evals.evaluators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OutputEvaluator&lt;/span&gt;

&lt;span class="n"&gt;cases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find flights from NYC to London for next Friday&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Should include airline, price range, and departure times&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;evaluator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OutputEvaluator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;rubric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rate the response on helpfulness (0-1). A helpful response includes &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
           &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;specific flight options with airlines, prices, and times. Penalize &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
           &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vague or generic responses.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us.anthropic.claude-sonnet-4-20250514-v1:0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;experiment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Experiment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;evaluators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;evaluator&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;reports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;experiment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_evaluations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;display&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  PydanticAI (10 líneas)
&lt;/h3&gt;

&lt;p&gt;PydanticAI envuelve casos en un &lt;code&gt;Dataset&lt;/code&gt; y proporciona modos de puntuación y aserción separados, dándote más control sobre criterios de aprobación/fallo:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_evals&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_evals.evaluators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LLMJudge&lt;/span&gt;

&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flight_search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find flights from NYC to London for next Friday&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Should include airline, price range, and departure times&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;evaluators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;LLMJudge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;rubric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rate the response on helpfulness. A helpful response includes &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                   &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;specific flight options with airlines, prices, and times. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                   &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Penalize vague or generic responses.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic:claude-sonnet-4-6&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;include_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;include_expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;include_reason&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate_sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;include_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  DeepEval (12 líneas)
&lt;/h3&gt;

&lt;p&gt;DeepEval usa &lt;code&gt;GEval&lt;/code&gt; con parámetros de evaluación explícitos, permitiéndote controlar qué campos ve el evaluador:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deepeval&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;evaluate&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deepeval.metrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GEval&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deepeval.test_case&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LLMTestCase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LLMTestCaseParams&lt;/span&gt;

&lt;span class="n"&gt;test_case&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LLMTestCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find flights from NYC to London for next Friday&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;actual_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find flights from NYC to London for next Friday&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Should include airline, price range, and departure times&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;metric&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GEval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Helpfulness&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;criteria&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rate the response on helpfulness. A helpful response includes &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
             &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;specific flight options with airlines, prices, and times. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
             &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Penalize vague or generic responses.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;evaluation_params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;LLMTestCaseParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INPUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;LLMTestCaseParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ACTUAL_OUTPUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;LLMTestCaseParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EXPECTED_OUTPUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;test_case&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Veredicto: ¿Qué Framework Gana?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspecto&lt;/th&gt;
&lt;th&gt;Strands&lt;/th&gt;
&lt;th&gt;PydanticAI&lt;/th&gt;
&lt;th&gt;DeepEval&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Líneas de código&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bedrock nativo&lt;/td&gt;
&lt;td&gt;Sí&lt;/td&gt;
&lt;td&gt;Sí&lt;/td&gt;
&lt;td&gt;Necesita wrapper personalizado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Formato de puntuación&lt;/td&gt;
&lt;td&gt;0.0-1.0&lt;/td&gt;
&lt;td&gt;0.0-1.0 + pass/fail&lt;/td&gt;
&lt;td&gt;0.0-1.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Razón incluida&lt;/td&gt;
&lt;td&gt;Sí&lt;/td&gt;
&lt;td&gt;Sí (configurable)&lt;/td&gt;
&lt;td&gt;Sí&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Evaluación por lotes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Experiment.run_evaluations()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Dataset.evaluate_sync()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;evaluate()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Método de prompting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Rúbrica directa → LLM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Rúbrica directa → LLM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;G-Eval (CoT + logprobs)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Strands&lt;/strong&gt; es el más conciso. &lt;strong&gt;PydanticAI&lt;/strong&gt; ofrece la mayor configuración (modos separados de puntuación vs aserción). &lt;strong&gt;DeepEval&lt;/strong&gt; usa &lt;a href="https://docs.confident-ai.com/docs/metrics-llm-evals?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GEval&lt;/a&gt;, una técnica respaldada por investigación del paper &lt;a href="https://arxiv.org/abs/2303.16634" rel="noopener noreferrer"&gt;"G-Eval: NLG Evaluation using GPT-4 with Better Human Alignment"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Por qué los puntajes pueden diferir:&lt;/strong&gt; Incluso con el mismo modelo y texto de rúbrica, GEval usa una estrategia de prompting fundamentalmente diferente:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Descomposición chain-of-thought&lt;/strong&gt; - Desglosa la evaluación en pasos explícitos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ponderación de logprobs&lt;/strong&gt; - Usa probabilidades de tokens para ponderar puntajes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Template estructurado&lt;/strong&gt; - Formato de prompt optimizado para alineación humana&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Esto es por diseño. GEval optimiza para correlación con juicios humanos, no para puntuación idéntica al prompting directo de rúbricas. Strands y PydanticAI optimizan para transparencia y personalizabilidad.&lt;/p&gt;


&lt;h2&gt;
  
  
  Ronda 2: Evaluación de Corrección de Herramientas
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Respuesta rápida:&lt;/strong&gt; Strands proporciona extracción de trajectory integrada y verificaciones determinísticas de herramientas (costo cero). DeepEval tiene un &lt;code&gt;ToolCorrectnessMetric&lt;/code&gt; dedicado con comparación basada en LLM. El &lt;code&gt;HasMatchingSpan&lt;/code&gt; de PydanticAI requiere instrumentación OpenTelemetry y no es comparable a los otros dos para validación simple de lista de herramientas.&lt;/p&gt;

&lt;p&gt;La corrección de herramientas mide si el agent llamó las herramientas correctas con los parámetros correctos. Esto es crítico para agents que interactúan con APIs y bases de datos, porque una llamada incorrecta a una herramienta puede causar efectos secundarios en el mundo real.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ PydanticAI excluido de comparación directa:&lt;/strong&gt; El evaluador &lt;code&gt;HasMatchingSpan&lt;/code&gt; de PydanticAI requiere traces completos de OpenTelemetry de ejecución de agent en vivo. No puede evaluar listas de herramientas pre-computadas como &lt;code&gt;["search_flights", "check_availability"]&lt;/code&gt;, haciéndolo fundamentalmente incomparable a &lt;code&gt;ToolCalled&lt;/code&gt; de Strands y &lt;code&gt;ToolCorrectnessMetric&lt;/code&gt; de DeepEval para validación básica de herramientas.&lt;/p&gt;
&lt;h3&gt;
  
  
  Strands Agents (con extracción de trajectory)
&lt;/h3&gt;

&lt;p&gt;Strands automáticamente extrae el uso de herramientas de traces de ejecución del agent, haciendo la evaluación de trajectory fluida:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_evals&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Experiment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Case&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_evals.evaluators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TrajectoryEvaluator&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_evals.extractors&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tools_use_extractor&lt;/span&gt;

&lt;span class="n"&gt;traj_eval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TrajectoryEvaluator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;rubric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The agent should search for flights first, then check availability. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
           &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Calling weather tools is optional but acceptable.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us.anthropic.claude-sonnet-4-20250514-v1:0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;cases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find flights from NYC to London for next Friday&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;expected_trajectory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_flights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;check_availability&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;task_with_trajectory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;traj_eval&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_trajectory_description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;tools_use_extractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extract_tools_description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;trajectory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tools_use_extractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extract_agent_tools_used_from_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trajectory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;trajectory&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;experiment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Experiment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;evaluators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;traj_eval&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;reports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;experiment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_evaluations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_with_trajectory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Bonus: Verificación determinística de herramientas (sin LLM necesario, costo cero)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Para verificaciones simples de "¿se llamó esta herramienta?", Strands proporciona verificación instantánea sin llamadas API:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_evals.evaluators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ToolCalled&lt;/span&gt;

&lt;span class="c1"&gt;# Check if a specific tool was called (instant, no API call)
&lt;/span&gt;&lt;span class="n"&gt;experiment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Experiment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;evaluators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;ToolCalled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_flights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  PydanticAI (con detección de herramientas basada en spans)
&lt;/h3&gt;

&lt;p&gt;PydanticAI usa spans de OpenTelemetry para detectar uso de herramientas, requiriendo código de evaluador personalizado para validación de trajectory:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_evals&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_evals.evaluators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Evaluator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EvaluatorContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HasMatchingSpan&lt;/span&gt;

&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flight_search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find flights from NYC to London for next Friday&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expected_tools&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_flights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;check_availability&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;evaluators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;HasMatchingSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name_contains&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_flights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;evaluation_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;called_search_flights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Custom evaluator for full trajectory check
&lt;/span&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ToolSequenceCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Evaluator&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EvaluatorContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;tool_spans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;span_tree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;tool_names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tool_spans&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expected_tools&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all_tools_called&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tool_names&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;correct_order&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_check_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_check_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;positions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;positions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;positions&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;positions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  DeepEval (con objetos ToolCall)
&lt;/h3&gt;

&lt;p&gt;DeepEval usa objetos estructurados &lt;code&gt;ToolCall&lt;/code&gt; con validación explícita de parámetros y verificaciones de orden:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deepeval&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;evaluate&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deepeval.metrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ToolCorrectnessMetric&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deepeval.test_case&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LLMTestCase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ToolCall&lt;/span&gt;

&lt;span class="n"&gt;test_case&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LLMTestCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find flights from NYC to London for next Friday&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;actual_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I found 3 flights...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools_called&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;ToolCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_flights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_parameters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;origin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NYC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LHR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="nc"&gt;ToolCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;check_availability&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_parameters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flight_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BA117&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;expected_tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;ToolCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_flights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_parameters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;origin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NYC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LHR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="nc"&gt;ToolCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;check_availability&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;metric&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ToolCorrectnessMetric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;should_consider_ordering&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;should_exact_match&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;test_case&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Veredicto: ¿Qué Framework Gana?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspecto&lt;/th&gt;
&lt;th&gt;Strands&lt;/th&gt;
&lt;th&gt;PydanticAI&lt;/th&gt;
&lt;th&gt;DeepEval&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Extracción de trajectory&lt;/td&gt;
&lt;td&gt;Extractor integrado&lt;/td&gt;
&lt;td&gt;Via spans OpenTelemetry&lt;/td&gt;
&lt;td&gt;Objetos ToolCall manuales&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Eval de trajectory basada en LLM&lt;/td&gt;
&lt;td&gt;TrajectoryEvaluator&lt;/td&gt;
&lt;td&gt;No comparable (solo OTEL)&lt;/td&gt;
&lt;td&gt;ToolCorrectnessMetric&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verificación determinística&lt;/td&gt;
&lt;td&gt;ToolCalled (costo cero)&lt;/td&gt;
&lt;td&gt;HasMatchingSpan (solo OTEL)&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validación de orden&lt;/td&gt;
&lt;td&gt;in_order_match_scorer&lt;/td&gt;
&lt;td&gt;Código personalizado&lt;/td&gt;
&lt;td&gt;should_consider_ordering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validación de parámetros&lt;/td&gt;
&lt;td&gt;Via rúbrica&lt;/td&gt;
&lt;td&gt;Via atributos de span&lt;/td&gt;
&lt;td&gt;should_exact_match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Funciona con listas de herramientas pre-computadas&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Sí&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;No (requiere traces en vivo)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Sí&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Strands&lt;/strong&gt; gana por simplicidad con extracción de trajectory integrada de mensajes del agent. &lt;strong&gt;DeepEval&lt;/strong&gt; tiene la API de ToolCall más estructurada con comparación dedicada basada en LLM. &lt;strong&gt;PydanticAI&lt;/strong&gt; es el más flexible via árboles de spans pero requiere instrumentación OpenTelemetry, haciéndolo adecuado solo para evaluación de agent en vivo, no análisis pre-computado.&lt;/p&gt;


&lt;h2&gt;
  
  
  Ronda 3: Detección de Hallucinations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Respuesta rápida:&lt;/strong&gt; DeepEval proporciona un &lt;code&gt;HallucinationMetric&lt;/code&gt; construido específicamente que descompone afirmaciones y verifica cada una contra el contexto. Strands y PydanticAI usan LLM-as-judge de propósito general con rúbricas personalizadas, lo cual es flexible pero menos especializado. DeepEval gana para detección de hallucinations con su métrica dedicada y conteo de contradicciones por contexto.&lt;/p&gt;

&lt;p&gt;La detección de hallucinations mide si el agent fabrica información no presente en el contexto fuente. Esta es una de las dimensiones de evaluación más críticas, con investigación reciente (&lt;a href="https://arxiv.org/abs/2601.19918" rel="noopener noreferrer"&gt;LSC, enero 2026&lt;/a&gt;) mostrando que métodos de detección zero-shot pueden identificar contenido fabricado sin datos de entrenamiento.&lt;/p&gt;
&lt;h3&gt;
  
  
  Strands Agents
&lt;/h3&gt;

&lt;p&gt;Strands usa &lt;code&gt;OutputEvaluator&lt;/code&gt; con una rúbrica enfocada en hallucinations:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_evals&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Experiment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Case&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_evals.evaluators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OutputEvaluator&lt;/span&gt;

&lt;span class="n"&gt;cases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What is the baggage policy for Delta flights to London?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Based on the context: 2 checked bags, 23kg each, free for international&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;hallucination_eval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OutputEvaluator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;rubric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Score 1.0 if the response ONLY contains information present in the &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
           &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expected output (ground truth). Score 0.0 if the response includes &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
           &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;any fabricated details such as specific prices, dates, or policies &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
           &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;not mentioned in the ground truth. Partially correct responses &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
           &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;should score between 0.3-0.7.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us.anthropic.claude-sonnet-4-20250514-v1:0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;experiment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Experiment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;evaluators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;hallucination_eval&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;reports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;experiment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_evaluations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  PydanticAI
&lt;/h3&gt;

&lt;p&gt;PydanticAI usa &lt;code&gt;LLMJudge&lt;/code&gt; con modos de puntuación y aserción separados para detección de hallucinations:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_evals&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_evals.evaluators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LLMJudge&lt;/span&gt;

&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;baggage_policy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What is the baggage policy for Delta flights to London?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Based on the context: 2 checked bags, 23kg each, free for international&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;evaluators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;LLMJudge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;rubric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Does the response ONLY contain information present in the &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                   &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expected output? Score 0.0 for fabricated details, 1.0 for &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                   &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fully grounded responses.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic:claude-sonnet-4-6&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;include_expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;include_reason&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;evaluation_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hallucination&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;assertion&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;include_reason&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;evaluation_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grounded&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate_sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  DeepEval (HallucinationMetric dedicado)
&lt;/h3&gt;

&lt;p&gt;DeepEval proporciona un &lt;code&gt;HallucinationMetric&lt;/code&gt; especializado que descompone respuestas en afirmaciones y verifica cada una contra el contexto fuente:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deepeval&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;evaluate&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deepeval.metrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HallucinationMetric&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deepeval.test_case&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LLMTestCase&lt;/span&gt;

&lt;span class="n"&gt;test_case&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LLMTestCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What is the baggage policy for Delta flights to London?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;actual_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What is the baggage policy for Delta flights to London?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Delta international flights include 2 checked bags at 23kg each, free of charge.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Carry-on must fit in overhead bin. One personal item allowed.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;metric&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HallucinationMetric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;test_case&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Veredicto: ¿Qué Framework Gana?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspecto&lt;/th&gt;
&lt;th&gt;Strands&lt;/th&gt;
&lt;th&gt;PydanticAI&lt;/th&gt;
&lt;th&gt;DeepEval&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Métrica dedicada&lt;/td&gt;
&lt;td&gt;No (via rúbrica OutputEvaluator)&lt;/td&gt;
&lt;td&gt;No (via rúbrica LLMJudge)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Sí (HallucinationMetric)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contexto como input&lt;/td&gt;
&lt;td&gt;Via expected_output&lt;/td&gt;
&lt;td&gt;Via expected_output&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Campo de contexto dedicado&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Método de puntuación&lt;/td&gt;
&lt;td&gt;LLM judge con rúbrica&lt;/td&gt;
&lt;td&gt;LLM judge con rúbrica&lt;/td&gt;
&lt;td&gt;Verificación afirmación por afirmación&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Granularidad&lt;/td&gt;
&lt;td&gt;Puntuación única&lt;/td&gt;
&lt;td&gt;Puntuación + aserción&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Conteo de contradicciones por contexto&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;DeepEval&lt;/strong&gt; gana aquí con un &lt;code&gt;HallucinationMetric&lt;/code&gt; construido específicamente que descompone afirmaciones y verifica cada una contra el contexto. &lt;strong&gt;Strands&lt;/strong&gt; y &lt;strong&gt;PydanticAI&lt;/strong&gt; usan LLM-as-judge de propósito general con rúbricas personalizadas. Este enfoque es flexible pero menos especializado para detección de hallucinations.&lt;/p&gt;


&lt;h2&gt;
  
  
  Ronda 4: Evaluación por Lotes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Respuesta rápida:&lt;/strong&gt; PydanticAI tiene el mejor reporte con diffing de baseline (comparar v1 vs v2). DeepEval tiene la mayor cantidad de métricas disponibles (30+). Strands tiene la API más limpia para mezclar evaluadores LLM y determinísticos en un solo experimento.&lt;/p&gt;

&lt;p&gt;La evaluación del mundo real ejecuta múltiples métricas en múltiples casos de prueba al mismo tiempo. Esta sección compara cómo cada framework maneja ejecución paralela, tipos de métricas mixtas, y reporte.&lt;/p&gt;
&lt;h3&gt;
  
  
  Strands Agents
&lt;/h3&gt;

&lt;p&gt;Strands combina múltiples evaluadores en un solo &lt;code&gt;Experiment&lt;/code&gt;, ejecutando automáticamente todas las combinaciones:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_evals&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Experiment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Case&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_evals.evaluators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;OutputEvaluator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TrajectoryEvaluator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ToolCalled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;cases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find flights NYC to London&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Flight options with prices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;expected_trajectory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_flights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="nc"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s the weather in Paris tomorrow?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Temperature and conditions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;expected_trajectory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="nc"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Book hotel in Tokyo for 3 nights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Booking confirmation with dates and price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;expected_trajectory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_hotels&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;book_hotel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;experiment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Experiment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;evaluators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;OutputEvaluator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rubric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Is the response helpful and specific?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nc"&gt;TrajectoryEvaluator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rubric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Did the agent use the right tools?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nc"&gt;ToolCalled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_flights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;reports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;experiment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_evaluations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_function&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;display&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  PydanticAI
&lt;/h3&gt;

&lt;p&gt;PydanticAI usa &lt;code&gt;Dataset.evaluate_sync()&lt;/code&gt; con un parámetro &lt;code&gt;max_concurrency&lt;/code&gt; para ejecución paralela:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_evals&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_evals.evaluators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LLMJudge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EqualsExpected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HasMatchingSpan&lt;/span&gt;

&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find flights NYC to London&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="n"&gt;expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Flight options with prices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nc"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s the weather in Paris tomorrow?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="n"&gt;expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Temperature and conditions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nc"&gt;Case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hotel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Book hotel in Tokyo for 3 nights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="n"&gt;expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Booking confirmation with dates and price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;evaluators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;LLMJudge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rubric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Is the response helpful and specific?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;include_reason&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate_sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_concurrency&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;include_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include_averages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  DeepEval
&lt;/h3&gt;

&lt;p&gt;DeepEval usa &lt;code&gt;AsyncConfig&lt;/code&gt; para controlar ejecución paralela y soporta el rango más amplio de métricas integradas:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deepeval&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;evaluate&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deepeval.metrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;GEval&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AnswerRelevancyMetric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HallucinationMetric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ToolCorrectnessMetric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deepeval.test_case&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LLMTestCase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LLMTestCaseParams&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deepeval.evaluate.configs&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncConfig&lt;/span&gt;

&lt;span class="n"&gt;test_cases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;build_test_case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;questions&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;metrics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;GEval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Helpfulness&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;criteria&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Is the response helpful and specific?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;evaluation_params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;LLMTestCaseParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INPUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LLMTestCaseParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ACTUAL_OUTPUT&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="nc"&gt;AnswerRelevancyMetric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nc"&gt;HallucinationMetric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;test_cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;test_cases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;async_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;AsyncConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_concurrent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Veredicto: ¿Qué Framework Gana?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspecto&lt;/th&gt;
&lt;th&gt;Strands&lt;/th&gt;
&lt;th&gt;PydanticAI&lt;/th&gt;
&lt;th&gt;DeepEval&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ejecución paralela&lt;/td&gt;
&lt;td&gt;&lt;code&gt;run_evaluations_async()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;parámetro &lt;code&gt;max_concurrency&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AsyncConfig(max_concurrent=N)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tipos de métricas mixtas&lt;/td&gt;
&lt;td&gt;LLM + determinístico&lt;/td&gt;
&lt;td&gt;LLM + determinístico + span&lt;/td&gt;
&lt;td&gt;Solo LLM (30+ métricas)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Formato de reporte&lt;/td&gt;
&lt;td&gt;Tabla Rich via &lt;code&gt;.display()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Tabla Rich via &lt;code&gt;.print()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Consola + dashboard Confident AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Diffing de reportes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Sí (parámetro &lt;code&gt;baseline=&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Via Confident AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exportar&lt;/td&gt;
&lt;td&gt;Archivo JSON&lt;/td&gt;
&lt;td&gt;Archivo YAML/JSON&lt;/td&gt;
&lt;td&gt;JSON/CSV + cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;PydanticAI&lt;/strong&gt; tiene el mejor reporte con diffing de baseline (comparar v1 vs v2). &lt;strong&gt;DeepEval&lt;/strong&gt; tiene las métricas más disponibles. &lt;strong&gt;Strands&lt;/strong&gt; tiene la API más limpia para mezclar evaluadores LLM y determinísticos.&lt;/p&gt;


&lt;h2&gt;
  
  
  ¿Cuál es la comparación completa de características?
&lt;/h2&gt;

&lt;p&gt;Esta tabla resume cada capacidad de evaluación en los tres frameworks. Úsala como referencia al elegir un framework para tus necesidades específicas de evaluación.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Característica&lt;/th&gt;
&lt;th&gt;Strands + evals&lt;/th&gt;
&lt;th&gt;PydanticAI + evals&lt;/th&gt;
&lt;th&gt;DeepEval&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LLM-as-Judge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OutputEvaluator&lt;/td&gt;
&lt;td&gt;LLMJudge&lt;/td&gt;
&lt;td&gt;GEval&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Evaluación de trajectory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;TrajectoryEvaluator + extractors&lt;/td&gt;
&lt;td&gt;SpanTree + custom&lt;/td&gt;
&lt;td&gt;ToolCorrectnessMetric&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Detección de hallucinations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Via rúbrica&lt;/td&gt;
&lt;td&gt;Via rúbrica&lt;/td&gt;
&lt;td&gt;HallucinationMetric&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Faithfulness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;FaithfulnessEvaluator (trace)&lt;/td&gt;
&lt;td&gt;Via rúbrica&lt;/td&gt;
&lt;td&gt;FaithfulnessMetric&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Verificaciones determinísticas&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Equals, Contains, ToolCalled&lt;/td&gt;
&lt;td&gt;Equals, Contains, IsInstance&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Evaluación multi-agent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;InteractionsEvaluator&lt;/td&gt;
&lt;td&gt;Evaluador personalizado&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Simulación multi-turn&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ActorSimulator&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;ConversationalTestCase&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Generación de casos de prueba&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ExperimentGenerator&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;&lt;code&gt;deepeval generate&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bedrock nativo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sí&lt;/td&gt;
&lt;td&gt;Sí&lt;/td&gt;
&lt;td&gt;Wrapper personalizado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OpenTelemetry&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Integrado&lt;/td&gt;
&lt;td&gt;Via Logfire&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Serialización de dataset&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;td&gt;YAML/JSON&lt;/td&gt;
&lt;td&gt;JSON/CSV&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Comparación de reportes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Diffing de baseline&lt;/td&gt;
&lt;td&gt;Confident AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Integración con pytest&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Via Experiment&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dataset.evaluate_sync()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;assert_test()&lt;/code&gt; / &lt;code&gt;deepeval test&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total de métricas integradas&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;12 evaluadores&lt;/td&gt;
&lt;td&gt;6 evaluadores + custom&lt;/td&gt;
&lt;td&gt;30+ métricas&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  Pruébalo tú mismo
&lt;/h2&gt;

&lt;p&gt;El notebook acompañante ejecuta todas las comparaciones con código en vivo. Puedes reproducir cada resultado de este artículo.&lt;/p&gt;

&lt;p&gt;Un notebook Jupyter acompañante con ejemplos de código ejecutables está disponible en el repositorio de GitHub. El notebook incluye comparaciones lado a lado de los tres frameworks en las mismas tareas de evaluación.&lt;/p&gt;
&lt;h3&gt;
  
  
  Configuración
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;blog-framework-comparison
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;¿Qué framework de evaluación de AI agents es más fácil de aprender?&lt;/strong&gt;&lt;br&gt;
Strands Agents requiere el menor número de líneas de código (7 líneas para LLM-as-Judge). PydanticAI está cerca con 10 líneas. DeepEval requiere la mayor configuración, especialmente para modelos que no son OpenAI donde necesitas una clase wrapper personalizada.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Strands, PydanticAI y DeepEval soportan Amazon Bedrock?&lt;/strong&gt;&lt;br&gt;
Strands y PydanticAI soportan Bedrock nativamente (configuración de una línea). DeepEval requiere un wrapper personalizado &lt;code&gt;DeepEvalBaseLLM&lt;/code&gt; que mapea la API de Bedrock a la interfaz de DeepEval. El wrapper agrega aproximadamente 25 líneas de código.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Necesito OpenTelemetry para evaluar AI agents?&lt;/strong&gt;&lt;br&gt;
Solo para evaluadores basados en trace en Strands (como &lt;code&gt;FaithfulnessEvaluator&lt;/code&gt; y &lt;code&gt;ToolSelectionAccuracyEvaluator&lt;/code&gt;). Los evaluadores basados en output en los tres frameworks funcionan sin OpenTelemetry. PydanticAI usa OpenTelemetry via &lt;a href="https://pydantic.dev/logfire?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Logfire&lt;/a&gt; para evaluación basada en spans.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Cuál es el costo de ejecutar evaluaciones de AI agents?&lt;/strong&gt;&lt;br&gt;
Cada evaluador basado en LLM hace llamadas API al modelo de evaluación, lo que incurre en costos de tokens. Strands proporciona evaluadores determinísticos (como &lt;code&gt;ToolCalled&lt;/code&gt;, &lt;code&gt;Equals&lt;/code&gt;, &lt;code&gt;Contains&lt;/code&gt;) que se ejecutan instantáneamente con costo cero. DeepEval y PydanticAI también tienen opciones determinísticas (&lt;code&gt;Equals&lt;/code&gt;, &lt;code&gt;Contains&lt;/code&gt;, &lt;code&gt;IsInstance&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Puedo usar múltiples frameworks de evaluación juntos?&lt;/strong&gt;&lt;br&gt;
Sí. Puedes usar las métricas especializadas de DeepEval (como &lt;code&gt;HallucinationMetric&lt;/code&gt;) junto con Strands Agents para el runtime del agent y captura de trajectory. Los frameworks evalúan outputs, no agents directamente, así que el framework de agent y el framework de evaluación son elecciones independientes.&lt;/p&gt;


&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;No hay un framework de evaluación "mejor" único. La elección correcta depende de tu stack, prioridades y qué estás comparando.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Punto clave: Diferentes metodologías producen diferentes puntajes por diseño.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strands y PydanticAI&lt;/strong&gt; envían rúbricas directamente al LLM (transparente, personalizable)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DeepEval&lt;/strong&gt; usa técnicas respaldadas por investigación como G-Eval (optimizado para alineación humana)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PydanticAI&lt;/strong&gt; requiere OpenTelemetry para evaluación de herramientas (solo traces en vivo)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strands y DeepEval&lt;/strong&gt; funcionan con datos pre-computados (pruebas más simples)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cuándo usar cada uno:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strands Agents&lt;/strong&gt; es la opción más cohesiva si quieres un framework unificado para creación y evaluación de agents. Creación de agents, llamado de herramientas, captura de trajectory y evaluación viven en el mismo ecosistema. El sistema de hooks y métricas integradas significa que la evaluación está instrumentada en el runtime del agent, no agregada después del hecho. Mejor para equipos que quieren un framework completo de agents con evaluación integrada. Soporta 12+ proveedores de modelos incluyendo AWS Bedrock, OpenAI, Anthropic, Gemini, Ollama, y LiteLLM (para 100+ proveedores más).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PydanticAI&lt;/strong&gt; es la opción más elegante si valoras type safety y pipelines de evaluación estructurados. Datasets YAML, diffing de reportes, y el protocolo &lt;code&gt;Evaluator&lt;/code&gt; lo hacen ideal para equipos que quieren evaluación-como-código con garantías fuertes. Mejor para equipos priorizando type safety y pipelines reproducibles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DeepEval&lt;/strong&gt; es la opción más completa si quieres métricas especializadas sin construirlas tú mismo. Más de 30 métricas, incluyendo detección de hallucinations construida específicamente y verificación de faithfulness, te permiten evaluar inmediatamente sin escribir rúbricas personalizadas. Mejor para evaluación framework-agnostic con técnicas validadas por investigación.&lt;/p&gt;

&lt;p&gt;Los conceptos de evaluación (LLM-as-judge, puntuación de trajectory, detección de hallucinations) son independientes del framework. Los &lt;a href="//../RESEARCH.md"&gt;papers de investigación&lt;/a&gt; y técnicas detrás de ellos funcionan independientemente de qué framework elijas. Para la lista completa de 45+ papers que informaron esta comparación, consulta el archivo &lt;a href="//../RESEARCH.md"&gt;RESEARCH.md&lt;/a&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Amazon Bedrock AgentCore: Una Cuarta Opción
&lt;/h2&gt;

&lt;p&gt;Amazon Bedrock AgentCore proporciona evaluadores integrados y despliegue administrado para agents. Si estás comprometido con AWS y quieres una solución completamente administrada, AgentCore vale la pena considerar junto con los frameworks de código abierto.&lt;/p&gt;
&lt;h3&gt;
  
  
  Evaluadores Integrados
&lt;/h3&gt;

&lt;p&gt;AgentCore incluye 13 evaluadores pre-construidos accesibles via el CLI de AgentCore y AWS SDK. Estos evaluadores cubren dimensiones comunes de evaluación sin requerir código personalizado:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Evaluador&lt;/th&gt;
&lt;th&gt;Qué Mide&lt;/th&gt;
&lt;th&gt;Cuándo Usar&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Builtin.Helpfulness&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Calidad y relevancia de output&lt;/td&gt;
&lt;td&gt;Mismo caso de uso que Strands OutputEvaluator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Builtin.GoalSuccessRate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Precisión de completar tareas&lt;/td&gt;
&lt;td&gt;Métrica binaria de éxito (comparar con puntuación de trajectory)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Builtin.ToolSelection&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Corrección de elección de herramientas&lt;/td&gt;
&lt;td&gt;Igual que Strands ToolCalled o DeepEval ToolCorrectnessMetric&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Builtin.Faithfulness&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fundamentación en contexto recuperado&lt;/td&gt;
&lt;td&gt;Igual que DeepEval FaithfulnessMetric&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Builtin.Harmfulness&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cumplimiento de seguridad y política&lt;/td&gt;
&lt;td&gt;Detecta outputs inseguros&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Cómo funcionan las evaluaciones:&lt;/strong&gt; Invocas el comando CLI &lt;code&gt;agentcore run eval&lt;/code&gt; con tu ID de agent, el nombre del evaluador deseado (como &lt;code&gt;Builtin.Helpfulness&lt;/code&gt;), y un archivo de casos de prueba. AgentCore ejecuta el agent en cada caso de prueba y devuelve un reporte JSON con puntajes y razonamiento para cada consulta. Consulta la &lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/agents-test.html?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Guía de Evaluación de AgentCore&lt;/a&gt; para ejemplos.&lt;/p&gt;
&lt;h3&gt;
  
  
  Captura de Trace para Observabilidad
&lt;/h3&gt;

&lt;p&gt;AgentCore captura traces completos de ejecución cuando habilitas el parámetro &lt;code&gt;enableTrace&lt;/code&gt; en la llamada API &lt;code&gt;invoke_agent&lt;/code&gt;. Los traces incluyen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rationale:&lt;/strong&gt; El razonamiento del agent antes de cada llamada de herramienta&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Invocaciones de herramientas:&lt;/strong&gt; Qué herramientas fueron llamadas con qué parámetros&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observaciones:&lt;/strong&gt; Resultados devueltos de cada herramienta&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pasos de orquestación:&lt;/strong&gt; La secuencia completa de toma de decisiones&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Todos los traces se registran automáticamente en &lt;a href="https://aws.amazon.com/cloudwatch/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Amazon CloudWatch&lt;/a&gt; para análisis y monitoreo. Puedes consultar traces usando CloudWatch Logs Insights o exportarlos a S3 para análisis por lotes. Consulta la &lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/agents-test.html?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Documentación de Tracing de Bedrock Agent&lt;/a&gt; para detalles del esquema de trace.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cuándo usar AgentCore:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ya estás en AWS y quieres un servicio administrado&lt;/li&gt;
&lt;li&gt;Necesitas observabilidad nativa de CloudWatch y logging de cumplimiento&lt;/li&gt;
&lt;li&gt;Tu equipo prefiere infraestructura-como-código (CDK/CloudFormation) sobre scripts de evaluación personalizados&lt;/li&gt;
&lt;li&gt;No necesitas evaluar agents en otros proveedores de cloud&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cuándo usar frameworks de código abierto:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Despliegue multi-cloud (Strands funciona con Bedrock, OpenAI, Anthropic, Ollama)&lt;/li&gt;
&lt;li&gt;Necesitas control fino sobre lógica de evaluación&lt;/li&gt;
&lt;li&gt;Quieres iterar rápidamente en métricas personalizadas sin desplegar funciones Lambda&lt;/li&gt;
&lt;li&gt;Investigación o prototipado donde la flexibilidad importa más que infraestructura administrada&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Recursos de AgentCore
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/agents.html?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Documentación de AWS Bedrock Agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/agents-test.html?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Evaluadores Integrados de AgentCore&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/agents-test.html?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Prueba y Evaluación de Agents&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Gracias!&lt;/p&gt;

&lt;p&gt;🇻🇪🇨🇱 &lt;a href="https://dev.to/elizabethfuentes12"&gt;Dev.to&lt;/a&gt; &lt;a href="https://www.linkedin.com/in/lizfue/" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt; &lt;a href="https://github.com/elizabethfuentes12/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; &lt;a href="https://twitter.com/elizabethfue12" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; &lt;a href="https://www.instagram.com/elifue.tech" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt; &lt;a href="https://www.youtube.com/channel/UCr0Gnc-t30m4xyrvsQpNp2Q" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag__user ltag__user__id__717518"&gt;
    &lt;a href="/elizabethfuentes12" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=150,height=150,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F717518%2Fb550b165-b8b9-405d-acfb-e5dc846765b0.png" alt="elizabethfuentes12 image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/elizabethfuentes12"&gt;Elizabeth Fuentes L&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/elizabethfuentes12"&gt;I help developers build production-ready AI applications through hands-on tutorials and open-source projects.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>ai</category>
      <category>python</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>Desbordamiento de Ventana de Contexto de IA: Solución con Puntero de Memoria</title>
      <dc:creator>Elizabeth Fuentes L</dc:creator>
      <pubDate>Thu, 14 May 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/aws-espanol/desbordamiento-de-ventana-de-contexto-de-ia-solucion-con-puntero-de-memoria-1g76</link>
      <guid>https://dev.to/aws-espanol/desbordamiento-de-ventana-de-contexto-de-ia-solucion-con-puntero-de-memoria-1g76</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;El desbordamiento de ventana de contexto** ocurre cuando las salidas de herramientas de un agente de IA exceden el límite de tokens que el modelo de lenguaje grande (LLM) puede procesar de una vez. El agente no falla: silenciosamente trunca datos, pierde contexto anterior o produce resultados incompletos. Este post muestra cómo el Patrón de Puntero de Memoria lo soluciona: desde agente único hasta coordinación multi-agente donde 145KB de datos nunca entran en ningún contexto de LLM.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Esta demo usa &lt;a href="https://strandsagents.com/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Agents&lt;/a&gt;. El Patrón de Puntero de Memoria es independiente del framework y se puede aplicar con LangGraph, AutoGen u otros frameworks de agentes que soporten contexto de herramientas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Código funcional:&lt;/strong&gt; &lt;a href="https://github.com/aws-samples/sample-why-agents-fail/tree/main/stop-ai-agents-wasting-tokens/01-context-overflow-demo" rel="noopener noreferrer"&gt;github.com/aws-samples/sample-why-agents-fail&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Serie: Por Qué Fallan los Agentes de IA
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Desbordamiento de Ventana de Contexto&lt;/strong&gt; (este post) — Patrón de Puntero de Memoria para datos grandes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/aws-samples/sample-why-agents-fail/tree/main/stop-ai-agents-wasting-tokens/02-mcp-timeout-demo" rel="noopener noreferrer"&gt;Herramientas MCP Que Nunca Responden&lt;/a&gt;&lt;/strong&gt; — Patrón asíncrono para APIs externas lentas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/aws-samples/sample-why-agents-fail/tree/main/stop-ai-agents-wasting-tokens/03-reasoning-loops-demo" rel="noopener noreferrer"&gt;Loops de Razonamiento en Agentes de IA&lt;/a&gt;&lt;/strong&gt; — Detectar y bloquear llamadas repetidas a herramientas&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  El Problema: Los Agentes No Pueden Manejar Salidas Grandes de Herramientas
&lt;/h2&gt;

&lt;p&gt;Cuando un agente de IA llama a una herramienta que devuelve datos grandes (logs del servidor, resultados de bases de datos, contenidos de archivos), la respuesta puede desbordar la ventana de contexto del LLM. El agente no falla con un error claro. Se degrada silenciosamente: trunca datos, pierde contexto o no completa la tarea.&lt;/p&gt;

&lt;p&gt;Una investigación de IBM (&lt;a href="https://arxiv.org/html/2511.22729v1" rel="noopener noreferrer"&gt;Solving Context Window Overflow in AI Agents, 2025&lt;/a&gt;) cuantifica esto:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;En flujos de trabajo de Ciencia de Materiales, las salidas de herramientas pueden alcanzar &lt;strong&gt;más de 2 millones de elementos&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;El enfoque tradicional consumió &lt;strong&gt;20,822,181 tokens&lt;/strong&gt; y falló&lt;/li&gt;
&lt;li&gt;El mismo flujo con punteros de memoria usó &lt;strong&gt;1,234 tokens&lt;/strong&gt; y tuvo éxito&lt;/li&gt;
&lt;li&gt;Eso es una reducción de más de &lt;strong&gt;16,000x&lt;/strong&gt; en este flujo de trabajo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Una observación comunitaria (&lt;a href="https://airbyte.com/agentic-data/context-window-limit" rel="noopener noreferrer"&gt;Context Window Limits Explained, Airbyte 2025&lt;/a&gt;) confirma que los equipos descubren estos límites "de la manera difícil" a través de errores silenciosos. El agente parece funcionar pero produce resultados incompletos o incorrectos.&lt;/p&gt;

&lt;p&gt;El concepto de pasar referencias en lugar de datos sin procesar también ha sido validado en configuraciones multi-agente. Una investigación de Amazon (&lt;a href="https://arxiv.org/pdf/2412.05449" rel="noopener noreferrer"&gt;Towards Effective GenAI Multi-Agent Collaboration, 2024&lt;/a&gt;) introduce "referenciación de carga útil", donde los agentes intercambian punteros a datos compartidos en lugar de incrustar cargas grandes en mensajes. Esto mejoró el rendimiento en tareas intensivas en código en un 23% y logró tasas de éxito de objetivos de extremo a extremo del 90% en benchmarks empresariales. Esto es exactamente lo que implementamos a continuación con &lt;a href="https://strandsagents.com/docs/user-guide/concepts/multi-agent/swarm/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Swarm&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por Qué Sucede Esto
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0su97tbog85m0q3srr5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0su97tbog85m0q3srr5.png" alt="El bucle del agente: User Query fluye a LLM, luego Tool Call, luego Tool Output (214KB), luego de regreso a LLM. La salida grande de herramienta causa desbordamiento de contexto" width="800" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cuando la salida de la herramienta es pequeña (unos pocos KB), esto funciona bien. Pero cuando una herramienta devuelve 200KB de logs del servidor:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;La salida completa se inyecta en la conversación&lt;/li&gt;
&lt;li&gt;La ventana de contexto del LLM se llena&lt;/li&gt;
&lt;li&gt;El contexto más antiguo (incluida la pregunta original) se expulsa&lt;/li&gt;
&lt;li&gt;El LLM no puede razonar sobre los datos porque no puede verlos todos&lt;/li&gt;
&lt;li&gt;El agente falla o produce respuestas incompletas&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Solución 1: Agente Único con Strands ToolContext
&lt;/h2&gt;

&lt;p&gt;El primer enfoque usa &lt;a href="https://strandsagents.com/docs/user-guide/concepts/agents/state/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;&lt;code&gt;agent.state&lt;/code&gt;&lt;/a&gt;, un almacén clave-valor nativo con alcance para cada instancia de agente. Las herramientas escriben datos grandes allí vía &lt;a href="https://strandsagents.com/docs/user-guide/concepts/tools/custom-tools/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;&lt;code&gt;ToolContext&lt;/code&gt;&lt;/a&gt; y devuelven una cadena de puntero corta al contexto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ToolContext&lt;/span&gt;

&lt;span class="c1"&gt;# context=True inyecta ToolContext como el último parámetro — requerido para acceder a agent.state
&lt;/span&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_application_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ToolContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Obtiene logs de aplicación. Devuelve un puntero de memoria para datasets grandes.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;logs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Podría ser 200KB+
&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;20_000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# Umbral: almacenar externamente por encima de 20KB
&lt;/span&gt;        &lt;span class="n"&gt;pointer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logs-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="c1"&gt;# Almacena la carga útil completa en agent.state — nunca entra al contexto del LLM
&lt;/span&gt;        &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Devuelve solo la clave del puntero (52 bytes) — esto es todo lo que ve el LLM
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Datos almacenados como puntero &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;. Usa herramientas de análisis para consultarlo.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Suficientemente pequeño para devolver directamente
&lt;/span&gt;
&lt;span class="nd"&gt;@tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;analyze_error_patterns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_pointer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ToolContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Analiza errores — resuelve puntero desde agent.state.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# Recupera el dataset completo desde agent.state usando la clave del puntero
&lt;/span&gt;    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_pointer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ERROR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# Devuelve un resumen (no datos sin procesar) — mantiene la respuesta pequeña
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Se encontraron &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; errores en &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;service&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; servicios&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El LLM nunca ve los 200KB. Solo ve &lt;code&gt;"Datos almacenados como puntero 'logs-payment-service'"&lt;/code&gt; (52 bytes). La siguiente herramienta lee los datos completos desde &lt;code&gt;agent.state&lt;/code&gt; y devuelve un resumen. Strands proporciona esta capacidad nativamente, sin diccionarios globales, sin hashlib, sin infraestructura externa.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resultados de Agente Único
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Métrica&lt;/th&gt;
&lt;th&gt;Sin Punteros&lt;/th&gt;
&lt;th&gt;Con Punteros de Memoria&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Datos en contexto&lt;/td&gt;
&lt;td&gt;214KB (logs completos)&lt;/td&gt;
&lt;td&gt;52 bytes (puntero)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comportamiento del agente&lt;/td&gt;
&lt;td&gt;Trunca/falla&lt;/td&gt;
&lt;td&gt;Procesa todos los datos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Errores detectados&lt;/td&gt;
&lt;td&gt;Parcial&lt;/td&gt;
&lt;td&gt;Completo (todos los servicios)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9922q2u1b8wl6miejb79.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9922q2u1b8wl6miejb79.png" alt="Gráfico de barras comparando uso de tokens con y sin Patrón de Puntero de Memoria en cuatro estrategias de gestión de contexto" width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Solución 2: Multi-Agente con Strands Swarm
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fih2nb4e6m3tkocud75ol.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fih2nb4e6m3tkocud75ol.png" alt="Flujo de datos de Strands Swarm: agentes Collector, Analyzer y Reporter compartiendo 145KB de datos a través de invocation_state sin entrar a ninguna ventana de contexto de LLM" width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Un solo agente funciona para pipelines lineales. Pero la respuesta a incidentes del mundo real involucra roles especializados: alguien obtiene datos, alguien los analiza, alguien escribe el reporte. &lt;a href="https://strandsagents.com/docs/user-guide/concepts/multi-agent/swarm/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Swarm&lt;/a&gt; coordina múltiples agentes autónomamente: define agentes con diferentes herramientas, y el Swarm maneja los traspasos.&lt;/p&gt;

&lt;p&gt;Este es el mismo patrón de "referenciación de carga útil" del &lt;a href="https://arxiv.org/pdf/2412.05449" rel="noopener noreferrer"&gt;paper de colaboración multi-agente de Amazon&lt;/a&gt;. Los agentes intercambian punteros a datos compartidos en lugar de pasar cargas sin procesar. La diferencia es que Strands Swarm maneja la coordinación automáticamente, y proporciona &lt;a href="https://strandsagents.com/docs/user-guide/concepts/multi-agent/multi-agent-patterns/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;&lt;code&gt;invocation_state&lt;/code&gt;&lt;/a&gt; como la API oficial para compartir datos entre agentes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ToolContext&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.multiagent&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Swarm&lt;/span&gt;

&lt;span class="c1"&gt;# invocation_state es un dict compartido entre todos los agentes en el Swarm — el almacén entre agentes
&lt;/span&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_application_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ToolContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;logs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 145KB+
&lt;/span&gt;    &lt;span class="n"&gt;pointer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logs-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="c1"&gt;# Almacena en invocation_state para que todos los agentes descendentes puedan acceder sin re-obtener
&lt;/span&gt;    &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invocation_state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logs&lt;/span&gt;
    &lt;span class="c1"&gt;# Solo la cadena de puntero viaja a través del contexto del LLM al siguiente agente
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Almacenado como &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;. Traspasar a analyzer.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="nd"&gt;@tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;analyze_error_patterns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logs_pointer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ToolContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Resuelve el puntero al dataset completo — sin contexto de LLM consumido
&lt;/span&gt;    &lt;span class="n"&gt;logs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invocation_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logs_pointer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;logs&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ERROR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total_errors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;  &lt;span class="c1"&gt;# campos adicionales omitidos por brevedad
&lt;/span&gt;    &lt;span class="c1"&gt;# Almacena resultados de análisis como otro puntero para el agente reporter
&lt;/span&gt;    &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invocation_state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error_analysis&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Cada agente tiene un rol enfocado; el Swarm decide el orden de traspaso autónomamente
&lt;/span&gt;&lt;span class="n"&gt;collector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;collector&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fetch_application_logs&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;MODEL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;analyzer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;analyzer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;analyze_error_patterns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;detect_latency_anomalies&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;MODEL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;reporter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reporter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;generate_incident_report&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;MODEL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;swarm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Swarm&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;collector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;analyzer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reporter&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;entry_point&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;collector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;swarm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Obtén logs, analiza y genera reporte de incidente.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El Swarm automáticamente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comienza con el collector, que obtiene 145KB de logs y los almacena en &lt;code&gt;invocation_state&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;El collector traspasa al analyzer con el puntero &lt;code&gt;"logs-payment-service"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;El analyzer ejecuta análisis de errores y latencia, almacena resultados en &lt;code&gt;invocation_state&lt;/code&gt;, traspasa al reporter&lt;/li&gt;
&lt;li&gt;El reporter genera el reporte de incidente final&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No se necesita código de orquestación ni lógica de traspaso manual. Cada agente tiene sus propias herramientas y el Swarm determina el flujo a partir de las descripciones de agentes y la tarea. Todo el intercambio de datos ocurre vía &lt;code&gt;tool_context.invocation_state&lt;/code&gt;, la misma API de &lt;code&gt;ToolContext&lt;/code&gt; usada en agente único, con un almacén diferente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resultados de Swarm
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Status: COMPLETED
Agents: collector → analyzer → reporter
Time: ~14s
Shared store:
  logs-payment-service: 145,310 bytes
  error_analysis: 135 bytes
  latency_analysis: 70 bytes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;145KB de logs procesados por tres agentes. Nada de eso entró nunca a ninguna ventana de contexto de LLM.&lt;/p&gt;

&lt;h3&gt;
  
  
  Investigación de Seguimiento
&lt;/h3&gt;

&lt;p&gt;Después de que el swarm se completa, los datos permanecen en el almacén compartido. Un agente investigador separado puede profundizar en servicios específicos sin re-obtener:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# El investigator reutiliza invocation_state poblado por el swarm — sin re-obtención de datos
&lt;/span&gt;&lt;span class="n"&gt;investigator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;investigator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;get_error_details&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;analyze_error_patterns&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;MODEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Cada pregunta resuelve el puntero desde invocation_state y ejecuta análisis en memoria
&lt;/span&gt;&lt;span class="nf"&gt;investigator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;¿Qué servicio tuvo más errores?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;investigator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Muéstrame los logs de error de cache-layer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;investigator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;¿Qué códigos de estado devuelven esos errores?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Todas las consultas leen de los mismos 145KB ya en invocation_state — sin re-obtención, sin desbordamiento de contexto
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cuándo Usar Cada Enfoque
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Agente único + &lt;code&gt;agent.state&lt;/code&gt;&lt;/strong&gt; — pipelines lineales donde un agente maneja obtención + análisis + reporte. Usa &lt;a href="https://strandsagents.com/docs/user-guide/concepts/tools/custom-tools/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;&lt;code&gt;ToolContext&lt;/code&gt;&lt;/a&gt; para acceder a &lt;code&gt;tool_context.agent.state&lt;/code&gt; desde herramientas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Swarm + &lt;code&gt;invocation_state&lt;/code&gt;&lt;/strong&gt; — roles especializados, flujos complejos, o cuando quieres coordinación autónoma. Usa &lt;a href="https://strandsagents.com/docs/user-guide/concepts/tools/custom-tools/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;&lt;code&gt;ToolContext&lt;/code&gt;&lt;/a&gt; para acceder a &lt;code&gt;tool_context.invocation_state&lt;/code&gt;: la API oficial de Strands para &lt;a href="https://strandsagents.com/docs/user-guide/concepts/multi-agent/multi-agent-patterns/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;intercambio de datos multi-agente&lt;/a&gt;. El &lt;a href="https://strandsagents.com/docs/user-guide/concepts/multi-agent/swarm/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Swarm&lt;/a&gt; gestiona traspasos, timeouts y detección de traspasos repetitivos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ambos&lt;/strong&gt; — usa &lt;a href="https://strandsagents.com/docs/user-guide/concepts/agents/conversation-management/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;&lt;code&gt;SlidingWindowConversationManager&lt;/code&gt;&lt;/a&gt; como protección adicional. Recorta automáticamente el historial de conversación y maneja &lt;code&gt;ContextWindowOverflowException&lt;/code&gt; con reintento.&lt;/p&gt;

&lt;p&gt;Estos enfoques son parte de &lt;strong&gt;ingeniería de contexto&lt;/strong&gt; para agentes de IA: la práctica de decidir qué información entra a la ventana de contexto del LLM y cuándo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pruébalo Tú Mismo
&lt;/h2&gt;

&lt;p&gt;Necesitas &lt;a href="https://python.org/downloads" rel="noopener noreferrer"&gt;Python 3.9+&lt;/a&gt;, &lt;a href="https://docs.astral.sh/uv/" rel="noopener noreferrer"&gt;uv&lt;/a&gt;, y una &lt;a href="https://platform.openai.com/api-keys" rel="noopener noreferrer"&gt;clave API de OpenAI&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/aws-samples/sample-why-agents-fail
&lt;span class="nb"&gt;cd &lt;/span&gt;sample-why-agents-fail/stop-ai-agents-wasting-tokens/01-context-overflow-demo
uv venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; uv pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"tu-clave-aquí"&lt;/span&gt;

uv run python test_context_overflow.py   &lt;span class="c"&gt;# Agente único: 4 escenarios&lt;/span&gt;
uv run python swarm_demo.py              &lt;span class="c"&gt;# Multi-agente: Collector → Analyzer → Reporter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O abre &lt;code&gt;test_context_overflow.ipynb&lt;/code&gt; en &lt;a href="https://kiro.dev?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt;, VS Code, o tu entorno de notebook preferido.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusiones Clave
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;El desbordamiento de contexto es silencioso&lt;/strong&gt; — los agentes no fallan, producen resultados incorrectos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Los punteros de memoria lo solucionan&lt;/strong&gt; — almacena datos grandes externamente, pasa referencias&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reducción de &amp;gt;16,000x en tokens&lt;/strong&gt; — validado por IBM Research en el benchmark de Ciencia de Materiales&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agente único usa &lt;code&gt;agent.state&lt;/code&gt;&lt;/strong&gt; — &lt;code&gt;@tool(context=True)&lt;/code&gt; + &lt;code&gt;ToolContext&lt;/code&gt; para almacenar y recuperar datos fuera del contexto&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-agente usa &lt;code&gt;invocation_state&lt;/code&gt;&lt;/strong&gt; — misma API de &lt;code&gt;ToolContext&lt;/code&gt;, compartida entre todos los agentes en el Swarm. No se necesita código de orquestación&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Los datos persisten para seguimiento&lt;/strong&gt; — después de que el pipeline se completa, los datos almacenados están disponibles para investigación sin re-obtención&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Preguntas Frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Por qué los agentes de IA se quedan sin contexto?
&lt;/h3&gt;

&lt;p&gt;Los agentes de IA se quedan sin contexto cuando las respuestas de herramientas se inyectan directamente en el historial de conversación del LLM. Cada respuesta consume tokens. Cuando las salidas acumuladas de herramientas exceden el límite de ventana de contexto del modelo, el LLM pierde contexto anterior, trunca datos o falla por completo. Esto sucede silenciosamente: el agente parece funcionar pero produce resultados incompletos o incorrectos.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué es el Patrón de Puntero de Memoria para agentes de IA?
&lt;/h3&gt;

&lt;p&gt;El Patrón de Puntero de Memoria almacena salidas grandes de herramientas (logs, datasets, resultados de consultas) en estado externo en lugar de en la ventana de contexto del LLM. Las herramientas devuelven una clave de referencia corta (el "puntero") que herramientas subsiguientes usan para recuperar los datos completos. IBM Research validó este patrón con una reducción de más de 16,000x en el benchmark de Ciencia de Materiales.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿En qué se diferencia agent.state de invocation_state en Strands Agents?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;agent.state&lt;/code&gt; tiene alcance para una sola instancia de agente. Úsalo para pipelines lineales donde un agente maneja todos los pasos. &lt;code&gt;invocation_state&lt;/code&gt; se comparte entre todos los agentes en un Strands Swarm. Úsalo cuando múltiples agentes especializados necesitan intercambiar datos sin pasar cargas grandes a través del contexto del LLM.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Puedo usar el Patrón de Puntero de Memoria con LangGraph u otros frameworks?
&lt;/h3&gt;

&lt;p&gt;Sí. El patrón requiere dos capacidades: un almacén clave-valor compartido accesible desde herramientas, y la capacidad de pasar cadenas de referencia cortas a través del contexto del LLM. LangGraph proporciona esto a través de su gestión de estado, AutoGen a través de memoria compartida, y CrewAI a través de contexto de tareas. La implementación de Strands usa &lt;code&gt;ToolContext&lt;/code&gt; como la API nativa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Investigación
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/html/2511.22729v1" rel="noopener noreferrer"&gt;Solving Context Window Overflow in AI Agents&lt;/a&gt; — IBM Research, Nov 2025&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/pdf/2412.05449" rel="noopener noreferrer"&gt;Towards Effective GenAI Multi-Agent Collaboration&lt;/a&gt; — Amazon, Dec 2024&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://airbyte.com/agentic-data/context-window-limit" rel="noopener noreferrer"&gt;Context Window Limits Explained&lt;/a&gt; — Airbyte blog (observación comunitaria), Dec 2025&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/html/2511.03728v1" rel="noopener noreferrer"&gt;Efficient On-Device Agents via Adaptive Context Management&lt;/a&gt; — Nov 2025&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Implementación
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/concepts/agents/state/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Agent State&lt;/a&gt; — ToolContext and agent.state&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/concepts/multi-agent/swarm/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Swarm&lt;/a&gt; — Multi-agent orchestration&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/concepts/agents/conversation-management/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Conversation Management&lt;/a&gt; — Sliding window and context overflow&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;¿Has alcanzado límites de ventana de contexto en tus agentes? ¿Qué estrategias funcionaron para ti? Comparte en los comentarios.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Siguiente en esta serie:&lt;/strong&gt; &lt;a href="https://github.com/aws-samples/sample-why-agents-fail/tree/main/stop-ai-agents-wasting-tokens/02-mcp-timeout-demo" rel="noopener noreferrer"&gt;Herramientas MCP Que Nunca Responden&lt;/a&gt; — patrones asíncronos para APIs externas lentas.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Todo el código en esta serie es open source bajo la Licencia MIT-0. &lt;a href="https://github.com/aws-samples/sample-why-agents-fail" rel="noopener noreferrer"&gt;Dale estrella al repositorio&lt;/a&gt; para seguir las actualizaciones.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Gracias!&lt;/p&gt;

&lt;p&gt;🇻🇪&lt;a href="https://dev.to/elizabethfuentes12"&gt;Dev.to&lt;/a&gt; - &lt;a href="https://www.linkedin.com/in/lizfue/" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt; - &lt;a href="https://github.com/elizabethfuentes12/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; - &lt;a href="https://twitter.com/elizabethfue12" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; - &lt;a href="https://www.instagram.com/elifue.tech" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt; - &lt;a href="https://www.youtube.com/channel/UCr0Gnc-t30m4xyrvsQpNp2Q" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>tutorial</category>
      <category>python</category>
      <category>programming</category>
    </item>
    <item>
      <title>Cómo Monitorear Costos de Agentes IA sin Configuración</title>
      <dc:creator>Elizabeth Fuentes L</dc:creator>
      <pubDate>Wed, 13 May 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/aws-espanol/como-monitorear-costos-de-agentes-ia-sin-configuracion-1fca</link>
      <guid>https://dev.to/aws-espanol/como-monitorear-costos-de-agentes-ia-sin-configuracion-1fca</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Strands Agents proporciona telemetría nativa y seguimiento de costos desde el primer momento. Deja de escribir contadores de tokens personalizados. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Construir agentes de IA es fácil. &lt;strong&gt;Desplegarlos a producción&lt;/strong&gt; es donde la mayoría de los equipos se encuentran con un muro.&lt;/p&gt;

&lt;p&gt;Una de las primeras preguntas de finanzas: &lt;em&gt;"¿Cuánto costará esto por solicitud?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;La mayoría de los frameworks de agentes te obligan a construir tu propio contador de tokens. Strands Agents te proporciona uno.&lt;/p&gt;

&lt;h2&gt;
  
  
  El Problema con el Conteo Personalizado de Tokens
&lt;/h2&gt;

&lt;p&gt;Cada aplicación de IA necesita monitoreo de costos. Pero rastrear tokens a través de:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Múltiples llamadas al modelo&lt;/li&gt;
&lt;li&gt;Invocaciones de herramientas&lt;/li&gt;
&lt;li&gt;Caché de prompts&lt;/li&gt;
&lt;li&gt;Flujos de trabajo multi-agente&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...requiere infraestructura personalizada que la mayoría de los equipos reconstruyen desde cero.&lt;/p&gt;

&lt;h2&gt;
  
  
  Telemetría Nativa en &lt;a href="https://strandsagents.com/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Agents&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Strands Agents incluye &lt;a href="https://strandsagents.com/docs/user-guide/observability-evaluation/metrics/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;telemetría de grado de producción&lt;/a&gt; por defecto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;calculator&lt;/span&gt;

&lt;span class="c1"&gt;# Crear un agente con herramientas
&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;calculator&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Invocar el agente con un prompt y obtener un AgentResult
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;¿Cuál es la raíz cuadrada de 144?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Acceder a métricas a través del AgentResult
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tokens totales: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accumulated_usage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;totalTokens&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tiempo de ejecución: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cycle_durations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; segundos&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Herramientas usadas: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Métricas de caché (cuando estén disponibles)
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cacheReadInputTokens&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accumulated_usage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tokens leídos de caché: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accumulated_usage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cacheReadInputTokens&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cacheWriteInputTokens&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accumulated_usage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tokens escritos en caché: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accumulated_usage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cacheWriteInputTokens&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Sin configuración. Sin código personalizado. Simplemente funciona.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Lo Que Obtienes
&lt;/h2&gt;

&lt;p&gt;Cada &lt;code&gt;AgentResult&lt;/code&gt; incluye:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Métrica&lt;/th&gt;
&lt;th&gt;Descripción&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;inputTokens&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tokens enviados al modelo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;outputTokens&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tokens generados por el modelo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;totalTokens&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Costo total (entrada + salida)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cacheReadInputTokens&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tokens leídos desde caché (caché de prompts de Bedrock)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cacheWriteInputTokens&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tokens escritos en caché&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Seguimiento de Tokens Multi-Agente
&lt;/h2&gt;

&lt;p&gt;Para sistemas multi-agente (ejecutor → validador → crítico), agrega métricas a través de todos los agentes:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.multiagent&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Swarm&lt;/span&gt;

&lt;span class="n"&gt;swarm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Swarm&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;critic&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;swarm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Consulta&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;total_tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;node_result&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;usage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node_result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accumulated_usage&lt;/span&gt;
    &lt;span class="n"&gt;total_tokens&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;totalTokens&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Costo total a través de todos los agentes: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;total_tokens&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Seguimiento por Ciclo
&lt;/h2&gt;

&lt;p&gt;Para agentes que ejecutan múltiples ciclos de razonamiento, rastrea tokens por ciclo:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;calculator&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;calculator&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Primera invocación
&lt;/span&gt;&lt;span class="n"&gt;result1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;¿Cuánto es 5 + 3?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Segunda invocación
&lt;/span&gt;&lt;span class="n"&gt;result2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;¿Cuál es la raíz cuadrada de 144?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Acceder a métricas de la última invocación
&lt;/span&gt;&lt;span class="n"&gt;latest_invocation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;latest_agent_invocation&lt;/span&gt;
&lt;span class="n"&gt;cycles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;latest_invocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cycles&lt;/span&gt;
&lt;span class="n"&gt;usage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;latest_invocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usage&lt;/span&gt;

&lt;span class="c1"&gt;# O acceder a todas las invocaciones
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;invocation&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent_invocations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Uso de invocación: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;invocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cycle&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;invocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cycles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Ciclo &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cycle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_loop_cycle_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cycle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# O imprimir el resumen (incluye todas las invocaciones)
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_summary&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Para una lista completa de atributos y sus tipos, consulta la referencia de API de &lt;a href="https://strandsagents.com/docs/api/python/strands.telemetry.metrics/#EventLoopMetrics/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;EventLoopMetrics&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Por Qué Esto Importa
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;La visibilidad de costos&lt;/strong&gt; es la diferencia entre un prototipo y una IA en producción.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38wu531cmi145pfs5vva.png%2F%3Ftrk%3D87c4c426-cddf-4799-a299-273337552ad8%26sc_channel%3Del" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38wu531cmi145pfs5vva.png%2F%3Ftrk%3D87c4c426-cddf-4799-a299-273337552ad8%26sc_channel%3Del" alt="Casos de Uso" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Con la telemetría de Strands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Presupuesta cargas de trabajo de IA antes del despliegue&lt;/li&gt;
&lt;li&gt;✅ Identifica consultas costosas en producción&lt;/li&gt;
&lt;li&gt;✅ Optimiza prompts con datos reales de tokens&lt;/li&gt;
&lt;li&gt;✅ Rastrea ahorros del caché de prompts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Todo sin escribir una sola línea de código de telemetría.&lt;/p&gt;
&lt;h2&gt;
  
  
  Funciona con Todos los Proveedores de Modelos
&lt;/h2&gt;

&lt;p&gt;El seguimiento de tokens funciona independientemente de tu proveedor de modelo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon Bedrock (Claude, Llama, Mistral)&lt;/li&gt;
&lt;li&gt;OpenAI (GPT-4, GPT-3.5)&lt;/li&gt;
&lt;li&gt;Anthropic API&lt;/li&gt;
&lt;li&gt;Ollama (modelos locales)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Misma API, mismas métricas, cero cambios de configuración.&lt;/p&gt;
&lt;h2&gt;
  
  
  Pruébalo
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;strands-agents
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Documentación completa: &lt;a href="https://strandsagents.com/docs/user-guide/concepts/agents/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;strandsagents.com/docs/user-guide/concepts/agents/&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Gracias!&lt;/p&gt;

&lt;p&gt;🇻🇪🇨🇱 &lt;a href="https://dev.to/elizabethfuentes12"&gt;Dev.to&lt;/a&gt; &lt;a href="https://www.linkedin.com/in/lizfue/" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt; &lt;a href="https://github.com/elizabethfuentes12/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; &lt;a href="https://twitter.com/elizabethfue12" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; &lt;a href="https://www.instagram.com/elifue.tech" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt; &lt;a href="https://www.youtube.com/channel/UCr0Gnc-t30m4xyrvsQpNp2Q" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag__user ltag__user__id__717518"&gt;
    &lt;a href="/elizabethfuentes12" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=150,height=150,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F717518%2Fb550b165-b8b9-405d-acfb-e5dc846765b0.png" alt="elizabethfuentes12 image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/elizabethfuentes12"&gt;Elizabeth Fuentes L&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/elizabethfuentes12"&gt;I help developers build production-ready AI applications through hands-on tutorials and open-source projects.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>ai</category>
      <category>python</category>
      <category>opensource</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Cómo Guiar Asistentes de IA para Construir Agentes Listos para Producción: 8 Patrones Esenciales</title>
      <dc:creator>Elizabeth Fuentes L</dc:creator>
      <pubDate>Mon, 11 May 2026 18:54:52 +0000</pubDate>
      <link>https://dev.to/aws-espanol/como-guiar-asistentes-de-ia-para-construir-agentes-listos-para-produccion-8-patrones-esenciales-1ifd</link>
      <guid>https://dev.to/aws-espanol/como-guiar-asistentes-de-ia-para-construir-agentes-listos-para-produccion-8-patrones-esenciales-1ifd</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Cuando le pides a un asistente de IA como &lt;a href="https://kiro.dev?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt; (el asistente de IA de AWS), Claude Code o ChatGPT que "construya un agente," obtienes código funcional. Pero no ves las decisiones de arquitectura que ocurren detrás de escena. El agente responde a consultas, pero podría desperdiciar tokens en bucles de razonamiento, alucinar respuestas a partir de datos incompletos, o congelarse con APIs lentas. Estas fallas son silenciosas hasta llegar a producción.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cuando le pides a asistentes de IA que construyan agentes, toman decisiones de arquitectura silenciosamente—eligiendo estrategias de recuperación, enfoques de validación y patrones de manejo de errores. Estos 8 patrones te dan el vocabulario para especificar decisiones de grado producción en tus prompts, previniendo alucinaciones y desperdicio de tokens antes de que se genere código.&lt;/p&gt;

&lt;p&gt;Este post cierra dos series que escribí documentando las fallas de agentes más costosas en producción: &lt;a href="https://dev.to/aws/stop-ai-agent-hallucinations-4-essential-techniques-2i94?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;Stop AI Agent Hallucinations (5 técnicas)&lt;/a&gt; y &lt;a href="https://dev.to/aws/why-ai-agents-fail-3-failure-modes-that-cost-you-tokens-and-time-1flb?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;Why AI Agents Fail (3 modos de falla)&lt;/a&gt;. &lt;strong&gt;Si conoces estos 8 patrones, puedes guiar a los asistentes de IA para evitarlos desde el inicio.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Esto no es una guía de implementación paso a paso. Es una referencia para saber qué existe y así reconocer cuándo usar cada patrón según tu caso de uso.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Código funcional para las 8 técnicas:&lt;/strong&gt; Enlazado en cada sección&lt;/p&gt;




&lt;h2&gt;
  
  
  Por Qué Esto Importa
&lt;/h2&gt;

&lt;p&gt;Los asistentes de IA generan código de agentes en segundos. &lt;a href="https://kiro.dev?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt;, Claude Code, Cursor y ChatGPT pueden crear estructuras de herramientas, configurar llamadas a LLM y conectar sistemas de recuperación más rápido que programar manualmente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pero la velocidad crea un problema: obtienes código funcional sin ver las concesiones.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cuando escribes "construye un agente de reservas con RAG," el asistente toma decisiones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;¿Qué estrategia de recuperación? (similitud vectorial, consultas de grafos, híbrido)&lt;/li&gt;
&lt;li&gt;¿Cómo manejar salidas grandes? (truncar, resumir, almacenamiento externo)&lt;/li&gt;
&lt;li&gt;¿Qué validación se ejecuta antes de usar una herramienta? (ninguna, prompts, hooks de framework)&lt;/li&gt;
&lt;li&gt;¿Cómo manejar APIs lentas? (bloquear, timeout, patrones asíncronos)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tu prompt no especifica esto. El asistente elige valores por defecto. Esos valores por defecto crean los modos de falla que este post documenta.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Los 8 Patrones de Falla (Referencia Rápida)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Fallas por Alucinación (5 patrones):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;GraphRAG&lt;/strong&gt; - RAG vectorial fabrica estadísticas a partir de fragmentos incompletos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic Tool Selection&lt;/strong&gt; - Demasiadas herramientas, el agente elige las equivocadas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Neurosymbolic Guardrails&lt;/strong&gt; - El agente ignora reglas de negocio en los prompts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime Guardrails (Steering)&lt;/strong&gt; - El agente viola reglas, necesita corrección no bloqueo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Agent Validation&lt;/strong&gt; - Un solo agente afirma éxito cuando las operaciones fallan&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Desperdicio Silencioso de Tokens (3 patrones):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Memory Pointer Pattern&lt;/strong&gt; - Datos grandes desbordan el contexto, causan truncamiento&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Async HandleId Pattern&lt;/strong&gt; - APIs lentas bloquean el agente indefinidamente&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DebounceHook + Explicit States&lt;/strong&gt; - El agente hace bucle con la misma llamada sin progreso&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No implementas los 8. Aprendes qué resuelven, luego especificas los que tu caso de uso necesita al hacer prompts.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué Son Estos 8 Patrones?
&lt;/h2&gt;

&lt;p&gt;Estos patrones resuelven las fallas de producción más costosas: alucinaciones por datos incompletos (GraphRAG, Semantic Tool Selection, Guardrails, Steering, Multi-Agent), y desperdicio silencioso de tokens (Memory Pointers, Async HandleId, DebounceHook). Aprendes qué resuelve cada uno, luego especificas los que tu caso de uso necesita al pedir a asistentes de IA. Esto previene depurar código de caja negra en producción.&lt;/p&gt;

&lt;h2&gt;
  
  
  Impacto Medido en Producción
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Patrón&lt;/th&gt;
&lt;th&gt;Resultado&lt;/th&gt;
&lt;th&gt;Fuente&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GraphRAG&lt;/td&gt;
&lt;td&gt;Conteos exactos vs aproximaciones fabricadas&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/aws/rag-vs-graphrag-when-agents-hallucinate-answers-2mcb?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;RAG vs GraphRAG&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Semantic Tool Selection&lt;/td&gt;
&lt;td&gt;86.4% menos errores, 89% menos costos de tokens&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/aws/reduce-agent-errors-and-token-costs-with-semantic-tool-selection-7mf?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;Tool Selection&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory Pointers&lt;/td&gt;
&lt;td&gt;20M tokens reducidos a 1,234 tokens&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/aws/ai-context-window-overflow-memory-pointer-fix-22bk?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;estudio IBM Materials Science&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async HandleId&lt;/td&gt;
&lt;td&gt;Bloqueo de 18 segundos eliminado, sin timeouts 424&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/aws/fix-mcp-timeouts-async-handleid-pattern-8ek?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;MCP Timeouts&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explicit States&lt;/td&gt;
&lt;td&gt;14 llamadas reducidas a 2 (mejora de 7x)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/aws/how-to-prevent-ai-agent-reasoning-loops-from-wasting-tokens-2652?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;Reasoning Loops&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Patrón 1: GraphRAG para Consultas Precisas
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué Es GraphRAG?
&lt;/h3&gt;

&lt;p&gt;GraphRAG reemplaza la similitud vectorial con consultas a bases de datos de grafos para datos estructurados. Cuando tu agente necesita conteos exactos, agregaciones o recorrido de relaciones, GraphRAG traduce lenguaje natural a consultas Cypher que retornan resultados precisos desde datos estructurados en lugar de estadísticas alucinadas desde fragmentos de texto. Úsalo para consultas estructuradas, mantén RAG vectorial para búsqueda semántica.&lt;/p&gt;

&lt;h3&gt;
  
  
  Qué Se Rompe
&lt;/h3&gt;

&lt;p&gt;RAG vectorial fabrica estadísticas. Preguntas "¿Cuántos hoteles en Miami tienen piscina y desayuno?" y la similitud vectorial recupera 3 fragmentos de texto que mencionan piscinas y desayuno. El LLM ve datos incompletos, calcula a partir de muestras y retorna "aproximadamente 120 hoteles" (fabricado a partir de 3 fragmentos de 200 hoteles).&lt;/p&gt;

&lt;p&gt;Las consultas fuera de dominio retornan respuestas alucinadas en lugar de admitir que no existen datos.&lt;/p&gt;

&lt;h3&gt;
  
  
  La Solución
&lt;/h3&gt;

&lt;p&gt;Reemplaza la recuperación vectorial con consultas de grafos para datos estructurados. Almacena hoteles, amenidades y relaciones en Neo4j. El LLM traduce "hoteles con piscinas y desayuno" a Cypher:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;h:&lt;/span&gt;&lt;span class="n"&gt;Hotel&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:HAS_AMENITY&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;a:&lt;/span&gt;&lt;span class="n"&gt;Amenity&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;a.name&lt;/span&gt; &lt;span class="ow"&gt;IN&lt;/span&gt; &lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'pool'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'breakfast'&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Resultado: 133 hoteles (conteo exacto desde la base de datos).&lt;/p&gt;

&lt;p&gt;Consulta fuera de dominio: "No se encontraron hoteles en la Antártida" en lugar de fabricar resultados.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcstm4df3sgeyh6d9zbkb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcstm4df3sgeyh6d9zbkb.png" alt="Vector RAG fabricates statistics from text chunks. GraphRAG returns exact counts from structured database queries" width="800" height="990"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Qué Decirle a Tu Asistente de IA
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Construye un agente de viajes usando GraphRAG con Neo4j. Para consultas 
estructuradas (hoteles, amenidades, disponibilidad), traduce a Cypher 
y ejecuta contra el grafo. Solo usa RAG vectorial para descripciones 
no estructuradas. Retorna conteos exactos desde recorrido del grafo."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Cuándo Usar
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Datos estructurados con relaciones (productos, inventario, ubicaciones)&lt;/li&gt;
&lt;li&gt;Consultas que requieren conteos, agregaciones o recorrido multi-salto&lt;/li&gt;
&lt;li&gt;Dominios donde fabricar estadísticas crea riesgo legal/financiero&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Detalles completos:&lt;/strong&gt; &lt;a href="https://dev.to/aws/rag-vs-graphrag-when-agents-hallucinate-answers-2mcb?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;RAG vs GraphRAG: When Agents Hallucinate Answers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aprende más:&lt;/strong&gt; &lt;a href="https://neo4j.com/docs/cypher-manual/current/" rel="noopener noreferrer"&gt;Documentación Neo4j Cypher&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Patrón 2: Semantic Tool Selection
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ¿Qué Es Semantic Tool Selection?
&lt;/h3&gt;

&lt;p&gt;Semantic tool selection usa embeddings vectoriales para filtrar herramientas antes de que el LLM las vea. Cuando tu agente tiene más de 10 herramientas, enviar todas las descripciones en cada llamada aumenta las tasas de error (el agente elige herramientas incorrectas) y los costos de tokens (pagando por descripciones no usadas). El filtrado semántico inserta descripciones de herramientas offline, luego en tiempo de ejecución compara la consulta con las 5 herramientas más relevantes, reduciendo errores en 86.4% y costos en 89%.&lt;/p&gt;
&lt;h3&gt;
  
  
  Qué Se Rompe
&lt;/h3&gt;

&lt;p&gt;Con 50 herramientas, ocurren dos fallas: (1) el agente elige herramientas incorrectas porque las descripciones se superponen, y (2) los costos de tokens explotan por enviar las 50 descripciones de herramientas en cada llamada al LLM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Impacto medido:&lt;/strong&gt; Las tasas de error aumentan con el conteo de herramientas, los costos de tokens escalan linealmente.&lt;/p&gt;
&lt;h3&gt;
  
  
  La Solución
&lt;/h3&gt;

&lt;p&gt;Usa embeddings vectoriales para filtrar herramientas antes de que el LLM las vea. Inserta descripciones de herramientas offline. En tiempo de ejecución, inserta la consulta del usuario, calcula similitud, pasa solo las 5 herramientas más relevantes al agente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resultados en producción:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Errores reducidos: 86.4%&lt;/li&gt;
&lt;li&gt;Costos de tokens reducidos: 89%&lt;/li&gt;
&lt;li&gt;Latencia: &amp;lt;10ms para filtrado de herramientas&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Qué Decirle a Tu Asistente de IA
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Construye un agente multi-herramienta con semantic tool selection. Usa FAISS 
y SentenceTransformers para insertar descripciones de herramientas offline. En 
tiempo de ejecución, inserta la consulta, recupera las 5 herramientas más similares, 
pasa solo esas al agente. Mantén memoria de conversación, intercambia herramientas dinámicamente."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Cuándo Usar
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Agentes con más de 10 herramientas&lt;/li&gt;
&lt;li&gt;Herramientas con descripciones que se superponen&lt;/li&gt;
&lt;li&gt;Aplicaciones sensibles a costos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Detalles completos:&lt;/strong&gt; &lt;a href="https://dev.to/aws/reduce-agent-errors-and-token-costs-with-semantic-tool-selection-7mf?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;Reduce Agent Errors and Token Costs with Semantic Tool Selection&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Patrón 3: Neurosymbolic Guardrails (Bloqueo)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ¿Qué Son Neurosymbolic Guardrails?
&lt;/h3&gt;

&lt;p&gt;Neurosymbolic guardrails aplican reglas de negocio a nivel de framework, por debajo del control del LLM. Cuando los prompts solos no pueden aplicar restricciones (máximo de huéspedes, fechas válidas, límites de presupuesto), los guardrails usan hooks de pre-ejecución para validar parámetros y cancelar operaciones inválidas. Las reglas viven en código, no en prompts, así que el LLM no puede evadirlas. Usa guardrails de bloqueo para restricciones duras que no pueden violarse.&lt;/p&gt;
&lt;h3&gt;
  
  
  Qué Se Rompe
&lt;/h3&gt;

&lt;p&gt;Los prompts no pueden aplicar reglas de negocio. Incluso con docstrings claros ("max_guests debe ser ≤10"), el LLM pasa &lt;code&gt;max_guests=15&lt;/code&gt; bajo presión porque los prompts son sugerencias, no restricciones. El agente viola reglas silenciosamente.&lt;/p&gt;
&lt;h3&gt;
  
  
  La Solución
&lt;/h3&gt;

&lt;p&gt;Usa hooks de framework para validar parámetros antes de la ejecución de herramientas. Si la validación falla, cancela la llamada de herramienta y retorna guía correctiva. Las reglas viven en código a nivel de framework, por debajo del control del LLM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Impacto medido:&lt;/strong&gt; Cero violaciones en prueba de 100 consultas (vs. 12 violaciones solo con prompts).&lt;/p&gt;
&lt;h3&gt;
  
  
  Qué Decirle a Tu Asistente de IA
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Construye un agente de reservas con guardrails usando hooks de Strands Agents. 
Crea un hook BeforeToolCallEvent que valide:
- max_guests ≤ 10
- check_in_date &amp;gt; hoy
- budget &amp;gt; 0

Si la validación falla, cancela la llamada de herramienta con event.cancel_tool() 
y retorna mensaje de error. No confíes en prompts para validación."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1jlee9gupk90w1xg00hx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1jlee9gupk90w1xg00hx.png" alt="Prompts can be ignored by LLM (top layer). Framework hooks enforce rules at code level (bottom layer, unbypassable)" width="800" height="651"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Cuándo Usar
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Reglas de negocio que no pueden violarse (cumplimiento, legales, financieras)&lt;/li&gt;
&lt;li&gt;Validación que requiere cálculo (matemáticas de fechas, verificaciones de inventario)&lt;/li&gt;
&lt;li&gt;Reglas que cambian frecuentemente&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Detalles completos:&lt;/strong&gt; &lt;a href="https://dev.to/aws/ai-agent-guardrails-rules-that-llms-cannot-bypass-596d?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;AI Agent Guardrails: Rules That LLMs Cannot Bypass&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Patrón 4: Runtime Guardrails (Dirigir, No Bloquear)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ¿Qué Es Dirigir vs Bloquear?
&lt;/h3&gt;

&lt;p&gt;Steering guardrails retornan guía correctiva en lugar de bloquear operaciones. Cuando el agente viola una regla suave (problemas de formato, ajustes de parámetros, redacción de datos), el steering retorna instrucciones vía Guide() para que el agente se autocorrija y reintente. Esto difiere de los guardrails de bloqueo (Patrón 3) que detienen flujos de trabajo completamente. Usa steering para reglas donde el agente puede corregirse, bloqueo para restricciones duras.&lt;/p&gt;
&lt;h3&gt;
  
  
  Qué Se Rompe
&lt;/h3&gt;

&lt;p&gt;Los guardrails duros (Patrón 3) bloquean operaciones y detienen flujos de trabajo. Para reglas suaves donde el agente puede autocorregirse (problemas de formato, ajustes de parámetros, redactar datos sensibles), el bloqueo crea fricción. El agente podría arreglar el problema por sí mismo si se le da guía.&lt;/p&gt;
&lt;h3&gt;
  
  
  La Solución
&lt;/h3&gt;

&lt;p&gt;Usa &lt;a href="https://github.com/agentcontrol/agent-control" rel="noopener noreferrer"&gt;Agent Control&lt;/a&gt; para retornar guía correctiva vía &lt;code&gt;Guide()&lt;/code&gt; en lugar de bloquear. Cuando el agente viola una regla suave, el plano de control retorna instrucciones: "Ajusta el parámetro X a Y y reintenta." El agente se autocorrige y completa la tarea sin intervención humana.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diferencia con el Patrón 3:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bloquear (Patrón 3):&lt;/strong&gt; Restricciones duras, el flujo de trabajo se detiene&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dirigir (Patrón 4):&lt;/strong&gt; Reglas suaves, el agente se autocorrige&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Qué Decirle a Tu Asistente de IA
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Construye un agente de reservas con Agent Control para reglas suaves. Conéctate 
al servidor Agent Control. Para reglas suaves (formato de parámetros, ajustes 
de fecha, redacción de datos), retorna Guide() con instrucciones de corrección 
en lugar de bloquear. El agente debe reintentar con la corrección aplicada.

Usa bloqueos duros (Patrón 3) solo para reglas de cumplimiento que no pueden 
violarse bajo ninguna circunstancia."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Cuándo Usar
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Reglas donde el agente puede autocorregirse (formato, ajustar parámetros)&lt;/li&gt;
&lt;li&gt;Flujos de trabajo donde el bloqueo crea UX pobre&lt;/li&gt;
&lt;li&gt;Reglas gestionadas centralmente vía API/dashboard (actualizar sin redesplegar)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Detalles completos:&lt;/strong&gt; &lt;a href="https://dev.to/aws/runtime-guardrails-for-ai-agents-steer-dont-block-278n?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;Runtime Guardrails for AI Agents: Steer, Don't Block&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Patrón 5: Multi-Agent Validation
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ¿Qué Es Multi-Agent Validation?
&lt;/h3&gt;

&lt;p&gt;Multi-agent validation despliega agentes especializados con diferentes roles (Executor, Validator, Critic) que verifican cruzadamente el trabajo de los demás. Los agentes únicos optimizan para parecer exitosos, no verificar resultados. Múltiples agentes con diferentes funciones de optimización atrapan errores que los demás pierden. El Executor realiza tareas, el Validator verifica contra la verdad fundamental, el Critic proporciona revisión final antes de retornar al usuario.&lt;/p&gt;
&lt;h3&gt;
  
  
  Qué Se Rompe
&lt;/h3&gt;

&lt;p&gt;Los agentes únicos no pueden autovalidarse. Cuando un agente reserva un hotel, afirma "Éxito: Reservado Grand Plaza Hotel" incluso si la API retornó un error o el hotel no existe en la base de datos. El agente optimiza para parecer exitoso, no verificar resultados.&lt;/p&gt;
&lt;h3&gt;
  
  
  La Solución
&lt;/h3&gt;

&lt;p&gt;Despliega múltiples agentes con diferentes roles: el Executor realiza tareas, el Validator verifica contra la verdad fundamental, el Critic proporciona revisión final. Los agentes comparten contexto y transfieren control autónomamente cuando su rol se completa.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Impacto medido:&lt;/strong&gt; Multi-agente atrapa errores que el agente único pierde (p.ej., reservar hoteles inexistentes).&lt;/p&gt;
&lt;h3&gt;
  
  
  Qué Decirle a Tu Asistente de IA
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Construye un sistema multi-agente usando Strands Swarm con 3 agentes:
1. Executor: Reserva hoteles, busca vuelos
2. Validator: Verifica cruzadamente operaciones contra la base de datos
3. Critic: Revisión final antes de retornar al usuario

Los agentes comparten contexto vía swarm.context. Usa transferencias autónomas. 
Los agentes deciden cuándo transferir según completación de tarea."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Cuándo Usar
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Operaciones de alto riesgo (financieras, médicas, legales)&lt;/li&gt;
&lt;li&gt;Tareas donde "parece exitoso" difiere de "realmente exitoso"&lt;/li&gt;
&lt;li&gt;Flujos de trabajo complejos con múltiples puntos de verificación&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Detalles completos:&lt;/strong&gt; &lt;a href="https://dev.to/aws/how-to-stop-ai-agents-from-hallucinating-silently-with-multi-agent-validation-3f7e?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;How to Stop AI Agents from Hallucinating Silently with Multi-Agent Validation&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Patrón 6: Memory Pointer Pattern
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ¿Qué Es el Memory Pointer Pattern?
&lt;/h3&gt;

&lt;p&gt;El Memory Pointer Pattern almacena datos grandes fuera del contexto del LLM y pasa referencias cortas en su lugar. Cuando las herramientas retornan logs de más de 200KB o resultados de base de datos de 1000 filas, pasarlos directamente causa truncamiento silencioso. Los memory pointers almacenan datos en agent.state, retornan un puntero al LLM y proporcionan herramientas separadas que resuelven punteros para acceder a datos completos. IBM redujo de 20M tokens a 1,234 tokens usando este patrón.&lt;/p&gt;
&lt;h3&gt;
  
  
  Qué Se Rompe
&lt;/h3&gt;

&lt;p&gt;El desbordamiento de ventana de contexto ocurre cuando las herramientas retornan más datos de los que el LLM puede procesar (logs de más de 200KB, resultados de base de datos de 1000 filas). El agente no colapsa. Trunca datos silenciosamente, pierde contexto, produce respuestas incompletas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caso real de producción (IBM Materials Science):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Antes: 20 millones de tokens, flujo de trabajo falló&lt;/li&gt;
&lt;li&gt;Después: 1,234 tokens, flujo de trabajo exitoso&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  La Solución
&lt;/h3&gt;

&lt;p&gt;Almacena datos grandes en &lt;code&gt;agent.state&lt;/code&gt;, pasa referencias cortas al LLM. Las herramientas retornan punteros como &lt;code&gt;"logs-app-server"&lt;/code&gt;. Las herramientas subsiguientes resuelven punteros para acceder a datos completos. El LLM solo ve: "Datos almacenados como logs-app-server. Usa analyze_errors(pointer)."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Datos en contexto reducidos:&lt;/strong&gt; 214KB → 52 bytes&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsbzqsm6ml5qo4e5lkkjs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsbzqsm6ml5qo4e5lkkjs.png" alt="Before: 20M tokens overflow context. After: Memory pointer reduces to 1,234 tokens, full data stored externally" width="800" height="1022"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Qué Decirle a Tu Asistente de IA
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Construye un agente de análisis de logs usando Memory Pointer Pattern. Cuando 
fetch_logs retorne más de 20KB:
1. Almacenar en agent.state con ID de puntero único
2. Retornar al LLM: 'Datos almacenados como logs-{app}. Usa analyze_logs(pointer).'
3. Implementar analyze_logs(pointer) que resuelva desde agent.state

Nunca pases datos grandes directamente al contexto del LLM."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Cuándo Usar
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Herramientas que retornan salidas grandes (logs, consultas de base de datos, archivos)&lt;/li&gt;
&lt;li&gt;Flujos de trabajo con múltiples pasos de procesamiento sobre los mismos datos grandes&lt;/li&gt;
&lt;li&gt;Aplicaciones sensibles a costos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Detalles completos:&lt;/strong&gt; &lt;a href="https://dev.to/aws/ai-context-window-overflow-memory-pointer-fix-22bk?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;AI Context Window Overflow: Memory Pointer Fix&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Patrón 7: Async HandleId Pattern
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ¿Qué Es el Async HandleId Pattern?
&lt;/h3&gt;

&lt;p&gt;El async handleId pattern previene que APIs externas lentas bloqueen tu agente. Cuando una API toma más de 30 segundos, las llamadas síncronas congelan todo el agente. Async handleId retorna un ID de trabajo inmediatamente, permitiendo que el agente continúe con otras tareas. Una herramienta check_status separada sondea por resultados cuando estén listos. Esto elimina errores de timeout 424 y mantiene los agentes responsivos.&lt;/p&gt;
&lt;h3&gt;
  
  
  Qué Se Rompe
&lt;/h3&gt;

&lt;p&gt;Las APIs externas que toman más de 30 segundos bloquean el agente indefinidamente. Ninguna otra herramienta puede ejecutarse. Después de ~7 segundos, muchas implementaciones retornan errores de timeout 424, congelando el flujo de trabajo.&lt;/p&gt;
&lt;h3&gt;
  
  
  La Solución
&lt;/h3&gt;

&lt;p&gt;Las herramientas retornan inmediatamente con un ID de trabajo en lugar de esperar. El agente almacena handleId y continúa. Una herramienta &lt;code&gt;check_status(job_id)&lt;/code&gt; separada sondea por resultados asincrónicamente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Impacto medido:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Antes: API de 18 segundos bloquea agente, timeout 424&lt;/li&gt;
&lt;li&gt;Después: Herramienta retorna en menos de 1 segundo, agente sondea cuando está listo&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Qué Decirle a Tu Asistente de IA
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Construye un agente con async handleId pattern para APIs lentas:

1. start_analysis(data): Envía trabajo, retorna job_id inmediatamente
2. check_status(job_id): Sondea por resultados

El agente llama start_analysis, almacena job_id, continúa con otras 
tareas, llama check_status cuando está listo. No implementes llamadas bloqueantes."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Cuándo Usar
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;APIs externas con tiempos de respuesta mayores a 5 segundos&lt;/li&gt;
&lt;li&gt;Procesamiento por lotes (análisis de video, transformaciones grandes)&lt;/li&gt;
&lt;li&gt;Cualquier sistema fuera de tu control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Detalles completos:&lt;/strong&gt; &lt;a href="https://dev.to/aws/fix-mcp-timeouts-async-handleid-pattern-8ek?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;Fix MCP Timeouts: Async HandleId Pattern&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Patrón 8: DebounceHook + Explicit States
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ¿Qué Previene los Bucles de Razonamiento?
&lt;/h3&gt;

&lt;p&gt;Los bucles de razonamiento ocurren cuando retroalimentación ambigua ("más puede estar disponible") señala que reintentar podría ayudar. Dos correcciones funcionan juntas: estados terminales explícitos (retornar SUCCESS/FAILED para que el LLM sepa cuándo detenerse) y DebounceHook (hook de framework que bloquea llamadas duplicadas). Las pruebas de producción mostraron que los estados explícitos redujeron las llamadas de 14 a 2, mientras que DebounceHook proporciona una red de seguridad para casos extremos.&lt;/p&gt;
&lt;h3&gt;
  
  
  Qué Se Rompe
&lt;/h3&gt;

&lt;p&gt;Los agentes hacen bucle llamando a la misma herramienta repetidamente sin progreso. Retroalimentación ambigua como "Se encontraron 3 resultados. Más pueden estar disponibles" señala que reintentar podría ayudar. El agente hace bucle indefinidamente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caso real de producción:&lt;/strong&gt; 847 pasos de razonamiento a $47/minuto, sin respuesta entregada.&lt;/p&gt;
&lt;h3&gt;
  
  
  La Solución (Dos Partes)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Parte A: Estados Terminales Explícitos&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Retorna estados claros de SUCCESS o FAILED. Cambia "Más pueden estar disponibles" a "SUCCESS: Se encontraron todos los 3 vuelos coincidentes."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parte B: Red de Seguridad DebounceHook&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
El hook de framework rastrea llamadas recientes a herramientas. Cuando el mismo par (tool_name, input) aparece dos veces, bloquea el tercer intento.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Impacto medido (demo de reserva de viajes):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retroalimentación ambigua: 14 llamadas&lt;/li&gt;
&lt;li&gt;SUCCESS explícito: 2 llamadas (reducción de 7x)&lt;/li&gt;
&lt;li&gt;DebounceHook: 12 llamadas (2 bloqueadas)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Qué Decirle a Tu Asistente de IA
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Construye un agente de viajes con protección anti-bucle:

1. Todas las herramientas retornan estados explícitos:
   - SUCCESS: [completación clara]
   - FAILED: [error claro]
   Nunca retornes 'más puede estar disponible'

2. Implementa DebounceHook:
   - Rastrea las últimas 3 llamadas de herramientas como (tool_name, input)
   - Si el mismo par aparece dos veces, bloquea el tercer intento
   - Retorna 'BLOCKED: Duplicado detectado'

Esto previene bucles sin límites manuales de reintentos."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Cuándo Usar
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Agentes propensos a bucles de reintento (búsqueda, agregadores de API)&lt;/li&gt;
&lt;li&gt;Aplicaciones sensibles a costos donde reintentos ilimitados son costosos&lt;/li&gt;
&lt;li&gt;Sistemas de producción donde bucles infinitos crean riesgo de disponibilidad&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Detalles completos:&lt;/strong&gt; &lt;a href="https://dev.to/aws/how-to-prevent-ai-agent-reasoning-loops-from-wasting-tokens-2652?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;How to Prevent AI Agent Reasoning Loops from Wasting Tokens&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Errores Comunes
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Error 1: Asumir Que los Valores Por Defecto Son Mejores Prácticas
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problema:&lt;/strong&gt; "Construye un agente de producción" asume que el asistente sabe qué significa producción.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solución:&lt;/strong&gt; Especifica patrones: "Usa GraphRAG, guardrails, patrones async, etc..."&lt;/p&gt;
&lt;h3&gt;
  
  
  Error 2: Confiar Solo en Prompts para Validación
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problema:&lt;/strong&gt; "Asegúrate de que max_guests &amp;lt; 10" en el prompt del sistema es ignorado bajo presión.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solución:&lt;/strong&gt; "Implementa hook BeforeToolCallEvent que valide y cancele llamadas inválidas."&lt;/p&gt;
&lt;h3&gt;
  
  
  Error 3: No Reconocer Cuándo Aplican los Patrones
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problema:&lt;/strong&gt; El agente funciona en demo, se rompe en casos extremos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solución:&lt;/strong&gt; Conoce los 8 patrones. Cuando veas alucinaciones, timeouts o bucles, reconocerás qué patrón lo resuelve.&lt;/p&gt;


&lt;h2&gt;
  
  
  Lo Que Esto Significa para el Desarrollo Asistido por IA
&lt;/h2&gt;

&lt;p&gt;Los asistentes de IA seguirán mejorando en generar código funcional. Pero código funcional y arquitectura lista para producción siguen siendo objetivos diferentes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La brecha no es la capacidad del asistente. Es la especificidad del prompt.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cuando escribes "construye un agente de reservas," el asistente optimiza para código que compila y responde a consultas.&lt;/p&gt;

&lt;p&gt;Cuando escribes "construye un agente de reservas usando GraphRAG para consultas estructuradas, guardrails para validación y patrones async para APIs de reservas," el asistente optimiza para código que compila, responde a consultas, previene alucinaciones, aplica reglas de negocio y maneja APIs lentas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Estos 8 patrones son el vocabulario para comunicar intención de producción.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No implementas los 8. Aprendes qué resuelven. Cuando ves alucinaciones, reconoces que GraphRAG aplica. Cuando ves timeouts, reconoces que async handleId aplica. Cuando ves bucles, reconoces que estados explícitos + DebounceHook aplican.&lt;/p&gt;

&lt;p&gt;Este conocimiento cambia cómo haces prompts a &lt;a href="https://kiro.dev?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt;, Claude Code, Cursor y ChatGPT. En lugar de depurar fallas de caja negra en producción, especificas los patrones que las previenen durante la generación.&lt;/p&gt;


&lt;h3&gt;
  
  
  Aprende Más (Guías de Implementación Completas)
&lt;/h3&gt;

&lt;p&gt;Cada patrón tiene una guía completa con código funcional:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;GraphRAG:&lt;/strong&gt; &lt;a href="https://dev.to/aws/rag-vs-graphrag-when-agents-hallucinate-answers-2mcb?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;RAG vs GraphRAG: When Agents Hallucinate Answers&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic Tool Selection:&lt;/strong&gt; &lt;a href="https://dev.to/aws/reduce-agent-errors-and-token-costs-with-semantic-tool-selection-7mf?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;Reduce Agent Errors and Token Costs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Neurosymbolic Guardrails:&lt;/strong&gt; &lt;a href="https://dev.to/aws/ai-agent-guardrails-rules-that-llms-cannot-bypass-596d?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;AI Agent Guardrails: Rules That LLMs Cannot Bypass&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime Guardrails (Steering):&lt;/strong&gt; &lt;a href="https://dev.to/aws/runtime-guardrails-for-ai-agents-steer-dont-block-278n?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;Runtime Guardrails for AI Agents: Steer, Don't Block&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Agent Validation:&lt;/strong&gt; &lt;a href="https://dev.to/aws/how-to-stop-ai-agents-from-hallucinating-silently-with-multi-agent-validation-3f7e?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;Stop AI Agents from Hallucinating Silently&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Pointers:&lt;/strong&gt; &lt;a href="https://dev.to/aws/ai-context-window-overflow-memory-pointer-fix-22bk?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;AI Context Window Overflow: Memory Pointer Fix&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Async HandleId:&lt;/strong&gt; &lt;a href="https://dev.to/aws/fix-mcp-timeouts-async-handleid-pattern-8ek?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;Fix MCP Timeouts: Async HandleId Pattern&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DebounceHook:&lt;/strong&gt; &lt;a href="https://dev.to/aws/how-to-prevent-ai-agent-reasoning-loops-from-wasting-tokens-2652?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;Prevent AI Agent Reasoning Loops&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Serie completa:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws/stop-ai-agent-hallucinations-4-essential-techniques-2i94?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;Stop AI Agent Hallucinations: 5 Essential Techniques&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws/why-ai-agents-fail-3-failure-modes-that-cost-you-tokens-and-time-1flb?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el"&gt;Why AI Agents Fail: 3 Failure Modes That Cost You Tokens and Time&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Cierre
&lt;/h2&gt;

&lt;p&gt;Cada patrón en este post existe porque algo se rompió en producción. Agentes que alucinaron estadísticas en demos de clientes. Bucles que quemaron tokens a $47/minuto. Desbordamientos de contexto que truncaron datos críticos. Timeouts que congelaron flujos de trabajo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ahora sabes qué se rompe y cómo prevenirlo al hacer prompts correctamente.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cuando le pides a &lt;a href="https://kiro.dev?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt;, Claude Code o ChatGPT que construya un agente, puedes especificar qué patrones aplican. Esa es la diferencia entre prototipos que se rompen y agentes que escalan.&lt;/p&gt;

&lt;p&gt;Úsalo.&lt;/p&gt;



&lt;p&gt;Gracias!&lt;/p&gt;

&lt;p&gt;🇻🇪🇨🇱 &lt;a href="https://dev.to/elizabethfuentes12"&gt;Dev.to&lt;/a&gt; &lt;a href="https://www.linkedin.com/in/lizfue/" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt; &lt;a href="https://github.com/elizabethfuentes12/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; &lt;a href="https://twitter.com/elizabethfue12" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; &lt;a href="https://www.instagram.com/elifue.tech" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt; &lt;a href="https://www.youtube.com/channel/UCr0Gnc-t30m4xyrvsQpNp2Q" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag__user ltag__user__id__717518"&gt;
    &lt;a href="/elizabethfuentes12" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=150,height=150,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F717518%2Fb550b165-b8b9-405d-acfb-e5dc846765b0.png" alt="elizabethfuentes12 image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/elizabethfuentes12"&gt;Elizabeth Fuentes L&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/elizabethfuentes12"&gt;I help developers build production-ready AI applications through hands-on tutorials and open-source projects.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>ai</category>
      <category>tutorial</category>
      <category>programming</category>
      <category>python</category>
    </item>
    <item>
      <title>Por Qué Fallan los Agentes de IA: 3 Modos de Fallo Que Cuestan Tokens y Tiempo</title>
      <dc:creator>Elizabeth Fuentes L</dc:creator>
      <pubDate>Fri, 08 May 2026 23:19:28 +0000</pubDate>
      <link>https://dev.to/aws-espanol/por-que-fallan-los-agentes-de-ia-3-modos-de-fallo-que-cuestan-tokens-y-tiempo-20b</link>
      <guid>https://dev.to/aws-espanol/por-que-fallan-los-agentes-de-ia-3-modos-de-fallo-que-cuestan-tokens-y-tiempo-20b</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Los agentes de IA no fallan como el software tradicional: no se bloquean con un stack trace. Fallan silenciosamente: devuelven respuestas incompletas, se congelan en APIs lentas o queman tokens llamando a la misma herramienta una y otra vez. El agente parece funcionar, pero la salida está mal, llega tarde o es costosa.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Esta serie cubre los tres modos de fallo más comunes con soluciones respaldadas por investigación. Cada técnica tiene una demostración ejecutable que mide la diferencia antes/después.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Código funcional:&lt;/strong&gt; &lt;a href="https://github.com/aws-samples/sample-why-agents-fail/tree/main/stop-ai-agents-wasting-tokens" rel="noopener noreferrer"&gt;github.com/aws-samples/sample-why-agents-fail&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Las demos usan &lt;a href="https://strandsagents.com/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Agents&lt;/a&gt; con &lt;a href="https://platform.openai.com/" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt; (GPT-4o-mini). Los patrones son independientes del framework: aplican a &lt;a href="https://langchain-ai.github.io/langgraph/" rel="noopener noreferrer"&gt;LangGraph&lt;/a&gt;, &lt;a href="https://github.com/microsoft/autogen" rel="noopener noreferrer"&gt;AutoGen&lt;/a&gt;, &lt;a href="https://github.com/crewAIInc/crewAI" rel="noopener noreferrer"&gt;CrewAI&lt;/a&gt; o cualquier framework que soporte llamadas a herramientas y hooks de ciclo de vida.&lt;/p&gt;

&lt;h2&gt;
  
  
  Esta Serie: 3 Soluciones Esenciales
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Desbordamiento de Ventana de Contexto&lt;/strong&gt; — Patrón de Puntero de Memoria para datos grandes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Herramientas MCP Que Nunca Responden&lt;/strong&gt; — Patrón handleId asíncrono para APIs externas lentas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loops de Razonamiento en Agentes de IA&lt;/strong&gt; — DebounceHook + estados claros de herramientas para bloquear llamadas repetidas&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  ¿Qué Sucede Cuando las Salidas de Herramientas Desbordan la Ventana de Contexto?
&lt;/h2&gt;

&lt;p&gt;El desbordamiento de ventana de contexto ocurre cuando una herramienta devuelve más datos de los que el LLM puede procesar: logs del servidor, resultados de bases de datos o contenidos de archivos que exceden el límite de tokens. El agente no falla con un error. Se degrada silenciosamente: trunca datos, pierde contexto o produce respuestas incompletas.&lt;/p&gt;

&lt;p&gt;Una investigación de &lt;a href="https://arxiv.org/html/2511.22729v1" rel="noopener noreferrer"&gt;IBM&lt;/a&gt; cuantifica esto: un flujo de trabajo de Ciencia de Materiales consumió &lt;strong&gt;20 millones de tokens y falló&lt;/strong&gt;. El mismo flujo con punteros de memoria usó &lt;strong&gt;1,234 tokens y tuvo éxito&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz197w37u0rmuq1w3y3n9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz197w37u0rmuq1w3y3n9.jpg" alt="Comparación de un agente de IA sin Patrón de Puntero de Memoria versus con él, mostrando cómo los datos grandes permanecen fuera de la ventana de contexto" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La solución — Patrón de Puntero de Memoria:&lt;/strong&gt; Almacena datos grandes en &lt;code&gt;agent.state&lt;/code&gt;, devuelve un puntero corto al contexto. La siguiente herramienta resuelve el puntero para acceder a los datos completos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ToolContext&lt;/span&gt;

&lt;span class="nd"&gt;@tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_application_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ToolContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Obtiene logs. Almacena datos grandes como puntero para evitar desbordamiento de contexto.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;logs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Podría ser 200KB+
&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;20_000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;pointer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logs-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Datos almacenados como puntero &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;. Usa herramientas de análisis para consultarlo.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;analyze_error_patterns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_pointer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ToolContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Analiza errores — resuelve puntero desde agent.state.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_pointer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ERROR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Se encontraron &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; errores en &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;service&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; servicios&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;El LLM nunca ve los 200KB: solo ve &lt;code&gt;"Datos almacenados como puntero 'logs-payment-service'"&lt;/code&gt; (52 bytes).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Por qué &lt;a href="https://strandsagents.com/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Agents&lt;/a&gt;?&lt;/strong&gt; La API de &lt;a href="https://strandsagents.com/docs/user-guide/concepts/tools/custom-tools/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;&lt;code&gt;ToolContext&lt;/code&gt;&lt;/a&gt; proporciona &lt;code&gt;agent.state&lt;/code&gt; como un almacén clave-valor nativo con alcance para cada agente: sin diccionarios globales, sin infraestructura externa. Para flujos multi-agente, &lt;a href="https://strandsagents.com/docs/user-guide/concepts/multi-agent/multi-agent-patterns/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;&lt;code&gt;invocation_state&lt;/code&gt;&lt;/a&gt; comparte datos entre agentes en un &lt;a href="https://strandsagents.com/docs/user-guide/concepts/multi-agent/swarm/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Swarm&lt;/a&gt; con la misma API.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Métrica&lt;/th&gt;
&lt;th&gt;Sin punteros&lt;/th&gt;
&lt;th&gt;Con Punteros de Memoria&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Datos en contexto&lt;/td&gt;
&lt;td&gt;214KB (logs completos)&lt;/td&gt;
&lt;td&gt;52 bytes (puntero)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comportamiento del agente&lt;/td&gt;
&lt;td&gt;Trunca o falla&lt;/td&gt;
&lt;td&gt;Procesa todos los datos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Errores detectados&lt;/td&gt;
&lt;td&gt;Parcial&lt;/td&gt;
&lt;td&gt;Completo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vsn5ydchfhy6lu831d0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vsn5ydchfhy6lu831d0.png" alt="Gráfico de barras mostrando uso de tokens en diferentes estrategias de gestión de contexto" width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demo completa:&lt;/strong&gt; &lt;a href="https://github.com/aws-samples/sample-why-agents-fail/tree/main/stop-ai-agents-wasting-tokens/01-context-overflow-demo" rel="noopener noreferrer"&gt;01-context-overflow-demo&lt;/a&gt; — implementaciones de agente único y multi-agente (Swarm) con notebooks.&lt;/p&gt;


&lt;h2&gt;
  
  
  ¿Por Qué los Agentes de IA se Congelan al Llamar APIs Externas?
&lt;/h2&gt;

&lt;p&gt;Los agentes de IA se congelan cuando las herramientas MCP llaman a APIs externas lentas o que no responden. El agente se bloquea en la llamada a la herramienta, el usuario no ve progreso, y después de 7 segundos muchas implementaciones devuelven un &lt;a href="https://community.openai.com/t/call-remote-mcp-server-tool-timed-out-resulting-in-error-424/1364167" rel="noopener noreferrer"&gt;error 424&lt;/a&gt;. &lt;a href="https://strandsagents.com/docs/user-guide/concepts/tools/mcp-tools/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;MCP (Model Context Protocol)&lt;/a&gt; les da a los agentes la capacidad de llamar herramientas externas, pero no maneja timeout o reintentos por defecto.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frlvwpo26agw84bvi620c.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frlvwpo26agw84bvi620c.jpg" alt="Llamada síncrona a herramienta MCP mostrando agente bloqueado mientras espera API lenta" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La solución — Patrón handleId asíncrono:&lt;/strong&gt; La herramienta devuelve inmediatamente un ID de trabajo. El agente consulta una herramienta separada &lt;code&gt;check_status&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mcp.server.fastmcp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastMCP&lt;/span&gt;

&lt;span class="n"&gt;mcp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastMCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timeout-demo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;JOBS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_long_job&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Devuelve handle inmediatamente — previene timeout.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;job_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;())[:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;JOBS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;processing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;_process_job&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Trabajo en segundo plano
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Trabajo iniciado. Handle: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Usa check_job_status para consultar.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_job_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Consulta estado del trabajo — devuelve &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;processing&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; o &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;completed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; con resultado.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JOBS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FAILED: Trabajo &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; no encontrado&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Todavía procesando...&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Escenario&lt;/th&gt;
&lt;th&gt;Tiempo de respuesta&lt;/th&gt;
&lt;th&gt;UX&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;API rápida (1s)&lt;/td&gt;
&lt;td&gt;3s total&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API lenta (15s)&lt;/td&gt;
&lt;td&gt;18s bloqueado&lt;/td&gt;
&lt;td&gt;Agente congelado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API fallida&lt;/td&gt;
&lt;td&gt;Error 424 después de 7s&lt;/td&gt;
&lt;td&gt;Agente falla&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;handleId asíncrono&lt;/td&gt;
&lt;td&gt;~4s (inmediato + consulta)&lt;/td&gt;
&lt;td&gt;Agente responde&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj1ef742j0hk1dcvuna54.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj1ef742j0hk1dcvuna54.jpg" alt="Visualización de línea de tiempo mostrando cuatro patrones de respuesta MCP" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Por qué &lt;a href="https://strandsagents.com/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Agents&lt;/a&gt;?&lt;/strong&gt; El &lt;a href="https://strandsagents.com/docs/user-guide/concepts/tools/mcp-tools/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;&lt;code&gt;MCPClient&lt;/code&gt;&lt;/a&gt; se conecta a cualquier servidor MCP. El agente descubre herramientas en tiempo de ejecución vía &lt;code&gt;list_tools_sync()&lt;/code&gt;: sin lista de herramientas codificada. Cuando el servidor MCP implementa el patrón asíncrono, el agente consulta automáticamente sin código de orquestación adicional.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demo completa:&lt;/strong&gt; &lt;a href="https://github.com/aws-samples/sample-why-agents-fail/tree/main/stop-ai-agents-wasting-tokens/02-mcp-timeout-demo" rel="noopener noreferrer"&gt;02-mcp-timeout-demo&lt;/a&gt; — servidor MCP local con los 4 escenarios y notebook.&lt;/p&gt;


&lt;h2&gt;
  
  
  ¿Por Qué los Agentes de IA Repiten la Misma Llamada a Herramienta?
&lt;/h2&gt;

&lt;p&gt;Los loops de razonamiento en agentes de IA ocurren cuando el agente llama a la misma herramienta repetidamente con parámetros idénticos, sin hacer progreso. La causa raíz es retroalimentación ambigua de la herramienta: respuestas como "puede haber más resultados disponibles" hacen que el agente piense que otra llamada producirá mejores resultados. &lt;a href="https://the-decoder.com/language-models-can-overthink-and-get-stuck-in-endless-thought-loops/" rel="noopener noreferrer"&gt;Las investigaciones muestran&lt;/a&gt; que los agentes pueden hacer loops cientos de veces sin entregar una respuesta.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76bs0qj49uby5z0t06np.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76bs0qj49uby5z0t06np.jpg" alt="Diagrama mostrando cómo la retroalimentación ambigua de herramientas causa loops versus cómo estados claros y DebounceHook los previenen" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solución 1 — Estados terminales claros:&lt;/strong&gt; Las herramientas devuelven &lt;code&gt;SUCCESS&lt;/code&gt; o &lt;code&gt;FAILED&lt;/code&gt; explícito en lugar de mensajes ambiguos:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Ambiguo (causa loops)
&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Vuelos encontrados: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Puede haber más resultados disponibles.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Claro (el agente se detiene)
&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SUCCESS: Vuelo &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;conf_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; reservado para &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;passenger&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Confirmación enviada.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Solución 2 — DebounceHook:&lt;/strong&gt; Detecta y bloquea llamadas duplicadas a herramientas a nivel de framework:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.hooks.registry&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HookProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HookRegistry&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.hooks.events&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeforeToolCallEvent&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DebounceHook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HookProvider&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Bloquea llamadas duplicadas a herramientas en una ventana deslizante.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;window_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;window_size&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register_hooks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HookRegistry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BeforeToolCallEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_duplicate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_duplicate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BeforeToolCallEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})))&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel_tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BLOCKED: Llamada duplicada a &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_history&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;window_size&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Estrategia&lt;/th&gt;
&lt;th&gt;Llamadas a herramientas&lt;/th&gt;
&lt;th&gt;Resultado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Retroalimentación ambigua (línea base)&lt;/td&gt;
&lt;td&gt;14 llamadas&lt;/td&gt;
&lt;td&gt;Sin respuesta definitiva&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DebounceHook&lt;/td&gt;
&lt;td&gt;12 llamadas (2 bloqueadas)&lt;/td&gt;
&lt;td&gt;Completa con bloqueos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Estados SUCCESS claros&lt;/td&gt;
&lt;td&gt;2 llamadas&lt;/td&gt;
&lt;td&gt;Completado inmediato&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwgxbs8mu8tl83f1s9s2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwgxbs8mu8tl83f1s9s2.png" alt="Gráfico de barras mostrando llamadas a herramientas en diferentes estrategias" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Por qué &lt;a href="https://strandsagents.com/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Agents&lt;/a&gt;?&lt;/strong&gt; La API de &lt;a href="https://strandsagents.com/docs/user-guide/concepts/agents/hooks/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;&lt;code&gt;HookProvider&lt;/code&gt;&lt;/a&gt; intercepta llamadas a herramientas vía &lt;code&gt;BeforeToolCallEvent&lt;/code&gt; antes de que se ejecuten. Establecer &lt;code&gt;event.cancel_tool&lt;/code&gt; bloquea la ejecución a nivel de framework: el LLM no puede omitirlo. Esto hace que los hooks sean componibles para apilar DebounceHook, LimitToolCounts y validadores personalizados en el mismo agente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demo completa:&lt;/strong&gt; &lt;a href="https://github.com/aws-samples/sample-why-agents-fail/tree/main/stop-ai-agents-wasting-tokens/03-reasoning-loops-demo" rel="noopener noreferrer"&gt;03-reasoning-loops-demo&lt;/a&gt; — los 4 escenarios con hooks y notebook.&lt;/p&gt;


&lt;h2&gt;
  
  
  Requisitos Previos
&lt;/h2&gt;

&lt;p&gt;Necesitas &lt;a href="https://python.org/downloads" rel="noopener noreferrer"&gt;Python 3.9+&lt;/a&gt;, &lt;a href="https://docs.astral.sh/uv/" rel="noopener noreferrer"&gt;uv&lt;/a&gt; (un gestor de paquetes rápido de Python), y una &lt;a href="https://platform.openai.com/api-keys" rel="noopener noreferrer"&gt;clave API de OpenAI&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/aws-samples/sample-why-agents-fail
&lt;span class="nb"&gt;cd &lt;/span&gt;sample-why-agents-fail/stop-ai-agents-wasting-tokens

&lt;span class="c"&gt;# Elige cualquier demo&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;01-context-overflow-demo   &lt;span class="c"&gt;# o 02-mcp-timeout-demo, 03-reasoning-loops-demo&lt;/span&gt;
uv venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; uv pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"tu-clave-aquí"&lt;/span&gt;

uv run python test_&lt;span class="k"&gt;*&lt;/span&gt;.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Cada demo es independiente con sus propias dependencias, script de prueba y notebook de &lt;a href="https://jupyter.org/" rel="noopener noreferrer"&gt;Jupyter&lt;/a&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Preguntas Frecuentes
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ¿Cuáles son los modos de fallo más comunes en agentes de IA?
&lt;/h3&gt;

&lt;p&gt;Los tres modos de fallo más comunes son el desbordamiento de ventana de contexto (la herramienta devuelve más datos de los que el LLM puede procesar), timeouts de herramientas MCP (APIs externas bloquean al agente indefinidamente) y loops de razonamiento (el agente repite la misma llamada a herramienta sin progresar). Cada modo de fallo causa desperdicio de tokens y degrada la calidad de respuesta.&lt;/p&gt;
&lt;h3&gt;
  
  
  ¿Cómo reduzco los costos de tokens de un agente de IA?
&lt;/h3&gt;

&lt;p&gt;Las dos técnicas más efectivas son los punteros de memoria y estados claros de herramientas. El Patrón de Puntero de Memoria almacena salidas grandes de herramientas en estado externo y pasa referencias cortas al contexto del LLM, reduciendo el uso de tokens de más de 200KB a menos de 100 bytes por llamada a herramienta. Estados terminales claros (&lt;code&gt;SUCCESS&lt;/code&gt;/&lt;code&gt;FAILED&lt;/code&gt;) en respuestas de herramientas previenen que el agente reintente operaciones completadas, lo que puede reducir las llamadas a herramientas de 14 a 2.&lt;/p&gt;
&lt;h3&gt;
  
  
  ¿Puedo usar estos patrones con frameworks distintos a Strands Agents?
&lt;/h3&gt;

&lt;p&gt;Sí. El Patrón de Puntero de Memoria funciona con cualquier framework que soporte contexto de herramientas (pasar estado entre herramientas). El patrón handleId asíncrono es un patrón de diseño de servidor MCP: funciona con cualquier agente compatible con MCP. DebounceHook requiere hooks de ciclo de vida, que están disponibles en &lt;a href="https://langchain-ai.github.io/langgraph/" rel="noopener noreferrer"&gt;LangGraph&lt;/a&gt;, &lt;a href="https://github.com/microsoft/autogen" rel="noopener noreferrer"&gt;AutoGen&lt;/a&gt; y &lt;a href="https://github.com/crewAIInc/crewAI" rel="noopener noreferrer"&gt;CrewAI&lt;/a&gt; con APIs diferentes.&lt;/p&gt;


&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Investigación
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/html/2511.22729v1" rel="noopener noreferrer"&gt;Solving Context Window Overflow in AI Agents&lt;/a&gt; — IBM Research, Nov 2025&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/pdf/2412.05449" rel="noopener noreferrer"&gt;Towards Effective GenAI Multi-Agent Collaboration&lt;/a&gt; — Amazon, Dec 2024&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://octopus.com/blog/mcp-timeout-retry" rel="noopener noreferrer"&gt;Resilient AI Agents With MCP&lt;/a&gt; — Octopus, May 2025&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://the-decoder.com/language-models-can-overthink-and-get-stuck-in-endless-thought-loops/" rel="noopener noreferrer"&gt;Language models can overthink&lt;/a&gt; — The Decoder, Jan 2025&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Implementación
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/concepts/agents/state/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Agent State&lt;/a&gt; — ToolContext and agent.state&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/concepts/tools/mcp-tools/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands MCP Tools&lt;/a&gt; — Connect any MCP server&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/concepts/agents/hooks/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands Hooks&lt;/a&gt; — Lifecycle events and tool cancellation&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;¿Qué modo de fallo has encontrado en tus agentes? Comparte en los comentarios.&lt;/p&gt;



&lt;p&gt;Gracias!&lt;/p&gt;

&lt;p&gt;🇻🇪🇨🇱 &lt;a href="https://dev.to/elizabethfuentes12"&gt;Dev.to&lt;/a&gt; &lt;a href="https://www.linkedin.com/in/lizfue/" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt; &lt;a href="https://github.com/elizabethfuentes12/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; &lt;a href="https://twitter.com/elizabethfue12" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; &lt;a href="https://www.instagram.com/elifue.tech" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt; &lt;a href="https://www.youtube.com/channel/UCr0Gnc-t30m4xyrvsQpNp2Q" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag__user ltag__user__id__717518"&gt;
    &lt;a href="/elizabethfuentes12" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=150,height=150,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F717518%2Fb550b165-b8b9-405d-acfb-e5dc846765b0.png" alt="elizabethfuentes12 image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/elizabethfuentes12"&gt;Elizabeth Fuentes L&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/elizabethfuentes12"&gt;I help developers build production-ready AI applications through hands-on tutorials and open-source projects.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>ai</category>
      <category>tutorial</category>
      <category>python</category>
      <category>programming</category>
    </item>
    <item>
      <title>IAM Principal Cost Allocation para Amazon Bedrock (Novedad)</title>
      <dc:creator>Hector Fernandez CloudparaTodo</dc:creator>
      <pubDate>Mon, 13 Apr 2026 07:30:00 +0000</pubDate>
      <link>https://dev.to/aws-espanol/iam-principal-cost-allocation-para-amazon-bedrock-novedad-26cm</link>
      <guid>https://dev.to/aws-espanol/iam-principal-cost-allocation-para-amazon-bedrock-novedad-26cm</guid>
      <description>&lt;p&gt;Hace unos meses inicie una serie de posts sobre como gobernar el uso de IA en AWS. En la primera entrega de esta serie hablamos de cómo dar acceso gobernado a LLMs en AWS desde el día 0: IAM Policies, Guardrails, Inference Profiles y un mecanismo de corte de presupuesto por equipo. Si no la leíste, te recomiendo empezar por ahí.&lt;br&gt;
&lt;a href="https://podcast.hectorfernandez.dev/p/episodio-9-necesito-una-api-key-para" rel="noopener noreferrer"&gt;Podcast: UNA API KEY para LLMs&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Pero había un punto que me quedaba pendiente: &lt;strong&gt;no podíamos saber quién dentro de un equipo estaba generando el consumo&lt;/strong&gt;. Cortábamos al equipo entero, y después había que armar una solución custom con Model Invocation Logging para identificar al responsable.&lt;/p&gt;

&lt;p&gt;AWS acaba de resolver la parte de &lt;strong&gt;visibilidad&lt;/strong&gt; de forma nativa. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Bloquear todo el acceso?&lt;/strong&gt; Eso sigue siendo otro tema, y creo que de forma nativa sería complejo de implementar un proxy (por ahora).&lt;/p&gt;
&lt;h2&gt;
  
  
  ¿Qué anunció AWS?
&lt;/h2&gt;

&lt;p&gt;El 8 de Abril de 2026, Amazon Bedrock lanzó soporte para &lt;strong&gt;asignación de costos por IAM principal&lt;/strong&gt;, hablando en criollo: por usuario o rol de IAM, directamente en Cost Explorer y en CUR 2.0 (Cost and Usage Report).&lt;/p&gt;

&lt;p&gt;¡BIEN! Ahora podemos ver el costo desglosado de Bedrock en Cost Explorer sin muchas vueltas. &lt;br&gt;
&lt;a href="https://aws.amazon.com/es/about-aws/whats-new/2026/04/bedrock-iam-cost-allocation/" rel="noopener noreferrer"&gt;Anuncio oficial&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ahora, algo que debes tener claro desde ya: esta funcionalidad es de &lt;strong&gt;billing&lt;/strong&gt;, no de enforcement. Los datos llegan a CUR 2.0 y Cost Explorer con &lt;strong&gt;24-48 horas de latencia&lt;/strong&gt;. Eso significa que puedes saber &lt;em&gt;quién&lt;/em&gt; gastó &lt;em&gt;cuánto&lt;/em&gt;, pero &lt;strong&gt;no puedes bloquear el acceso en tiempo real con esta data&lt;/strong&gt;. Para eso, el Budget Cut Lambda de la Parte 1 sigue siendo necesario.&lt;/p&gt;
&lt;h2&gt;
  
  
  ¿Qué cambia respecto a la Parte 1 que habíamos hablado?
&lt;/h2&gt;

&lt;p&gt;En la primera publicación usamos &lt;strong&gt;Inference Profiles con tags&lt;/strong&gt; (&lt;code&gt;CostCenter&lt;/code&gt;, &lt;code&gt;Team&lt;/code&gt;) para atribuir costos por equipo. Eso sigue siendo válido para agrupar por carga de trabajo. Pero ahora hay una capa adicional: la &lt;strong&gt;identidad del que hace la llamada&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mecanismo&lt;/th&gt;
&lt;th&gt;Granularidad&lt;/th&gt;
&lt;th&gt;¿Qué resuelve?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Inference Profile + Resource Tags&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Por equipo / carga de trabajo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"¿Cuánto gastó el equipo backend en Haiku?"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;IAM Principal Cost Allocation&lt;/strong&gt; (NUEVO)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Por usuario / rol&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"¿Cuánto gastó &lt;code&gt;user@empresa.com&lt;/code&gt; en todos los modelos?"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Juntando todo esto, tenemos: &lt;strong&gt;quién&lt;/strong&gt; gastó &lt;strong&gt;cuánto&lt;/strong&gt;, en &lt;strong&gt;qué modelo&lt;/strong&gt;, para &lt;strong&gt;qué equipo&lt;/strong&gt;. Pero recuerda: esta foto la ves con &lt;strong&gt;24-48h de retraso&lt;/strong&gt;. Es para análisis y chargeback, no para corte en tiempo real.&lt;/p&gt;
&lt;h2&gt;
  
  
  Pre-requisitos
&lt;/h2&gt;

&lt;p&gt;Todo lo de la Parte 1 más (te invito a leerla)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tags en tus IAM users/roles&lt;/strong&gt; con atributos de negocio (&lt;code&gt;team&lt;/code&gt;, &lt;code&gt;business-unit&lt;/code&gt;, &lt;code&gt;project&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Acceso a la consola de &lt;strong&gt;Billing and Cost Management&lt;/strong&gt; (si es por Organizations, lo haces desde la cuenta management)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CUR 2.0&lt;/strong&gt; habilitado (el CUR legacy no soporta esto)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Paso 1: Taggear los IAM Principals
&lt;/h3&gt;

&lt;p&gt;Si usas SSO, cada Permission Set genera un rol en la cuenta target con formato &lt;code&gt;AWSReservedSSO_{PermissionSetName}_{hash}&lt;/code&gt;. Estos roles se pueden taggear.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
Taggea los roles SSO con atributos de negocio.
Estos tags son los que aparecerán en Cost Explorer y CUR 2.0.
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="n"&gt;iam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;iam&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Mapeo: role SSO -&amp;gt; tags de negocio
&lt;/span&gt;&lt;span class="n"&gt;SSO_ROLES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWSReservedSSO_BackendDev_a1b2c3d4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;team&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;backend&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;business-unit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BU-ENG-001&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;department&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;engineering&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;environment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;development&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWSReservedSSO_FrontendDev_e5f6g7h8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;team&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;frontend&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;business-unit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BU-ENG-002&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;department&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;engineering&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;environment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;development&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWSReservedSSO_DataTeam_i9j0k1l2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;team&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;business-unit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BU-DATA-001&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;department&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data-science&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;environment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;development&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;role_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags_dict&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;SSO_ROLES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tags_dict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;

    &lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tag_role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RoleName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;role_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅ &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;role_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; taggeado con: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tags_dict&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si además tienen &lt;strong&gt;IAM Users&lt;/strong&gt; (para casos legacy o service accounts), se taggean igual:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iam tag-user &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user-name&lt;/span&gt; &lt;span class="s2"&gt;"superuser-pipeline-sa"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tags&lt;/span&gt; &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;team,Value&lt;span class="o"&gt;=&lt;/span&gt;data &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;business-unit,Value&lt;span class="o"&gt;=&lt;/span&gt;BU-DATA-001 &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;project,Value&lt;span class="o"&gt;=&lt;/span&gt;recommendation-engine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota importante de AWS:&lt;/strong&gt; Los tags solo aparecen para activación en la consola de Billing &lt;strong&gt;después de que el principal haya hecho al menos una llamada a Bedrock&lt;/strong&gt;. Si taggeaste un rol pero nadie lo ha usado aún, no vas a verlo en Cost Allocation Tags.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Paso 2: Activar los tags como Cost Allocation Tags
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6twri5bir2oihv2b3jte.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6twri5bir2oihv2b3jte.png" alt="Activar allocation tag" width="800" height="283"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Billing and Cost Management Console
    → Cost Organization 
        → Cost Allocation Tags (Etiquetas de asignación de costos)
            → Filtrar por "IAM principal type"
            → Seleccionar: team, business-unit, department
            → Click "Activate"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Después de activarlos, los tags tardan &lt;strong&gt;hasta 24 horas&lt;/strong&gt; en estar disponibles para filtrado en Cost Explorer y CUR.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Verificar qué tags están activos via CLI&lt;/span&gt;
aws ce list-cost-allocation-tags &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--status&lt;/span&gt; Active &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tag-keys&lt;/span&gt; &lt;span class="s2"&gt;"team"&lt;/span&gt; &lt;span class="s2"&gt;"business-unit"&lt;/span&gt; &lt;span class="s2"&gt;"department"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--type&lt;/span&gt; &lt;span class="s2"&gt;"iamPrincipal"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Paso 3: Habilitar IAM Principal en CUR 2.0
&lt;/h3&gt;

&lt;p&gt;Si hay un paso para que todo esto funcione, es este. &lt;/p&gt;

&lt;p&gt;Activa la columna &lt;code&gt;line_item_iam_principal&lt;/code&gt; en los reportes de costos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Billing and Cost Management Console
    → Data Exports
        → Create export → Standard data export (CUR 2.0)
            → Additional export content:
                ✅ Include caller identity (IAM principal) allocation data
            → Destino: S3 bucket + Athena integration
        → Save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;¿Qué genera esto?&lt;/strong&gt; Cada línea del CUR 2.0 ahora incluye el ARN exacto del principal que hizo la llamada a Bedrock. Y los tags del principal aparecen con prefijo &lt;code&gt;iamPrincipal/&lt;/code&gt; para no colisionar con resource tags.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Cómo se ve la data?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  En Cost Explorer: filtrar por equipo/usuario
&lt;/h3&gt;

&lt;p&gt;Una vez activados los tags, Cost Explorer permite agrupar directamente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cost Explorer
    → Filtro: Service = "Amazon Bedrock"
    → Group by: Tag → "iamPrincipal/team"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vas a ver algo como:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csvs"&gt;&lt;code&gt;&lt;span class="k"&gt;iamPrincipal&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="k"&gt;team&lt;/span&gt;    &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Costo&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;USD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;---------------------|------------&lt;/span&gt;
&lt;span class="k"&gt;backend&lt;/span&gt;              &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;$142.30&lt;/span&gt;
&lt;span class="k"&gt;data&lt;/span&gt;                 &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;$89.50&lt;/span&gt;
&lt;span class="k"&gt;frontend&lt;/span&gt;             &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;$23.10&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sin&lt;/span&gt; &lt;span class="k"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;            &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;$5.40&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;¿Y si quieres ver quién dentro del equipo backend está consumiendo más? Cambias el Group by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cost Explorer
    → Filtro: Service = "Amazon Bedrock"
    → Filtro: Tag "iamPrincipal/team" = "backend"
    → Group by: Tag → "iamPrincipal/business-unit"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;BONUS&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  (Opcional) CUR 2.0 + Athena: análisis avanzado por usuario individual
&lt;/h3&gt;

&lt;p&gt;Si Cost Explorer no te da suficiente granularidad y necesitas ver &lt;strong&gt;por usuario individual&lt;/strong&gt;, puedes consultar CUR 2.0 directamente con Amazon Athena.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Sobre costos de Athena:&lt;/strong&gt; Athena cobra &lt;strong&gt;$5 USD por TB escaneado&lt;/strong&gt; (con el modo on-demand). Para CUR de organizaciones pequeñas/medianas esto suele ser centavos por consulta. Si quieres reducir costos, activa la compresión del CUR (formato Parquet) y particiona por mes. También existe el modo Provisioned Capacity para uso intensivo.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- ¿Quiénes son los top 10 consumers de Bedrock este mes?&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;line_item_iam_principal&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;iam_principal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'iamPrincipal/team'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'iamPrincipal/business-unit'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;business_unit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;line_item_product_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line_item_unblended_cost&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_cost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line_item_usage_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_usage&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;cur_2_0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bedrock_usage&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
    &lt;span class="n"&gt;line_item_product_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'AmazonBedrock'&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;EXTRACT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;MONTH&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;billing_period&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;EXTRACT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;MONTH&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;CURRENT_DATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;total_cost&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resultado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csvs"&gt;&lt;code&gt;&lt;span class="k"&gt;iam&lt;/span&gt;&lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="k"&gt;principal&lt;/span&gt;                                          &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="k"&gt;team&lt;/span&gt;     &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="k"&gt;business&lt;/span&gt;&lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="k"&gt;unit&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="k"&gt;total&lt;/span&gt;&lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="k"&gt;cost&lt;/span&gt;
&lt;span class="err"&gt;-------------------------------------------------------+----------+---------------+-----------&lt;/span&gt;
&lt;span class="nv"&gt;arn:aws:sts:&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;123456&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="k"&gt;assumed&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="k"&gt;BackendDev&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="k"&gt;javier&lt;/span&gt;&lt;span class="err"&gt;@...&lt;/span&gt;  &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="k"&gt;backend&lt;/span&gt;  &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="k"&gt;BU&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;ENG&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;001&lt;/span&gt;    &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;$67.30&lt;/span&gt;
&lt;span class="nv"&gt;arn:aws:sts:&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;123456&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="k"&gt;assumed&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="k"&gt;DataTeam&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="k"&gt;maria&lt;/span&gt;&lt;span class="err"&gt;@...&lt;/span&gt;     &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;     &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="k"&gt;BU&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;DATA&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;001&lt;/span&gt;   &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;$52.10&lt;/span&gt;
&lt;span class="nv"&gt;arn:aws:sts:&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;123456&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="k"&gt;assumed&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="k"&gt;BackendDev&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="k"&gt;luis&lt;/span&gt;&lt;span class="err"&gt;@...&lt;/span&gt;    &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="k"&gt;backend&lt;/span&gt;  &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="k"&gt;BU&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;ENG&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;001&lt;/span&gt;    &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;$41.20&lt;/span&gt;
&lt;span class="err"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sin proxies, sin Lambdas, sin CloudTrail scraping: &lt;strong&gt;sabes exactamente que Javier del equipo backend gastó $67.30 este mes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Recuerda que cada ejecución tiene un costo mínimo en Athena, pero para un CUR particionado en Parquet suele ser menos de $0.01 por consulta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap ¿Cómo se complementa con la Parte 1?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Componente&lt;/th&gt;
&lt;th&gt;Parte 1&lt;/th&gt;
&lt;th&gt;Parte 2 (lo nuevo)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;¿Quién puede usar qué modelo?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;IAM Policy per-team&lt;/td&gt;
&lt;td&gt;Sin cambios&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;¿Se protege PII?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bedrock Guardrails&lt;/td&gt;
&lt;td&gt;Sin cambios&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;¿Cuánto gastó cada equipo?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Inference Profile tags → Cost Explorer&lt;/td&gt;
&lt;td&gt;IAM Principal tags → Cost Explorer (más granular)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;¿Cuánto gastó cada usuario?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No disponible&lt;/td&gt;
&lt;td&gt;✅ &lt;code&gt;line_item_iam_principal&lt;/code&gt; en CUR 2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;¿Se corta al exceder presupuesto?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Budget Cut Lambda (per-team, ~10 min)&lt;/td&gt;
&lt;td&gt;⚠️ CUR tiene 24h de delay. &lt;strong&gt;NO sirve para bloquear&lt;/strong&gt;. El Budget Cut Lambda sigue siendo el único mecanismo de corte&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Lo que sigue sin resolver nativamente
&lt;/h3&gt;

&lt;p&gt;Seamos honestos: hay cosas que necesitan un proxy real, y para dar gobierno a la IA es lo mejor que puedes pensar. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Corte per-user en tiempo real&lt;/strong&gt;: CUR tiene 24h de delay. Si necesitas cortar a un usuario específico en minutos, necesitas Model Invocation Logging + Lambda + &lt;code&gt;iam:PutRolePolicy&lt;/code&gt; dirigido al session principal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching de respuestas&lt;/strong&gt;: No hay caching nativo de Bedrock. Un proxy como LiteLLM puede cachear respuestas repetitivas y ahorrar costos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-provider routing&lt;/strong&gt;: Si quieres probar OpenAI y Anthropic directamente (no vía Bedrock), necesitas una capa de abstracción&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observabilidad semántica&lt;/strong&gt;: Para ver el árbol de razonamiento de un agente, necesitas OTel + Langfuse. CloudTrail te dice "quién llamó", pero no "por qué razonó así"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La clave es que los tags del &lt;strong&gt;principal&lt;/strong&gt; (quién) y los tags del &lt;strong&gt;recurso&lt;/strong&gt; (qué) se complementan en CUR 2.0. No se pisan porque CUR los distingue con prefijos: &lt;code&gt;iamPrincipal/team&lt;/code&gt; vs &lt;code&gt;resourceTag/Team&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusiones
&lt;/h2&gt;

&lt;p&gt;Con este anuncio, el modelo de gobierno nativo que planteamos en la Parte 1 gana una pieza que faltaba: &lt;strong&gt;visibilidad de costos por usuario&lt;/strong&gt;, sin añadir infraestructura, sin scraping de CloudTrail, sin Lambdas custom para atribución. Es una herramienta de análisis y chargeback, no de enforcement. El bloqueo en tiempo real sigue dependiendo del Budget Cut Lambda y CloudWatch.&lt;/p&gt;

&lt;p&gt;Siempre pensemos en etapas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Día 0&lt;/strong&gt;: IAM Policies + Guardrails + Inference Profiles → acceso gobernado&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Día 1&lt;/strong&gt;: Budget Cut Lambda, protección contra gastos descontrolados&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ahora (Día 2)&lt;/strong&gt;: IAM Principal Cost Allocation, saber exactamente quién gasta qué&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;¿Falta mucho por hacer? Sí. Un proxy real (LiteLLM), observabilidad semántica (Langfuse), y caching siguen siendo evoluciones deseables si la organización escala. Pero la base está puesta, y es 100% nativa.&lt;/p&gt;

&lt;p&gt;Lo bueno de todo esto: &lt;strong&gt;AWS&lt;/strong&gt; sigue mejorando sus productos, pero nosotros como arquitectos debemos de saber identificar el uso para nuestras necesidades.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hoy es cost allocation por principal. Mañana quizás sea throttling per-user nativo o guardrails a nivel de servicio que no requieran &lt;code&gt;guardrailConfig&lt;/code&gt; en el código del dev.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lo que conoces hoy en día en cloud no queda de un lado, todo ese conocimiento es MUY necesario para disponibilizar IA de forma responsable y sobre todo cuantificable. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;¿Te gustaría que estemos en 📩 contacto?&lt;/em&gt;&lt;br&gt;
Te espero en LinkedIn o desde el Podcast: Cloud para Todos&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Héctor Fernández&lt;/strong&gt;&lt;br&gt;
AWS Community Builder&lt;/p&gt;

&lt;p&gt;&lt;a href="https://podcast.hectorfernandez.dev" rel="noopener noreferrer"&gt;https://podcast.hectorfernandez.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>bedrock</category>
      <category>aws</category>
      <category>gobierno</category>
    </item>
  </channel>
</rss>
