<?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.us-east-2.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>Memoria de Agentes de IA: Conversación vs Contexto</title>
      <dc:creator>Elizabeth Fuentes L</dc:creator>
      <pubDate>Wed, 17 Jun 2026 00:25:37 +0000</pubDate>
      <link>https://dev.to/aws-espanol/memoria-de-agentes-de-ia-conversacion-vs-contexto-4j3e</link>
      <guid>https://dev.to/aws-espanol/memoria-de-agentes-de-ia-conversacion-vs-contexto-4j3e</guid>
      <description>&lt;p&gt;Tu agente quema su presupuesto de tokens en una sola llamada a una herramienta, o se olvida de lo que el usuario dijo hace tres turnos. Misma causa raíz: tiene &lt;strong&gt;dos tipos de memoria&lt;/strong&gt; y se mezclaron. Una guarda la conversación; la otra guarda salidas grandes de herramientas, como logs. Necesitan almacenamiento distinto y recuperación distinta, y tratarlas como un solo almacén es lo que hace a los agentes lentos, caros y equivocados.&lt;/p&gt;

&lt;p&gt;Este post muestra cómo mantenerlas separadas: el framework ahora descarga los datos grandes por ti (se acabó el código de punteros a mano), y en producción las dos memorias se mapean a dos servicios de AWS. Lo desplegué y medí la diferencia.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Continúa &lt;a href="https://dev.to/aws/ai-context-window-overflow-memory-pointer-fix-3akc"&gt;AI Context Window Overflow: Memory Pointer Fix&lt;/a&gt;. El código 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 se trasladan a otros frameworks. Repo: &lt;a href="https://github.com/aws-samples/sample-why-agents-fail" rel="noopener noreferrer"&gt;sample-why-agents-fail&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  ¿Cuáles son los dos tipos de memoria de un agente?
&lt;/h2&gt;

&lt;p&gt;Un agente de IA tiene dos tipos de memoria: la &lt;strong&gt;memoria de conversación&lt;/strong&gt; guarda lo que se dijo (turnos, preferencias, hechos) y se recupera por significado, mientras que la &lt;strong&gt;memoria de contexto&lt;/strong&gt; guarda salidas grandes de herramientas (logs, datasets, documentos) y se recupera por un identificador exacto. Son almacenes distintos con recuperación distinta, y usar uno donde corresponde el otro es la causa raíz tanto de "mi agente se olvida de las cosas" como de "mi agente reventó el presupuesto de tokens".&lt;/p&gt;

&lt;p&gt;Antes de cualquier código, deja clara la distinción:&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;Memoria de conversación&lt;/th&gt;
&lt;th&gt;Memoria de contexto&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Guarda&lt;/td&gt;
&lt;td&gt;Turnos, preferencias, hechos extraídos&lt;/td&gt;
&lt;td&gt;Salidas grandes de herramientas (logs, datasets)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Se recupera por&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Significado&lt;/strong&gt; (similitud semántica)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Identificador exacto&lt;/strong&gt; (una referencia)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pregunta que responde&lt;/td&gt;
&lt;td&gt;"¿Qué me dijo el usuario antes?"&lt;/td&gt;
&lt;td&gt;"Devuélveme ese archivo de log de 5 MB, exacto"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mal encaje para&lt;/td&gt;
&lt;td&gt;Un blob de log de 5 MB&lt;/td&gt;
&lt;td&gt;"¿Cuál era el nombre del usuario?"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Esa tabla es todo el artículo. Lo que sigue es solo dónde vive cada fila en el código.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué la memoria de contexto se desborda primero
&lt;/h2&gt;

&lt;p&gt;Las salidas grandes de herramientas desbordan la ventana de contexto porque son &lt;strong&gt;indivisibles y se reenvían en cada llamada al modelo&lt;/strong&gt;. Una herramienta que devuelve 200 KB de logs no cuesta 200 KB una sola vez. Ese payload viaja en la entrada de cada turno posterior hasta que empuja la pregunta original fuera de la ventana.&lt;/p&gt;

&lt;p&gt;El primer post lo cuantificó con 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;): un flujo de ciencia de materiales que consumía 20.822.181 tokens y fallaba bajó a 1.234 tokens y tuvo éxito una vez que los datos grandes se almacenaron fuera del contexto y se referenciaron con un puntero.&lt;/p&gt;

&lt;h2&gt;
  
  
  El arreglo, antes y ahora: deja de poner datos en la conversación
&lt;/h2&gt;

&lt;p&gt;El post original almacenaba los datos grandes a mano: una herramienta los escribía en &lt;code&gt;agent.state&lt;/code&gt; y devolvía una cadena-puntero corta; la siguiente herramienta los leía de vuelta con esa clave. Funciona, pero la lógica de descarga vivía dentro de cada herramienta.&lt;/p&gt;

&lt;p&gt;Strands ahora trae ese mismo patrón como un plugin de primera clase, &lt;code&gt;ContextOffloader&lt;/code&gt;, así que tus herramientas vuelven a ser funciones ordinarias:&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.vended_plugins.context_offloader&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ContextOffloader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileStorage&lt;/span&gt;

&lt;span class="c1"&gt;# Herramientas ordinarias — sin lógica de punteros, sin agent.state dentro
&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="n"&gt;MODEL&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;count_errors_by_service&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;ContextOffloader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;FileStorage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./artifacts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                              &lt;span class="n"&gt;max_result_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;preview_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)],&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fetch 2 hours of logs for &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;api-gateway&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; and tell me the top error service.&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;Cuando el resultado de una herramienta supera &lt;code&gt;max_result_tokens&lt;/code&gt;, el plugin lo intercepta, almacena cada bloque en el backend y deja un pequeño preview más una referencia en el contexto. El agente obtiene una herramienta &lt;code&gt;retrieve_offloaded_content(reference)&lt;/code&gt; para traer de vuelta los datos completos &lt;strong&gt;por referencia exacta&lt;/strong&gt; cuando realmente los necesita.&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%2F9h60cvdragnocw00ck08.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%2F9h60cvdragnocw00ck08.png" alt="Patrón Memory Pointer manual vs nativo: a la izquierda, las herramientas guardan datos en agent.state y devuelven un puntero a mano; a la derecha, herramientas ordinarias devuelven datos y el plugin ContextOffloader descarga los resultados grandes a un backend de almacenamiento, dejando solo un preview más una referencia en el contexto, cerca de 97% menos tokens" width="800" height="776"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  ¿Qué es el patrón Memory Pointer nativo en Strands?
&lt;/h3&gt;

&lt;p&gt;El patrón Memory Pointer nativo es &lt;code&gt;ContextOffloader&lt;/code&gt;, un plugin que intercepta resultados de herramientas demasiado grandes en tiempo de ejecución, almacena cada bloque en un backend de almacenamiento y reemplaza el resultado en contexto con un preview más una referencia. Los datos grandes nunca inundan la ventana de contexto, y tus herramientas nunca tocan lógica de punteros.&lt;/p&gt;
&lt;h3&gt;
  
  
  Resultados medidos
&lt;/h3&gt;

&lt;p&gt;Corrí la misma consulta con tres estrategias. Misma consulta, &lt;code&gt;gpt-4o-mini&lt;/code&gt;, 2 horas de logs:&lt;/p&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;Tokens en contexto&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sin gestión&lt;/td&gt;
&lt;td&gt;~18.000 a 20.000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;ContextOffloader&lt;/code&gt; (FileStorage)&lt;/td&gt;
&lt;td&gt;~490&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;context_manager="auto"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~1.000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Eso es aproximadamente &lt;strong&gt;97% menos tokens&lt;/strong&gt; para la misma respuesta. Los números varían por ejecución porque los datos de log son aleatorios; &lt;code&gt;test_native_pointer.py&lt;/code&gt; los reproduce.&lt;/p&gt;

&lt;p&gt;Una salvedad honesta: el offloader es una &lt;strong&gt;red de seguridad&lt;/strong&gt;, no la victoria completa. El gran ahorro viene de combinarlo con una &lt;strong&gt;herramienta selectiva&lt;/strong&gt;. Mi &lt;code&gt;count_errors_by_service&lt;/code&gt; calcula la respuesta del lado del servidor y devuelve un resumen pequeño, así el agente responde desde el resumen y los logs se quedan descargados. Sin una herramienta selectiva, un agente que necesite el dataset completo simplemente llamará a &lt;code&gt;retrieve_offloaded_content&lt;/code&gt; y lo traerá todo de vuelta. El offloader garantiza que no desbordes; las herramientas selectivas son las que mantienen bajo el conteo de tokens.&lt;/p&gt;
&lt;h3&gt;
  
  
  Una línea para la mayoría de los agentes
&lt;/h3&gt;

&lt;p&gt;Para un agente típico de múltiples turnos no configuras la descarga y el resumen por separado:&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;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="n"&gt;MODEL&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;context_manager&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&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;Esto compone un &lt;code&gt;SummarizingConversationManager&lt;/code&gt; (resume el historial antiguo con compresión proactiva) y un &lt;code&gt;ContextOffloader&lt;/code&gt; (en memoria) con valores por defecto validados por benchmark. Lo que pases explícitamente tiene prioridad.&lt;/p&gt;
&lt;h2&gt;
  
  
  La misma idea, sobre almacenamiento real en Amazon S3
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;FileStorage&lt;/code&gt; escribe en disco local. Cambia una línea y las salidas grandes de herramientas aterrizan en un bucket S3 real, recuperadas por referencia exacta, nunca en la ventana:&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.vended_plugins.context_offloader&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ContextOffloader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;S3Storage&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="n"&gt;MODEL&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;count_errors_by_service&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;ContextOffloader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;S3Storage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CONTEXT_BUCKET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;log-artifacts/&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;Un dataset de logs de 83 KB se almacenó en S3, ~486 tokens quedaron en contexto, y los datos volvieron &lt;strong&gt;byte por byte por su referencia exacta&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;📊 Tokens left in LLM context:  486
📦 Objects offloaded to S3:     1
   pointer in context:  s3://…/log-artifacts/1781569100199_1_call_…_0
   storage.retrieve()  → 77,050 bytes  (text/plain)
   verified: 200 log events recovered verbatim — exact data, no loss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Esa es la segunda fila de la tabla, en forma de producción: &lt;strong&gt;recuperación por identificador exacto&lt;/strong&gt;. No quieres "los logs más parecidos a mi consulta". Quieres &lt;em&gt;esos&lt;/em&gt; logs, exactos. Eso es almacenamiento de objetos, no búsqueda semántica.&lt;/p&gt;
&lt;h2&gt;
  
  
  Producción: dos memorias, a propósito
&lt;/h2&gt;

&lt;p&gt;En producción la separación se vuelve arquitectura. Un agente en &lt;a href="https://aws.amazon.com/bedrock/agentcore/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Amazon Bedrock AgentCore&lt;/a&gt; mantiene cada memoria donde corresponde:&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%2Fkeh19c3490433hi0lpvy.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%2Fkeh19c3490433hi0lpvy.png" alt="Arquitectura de producción: un agente en AgentCore Runtime con memoria de conversación en AgentCore Memory recuperada por similitud semántica, y memoria de datos en Amazon S3 recuperada por referencia exacta, con el rol de ejecución otorgando acceso a S3" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Conversación → AgentCore Memory.&lt;/strong&gt; Turnos, preferencias y hechos extraídos, recuperados por similitud semántica (&lt;code&gt;RetrieveMemoryRecords&lt;/code&gt;: embeddings, &lt;code&gt;top_k&lt;/code&gt;, score de relevancia), con alcance por usuario mediante &lt;code&gt;actor_id&lt;/code&gt;. Conectado mediante el &lt;code&gt;AgentCoreMemorySessionManager&lt;/code&gt; de Strands.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memoria de contexto → Amazon S3.&lt;/strong&gt; El mismo &lt;code&gt;ContextOffloader&lt;/code&gt;, con &lt;code&gt;S3Storage&lt;/code&gt; en vez de &lt;code&gt;FileStorage&lt;/code&gt;. Recuperada por referencia exacta.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;¿Por qué no poner los logs también en AgentCore Memory? Porque AgentCore Memory recupera la memoria &lt;em&gt;semánticamente más parecida&lt;/em&gt;, que es exactamente lo equivocado para "devuelve este dataset textual por id". La conversación quiere significado; los datos quieren una clave exacta. Un agente, dos memorias, cada una haciendo lo que sabe hacer bien.&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;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="nc"&gt;BedrockModel&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="n"&gt;REGION&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;count_errors_by_service&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;session_manager&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;AgentCoreMemorySessionManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory_config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REGION&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;     &lt;span class="c1"&gt;# conversación
&lt;/span&gt;    &lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;ContextOffloader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;S3Storage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CONTEXT_BUCKET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&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;# datos
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Observabilidad y evaluación vienen gratis
&lt;/h2&gt;

&lt;p&gt;En AgentCore, la observabilidad completa está integrada. Agregas la librería de instrumentación y obtienes trazas, métricas y logs de cada invocación sin escribir nada de código de monitoreo. El despliegue ya la habilitó: el agente emite trazas y métricas de OpenTelemetry (OTEL) bajo el namespace &lt;code&gt;bedrock-agentcore&lt;/code&gt;, y un &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/GenAI-observability.html?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;panel de CloudWatch GenAI Observability&lt;/a&gt; muestra vistas de agente, sesión y trazas (latencia, tasa de error, uso de tokens, llamadas a herramientas) listas para usar.&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%2F7ocrjume9zahbcrm8gpk.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%2F7ocrjume9zahbcrm8gpk.png" alt="Descripción de la imagen" width="799" height="242"&gt;&lt;/a&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%2Fxicr8xxik4dbfo41hbrq.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%2Fxicr8xxik4dbfo41hbrq.png" alt="Descripción de la imagen" width="799" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Así diagnostiqué en segundos el error de permisos &lt;code&gt;ListEvents&lt;/code&gt; de antes: la traza fallida estaba justo ahí en CloudWatch, sin configuración extra. Ver &lt;a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability-view.html?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Visualizar datos de observabilidad de agentes AgentCore&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;La misma instrumentación alimenta &lt;a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/evaluations.html?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;AgentCore Evaluations&lt;/a&gt;: puntuación automatizada con LLM-como-juez de la finalización de tareas y la precisión de las llamadas a herramientas a partir de las mismas trazas, para que midas la calidad del agente de forma continua en lugar de solo en el lanzamiento.&lt;/p&gt;
&lt;h2&gt;
  
  
  Qué memoria, cuándo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;¿Solo el problema de datos, local?&lt;/strong&gt; &lt;code&gt;ContextOffloader(FileStorage(...))&lt;/code&gt;. Herramientas ordinarias, sin código de punteros.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;¿Un agente típico de múltiples turnos?&lt;/strong&gt; &lt;code&gt;context_manager="auto"&lt;/code&gt;. Resumen más descarga en una línea.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;¿Producción?&lt;/strong&gt; AgentCore Memory para la conversación, &lt;code&gt;ContextOffloader(S3Storage(...))&lt;/code&gt; para los datos. Mantenlas separadas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;En cualquier caso:&lt;/strong&gt; combina el offloader con herramientas selectivas que devuelvan resúmenes, no blobs crudos. El offloader previene el desbordamiento; las herramientas selectivas mantienen bajo el conteo de tokens.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Pruébalo tú mismo
&lt;/h2&gt;

&lt;p&gt;Necesitas Python 3.11+, &lt;a href="https://docs.astral.sh/uv/" rel="noopener noreferrer"&gt;uv&lt;/a&gt; y una &lt;code&gt;OPENAI_API_KEY&lt;/code&gt; (o cambia el modelo por &lt;code&gt;BedrockModel&lt;/code&gt;). Los pasos de S3 y AgentCore también requieren credenciales de AWS.&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

uv run python test_native_pointer.py              &lt;span class="c"&gt;# local, comparación medida de tokens&lt;/span&gt;
&lt;span class="nv"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;you uv run python test_s3_offload_local.py   &lt;span class="c"&gt;# descarga real a S3&lt;/span&gt;
&lt;span class="c"&gt;# Despliegue de producción + recorrido de dos memorias: setup_agentcore_s3.ipynb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Notebooks: &lt;code&gt;test_native_pointer.ipynb&lt;/code&gt; (local) y &lt;code&gt;setup_agentcore_s3.ipynb&lt;/code&gt; (provisionar + desplegar + invocar en AWS).&lt;/p&gt;
&lt;h2&gt;
  
  
  Puntos clave
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Un agente tiene dos memorias.&lt;/strong&gt; Conversación (semántica) y datos (referencia exacta). La mayoría de los problemas de contexto son una puesta donde corresponde la otra.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ya no construyes el lado de datos a mano.&lt;/strong&gt; &lt;code&gt;ContextOffloader&lt;/code&gt; es el patrón Memory Pointer como plugin; las herramientas siguen siendo funciones ordinarias.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Medí ~97% menos tokens&lt;/strong&gt; en este demo, y verifiqué un dataset de 83 KB descargado a S3 real y recuperado byte por byte por referencia.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;En producción, mantén las dos memorias separadas.&lt;/strong&gt; AgentCore Memory para conversación, S3 para datos. Recuperar logs por significado es el diseño equivocado.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;El offloader es una red de seguridad; las herramientas selectivas son la victoria.&lt;/strong&gt; Devuelve resúmenes, no blobs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;En AgentCore, la observabilidad y la evaluación son gratis.&lt;/strong&gt; Agrega la librería y obtén trazas, métricas y puntuación con LLM-como-juez sin código de monitoreo.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;¿&lt;code&gt;ContextOffloader&lt;/code&gt; necesita AWS?&lt;/strong&gt; No. Con &lt;code&gt;FileStorage&lt;/code&gt; o &lt;code&gt;InMemoryStorage&lt;/code&gt; corre totalmente en local. Solo necesitas AWS cuando eliges &lt;code&gt;S3Storage&lt;/code&gt; o despliegas en AgentCore.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Puedo guardar archivos grandes en AgentCore Memory en vez de S3?&lt;/strong&gt; Puedes, pero no deberías. AgentCore Memory recupera por similitud semántica, así que devuelve la memoria &lt;em&gt;más parecida&lt;/em&gt;, no un archivo exacto. Las salidas grandes de herramientas necesitan recuperación por identificador exacto, que es lo que da S3 (mediante &lt;code&gt;ContextOffloader&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Necesito Docker para desplegar en AgentCore?&lt;/strong&gt; No. El starter toolkit construye la imagen en la nube con AWS CodeBuild por defecto. Docker solo se necesita para una compilación local.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Cuál es la diferencia entre &lt;code&gt;agent.state&lt;/code&gt; y &lt;code&gt;ContextOffloader&lt;/code&gt;?&lt;/strong&gt; &lt;code&gt;agent.state&lt;/code&gt; es el patrón Memory Pointer manual: escribes y lees punteros dentro de tus herramientas. &lt;code&gt;ContextOffloader&lt;/code&gt; es la misma idea como plugin: las herramientas siguen siendo ordinarias y el framework descarga los resultados grandes por ti.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Cuál de mis dos memorias me está costando tokens?&lt;/strong&gt; La de datos. La memoria de conversación es texto pequeño; las explosiones de tokens vienen de las salidas grandes de herramientas viajando en el contexto. Esa es la memoria que arregla &lt;code&gt;ContextOffloader&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;¿Cuál de las dos memorias de tu agente está fugando tokens? Cuéntame en los comentarios.&lt;/p&gt;
&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Investigación&lt;/strong&gt;&lt;/p&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, 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, 2024 (referenciación de payloads entre agentes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Implementación&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://strandsagents.com/docs/user-guide/concepts/context-management/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Strands · Context Management&lt;/a&gt;&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;&lt;/li&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;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/memory-get-started.html?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Amazon Bedrock AgentCore Memory — Get started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-permissions.html?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;AgentCore Runtime — IAM permissions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability-view.html?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;AgentCore — Observability in CloudWatch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/evaluations.html?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;AgentCore — Evaluations&lt;/a&gt;&lt;/li&gt;
&lt;li&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;Código: 01-context-overflow-demo&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>aws</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>AWS Agent Toolkit: Evita que tu Agente de IA Alucine APIs</title>
      <dc:creator>Elizabeth Fuentes L</dc:creator>
      <pubDate>Fri, 12 Jun 2026 20:08:28 +0000</pubDate>
      <link>https://dev.to/aws-espanol/aws-agent-toolkit-evita-que-tu-agente-de-ia-alucine-apis-3h5c</link>
      <guid>https://dev.to/aws-espanol/aws-agent-toolkit-evita-que-tu-agente-de-ia-alucine-apis-3h5c</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Tu agente de programación con IA alucina APIs de AWS porque está adivinando con datos de entrenamiento congelados en el pasado.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;El Agent Toolkit for AWS arregla la fuente de verdad: le da a cualquier agente compatible con MCP documentación de AWS en vivo, skills probadas y guardrails. Aquí está el antes y después, y cómo instalarlo en un comando.&lt;/p&gt;

&lt;p&gt;Pídele a un agente de programación que "configure un bucket de S3 con valores de seguridad razonables" y mira lo que pasa.&lt;/p&gt;

&lt;p&gt;Escribe una política desde la memoria. La política usa un parámetro de API que cambió de nombre hace dos releases. El despliegue falla. El agente reintenta con una variación. También falla. Tres iteraciones después tienes un bucket que técnicamente existe, el bloqueo de acceso público a medias, y una transcripción que quemó unos cuantos miles de tokens en el camino.&lt;/p&gt;

&lt;p&gt;Los agentes de programación con IA no fallan de forma ruidosa cuando tocan AWS. Fallan de forma plausible. El código se ve bien, los nombres de servicio son reales, y el error solo aparece al desplegar, o peor, en la revisión de seguridad.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Por qué los asistentes de programación con IA alucinan al escribir código de AWS?
&lt;/h2&gt;

&lt;p&gt;Porque el modelo está adivinando con datos de entrenamiento congelados en el pasado. AWS lanzó servicios nuevos y cambió superficies de API después de ese corte, así que el agente recurre a lo que recuerda, no a lo que es cierto hoy. No sabe lo que no sabe, y no tiene forma de verificar antes de escribir.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué es el Agent Toolkit for AWS?
&lt;/h2&gt;

&lt;p&gt;El &lt;a href="https://aws.amazon.com/products/developer-tools/agent-toolkit-for-aws/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Agent Toolkit for AWS&lt;/a&gt; es un toolkit oficial, soportado por AWS, que les da a los agentes de programación con IA las herramientas, el conocimiento y los guardrails que necesitan para construir, desplegar y operar aplicaciones en AWS. El AWS MCP Server que está debajo alcanzó disponibilidad general el 6 de mayo de 2026. Es open source (Apache-2.0).&lt;/p&gt;

&lt;p&gt;Tiene cuatro componentes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS MCP Server&lt;/strong&gt;: un servidor gestionado de Model Context Protocol. Un solo endpoint con acceso a más de 15,000 operaciones de API de AWS (vía la herramienta &lt;code&gt;call_aws&lt;/code&gt;, usando tus credenciales de IAM), más ejecución de scripts de Python en sandbox y búsqueda en la documentación que no necesita autenticación.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent skills&lt;/strong&gt;: paquetes curados de instrucciones, scripts y material de referencia que el agente carga bajo demanda. El agente recupera solo lo relevante a la tarea actual, así que no gasta contexto. Piensa en "el procedimiento probado para configurar X", no en una adivinanza genérica.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plugins&lt;/strong&gt;: paquetes de instalación única para Claude Code y Codex que agrupan la configuración del MCP Server más un conjunto curado de skills. &lt;code&gt;aws-core&lt;/code&gt; es el que se usa para empezar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rules files&lt;/strong&gt;: configuración a nivel de proyecto que le dice al agente cómo trabajar en tu proyecto. Usar el MCP Server, descubrir skills, buscar en la documentación antes de actuar.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ¿Por qué no dejar que el agente llame a AWS directamente?
&lt;/h2&gt;

&lt;p&gt;Porque "directamente" significa "desde la memoria". El MCP Server cambia la fuente de verdad: de los datos de entrenamiento del modelo a &lt;strong&gt;la documentación y las APIs de AWS en vivo.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dos cosas importan acá:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;La búsqueda en documentación no necesita credenciales.&lt;/strong&gt; El agente puede consultar la forma actual de hacer algo antes de escribir una línea de código. Para esa parte no se necesita cuenta de AWS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La ejecución de scripts está en sandbox.&lt;/strong&gt; Cuando el agente corre Python contra AWS, lo hace aislado de tu sistema de archivos y red local, y cada llamada queda registrada en CloudTrail con métricas en CloudWatch.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ese segundo punto es el que los equipos pasan por alto. El MCP Server agrega dos condition keys a cada request, &lt;code&gt;aws:ViaAWSMCPService&lt;/code&gt; y &lt;code&gt;aws:CalledViaAWSMCP&lt;/code&gt;, para que tus políticas de IAM distingan una acción de un agente de una acción humana. Puedes mantener a un agente en solo lectura aunque el rol subyacente permita escrituras. El agente gana capacidad; tú mantienes el control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Antes y después
&lt;/h2&gt;

&lt;p&gt;Mismo prompt, mismo modelo. La única variable es el Toolkit.&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 solo&lt;/th&gt;
&lt;th&gt;Agente + Toolkit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fuente de verdad&lt;/td&gt;
&lt;td&gt;Datos de entrenamiento (congelados)&lt;/td&gt;
&lt;td&gt;Docs y APIs de AWS en vivo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Servicios obsoletos&lt;/td&gt;
&lt;td&gt;Los elige en silencio&lt;/td&gt;
&lt;td&gt;Las skills lo guían a los actuales&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Despliegues fallidos&lt;/td&gt;
&lt;td&gt;Reintenta, adivina, reintenta&lt;/td&gt;
&lt;td&gt;Valida contra docs reales primero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trazabilidad&lt;/td&gt;
&lt;td&gt;Ninguna&lt;/td&gt;
&lt;td&gt;CloudTrail + CloudWatch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Costo en tokens&lt;/td&gt;
&lt;td&gt;Quemado en reintentos&lt;/td&gt;
&lt;td&gt;Gastado una vez, correctamente&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;AWS describe el beneficio como agentes que construyen "con menos errores, menor costo de tokens, y controles de seguridad de nivel empresarial". El mecanismo detrás de eso es la tabla de arriba: el agente deja de improvisar desde la memoria obsoleta y empieza a actuar sobre documentación actual y procedimientos probados.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ponlo a andar en tu agente
&lt;/h2&gt;

&lt;p&gt;Necesitas &lt;code&gt;uv&lt;/code&gt; instalado (ese es el comando &lt;code&gt;uvx&lt;/code&gt; de abajo) y, para todo lo que efectivamente llame a AWS, credenciales locales de AWS. La búsqueda en documentación y el descubrimiento de skills funcionan sin credenciales.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude Code.&lt;/strong&gt; El marketplace &lt;code&gt;claude-plugins-official&lt;/code&gt; viene por defecto, así que un solo comando lo instala:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;plugin &lt;span class="nb"&gt;install &lt;/span&gt;aws-core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Si dice "Plugin not found", actualiza primero el marketplace con &lt;code&gt;/plugin marketplace update claude-plugins-official&lt;/code&gt;, y luego instala con el nombre explícito &lt;code&gt;aws-core@claude-plugins-official&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Hay dos plugins más que vale la pena conocer: &lt;code&gt;aws-agents&lt;/code&gt; (construir agentes con Bedrock y AgentCore) y &lt;code&gt;aws-data-analytics&lt;/code&gt; (S3 Tables, Glue, Athena). Empieza con &lt;code&gt;aws-core&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Codex:&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;codex plugin marketplace add aws/agent-toolkit-for-aws
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Luego abre Codex y corre &lt;code&gt;/plugins&lt;/code&gt; para instalar &lt;code&gt;aws-core&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kiro (o cualquier agente compatible con MCP).&lt;/strong&gt; Agrega el servidor a &lt;code&gt;.kiro/settings/mcp.json&lt;/code&gt;. Fija la versión para reproducibilidad y seguridad de la cadena de suministro:&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;"mcpServers"&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;"aws"&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;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uvx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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="s2"&gt;"mcp-proxy-for-aws@1.6.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"https://aws-mcp.us-east-1.api.aws/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--metadata"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AWS_REGION=us-west-2"&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;p&gt;Y agrega las skills:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add aws/agent-toolkit-for-aws/skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Cursor:&lt;/strong&gt; Settings &amp;gt; Plugins &amp;gt; Team Marketplaces &amp;gt; Add Marketplace &amp;gt; Import from Repo, apuntando a &lt;code&gt;aws/agent-toolkit-for-aws&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Funciona con cualquier agente compatible con MCP, y si estás construyendo agentes autónomos con frameworks como Strands, LangChain o Bedrock AgentCore, el mismo MCP Server es la interfaz de AWS que quieres por debajo de ellos.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prueba el prompt del bucket otra vez
&lt;/h2&gt;

&lt;p&gt;Instalé &lt;code&gt;aws-core&lt;/code&gt; y volví a correr exactamente el mismo prompt. Esta vez el agente buscó en la documentación actual, sacó el procedimiento probado de una skill, y el bloqueo de acceso público quedó configurado correctamente en el primer intento. El parámetro obsoleto nunca apareció, porque el agente no estaba adivinando. Estaba leyendo.&lt;/p&gt;

&lt;p&gt;Ese es todo el cambio: &lt;strong&gt;deja que tu agente deje de adivinar en AWS, y déjalo leer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Está disponible sin costo adicional. Solo pagas por los recursos de AWS que realmente uses.&lt;/p&gt;

&lt;p&gt;Este recorrido usa el Agent Toolkit for AWS, pero la idea de fondo (darle al agente una fuente de verdad en vivo y procedimientos probados en lugar de datos de entrenamiento congelados) es un patrón general de agentes que aplica a otras nubes y frameworks de agentes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;¿Qué son las agent skills en el Agent Toolkit for AWS?&lt;/strong&gt;&lt;br&gt;
Las skills son paquetes curados de instrucciones, scripts y material de referencia que un agente recupera bajo demanda. En lugar de adivinar un procedimiento, el agente saca uno probado (por ejemplo, los pasos validados para asegurar un bucket de S3) en el momento que lo necesita.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Necesito una cuenta de AWS para usarlo?&lt;/strong&gt;&lt;br&gt;
No para todo. La búsqueda en documentación y el descubrimiento de skills funcionan sin credenciales. Solo necesitas credenciales locales de AWS cuando el agente hace llamadas reales a la API o corre scripts contra tu cuenta.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Qué agentes de programación soporta?&lt;/strong&gt;&lt;br&gt;
Claude Code, Codex y Cursor instalan los plugins directamente. Kiro y cualquier otro agente compatible con MCP pueden agregar el AWS MCP Server vía configuración. Si construyes agentes autónomos con frameworks como Strands, LangChain o Bedrock AgentCore, el mismo MCP Server es la interfaz de AWS por debajo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿En qué se diferencia de dejar que el agente use el AWS CLI?&lt;/strong&gt;&lt;br&gt;
El CLI ejecuta lo que sea que el agente adivinó. El Toolkit cambia la fuente de verdad primero: el agente consulta docs en vivo y skills probadas antes de actuar, corre scripts en un sandbox, y registra cada llamada en CloudTrail con métricas en CloudWatch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Cuánto cuesta?&lt;/strong&gt;&lt;br&gt;
El Toolkit está disponible sin costo adicional. Solo pagas por los recursos de AWS que el agente realmente cree o use.&lt;/p&gt;

&lt;p&gt;¿Qué flujo de trabajo de AWS es el que tu agente de programación hace mal más seguido? Cuéntame en los comentarios. Quiero ver si el Toolkit lo arregla.&lt;/p&gt;
&lt;h2&gt;
  
  
  Recursos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/products/developer-tools/agent-toolkit-for-aws/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Página del producto Agent Toolkit for AWS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/agent-toolkit/latest/userguide/what-is-agent-toolkit.html?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Documentación oficial del Agent Toolkit for AWS (Guía del usuario)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/aws/the-aws-mcp-server-is-now-generally-available/?trk=87c4c426-cddf-4799-a299-273337552ad8&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;The AWS MCP Server is now generally available (Blog de AWS)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/aws/agent-toolkit-for-aws" rel="noopener noreferrer"&gt;Repositorio en GitHub: aws/agent-toolkit-for-aws&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtu.be/d1GHVtEFy2A" rel="noopener noreferrer"&gt;Video demo: Introducing Agent Toolkit for AWS&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>aws</category>
      <category>tutorial</category>
      <category>spanish</category>
    </item>
    <item>
      <title>5 patrones Multi-Agent con Strands Agents: cuál usar y cuándo</title>
      <dc:creator>ricardoceci</dc:creator>
      <pubDate>Wed, 03 Jun 2026 11:43:51 +0000</pubDate>
      <link>https://dev.to/aws-espanol/5-patrones-multi-agent-con-strands-agents-cual-usar-y-cuando-53ee</link>
      <guid>https://dev.to/aws-espanol/5-patrones-multi-agent-con-strands-agents-cual-usar-y-cuando-53ee</guid>
      <description>&lt;p&gt;Tenés un agente que busca vuelos. Otro que consulta el clima. Otro que aplica políticas corporativas. ¿Cómo los hacés trabajar juntos?&lt;/p&gt;

&lt;p&gt;Strands Agents ofrece 5 patrones para coordinar múltiples agentes. Cada uno resuelve un problema distinto. La diferencia clave: &lt;strong&gt;quién decide el orden de ejecución&lt;/strong&gt;. El modelo, los agentes entre sí, o vos en el código.&lt;/p&gt;

&lt;p&gt;En este artículo explico cada patrón con código, muestro las diferencias con una tabla comparativa, y te doy un framework de decisión para elegir el correcto.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Todos los ejemplos usan Strands Agents (junio 2026). El código completo está en &lt;a href="https://github.com/ricardoceci/curso-strands-agentcore-2026/tree/main/clase-3" rel="noopener noreferrer"&gt;github.com/ricardoceci/curso-strands-agentcore-2026/tree/main/clase-3&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  El caso de ejemplo: Travel Agent corporativo
&lt;/h2&gt;

&lt;p&gt;Todos los ejemplos usan el mismo caso: un agente de viajes corporativo que coordina búsqueda de vuelos, consulta de clima, y generación de recomendaciones. Tres capacidades, tres agentes especializados.&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="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;date&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;dict&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 one-way flights via Duffel sandbox.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# ... llamada a la API de Duffel
&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;route&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;destination&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;offers&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="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_weather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&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;target_date&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;dict&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 forecast for a city on a specific date.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# ... llamada a Open-Meteo
&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;city&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_temp_c&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;precipitation_mm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Patrón 1: Agents as Tools
&lt;/h2&gt;

&lt;p&gt;Un agente orquestador usa a otros agentes como si fueran tools. El orquestador decide cuándo delegar y a quién.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cómo funciona
&lt;/h3&gt;

&lt;p&gt;Pasás el sub-agente directo en el array de &lt;code&gt;tools&lt;/code&gt; del agente principal. El SDK lo convierte a tool automáticamente. Cuando el modelo del orquestador decide que necesita esa capacidad, invoca al sub-agente. Todo corre en el mismo proceso Python.&lt;/p&gt;

&lt;p&gt;Si necesitás más control, usá &lt;code&gt;.as_tool()&lt;/code&gt; para customizar nombre y descripción, o el decorator &lt;code&gt;@tool&lt;/code&gt; para wrappear la llamada completa.&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;# Sub-agente especializado en clima
&lt;/span&gt;&lt;span class="n"&gt;weather_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;get_weather&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;Sos un experto en clima. Respondé conciso.&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;# El agente principal recibe al sub-agente directo en tools[]
&lt;/span&gt;&lt;span class="n"&gt;travel_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;weather_agent&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- el SDK lo convierte a tool
&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;Sos un asistente de viajes corporativos.&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;travel_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;Vuelos de EZE a MIA el 20 de agosto. ¿Cómo va a estar el clima?&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;
  
  
  Cuándo usarlo
&lt;/h3&gt;

&lt;p&gt;Cuando tenés pocos sub-agentes con capacidades claramente distintas y querés que el modelo principal decida cuándo llamar a cada uno. Es el patrón más directo. Requiere la menor coordinación.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cuándo NO usarlo
&lt;/h3&gt;

&lt;p&gt;Si necesitás que los agentes se comuniquen entre sí (no solo con el orquestador) o si el orden de ejecución importa. En esos casos, Graph o Swarm son mejores opciones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Patrón 2: Swarm
&lt;/h2&gt;

&lt;p&gt;Un grupo de agentes que se coordinan autónomamente mediante handoffs (transferencias de control). Cada agente decide cuándo pasarle el trabajo a otro.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cómo funciona
&lt;/h3&gt;

&lt;p&gt;Strands equipa a cada agente del Swarm con herramientas de coordinación: un handoff tool para transferir control a otro agente, y un shared context que todos pueden leer. Los agentes deciden solos el orden de ejecució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.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;flight_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;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_agent&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;search_flights&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;Buscás vuelos. Cuando termines, pasá el control al weather_agent.&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;weather_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;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_agent&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_weather&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;Consultás el clima en destino.&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;summary_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;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;summary_agent&lt;/span&gt;&lt;span class="sh"&gt;"&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;Combinás vuelos y clima en una recomendación clara.&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;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;flight_agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weather_agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;summary_agent&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;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;Necesito viajar de Buenos Aires a Miami el 20 de agosto&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;
  
  
  Cuándo usarlo
&lt;/h3&gt;

&lt;p&gt;Cuando el problema se descompone en partes que distintos especialistas resuelven mejor, y el orden puede variar según la tarea. Swarm es ideal para problemas donde no sabés de antemano qué agente debería actuar primero.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cuándo NO usarlo
&lt;/h3&gt;

&lt;p&gt;Si necesitás un flujo de ejecución predecible y repetible. Swarm decide el orden en runtime. Dos ejecuciones del mismo prompt pueden seguir caminos distintos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Patrón 3: Graph
&lt;/h2&gt;

&lt;p&gt;Un grafo dirigido donde cada nodo es un agente y las aristas definen el flujo de ejecución. Vos definís la estructura, el framework ejecuta en orden.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cómo funciona
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;GraphBuilder&lt;/code&gt; te da una API para definir nodos (agentes) y aristas (conexiones). El framework pasa la salida de un nodo como entrada al siguiente. Soporta grafos acíclicos (pipelines) y cíclicos (loops de refinamiento), lo que te da flexibilidad para implementar iteraciones de revisión 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.multiagent&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GraphBuilder&lt;/span&gt;

&lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;GraphBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&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&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flight_agent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&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;weather_agent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;summary_agent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&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&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;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="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summary&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="nf"&gt;build&lt;/span&gt;&lt;span class="p"&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;graph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Viaje de Buenos Aires a Miami, 20 de agosto&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;
  
  
  Cuándo usarlo
&lt;/h3&gt;

&lt;p&gt;Cuando el flujo de trabajo tiene un orden estricto que no debería cambiar. Por ejemplo: buscar vuelos primero, después consultar clima, después generar recomendación. Si el pipeline es el mismo cada vez, Graph es el patrón correcto.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cuándo NO usarlo
&lt;/h3&gt;

&lt;p&gt;Si necesitás flexibilidad en el orden de ejecución. Un Graph con muchos niveles de profundidad también suma latencia, porque cada nodo espera al anterior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Patrón 4: Workflow
&lt;/h2&gt;

&lt;p&gt;Un grafo de tareas con dependencias explícitas y ejecución paralela automática. Cada tarea se asigna a un agente con un system_prompt específico.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cómo funciona
&lt;/h3&gt;

&lt;p&gt;A diferencia de Graph (que opera con agentes como nodos), Workflow opera con &lt;strong&gt;tareas&lt;/strong&gt;. Cada tarea tiene un ID, una descripción, dependencias, y prioridad. El framework resuelve el orden de ejecución, paraleliza lo que puede, y pasa resultados entre tareas.&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;workflow&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;workflow&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;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;create&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;workflow_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;travel_planning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tasks&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;task_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;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;description&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;Buscar los 5 vuelos más baratos de EZE a MIA el 20 de agosto&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;system_prompt&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;Sos un experto en búsqueda de vuelos.&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;priority&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task_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;check_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;description&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;Consultar el clima en Miami para el 20 de agosto&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;system_prompt&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;Sos un experto en clima.&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;priority&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task_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;generate_report&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;description&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;Combinar vuelos y clima en un reporte ejecutivo&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;dependencies&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_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;system_prompt&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;Sos un analista de viajes corporativos.&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;priority&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="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;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="nf"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;execute&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;workflow_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;travel_planning&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;En este ejemplo, &lt;code&gt;search_flights&lt;/code&gt; y &lt;code&gt;check_weather&lt;/code&gt; corren en paralelo (no tienen dependencias entre sí). &lt;code&gt;generate_report&lt;/code&gt; espera a que ambos terminen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cuándo usarlo
&lt;/h3&gt;

&lt;p&gt;Cuando tenés un proceso repetible con pasos que se pueden paralelizar. Workflow es el patrón más cercano a un pipeline de datos tradicional: dependencias explícitas, ejecución determinista, resultados que fluyen entre tareas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cuándo NO usarlo
&lt;/h3&gt;

&lt;p&gt;Si necesitás que los agentes tomen decisiones sobre qué hacer a continuación. Workflow ejecuta exactamente lo que vos definiste, sin autonomía.&lt;/p&gt;

&lt;h2&gt;
  
  
  Patrón 5: A2A (Agent-to-Agent)
&lt;/h2&gt;

&lt;p&gt;Un protocolo abierto (creado por Google, ahora en la Linux Foundation) para que agentes se comuniquen entre sí via HTTP. Los agentes pueden estar escritos en distintos frameworks, en distintos servidores.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cómo funciona
&lt;/h3&gt;

&lt;p&gt;Un agente se expone como servidor A2A con una Agent Card (metadata JSON en &lt;code&gt;/.well-known/agent-card.json&lt;/code&gt;). Otro agente lo consume como cliente. La comunicación es HTTP/JSON.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Servidor:&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="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.multiagent.a2a&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;A2AServer&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uvicorn&lt;/span&gt;

&lt;span class="n"&gt;weather_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;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_agent&lt;/span&gt;&lt;span class="sh"&gt;"&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;Consulta pronósticos de clima por ciudad y fecha&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_weather&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;a2a_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;A2AServer&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="n"&gt;weather_agent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a2a_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_fastapi_app&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;uvicorn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.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;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;9000&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;Cliente:&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="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.agent.a2a_agent&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;A2AAgent&lt;/span&gt;

&lt;span class="n"&gt;remote_weather&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;A2AAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;agent_url&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://localhost:9000&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;# Usarlo como nodo en un Graph
&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;GraphBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&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&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flight_agent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&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;remote_weather&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&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&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;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="nf"&gt;build&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;
  
  
  Cuándo usarlo
&lt;/h3&gt;

&lt;p&gt;Cuando los agentes viven en servidores distintos, están escritos en frameworks distintos (Strands, LangGraph, CrewAI), o pertenecen a equipos u organizaciones distintas. A2A es el único patrón que cruza la frontera del proceso local.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cuándo NO usarlo
&lt;/h3&gt;

&lt;p&gt;Si todos los agentes corren en el mismo proceso. A2A agrega latencia de red (50-1000ms por llamada, según &lt;a href="https://medium.com/@rasid2006/understanding-communication-patterns-in-strands-agents-framework-3e53d7918182" rel="noopener noreferrer"&gt;benchmarks de la comunidad&lt;/a&gt;). Para comunicación local, los otros patrones son órdenes de magnitud más rápidos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparación entre los 5 patrones Multi-Agent
&lt;/h2&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;Agents as Tools&lt;/th&gt;
&lt;th&gt;Swarm&lt;/th&gt;
&lt;th&gt;Graph&lt;/th&gt;
&lt;th&gt;Workflow&lt;/th&gt;
&lt;th&gt;A2A&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Quién decide el orden&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;El modelo orquestador&lt;/td&gt;
&lt;td&gt;Los agentes (handoffs)&lt;/td&gt;
&lt;td&gt;Vos (aristas)&lt;/td&gt;
&lt;td&gt;Vos (dependencias)&lt;/td&gt;
&lt;td&gt;N/A (protocolo)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Comunicación&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;In-process&lt;/td&gt;
&lt;td&gt;In-process&lt;/td&gt;
&lt;td&gt;In-process&lt;/td&gt;
&lt;td&gt;In-process&lt;/td&gt;
&lt;td&gt;HTTP/JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Latencia&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Microsegundos&lt;/td&gt;
&lt;td&gt;Microsegundos&lt;/td&gt;
&lt;td&gt;Microsegundos&lt;/td&gt;
&lt;td&gt;Microsegundos&lt;/td&gt;
&lt;td&gt;50-1000ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ejecución paralela&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No nativa&lt;/td&gt;
&lt;td&gt;Decidida por agentes&lt;/td&gt;
&lt;td&gt;Soportada&lt;/td&gt;
&lt;td&gt;Automática&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Grafos cíclicos&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Sí&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-framework&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Sí&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Composable&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sí (como tool)&lt;/td&gt;
&lt;td&gt;Sí (nodo en Graph)&lt;/td&gt;
&lt;td&gt;Sí (nodo en otro Graph)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Sí (nodo en Graph)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Framework de decisión
&lt;/h2&gt;

&lt;p&gt;Cuando tenés que elegir un patrón, seguí estas preguntas:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Los agentes están en el mismo proceso?&lt;/strong&gt;&lt;br&gt;
Si no: &lt;strong&gt;A2A&lt;/strong&gt; (es el único que cruza la red).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿El orden de ejecución importa?&lt;/strong&gt;&lt;br&gt;
Si sí, ¿es siempre el mismo?: &lt;strong&gt;Graph&lt;/strong&gt; (estructura fija).&lt;br&gt;
Si sí, ¿con tareas paralelas?: &lt;strong&gt;Workflow&lt;/strong&gt; (dependencias + paralelismo).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Los agentes necesitan decidir solos quién actúa?&lt;/strong&gt;&lt;br&gt;
Si sí: &lt;strong&gt;Swarm&lt;/strong&gt; (handoffs autónomos).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Es delegación directa de un orquestador a especialistas?&lt;/strong&gt;&lt;br&gt;
Si sí: &lt;strong&gt;Agents as Tools&lt;/strong&gt; (el más directo).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Ninguno encaja perfecto?&lt;/strong&gt;&lt;br&gt;
Los patrones se componen entre sí. Un Graph puede tener un Swarm como nodo. Un agente con tools puede incluir un A2AAgent remoto. Strands te deja mezclar patrones sin restricciones.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  ¿Swarm usa A2A internamente?
&lt;/h3&gt;

&lt;p&gt;No. Swarm usa llamadas de función Python dentro del mismo proceso. La confusión viene porque ambos involucran "comunicación entre agentes", pero Swarm es local (microsegundos) y A2A es remoto (HTTP).&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Graph soporta ejecución condicional?
&lt;/h3&gt;

&lt;p&gt;Sí. Las aristas pueden tener condiciones que se evalúan en runtime. Esto te da grafos que se comportan como árboles de decisión: según el output de un nodo, el flujo toma un camino u otro.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Puedo combinar patrones?
&lt;/h3&gt;

&lt;p&gt;Sí, y es una de las fortalezas de Strands. Un Swarm puede vivir como nodo dentro de un Graph. Un Graph puede usar un A2AAgent remoto como nodo. Un agente con tools puede incluir otro agente como tool. La composición es libre.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cuál es el más eficiente en tokens?
&lt;/h3&gt;

&lt;p&gt;Agents as Tools y Graph consumen menos tokens porque la coordinación es determinista. Swarm puede consumir más porque los agentes razonan sobre a quién pasarle el control. A2A agrega overhead de protocolo HTTP.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Workflow reemplaza a Graph?
&lt;/h3&gt;

&lt;p&gt;No. Workflow es una tool de Strands (viene de &lt;code&gt;strands-agents-tools&lt;/code&gt;), mientras que Graph es un orquestador nativo del SDK. Workflow opera con tareas, Graph opera con agentes. Si necesitás que cada nodo sea un agente completo con su propio system prompt y tools, usá Graph. Si necesitás un pipeline de tareas con dependencias, usá Workflow.&lt;/p&gt;

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

&lt;p&gt;Los 5 patrones no compiten entre sí. Cada uno resuelve una necesidad distinta de coordinación. La clave es empezar con el más directo que funcione para tu caso (probablemente Agents as Tools) y escalar hacia patrones más complejos cuando lo necesites.&lt;/p&gt;

&lt;p&gt;Si querés ver estos patrones implementados en vivo con el caso del Travel Agent corporativo, están todos en el repositorio del curso:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ricardoceci/curso-strands-agentcore-2026" rel="noopener noreferrer"&gt;github.com/ricardoceci/curso-strands-agentcore-2026&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Y si querés aprender Strands desde cero, el curso completo (gratuito, en español) está en &lt;a href="https://www.ricardoceci.dev" rel="noopener noreferrer"&gt;ricardoceci.dev&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recursos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://strandsagents.com/docs/user-guide/concepts/multi-agent/multi-agent-patterns/" rel="noopener noreferrer"&gt;Documentación Multi-Agent de Strands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/machine-learning/multi-agent-collaboration-patterns-with-strands-agents-and-amazon-nova/" rel="noopener noreferrer"&gt;Multi-Agent Collaboration Patterns with Strands and Amazon Nova (AWS Blog)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.strandsagents.com/" rel="noopener noreferrer"&gt;Strands Agents Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/google/A2A" rel="noopener noreferrer"&gt;A2A Protocol (Linux Foundation)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/machine-learning/introducing-agent-to-agent-protocol-support-in-amazon-bedrock-agentcore-runtime/" rel="noopener noreferrer"&gt;A2A protocol support in AgentCore Runtime (AWS Blog)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>multiagent</category>
    </item>
    <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>
  </channel>
</rss>
