<?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: Laura Bolaños</title>
    <description>The latest articles on DEV Community by Laura Bolaños (@reinalau).</description>
    <link>https://dev.to/reinalau</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%2Fuser%2Fprofile_image%2F3629379%2F49e3cfe8-aac1-4f47-b540-d042f1e6d241.jpg</url>
      <title>DEV Community: Laura Bolaños</title>
      <link>https://dev.to/reinalau</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/reinalau"/>
    <language>en</language>
    <item>
      <title>Camino a AWS Certified Developer — Associate (DVA-C03): qué cambia y por qué</title>
      <dc:creator>Laura Bolaños</dc:creator>
      <pubDate>Mon, 07 Sep 2026 22:57:21 +0000</pubDate>
      <link>https://dev.to/aws-builders/camino-a-aws-certified-developer-associate-dva-c03-que-cambia-y-por-que-5575</link>
      <guid>https://dev.to/aws-builders/camino-a-aws-certified-developer-associate-dva-c03-que-cambia-y-por-que-5575</guid>
      <description>&lt;p&gt;El 1 de septiembre de 2026, AWS anunció oficialmente cambios de versión en varios de sus exámenes. Entre ellos, AWS Certified Developer — Associate. En este artículo te comparto mi experiencia con la versión actual que rendí y los cambios de lo que se viene.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🎯 Hasta el 30 de noviembre de 2026 se puede rendir la versión actual DVA-C02. El registro para DVA-C03 abre el 27 de octubre de 2026, con disponibilidad general (GA) a partir del 1 de diciembre de 2026.*&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hace unos meses escribí sobre mi experiencia rindiendo el examen &lt;a href="https://medium.com/@reinalau/camino-a-lograr-aws-certified-developer-associate-dva-c02-9a59112df16f" rel="noopener noreferrer"&gt;&lt;strong&gt;DVA-C02&lt;/strong&gt;&lt;/a&gt;, con los temas clave que profundicé y algunos tips de estudio. AWS &lt;a href="https://aws.amazon.com/es/blogs/training-and-certification/september-2026-new-offerings/" rel="noopener noreferrer"&gt;acaba de anunciar&lt;/a&gt; la actualización a &lt;strong&gt;DVA-C03&lt;/strong&gt;, y me pareció importante escribir esta continuación: no para reemplazar ese artículo, sino para sumarle lo nuevo. Si estás por empezar a estudiar hoy, mi recomendación es leer ambos: la base técnica de servicios (Lambda, DynamoDB, API Gateway, Cognito, etc.) sigue totalmente vigente, y acá vas a encontrar el "qué se agrega" y el "por qué" de esta actualización.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;El calendario a tener en cuenta según el anuncio:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5d5uxs5sfztbef7abxlg.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5d5uxs5sfztbef7abxlg.jpg" alt="Calendario certificacion" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué actualizan el examen
&lt;/h2&gt;

&lt;p&gt;El &lt;a href="https://aws.amazon.com/es/blogs/training-and-certification/september-2026-new-offerings/" rel="noopener noreferrer"&gt;blog oficial de AWS Training and Certification&lt;/a&gt; plantea que el rol de developer ya no es solamente escribir código de aplicación. Hoy un developer usa herramientas de &lt;em&gt;AI-assisted development&lt;/em&gt; para generar, revisar y optimizar código; integra servicios de AI administrados dentro de sus aplicaciones; y tiene que asegurar las interacciones de agentes de AI. Es decir, &lt;em&gt;AI-assisted development&lt;/em&gt; pasa a ser una competencia central del rol, no un "extra".&lt;/p&gt;

&lt;p&gt;Esto no significa que el examen se convierta en una certificación de AI. De hecho, el anuncio oficial aclara qué queda &lt;strong&gt;fuera de alcance&lt;/strong&gt;: prompt engineering, diseño de RAG, selección de modelos de AI, Amazon SageMaker AI y diseño de frameworks de gobernanza de AI. Es decir, no van a pedirte que diseñes soluciones de AI desde cero, sino que sepas &lt;strong&gt;cómo un developer integra y asegura&lt;/strong&gt; estas capacidades dentro del ciclo de vida de una aplicación en AWS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Datos generales de la nueva versión de examen
&lt;/h2&gt;

&lt;p&gt;Según el anuncio, son los mismos parámetros generales que ya conocemos de DVA-C02:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Duración: 130 minutos&lt;/li&gt;
&lt;li&gt;Preguntas: 65 (50 puntuadas, 15 no puntuadas)&lt;/li&gt;
&lt;li&gt;Puntaje de aprobación: 720 sobre 1000&lt;/li&gt;
&lt;li&gt;Precio: 150 USD&lt;/li&gt;
&lt;li&gt;Idiomas: todos los actualmente soportados, disponibles desde el lanzamiento&lt;/li&gt;
&lt;li&gt;Modalidad: Pearson VUE (centro de examen u online proctoring)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lo estructural: los 4 dominios se mantienen
&lt;/h2&gt;

&lt;p&gt;Buena noticia para quien ya venía estudiando: los dominios y sus porcentajes &lt;strong&gt;no cambian&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Development with AWS Services — 30%&lt;/li&gt;
&lt;li&gt;Security — 26%&lt;/li&gt;
&lt;li&gt;Testing and Deployment — 22%&lt;/li&gt;
&lt;li&gt;Troubleshooting and Optimization — 22%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lo que cambia es el contenido dentro de cada dominio: se consolidan skills que estaban demasiado fragmentadas, y se agregan skills nuevas relacionadas a AI y a Containers.&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;Tip:&lt;/strong&gt; si ya armaste tu planilla de "brechas de conocimiento" como sugería en mi artículo anterior, no la tires. Vas a poder reusar casi todo el contenido de Lambda, DynamoDB, API Gateway, Cognito, SQS/SNS, EventBridge, Step Functions, CI/CD, CloudFormation/CDK, seguridad de datos y STS. Lo que necesitás es agregar una columna nueva para lo que sigue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incorporación 1: una Task completa de seguridad de AI (Task 2.3)
&lt;/h2&gt;

&lt;p&gt;Esta es, para mí, la adición más relevante de todo el cambio. Dentro del dominio de Security se agrega la &lt;strong&gt;Task 2.3 — AI security&lt;/strong&gt;, con 5 skills nuevas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gestión de accesos para servicios de AI.&lt;/strong&gt; Aplica el mismo principio de mínimo privilegio que ya se usa para Lambda o DynamoDB, pero ahora sobre quién puede invocar un modelo, un agente o una Knowledge Base. Por ejemplo, se puede restringir con una política de recursos qué rol de IAM puede ejecutar &lt;code&gt;bedrock:InvokeModel&lt;/code&gt; sobre un modelo específico.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Controles de privacidad de datos&lt;/strong&gt;, principalmente vía &lt;strong&gt;VPC endpoints&lt;/strong&gt; (AWS PrivateLink) y la garantía de que los inputs/outputs no se usen para entrenar modelos. Este punto no es solo teórico: el servicio nunca comparte tus datos con los proveedores de modelos ni los usa para entrenar foundation models, y todas las invocaciones se mantienen dentro de la red de AWS. Para reforzar el aislamiento de red, la &lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/vpc-interface-endpoints.html" rel="noopener noreferrer"&gt;guía de VPC interface endpoints de Bedrock&lt;/a&gt; explica que se puede crear un endpoint de interfaz (AWS PrivateLink) usando nombres de servicio como &lt;code&gt;com.amazonaws.region.bedrock-runtime&lt;/code&gt; o &lt;code&gt;com.amazonaws.region.bedrock-agent-runtime&lt;/code&gt;, evitando así exponer el tráfico a internet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Content filtering y protección contra prompt injection.&lt;/strong&gt; Acá entra Amazon Bedrock Guardrails como mecanismo central. Los content filters detectan categorías de contenido dañino (odio, insultos, contenido inapropiado, violencia, mala conducta) y, dentro de esa misma categoría, un filtro específico de &lt;strong&gt;prompt attacks&lt;/strong&gt; detecta jailbreaks, prompt injections y prompt leakages. Hay que entender la diferencia entre prompt injection directo (el usuario intenta manipular el prompt) e indirecto (la instrucción maliciosa viene escondida en un dato externo, como un documento o una página web, que el agente procesa sin que el usuario lo note).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Seguridad en interacciones de agentes de AI&lt;/strong&gt;: autorización de uso de tools, aislamiento de sesión (&lt;em&gt;session isolation&lt;/em&gt;) y flujos de aprobación humana (&lt;em&gt;human-in-the-loop&lt;/em&gt;). Esto conecta directo con Amazon Bedrock AgentCore Identity: este servicio provee autenticación, autorización y gestión de credenciales diseñada específicamente para agentes de AI y workloads automatizados, con identidades de workload que tienen atributos especializados para habilitar capacidades específicas de agentes, manteniendo compatibilidad con patrones estándar de identidad de workload de la industria.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Protección de contenido sensible en logs de monitoreo.&lt;/strong&gt; Amazon Bedrock permite habilitar &lt;em&gt;model invocation logging&lt;/em&gt; hacia CloudWatch Logs o S3, pero por defecto esos logs pueden incluir el input/output completo de cada invocación. El mecanismo para proteger datos sensibles ahí es Amazon Bedrock Guardrails (vía la API &lt;code&gt;ApplyGuardrail&lt;/code&gt;), que permite enmascarar esa información antes de que quede registrada — mismo espíritu que ya aplicabas con &lt;code&gt;StreamViewType: KEYS_ONLY&lt;/code&gt; en DynamoDB Streams para no exponer información sensible.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmigjocxorfzs50iyd5j5.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmigjocxorfzs50iyd5j5.png" alt="skills de Task 2.3" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;Tip de estudio:&lt;/strong&gt; si en DVA-C02 profundizaste STS, IAM trust relationships y KMS, ya tenés el 70% del razonamiento necesario para esta Task nueva. Lo que hay que sumar es el vocabulario y los mecanismos específicos de AI (Guardrails, AgentCore Identity, PrivateLink hacia Bedrock).&lt;/p&gt;

&lt;h2&gt;
  
  
  Incorporación 2: AI-assisted development transversal a los 4 dominios
&lt;/h2&gt;

&lt;p&gt;Esto se distribuye en los cuatro dominios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Domain 1 (Development):&lt;/strong&gt; generación de código y revisión automatizada.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain 3 (Testing and Deployment):&lt;/strong&gt; automatización de pruebas y pruebas de regresión, y soporte para workflows de CI/CD incluyendo aprobaciones automatizadas de despliegue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain 4 (Troubleshooting and Optimization):&lt;/strong&gt; análisis de errores, sugerencias de troubleshooting y recomendaciones de optimización de performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Un dato importante para no confundirse en el estudio: según la &lt;a href="https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/q-developer-ide-end-of-support.html" rel="noopener noreferrer"&gt;documentación oficial sobre el fin de soporte de los plugins IDE de Amazon Q Developer&lt;/a&gt;, AWS discontinuará el soporte de estos plugins el 30 de abril de 2027, y como alternativa para capacidades similares (coding agéntico, chat, soporte MCP) recomienda explorar &lt;strong&gt;Kiro&lt;/strong&gt;. &lt;br&gt;
Kiro es el IDE agéntico que propone &lt;em&gt;spec-driven development&lt;/em&gt;: convierte un prompt en requerimientos, diseño de sistema y lista de tareas antes de generar código, en lugar de saltar directo a la implementación. Como developer, puede que el examen busca que entiendas el concepto de &lt;em&gt;spec-driven development&lt;/em&gt; como enfoque de AI-assisted development.&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;Tip:&lt;/strong&gt; una lectura valida es entender en qué punto del ciclo de vida de desarrollo entra cada práctica de AI-assisted development, que es como está formulada la skill en cada dominio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incorporación 3: Gestión de contenedores
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Construcción y gestión de imágenes de contenedor con &lt;strong&gt;Amazon ECR&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Despliegue de aplicaciones containerizadas con &lt;strong&gt;Amazon ECS&lt;/strong&gt;, &lt;strong&gt;Amazon EKS&lt;/strong&gt; y &lt;strong&gt;AWS Fargate&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En mi artículo anterior ya mencionaba ECS/EKS dentro de "Contenedores ECS, EKS" enfocado en monitoreo y observabilidad (CloudWatch, X-Ray) ya que no se me presentaron otro tipo de preguntas. Pero la diferencia es que en la nueva versión del examen amplía el foco: no solo observar un contenedor en ejecución, sino todo el ciclo — construir la imagen, subirla a ECR, y decidir entre ECS, EKS o Fargate según el caso de uso (gestión de clúster propia vs. serverless de contenedores).&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzpe9744wfi8v76ranh5u.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzpe9744wfi8v76ranh5u.jpg" alt="AWS Containers" width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;Tip de laboratorio:&lt;/strong&gt; armá una imagen simple, subila a ECR, y desplegarla primero en ECS con Fargate y después evaluá qué cambiaría si migraras esa misma imagen a EKS. La diferencia conceptual entre "quién administra el plano de control y los nodos" es justamente lo que suelen evaluar en preguntas de este estilo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incorporación 4: Nuevos servicios que entran en el alcance del examen
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Bedrock&lt;/strong&gt; → el servicio administrado para trabajar con modelos fundacionales.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Bedrock AgentCore&lt;/strong&gt; → la plataforma para construir, desplegar y operar agentes de AI de forma segura y a escala, sin gestionar infraestructura. Se compone de Runtime (entorno serverless para correr agentes), Memory (memoria de corto y largo plazo), Gateway, Identity, herramientas built-in (ejecución de código en sandbox, browser tool) y Observability (monitoreo vía CloudWatch). Para un developer, dos piezas son especialmente relevantes:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gateway&lt;/strong&gt;, porque permite convertir APIs existentes (OpenAPI, Smithy) y funciones &lt;strong&gt;AWS Lambda&lt;/strong&gt; en tools compatibles con el protocolo &lt;strong&gt;MCP&lt;/strong&gt; (Model Context Protocol), sin tener que gestionar esa infraestructura — reutiliza directo el conocimiento de Lambda/API Gateway que ya tenés de DVA-C02.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identity&lt;/strong&gt;, porque conecta directo con la Task 2.3 de seguridad que nombré más arriba.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Q&lt;/strong&gt; → el asistente conversacional generativo para entender, construir, extender y operar aplicaciones y recursos de AWS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kiro&lt;/strong&gt; → como comenté arriba, el IDE agéntico de &lt;em&gt;spec-driven development&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Data Firehose&lt;/strong&gt; → para entrega de streams de datos a destinos como S3, Redshift o servicios de terceros; relevante si tu arquitectura de eventos (la que ya veías con EventBridge y Kinesis Data Streams) necesita alimentar pipelines de AI o analítica.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS PrivateLink&lt;/strong&gt; → ya mencionado en la Task de AI security, pero también entra como servicio in-scope de forma general, para conexiones privadas VPC-a-servicio sin pasar por internet.&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F12bcx435xqy7632tgnyi.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F12bcx435xqy7632tgnyi.jpg" alt="AWS AgentCore" width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que se consolida (menos memorizar features aislados, más criterio)
&lt;/h2&gt;

&lt;p&gt;Se aclara que varias skills fragmentadas se agrupan en unidades más grandes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Área&lt;/th&gt;
&lt;th&gt;Antes (DVA-C02)&lt;/th&gt;
&lt;th&gt;Ahora (DVA-C03)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;td&gt;4 skills separadas&lt;/td&gt;
&lt;td&gt;1 skill integral&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Seguridad (cifrado y datos sensibles)&lt;/td&gt;
&lt;td&gt;13 skills en 2 tasks&lt;/td&gt;
&lt;td&gt;7 skills en 1 task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testing&lt;/td&gt;
&lt;td&gt;11 skills en 2 tasks&lt;/td&gt;
&lt;td&gt;5 skills en 1 task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Observability&lt;/td&gt;
&lt;td&gt;8 skills&lt;/td&gt;
&lt;td&gt;5 skills&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optimization&lt;/td&gt;
&lt;td&gt;9 skills&lt;/td&gt;
&lt;td&gt;6 skills&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;En la práctica, significa que el examen deja de preguntar detalles muy puntuales y aislados, y empieza a evaluar más el &lt;strong&gt;razonamiento integrado&lt;/strong&gt; sobre un tema (por ejemplo, DynamoDB completo como una sola competencia, en vez de partir claves, índices, capacidad y TTL en preguntas separadas). Si con la guía actual DVA-C02 ya estudiaste estos temas a fondo como yo, no vas a tener que aprender contenido nuevo acá. El cambio es más de "cómo te lo preguntan".&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo armaría el plan de estudio hoy hasta esperar el detalle de la nueva guía de examen
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Base técnica:&lt;/strong&gt; seguí usando mi &lt;a href="https://medium.com/@reinalau/camino-a-lograr-aws-certified-developer-associate-dva-c02-9a59112df16f" rel="noopener noreferrer"&gt;artículo de DVA-C02&lt;/a&gt; como guía de los servicios core (Lambda, DynamoDB, API Gateway, Cognito, SQS/SNS, EventBridge, Step Functions, S3, CI/CD, CDK/CloudFormation, CloudWatch/X-Ray, seguridad de datos y STS). Ahí no cambió la profundidad requerida.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sumá una capa de AI aplicada al developer:&lt;/strong&gt; entendé conceptualmente Amazon Bedrock, AgentCore (sobre todo Gateway e Identity), Amazon Q Developer y Kiro — no como usuario experto, sino como developer que decide cuándo y cómo integrarlos, y qué controles de seguridad aplicar (Guardrails, PrivateLink, aislamiento de sesión, human-in-the-loop).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Profundizá contenedores end-to-end:&lt;/strong&gt; ECR para las imágenes, y el criterio de decisión entre ECS, EKS y Fargate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repasá la guía de examen oficial completa&lt;/strong&gt; apenas se publique (disponible cuando abra el registro, el 27 de octubre de 2026), porque ahí van a estar los task statements detallados de cada skill.&lt;/li&gt;
&lt;li&gt;Mantené la costumbre que ya recomendaba antes: hacé laboratorios chicos de todo lo anterior y practicar muchas preguntas de examen, más que memorizar teoría aislada.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;⭐ Y el mismo disclaimer que hice en el artículo anterior sigue más vigente que nunca: &lt;strong&gt;cuidado con usar AI para el estudio profundo de servicios sin chequear contra documentación oficial&lt;/strong&gt; — pueden aparecer alucinaciones si se utiliza el conocimiento del modelo que uses. Contrastá contra la documentación oficial via los MCP oficiales de AWS:&lt;br&gt;
"awslabs.aws-documentation-mcp-server" y "aws-knowledge-mcp-server" &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Conclusión:&lt;/strong&gt; Si estás arrancando de cero con esta certificación, te recomiendo leer primero &lt;a href="https://medium.com/@reinalau/camino-a-lograr-aws-certified-developer-associate-dva-c02-9a59112df16f" rel="noopener noreferrer"&gt;mi artículo sobre DVA-C02&lt;/a&gt; para la base de servicios, y usar este como complemento de lo nuevo que trae DVA-C03.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fuentes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/es/blogs/training-and-certification/september-2026-new-offerings/" rel="noopener noreferrer"&gt;Certification updates from AWS Training and Certification: September 2026 — AWS Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/bedrock/security-privacy-responsible-ai/" rel="noopener noreferrer"&gt;Security, privacy, and responsible AI — Amazon Bedrock&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/vpc-interface-endpoints.html" rel="noopener noreferrer"&gt;Use interface VPC endpoints (AWS PrivateLink) to create a private connection to Amazon Bedrock&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails-components.html" rel="noopener noreferrer"&gt;Create your guardrail — Amazon Bedrock Guardrails&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/identity-overview.html" rel="noopener noreferrer"&gt;Overview of Amazon Bedrock AgentCore Identity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/machine-learning/introducing-amazon-bedrock-agentcore-gateway-transforming-enterprise-ai-agent-tool-development/" rel="noopener noreferrer"&gt;Introducing Amazon Bedrock AgentCore Gateway&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/q-developer-ide-end-of-support.html" rel="noopener noreferrer"&gt;Amazon Q Developer IDE plugins end of support&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>certification</category>
      <category>developerassociate</category>
    </item>
    <item>
      <title>A2A en Strands: agentes remotos con routing determinístico y discovery dinámico</title>
      <dc:creator>Laura Bolaños</dc:creator>
      <pubDate>Tue, 01 Sep 2026 16:20:05 +0000</pubDate>
      <link>https://dev.to/aws-builders/a2a-en-strands-agentes-remotos-con-routing-deterministico-y-discovery-dinamico-36gn</link>
      <guid>https://dev.to/aws-builders/a2a-en-strands-agentes-remotos-con-routing-deterministico-y-discovery-dinamico-36gn</guid>
      <description>&lt;p&gt;Culminando la serie de artículos que trata la orquestación de multiagentes en Strands Agents, voy a explicar cómo implementar agentes remotos con A2A y algunas de sus variantes, utilizando routing determinístico y discovery dinámico de agentes. Lo ejemplifiqué con la construcción de dos casos de uso: &lt;code&gt;privacy-aware-routing&lt;/code&gt; y &lt;code&gt;multi-agent-trip-planner&lt;/code&gt;. Para probarlos utilicé Gemma4 + Ollama (local) y la API de Gemini, capa gratuita.&lt;/p&gt;

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

&lt;p&gt;La idea principal es experimentar con ejemplos sencillos algunas casuísticas del protocolo de comunicación A2A en tu notebook, sin necesidad de una cuenta de AWS ni de servicios como Bedrock. Strands hace de wrapper sobre el protocolo, permitiendo una comunicación fluida con agentes de IA remotos, más allá del framework o lenguaje con el que estén implementados.&lt;/p&gt;

&lt;p&gt;🗒 &lt;strong&gt;Los casos de usos&lt;/strong&gt; que elegí &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy aware routing&lt;/strong&gt;: Un asistente financiero-bancario de atención al cliente (orquestador, Gemini vía API key) necesita resolver consultas que a veces incluyen datos sensibles (ej. número de cuenta, DNI, monto de la cuenta). En lugar de resolver el agente general, el orquestador detecta cuándo la consulta toca datos sensibles y delega esa parte a un &lt;strong&gt;agente A2A local&lt;/strong&gt; (servidor separado, Gemma vía Ollama, corriendo en &lt;code&gt;localhost&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi agent trip planner&lt;/strong&gt;: Un asistente de viajes de atención al cliente donde el orquestador (Gemini vía API key) no conoce de antemano a qué agente delegar cada parte de la consulta. En su lugar, &lt;strong&gt;descubre en runtime&lt;/strong&gt; tres agentes especialistas: vuelos, hoteles e itinerario. Cada uno expuesto como servidor A2A local (Gemma vía Ollama), y decide dinámicamente a cuál invocar según lo que necesita resolver.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En las secciones siguientes cubro los conceptos clave del protocolo A2A en Strands y el diseño e implementación de los casos de uso.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conceptos Clave
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ➡ Protocolo A2A en Strands
&lt;/h3&gt;

&lt;p&gt;A2A (Agent-to-Agent) es un protocolo abierto —no una invención de Strands ni de AWS— que define cómo agentes que corren en procesos, máquinas o incluso organizaciones distintas se &lt;strong&gt;descubren&lt;/strong&gt; y &lt;strong&gt;comunican&lt;/strong&gt; entre sí por HTTP/JSON-RPC.&lt;/p&gt;

&lt;p&gt;Strands no reinventa el protocolo: lo envuelve (&lt;em&gt;wraps&lt;/em&gt;) con una interfaz familiar, como si invocáramos a un agente normal de Strands. El desarrollador no ve protocolo: visualiza un agente. Un ejemplo sencillo:&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.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="c1"&gt;# Create an A2AAgent pointing to a remote A2A server
&lt;/span&gt;&lt;span class="n"&gt;privacy_agent&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;endpoint&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="c1"&gt;# Invoked exactly like a local Agent — Strands' wrapper keeps
# the A2A protocol completely hidden from the caller
&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;privacy_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;I need the balance for account 1234-5678-9&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="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# {'role': 'assistant', 'content': [{'text': 'The balance for account 1234-5678-9 is $85,400'}]}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El constructor de &lt;code&gt;A2AAgent&lt;/code&gt; acepta varios parámetros:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;endpoint&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Required&lt;/td&gt;
&lt;td&gt;Base URL of the remote A2A agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Agent name (auto-populated from agent card if not provided)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Agent description (auto-populated from agent card if not provided)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timeout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;td&gt;Timeout for HTTP operations in seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;a2a_client_factory&lt;/td&gt;
&lt;td&gt;ClientFactory&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Optional pre-configured A2A client factory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Dos piezas hacen posible el wrap en Strands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;A2AServer&lt;/code&gt;&lt;/strong&gt;: expone cualquier &lt;code&gt;Agent&lt;/code&gt; de Strands como servicio A2A, publicando automáticamente un &lt;em&gt;agent card&lt;/em&gt; en &lt;code&gt;/.well-known/agent-card.json&lt;/code&gt; → es la carta de presentación del agente (nombre, descripción, skills que ofrece).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;A2AAgent&lt;/code&gt;&lt;/strong&gt;: el lado cliente. Envuelve el endpoint de un agente remoto y lo invocás con la misma interfaz que un agente local.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Al igual que un &lt;code&gt;Agent&lt;/code&gt; local, &lt;code&gt;A2AAgent&lt;/code&gt; soporta invocación asíncrona (&lt;code&gt;invoke_async&lt;/code&gt;) y streaming de respuestas (&lt;code&gt;stream_async&lt;/code&gt;). En los ejemplos de este artículo, el orquestador transmite su respuesta en streaming, mientras que cada llamada A2A a un agente remoto es sincrónica (espera la respuesta completa del especialista antes de seguir).&lt;/p&gt;

&lt;p&gt;La metadata del agente remoto es la &lt;em&gt;agent card&lt;/em&gt; y se puede consultar explícitamente con &lt;code&gt;get_agent_card()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;card&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;privacy_agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_agent_card&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;Agent: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&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;Description: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&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;Skills: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;skills&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;Cada &lt;em&gt;skill&lt;/em&gt; dentro del agent card describe una capacidad puntual del agente: un identificador, una descripción en lenguaje natural, y opcionalmente ejemplos de uso. Es lo que le permite al orquestador razonar "este agente sabe buscar vuelos" sin necesidad de que el desarrollador lo codifique explícitamente. La decisión de a quién delegar se basa en hacer &lt;em&gt;match&lt;/em&gt; entre la consulta del usuario y las skills descubiertas.&lt;/p&gt;

&lt;p&gt;El método anterior el que después va a cobrar protagonismo real en el ejemplo &lt;code&gt;multi-agent-trip-planner&lt;/code&gt;: ahí el orquestador no invoca un endpoint conocido de antemano, sino que consulta las agent cards de varios agentes remotos para decidir dinámicamente a cuál delegar cada tarea.&lt;/p&gt;

&lt;h3&gt;
  
  
  ➡ A2AAgent dentro de otros patrones de orquestación en Strands
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;A2AAgent&lt;/code&gt; se puede combinar dentro de otros patrones de orquestación multiagente en Strands.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Como tool de un agente orquestador&lt;/strong&gt;: es exactamente el enfoque que desarrollamos en la POC &lt;code&gt;privacy-aware-routing&lt;/code&gt; — el &lt;code&gt;A2AAgent&lt;/code&gt; se envuelve en un &lt;code&gt;@tool&lt;/code&gt; y el agente padre lo invoca como una función más, como si fuera un subagente. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Como nodo dentro de un Graph&lt;/strong&gt;: un &lt;code&gt;A2AAgent&lt;/code&gt; puede integrarse como nodo remoto en un flujo de Graph, mezclando agentes locales y remotos dentro del mismo grafo de dependencias. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;En Swarm&lt;/strong&gt;: al día de hoy NO está soportado, ya que ese patrón depende de handoffs basados en tools que el protocolo A2A todavía no contempla.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Habiendo visto el lado CLIENTE, veamos ahora cómo se expone un agente para que otros puedan consumirlo vía A2A.&lt;/p&gt;

&lt;h3&gt;
  
  
  ➡ Creando un A2A Server
&lt;/h3&gt;

&lt;p&gt;Del lado servidor, exponer un agente de Strands como servicio A2A es tan simple como envolverlo con &lt;code&gt;A2AServer&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context_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="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&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;Privacy 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;Resolves queries containing sensitive customer data&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;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_factory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;create_agent&lt;/span&gt;&lt;span class="p"&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;serve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El servidor publica automáticamente el &lt;em&gt;agent card&lt;/em&gt; en &lt;code&gt;/.well-known/agent-card.json&lt;/code&gt; y queda escuchando requests A2A en el puerto configurado (por default, &lt;code&gt;9000&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Un detalle de diseño importante: se recomienda pasar un &lt;strong&gt;&lt;code&gt;agent_factory&lt;/code&gt;&lt;/strong&gt; (una función que crea un agente nuevo) en lugar de una instancia única de &lt;code&gt;Agent&lt;/code&gt;. La razón es el &lt;strong&gt;aislamiento por conversación&lt;/strong&gt;: A2A identifica cada conversación con un &lt;code&gt;context_id&lt;/code&gt;, y el servidor crea y reutiliza un agente dedicado por cada &lt;code&gt;context_id&lt;/code&gt;, para que distintas conversaciones nunca mezclen su historial entre sí. Pasar un único &lt;code&gt;Agent&lt;/code&gt; compartido está deprecado!&lt;/p&gt;

&lt;p&gt;Para los casos de uso de este artículo, ese &lt;strong&gt;aislamiento&lt;/strong&gt; no es el foco central: cada servidor atiende un tipo de tarea puntual, sin mantener contexto conversacional extenso.&lt;br&gt;
Pero es un concepto clave a tener presente si tu agente remoto necesita recordar el hilo de una conversación a lo largo de varios turnos.&lt;/p&gt;
&lt;h3&gt;
  
  
  ➡ Strands A2A Tool: descubrimiento dinámico
&lt;/h3&gt;

&lt;p&gt;Cuando hay &lt;strong&gt;varios&lt;/strong&gt; agentes remotos disponibles y no querés hardcodear el mapeo "qué tarea va a qué agente", Strands ofrece &lt;code&gt;A2AClientToolProvider&lt;/code&gt; (parte del paquete &lt;code&gt;strands-agents-tools&lt;/code&gt;). Le da al agente orquestador un conjunto de tools para &lt;strong&gt;descubrir&lt;/strong&gt; agentes A2A y comunicarse con ellos usando lenguaje natural:&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.a2a_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;A2AClientToolProvider&lt;/span&gt;

&lt;span class="c1"&gt;# known_agent_urls es opcional: podés pasar endpoints conocidos
# o dejar que el provider los descubra en runtime
&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;A2AClientToolProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;known_agent_urls&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;http://localhost:9001&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Flights Agent
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:9002&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Hotels Agent
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:9003&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Itinerary 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;orchestrator&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="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tools&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;orchestrator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Quiero ir a Barcelona en marzo, 5 días&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 provider expone tres capacidades:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Agent Discovery&lt;/strong&gt;: descubre automáticamente los agentes A2A disponibles y sus capacidades (leyendo sus &lt;em&gt;agent cards&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protocol Communication&lt;/strong&gt;: envía mensajes usando el protocolo A2A estandarizado.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Natural Language Interface&lt;/strong&gt;: el orquestador interactúa con los agentes remotos en lenguaje natural, sin que el desarrollador tenga que codear explícitamente "si la consulta es sobre vuelos, llamá a este endpoint".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esta es la pieza central de la POC de este articulo &lt;code&gt;multi-agent-trip-planner&lt;/code&gt;: acá el &lt;em&gt;agent card&lt;/em&gt; deja de ser un detalle de implementación y pasa a ser el mecanismo que le permite al orquestador razonar sobre a quién delegar cada subtarea, sin que ese mapeo esté escrito en el código.&lt;/p&gt;

&lt;h3&gt;
  
  
  ➡ Evaluaciones (Evals)
&lt;/h3&gt;

&lt;p&gt;Strands cuenta con un &lt;a href="https://strandsagents.com/docs/user-guide/evals-sdk/quickstart/" rel="noopener noreferrer"&gt;marco oficial&lt;/a&gt; para realizar evaluaciones, desde resultados simples hasta el análisis de interacciones multiagente complejas.&lt;/p&gt;

&lt;p&gt;Para evaluar los workflows usé &lt;code&gt;strands-agents-evals&lt;/code&gt; con el mismo provider configurado en &lt;code&gt;.env&lt;/code&gt; (Gemini) como juez via LiteLLM para mantener el proyecto sobre una sola credencial y sin cuenta de AWS. En producción la práctica estándar es usar un modelo más capaz e independiente como judge.&lt;/p&gt;

&lt;p&gt;En ambos proyectos combiné dos tipos de evaluación, para mostrar las dos familias que ofrece el SDK:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Determinística&lt;/strong&gt; (código puro, sin LLM): rápida y barata, ideal para CI (Continuous Integration). Verifica la &lt;em&gt;decisión&lt;/em&gt; del orquestador inspeccionando qué herramientas invocó (su trayectoria). Es el corazón de cada patrón A2A.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM-as-a-judge&lt;/strong&gt;: un modelo puntúa, con una rúbrica, cualidades más subjetivas de la respuesta final: &lt;em&gt;Correctness&lt;/em&gt; (¿el dato coincide con la referencia?) y &lt;em&gt;Faithfulness&lt;/em&gt; (¿se apoya en los datos de las tools y no inventa ni evade?).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La diferencia está en &lt;em&gt;qué&lt;/em&gt; mide la capa determinística en cada patrón, porque cada uno decide algo distinto:&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;&lt;strong&gt;privacy-aware-routing&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;multi-agent-trip-planner&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Qué decide el orquestador&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Si delega o responde directo&lt;/td&gt;
&lt;td&gt;A quién descubre y a quién delega&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Eval determinística&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;La tool &lt;code&gt;handle_sensitive_query&lt;/code&gt; se llama solo ante datos sensibles, y NO ante consultas genéricas&lt;/td&gt;
&lt;td&gt;El orquestador primero descubre (&lt;code&gt;a2a_list_discovered_agents&lt;/code&gt;) y luego delega (&lt;code&gt;a2a_send_message&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Evaluadores usados&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ToolCalled&lt;/code&gt; + &lt;code&gt;ToolNotCalled&lt;/code&gt; (custom)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ToolCalled&lt;/code&gt; (discovery y delegación)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Eval LLM-judge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Correctness + Faithfulness sobre la respuesta del agente local&lt;/td&gt;
&lt;td&gt;Correctness + Faithfulness sobre la propuesta de viaje integrada&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Problema que detecta&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;El modelo local inventa un saldo en lugar de usar la tool&lt;/td&gt;
&lt;td&gt;Un especialista inventa vuelos/hoteles/atracciones en lugar de usar su tool&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Descripción de la solución
&lt;/h2&gt;

&lt;p&gt;📦 &lt;strong&gt;Un único repositorio de GitHub con los dos proyectos:&lt;/strong&gt;  &lt;a href="https://github.com/reinalau/strands-a2a-patterns" rel="noopener noreferrer"&gt;github.com/reinalau/strands-a2a-patterns&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flwv14r1fm36ssc7rqpbg.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flwv14r1fm36ssc7rqpbg.jpg" alt="Privacy Aware routing" width="686" height="714"&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffn4q481vtkpnxhj9jj93.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffn4q481vtkpnxhj9jj93.jpg" alt="Multi-Agent travel trip planner" width="688" height="708"&gt;&lt;/a&gt;&lt;br&gt;
El repositorio reúne &lt;strong&gt;dos proyectos independientes&lt;/strong&gt;, cada uno es un caso de uso de A2A:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;privacy-aware-routing&lt;/code&gt;&lt;/strong&gt; - ruteo determinístico hacia un único agente remoto: el orquestador decide, según el contenido de la consulta, si responde él mismo o delega la parte sensible a un agente que corre 100% local.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;multi-agent-trip-planner&lt;/code&gt;&lt;/strong&gt; - descubrimiento dinámico de agentes: el orquestador no conoce de antemano a los especialistas, sino que los descubre en runtime leyendo sus &lt;em&gt;agent cards&lt;/em&gt; y delega cada subtarea al que corresponda. A su vez cada agente tiene tools disponibles.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ambos comparten la misma base técnica (Strands, con Gemini como orquestador y Gemma local como agente remoto) y la misma forma de ejecutarse. El siguiente setup aplica a los dos por igual. Se usan los dos modelos, Ollama local y Gemini via Apikey:&lt;/p&gt;

&lt;p&gt;a. Ollama + el pequeño modelo &lt;strong&gt;gemma4:e2b-it-qat&lt;/strong&gt; (que pesa poco más de 4gb) ejecutando en Docker. Para esto necesitás tener &lt;strong&gt;Docker Desktop&lt;/strong&gt; instalado y corriendo; una vez levantado, los comandos de abajo crean el contenedor de Ollama y descargan el modelo.&lt;br&gt;
En el caso de &lt;code&gt;multi-agent-trip-planner&lt;/code&gt; hay tres agentes remotos que pueden recibir requests casi al mismo tiempo; si querés que esas llamadas se ejecuten en paralelo, hay que indicárselo a Ollama, cuyo comportamiento por default procesa una inferencia por vez:&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;# Start the Ollama server with a persistent volume&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; ollama &lt;span class="nt"&gt;-p&lt;/span&gt; 11434:11434 &lt;span class="nt"&gt;-v&lt;/span&gt; ollama_data:/root/.ollama ollama/ollama

&lt;span class="c"&gt;# Start the Ollama server with real parallelism (3 simultaneous requests)&lt;/span&gt;
&lt;span class="c"&gt;# docker run -d --name ollama -p 11434:11434 -v ollama_data:/root/.ollama -e OLLAMA_NUM_PARALLEL=3  ollama/ollama&lt;/span&gt;

&lt;span class="c"&gt;# Download the model&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; ollama ollama pull gemma4:e2b-it-qat

&lt;span class="c"&gt;# Test that the model responds&lt;/span&gt;
&lt;span class="c"&gt;# If the container has already been created and &lt;/span&gt;
&lt;span class="c"&gt;# is currently stopped, simply use: docker start ollama&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; ollama ollama run gemma4:e2b-it-qat

&lt;span class="c"&gt;# Verify that the model is running&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; ollama ollama ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;b. API de Gemini. La API key se puede generar de manera gratuita desde &lt;a href="https://aistudio.google.com/" rel="noopener noreferrer"&gt;aquí&lt;/a&gt; y utilizar al menos estos modelos (experimentá con los que te permita tu cuenta):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gemini-3.6-flash
gemini-3.5-flash-lite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;El código de los 2 proyectos está en Python&lt;/strong&gt; y la estructura se diseñó de manera que su lógica sea más explicativa; todo el detalle lo encontrás en el &lt;code&gt;README.md&lt;/code&gt; de cada proyecto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;strands-a2a-patterns/
├── README.md                          # Overview of both use cases
│
├── privacy-aware-routing/             # Case 1: deterministic routing, single remote agent
│   ├── README.md
│   ├── requirements.txt
│   ├── .env.example
│   ├── common/                        # shared: prompts, config, logging
│   ├── remote_agent/
│   │   ├── server.py                  # A2AServer — Gemma via Ollama
│   │   └── bank_tools.py              # mock bank tools with fixed data
│   ├── orchestrator/
│   │   └── main.py                    # Gemini — A2AAgent as tool
│   ├── tests/
│   │   ├── test_routing_logic.py      # does the orchestrator delegate when it should?
│   │   └── test_a2a_integration.py    # real integration test against the running local server
│   ├── evals/
│   │   ├── eval_routing.py            # deterministic: routing decision
│   │   └── eval_response_quality.py   # LLM-judge: correctness + faithfulness
│   └── logs/
│       └── .gitkeep                    # run logs land here (gitignored)
│
└── multi-agent-trip-planner/          # Case 2: dynamic discovery, 3 remote agents
    ├── README.md
    ├── requirements.txt
    ├── .env.example
    ├── common/                        # shared: prompts, config, logging
    ├── remote_agents/
    │   ├── travel_tools.py            # mock travel tools with fixed data
    │   ├── flights_server.py          # A2AServer — Gemma via Ollama (port 9001)
    │   ├── hotels_server.py           # A2AServer — Gemma via Ollama (port 9002)
    │   └── itinerary_server.py        # A2AServer — Gemma via Ollama (port 9003)
    ├── orchestrator/
    │   └── main.py                    # Gemini — A2AClientToolProvider
    ├── tests/
    │   ├── test_discovery.py          # does the orchestrator discover before delegating?
    │   └── test_a2a_integration.py    # real integration test against the 3 running servers
    ├── evals/
    │   ├── eval_discovery.py          # deterministic: discovery + delegation decision
    │   └── eval_response_quality.py   # LLM-judge: correctness + faithfulness
    └── logs/
        └── .gitkeep                    # run logs land here (gitignored)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Ejecución Local
&lt;/h3&gt;

&lt;p&gt;Una vez que tenemos el repositorio clonado y el Docker de Ollama con el modelo gemma 4 (o la API key de Gemini generada), pasamos a armar el ambiente. Los pasos son los mismos en ambos proyectos; cada uno tiene su propio &lt;code&gt;requirements.txt&lt;/code&gt; y su &lt;code&gt;.env&lt;/code&gt;, así que hay que pararse dentro de la carpeta del proyecto que quieras correr.&lt;/p&gt;

&lt;p&gt;En &lt;code&gt;requirements.txt&lt;/code&gt; (con comentarios que explican para qué se necesita cada dependencia) encontramos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;strands-agents[a2a,ollama,litellm]&amp;gt;=1.0.0
strands-agents-tools&amp;gt;=0.1.0
python-dotenv&amp;gt;=1.0.0
pytest&amp;gt;=8.0.0
pytest-asyncio&amp;gt;=0.23.0
strands-agents-evals&amp;gt;=1.2.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conviene instalar las dependencias en un entorno virtual, para aislarlas del resto del sistema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="c"&gt;# Activar: source .venv/bin/activate (Linux/macOS)&lt;/span&gt;
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;p&gt;En &lt;code&gt;.env&lt;/code&gt; se configuran, entre otros, la API key y el modelo del orquestador (Gemini), el modelo local (Gemma vía Ollama) y el nivel de log. Alcanza con copiar el ejemplo y completar la key:&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="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;En &lt;code&gt;common/config.py&lt;/code&gt;&lt;/strong&gt; está la carga y validación de esas variables (falla temprano con un mensaje si falta la API key), y &lt;strong&gt;en &lt;code&gt;common/prompts.py&lt;/code&gt;&lt;/strong&gt; los system prompts del orquestador y de cada agente remoto.&lt;/p&gt;

&lt;p&gt;📝 &lt;strong&gt;Sobre cambiar de modelo:&lt;/strong&gt; el orquestador usa &lt;code&gt;LiteLLMModel&lt;/code&gt;, así que no está atado a Gemini: LiteLLM soporta muchos proveedores (Anthropic, OpenAI, Bedrock, etc.). Alcanza con cambiar &lt;code&gt;ORCHESTRATOR_MODEL_ID&lt;/code&gt; en el &lt;code&gt;.env&lt;/code&gt; por el identificador del proveedor que quieras (con su prefijo, p. ej. &lt;code&gt;anthropic/…&lt;/code&gt;, &lt;code&gt;openai/…&lt;/code&gt;) y setear su API key. La lista completa está en la &lt;a href="https://strandsagents.com/docs/user-guide/concepts/model-providers/litellm/" rel="noopener noreferrer"&gt;documentación de model providers de Strands&lt;/a&gt;. Del mismo modo, el agente remoto no tiene por qué correr en Ollama: podés reemplazar &lt;code&gt;OllamaModel&lt;/code&gt; por otro provider en el código del servidor (&lt;code&gt;server.py&lt;/code&gt; / los &lt;code&gt;*_server.py&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Las pruebas&lt;/strong&gt; están en &lt;code&gt;tests/&lt;/code&gt; y se pueden correr una a una  (recomendado! ya que puede ser algo lento localmente) o todas juntas: unitarias rápidas (deciden si el orquestador delega/descubre cuando corresponde) e integración real contra los servidores A2A (arrancan solos como subprocesos; requieren Ollama corriendo).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Procedemos a ejecutar el caso real.&lt;/strong&gt; Acá está la diferencia práctica entre los dos proyectos: cada uno corre como &lt;strong&gt;varios procesos independientes&lt;/strong&gt;, uno por terminal. Primero se levantan los agentes remotos (cada &lt;code&gt;A2AServer&lt;/code&gt; queda escuchando en su puerto) y, con todos activos, se lanza el orquestador que los descubre y les habla por la red. El orden de arranque de los servidores no importa, pero deben estar todos activos antes de correr el orquestador.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;privacy-aware-routing&lt;/code&gt; — 2 procesos (2 terminales):&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;Terminal - Comando&lt;/th&gt;
&lt;th&gt;Rol&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Terminal 1&lt;/strong&gt;: &lt;code&gt;python -m remote_agent.server&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Agente remoto local (Gemma), expone su agent card en el puerto 9000. Cuenta con tools para responder sobre datos sensibles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Terminal 2&lt;/strong&gt;: &lt;code&gt;python -m orchestrator.main "Quiero consultar el saldo de mi cuenta 1234-5678-9"&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Orquestador (Gemini): decide si delega y arma la respuesta&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;multi-agent-trip-planner&lt;/code&gt; — 4 procesos (4 terminales):&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;Terminal - Comando&lt;/th&gt;
&lt;th&gt;Rol&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Terminal 1&lt;/strong&gt;: &lt;code&gt;python -m remote_agents.flights_server&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Especialista de vuelos (Gemma), puerto 9001&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Terminal 2&lt;/strong&gt;: &lt;code&gt;python -m remote_agents.hotels_server&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Especialista de hoteles (Gemma), puerto 9002&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Terminal 3&lt;/strong&gt;: &lt;code&gt;python -m remote_agents.itinerary_server&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Especialista de itinerario (Gemma), puerto 9003&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Terminal 4&lt;/strong&gt;: &lt;code&gt;python -m orchestrator.main "Quiero ir a Barcelona en marzo, 5 dias"&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Orquestador (Gemini): descubre los 3 agentes, delega y arma la propuesta&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;El orquestador acepta la consulta como argumento o, si se lo llama sin argumentos, abre un chat interactivo. &lt;br&gt;
Para salir de los servidores remotos: &lt;code&gt;Ctrl+C&lt;/code&gt; en su terminal; para salir del chat interactivo: escribí &lt;code&gt;exit&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Los logs&lt;/strong&gt; de las llamadas están en &lt;code&gt;logs/&lt;/code&gt;, separado por proceso. Contiene la data cruda que permite seguir el intercambio A2A: qué descubrió y delegó el orquestador, y qué tool ejecutó cada agente remoto (líneas &lt;code&gt;TOOL CALL: ...&lt;/code&gt;). Permite verificar que la respuesta salió de los datos y no de una &lt;strong&gt;alucinación&lt;/strong&gt; del modelo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;El resultado final&lt;/strong&gt; se visualiza en pantalla, en streaming (token a token).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Por último, hacemos una evaluación sobre el sistema multiagente real con el concepto de &lt;em&gt;"Evals".&lt;/em&gt;&lt;/strong&gt; Cada proyecto trae dos scripts: uno determinístico (verifica la decisión del orquestador) y uno LLM-as-judge (puntúa la calidad de la respuesta final). Ejecutan el flujo real contra el modelo configurado, muestran los resultados en consola y guardan el reporte completo en &lt;code&gt;evals/outputs/&amp;lt;nombre&amp;gt;_&amp;lt;timestamp&amp;gt;.json&lt;/code&gt;. (Leer más detalle arriba en Conceptos Clave)&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;# privacy-aware-routing&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; evals.eval_routing
python &lt;span class="nt"&gt;-m&lt;/span&gt; evals.eval_response_quality

&lt;span class="c"&gt;# multi-agent-trip-planner&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; evals.eval_discovery
python &lt;span class="nt"&gt;-m&lt;/span&gt; evals.eval_response_quality
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;Los dos proyectos implementan casos de uso distintos que se apoyan en la misma base exponer agentes con &lt;code&gt;A2AServer&lt;/code&gt;, pero difieren en &lt;strong&gt;cómo el orquestador consume esos agentes remotos&lt;/strong&gt;: uno con &lt;strong&gt;ruteo determinístico&lt;/strong&gt; hacia un agente conocido (&lt;code&gt;A2AAgent&lt;/code&gt;, endpoint fijo); y el otro, mediante un &lt;strong&gt;descubrimiento dinámico&lt;/strong&gt; de varios especialistas (&lt;code&gt;A2AClientToolProvider&lt;/code&gt;, que lee los &lt;em&gt;agent cards&lt;/em&gt; en runtime). &lt;/p&gt;

&lt;p&gt;⭐ En ambos casos de uso, la abstracción de Strands esconde la mecánica del protocolo (resolución del agent card, HTTP, JSON-RPC). Del lado del orquestador, delegar a un proceso remoto se ve casi igual que llamar a una función local.&lt;/p&gt;

&lt;p&gt;Algunas lecciones que dejaron las ejecuciones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;El &lt;em&gt;agent card&lt;/em&gt; es el contrato.&lt;/strong&gt; En el caso de discovery, la calidad de las &lt;code&gt;skills&lt;/code&gt; publicadas (nombre y descripción) es lo que le permite al orquestador rutear bien sin mapeos hardcodeados. Un agent card incompleta degrada la decisión.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Los modelos locales chicos usan tools de forma intermitente.&lt;/strong&gt; Con &lt;code&gt;gemma4:e2b-it-qat&lt;/code&gt;, a veces el agente responde sin llamar la tool y ahí puede inventar datos. Por eso importa (a) instruirlo en el prompt a usar siempre la tool, y (b) verificarlo: los logs &lt;code&gt;TOOL CALL: ...&lt;/code&gt; distinguen un dato real de una alucinación.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluar los agentes exige leer el campo "reasons", no solo el score.&lt;/strong&gt; Un &lt;code&gt;0.0&lt;/code&gt; puede venir de la infraestructura (un &lt;code&gt;503&lt;/code&gt; del juez, un modelo dado de baja), no de una mala respuesta. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latencia y concurrencia son decisiones de arquitectura.&lt;/strong&gt; La inferencia local es lenta; con tres especialistas compartiendo un Ollama secuencial, las tareas se encolan. Habilitar paralelismo (&lt;code&gt;OLLAMA_NUM_PARALLEL&lt;/code&gt;) y ajustar los timeouts del cliente A2A cambia notablemente la experiencia.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Los dos casos ejemplificados son solo algunas variantes de A2A.  Strands habilita capacidades que dejo anotadas para &lt;strong&gt;futuras pruebas&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Agentes remotos dentro de patrones multiagente&lt;/strong&gt;: un &lt;code&gt;A2AAgent&lt;/code&gt; puede ser un nodo más en un &lt;em&gt;Graph workflow&lt;/em&gt;, mezclando agentes locales y remotos en un mismo pipeline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interrupciones (input_required)&lt;/strong&gt;: Similar a "human-in-the-loop", un agente remoto puede pausar una tarea y esperar una aprobación antes de continuar (por ejemplo, confirmar una reserva), y reanudar exactamente donde quedó.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistencia de conversaciones&lt;/strong&gt;: con un &lt;code&gt;SessionManager&lt;/code&gt; por &lt;code&gt;context_id&lt;/code&gt;, cada conversación puede sobrevivir al reinicio del proceso en lugar de vivir solo en memoria.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notificaciones push y despliegue distribuido&lt;/strong&gt;: para tareas largas y para exponer cada agente productivamente (por ejemplo, detrás de balanceadores, en AWS Fargate o vía AgentCore), escalando el sistema más allá de una notebook.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Utilizar MCP de manera complementaria&lt;/strong&gt;: Cada especialista puede usar MCP para sus tools y A2A para colaborar con otros. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⭐ Por último, aquí construyo los agentes en Python con Strands, pero como A2A es un estándar abierto (hoy bajo la Linux Foundation) y viaja sobre HTTP/JSON-RPC, la interoperabilidad ocurre a nivel del protocolo, no del framework. Un agente Strands puede comunicarse con agentes construidos en otros frameworks y lenguajes que también implementen A2A. El protocolo tiene SDKs oficiales en Python, JavaScript, Java, C#/.NET, Go y Rust, sin que ninguno conozca la lógica interna del otro.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Curso Fundamentos Strands&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://skillbuilder.aws/learn/CFBFZNS11J/building-ai-agent-harnesses-with-strands-agents/D6YG8S6N7M" rel="noopener noreferrer"&gt;Building AI Agent Harnesses with Strands Agents&lt;/a&gt;&lt;br&gt;
&lt;a href="https://builder.aws.com/content/3GYPrAplMhl2IAl7jJW0yeKzXrR/building-ai-agent-harnesses-with-strands-agents-a-free-14-video-course/" rel="noopener noreferrer"&gt;Building AI Agent Harnesses – Video Course&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Documentación Strands Agents&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/evals-sdk/evaluators/" rel="noopener noreferrer"&gt;Evals&lt;/a&gt;&lt;br&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/concepts/multi-agent/agent-to-agent/" rel="noopener noreferrer"&gt;Agent-to-Agent (A2A) Protocol&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Protocolo A2A (Agent2Agent)&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://a2a-protocol.org/latest/" rel="noopener noreferrer"&gt;Sitio oficial del protocolo A2A&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>strandsagents</category>
      <category>ai</category>
      <category>multiagents</category>
    </item>
    <item>
      <title>Workflow en Strands: un DAG de tareas ejecutado como herramienta única</title>
      <dc:creator>Laura Bolaños</dc:creator>
      <pubDate>Wed, 19 Aug 2026 21:31:31 +0000</pubDate>
      <link>https://dev.to/aws-builders/workflow-en-strands-un-dag-de-tareas-ejecutado-como-herramienta-unica-2a43</link>
      <guid>https://dev.to/aws-builders/workflow-en-strands-un-dag-de-tareas-ejecutado-como-herramienta-unica-2a43</guid>
      <description>&lt;p&gt;En estas notas te explico cómo orquestar agentes con la arquitectura Workflow de Strands Agents utilizando paralelismo y join points, ejemplificado con la construcción de un entregable para preparar y gestionar reuniones como Solutions Architect. Testado de dos maneras: con Gemma4 + Ollama (local) y con la API de Gemini.&lt;/p&gt;

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

&lt;p&gt;La idea principal es experimentar con el patrón de orquestación workflow sin gastar en suscripciones pagas de tokens, dejando el deploy en AWS para otra instancia. Durante el desarrollo y testing me encontré con algunos inconvenientes de features no implementadas y descubrí proveedores de modelos soportados al día de hoy. &lt;/p&gt;

&lt;p&gt;🗒 &lt;strong&gt;El caso de uso&lt;/strong&gt; que elegí para representar el patrón de orquestación &lt;em&gt;&lt;strong&gt;Workflow&lt;/strong&gt;&lt;/em&gt; es un copiloto para Solutions Architects que prepara y cierra discovery meetings con clientes: investiga la empresa, mapea a los stakeholders, genera preguntas de discovery fundamentadas en ese contexto, resume la reunión una vez ocurrida, y sintetiza todo en un entregable de seguimiento (email + próximos pasos + gaps a resolver).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;La idea del diseño se sacó del &lt;a href="https://partyrock.aws/u/jfescott/CkWsGSARh/Prepare-and-Manage-meeting-as-a-Solution-Architect" rel="noopener noreferrer"&gt;playground de PartyRock&lt;/a&gt; de Jeff Escott&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;En las secciones siguientes cubro los conceptos claves del patrón, el diseño e implementación del caso de uso, los hallazgos reales de ejecución con ambos modelos, y las evaluaciones con &lt;code&gt;strands-agents-evals&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conceptos Claves
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ➡ Workflow de Agentes es un patrón de orquestación coordinado en una secuencia definida.
&lt;/h3&gt;

&lt;p&gt;Cada Agente realiza una serie de tareas definidas. El desarrollador puede descomponer tareas complejas en componentes manejables y distribuirlos entre agentes especializados. Cada workflow tiene un control explícito del &lt;strong&gt;orden de ejecución de las tareas&lt;/strong&gt;, &lt;strong&gt;las dependencias&lt;/strong&gt; y  &lt;strong&gt;el flujo de información entre tareas&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Workflow es apropiado:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cuando se tiene un proceso complejo pero repetible que se desea encapsular en una herramienta única, confiable y reutilizable, un &lt;code&gt;Workflow&lt;/code&gt;es un grafo de tareas definido por el desarrollador que un agente puede ejecutar como una acción única y potente.&lt;/li&gt;
&lt;li&gt;Si tenemos complejidad en los procesos con múltiples pasos, resolución de tareas con distintas etapas secuenciales.&lt;/li&gt;
&lt;li&gt;Procesos que requieren subagentes especializados en cada etapa o un seguimiento detallado en cada paso&lt;/li&gt;
&lt;li&gt;Ejecutar tareas independientes en paralelo, mientras hay gestión de independencias. De esta manera optimizamos recursos.&lt;/li&gt;
&lt;li&gt;Si tenemos que &lt;strong&gt;reintentar pasos por error&lt;/strong&gt; tareas especificas sin reiniciar todo el flujo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para clarificar las diferencias entre patrones de orquestación, te muestro una comparativa entre workflow, graph y swarm. La mayor diferencia a considerar es &lt;strong&gt;cómo se determina la ruta de ejecución&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;&lt;/th&gt;
&lt;th&gt;Workflow&lt;/th&gt;
&lt;th&gt;Graph&lt;/th&gt;
&lt;th&gt;Swarm&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Concepto fundamental&lt;/td&gt;
&lt;td&gt;Un grafo de tareas predefinido (DAG) ejecutado como una herramienta única y no conversacional.&lt;/td&gt;
&lt;td&gt;Un flowchart estructurado y definido por el desarrollador, donde un agente decide qué camino tomar.&lt;/td&gt;
&lt;td&gt;Un equipo dinámico y colaborativo de agentes que se pasan las tareas de forma autónoma.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Estructura&lt;/td&gt;
&lt;td&gt;Un desarrollador define todas las tareas y sus dependencias en el código.&lt;/td&gt;
&lt;td&gt;Un desarrollador define todos los nodos (agentes) y aristas (transiciones) con antelación.&lt;/td&gt;
&lt;td&gt;Un desarrollador proporciona un pool de agentes. Los propios agentes deciden el camino.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flujo de ejecución&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Determinista y paralelo&lt;/strong&gt;. El flujo está determinado por el grafo de dependencias. Las tareas independientes se ejecutan en paralelo.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Controlado pero dinámico&lt;/strong&gt;. El flujo sigue los bordes del grafo, pero la decisión de un LLM en cada nodo determina el camino (path).&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Secuencial y autónomo&lt;/strong&gt;. Un agente ejecuta una tarea y luego cede el control a otro agente adecuado en el enjambre.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;¿Permite ciclo?&lt;/td&gt;
&lt;td&gt;NO.&lt;/td&gt;
&lt;td&gt;SÍ.&lt;/td&gt;
&lt;td&gt;SÍ.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mecanismos de compartir estados&lt;/td&gt;
&lt;td&gt;La herramienta captura automáticamente los outputs de las tareas y los pasa como inputs a las tareas dependientes.&lt;/td&gt;
&lt;td&gt;Se pasa un objeto de estado compartido a todos los agentes, quienes pueden leerlo y modificarlo libremente.&lt;/td&gt;
&lt;td&gt;Todos los agentes disponen de un contexto compartido o memoria de trabajo, que contiene la solicitud original, el historial de tareas y el conocimiento de los agentes anteriores.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Historial de conversaciones&lt;/td&gt;
&lt;td&gt;Contexto específico de la tarea. Una tarea recibe un resumen curado de los resultados relevantes de sus dependencias, no el historial completo.&lt;/td&gt;
&lt;td&gt;Transcripción completa. Todo el historial de diálogo forma parte del estado compartido, lo que proporciona a cada agente un contexto completo y abierto.&lt;/td&gt;
&lt;td&gt;Transcripción compartida. El contexto compartido proporciona un historial completo de los handoffs de agentes y el conocimiento aportado por agentes anteriores, disponible para el agente actual.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Control del comportamiento&lt;/td&gt;
&lt;td&gt;La solicitud del usuario puede activar un workflow predefinido, pero no puede alterar su estructura interna.&lt;/td&gt;
&lt;td&gt;La información que introduce el usuario en cada paso puede influir directamente en la ruta que seguirá el grafo a continuación.&lt;/td&gt;
&lt;td&gt;La indicación inicial del usuario define el objetivo, pero a partir de ahí el Swarm corre de forma autónoma.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Escalabilidad&lt;/td&gt;
&lt;td&gt;Escala bien en operaciones repetibles y complejas.&lt;/td&gt;
&lt;td&gt;Escala bien en procesos complejos(muchas ramificaciones, condiciones).&lt;/td&gt;
&lt;td&gt;Escala en función del número de agentes especializados en el equipo y de la complejidad de la tarea colaborativa.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manejo de errores&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Sistémico&lt;/strong&gt;. Un fallo en una tarea detendrá todas las tareas dependientes posteriores. Es probable que todo el workflow entre en estado &lt;code&gt;failed&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Controlable&lt;/strong&gt;. Un desarrollador puede definir aristas de "error" explícitas para dirigir el flow a un nodo de error-handling si falla un paso.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Agent-driven&lt;/strong&gt;. Un agente puede decidir transferir la tarea a un especialista en manejo de errores. El sistema se basa en tiempos de espera y límites de transferencia para evitar bucles infinitos.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  ➡ Conceptos genéricos de Workflow agénticos
&lt;/h3&gt;

&lt;p&gt;Para la implementación del caso de uso, solo usé la &lt;code&gt;workflow tool&lt;/code&gt;, una de las ~45 tools nativas disponibles en &lt;code&gt;strands-agents-tools&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Componentes claves en la arquitectura:&lt;/p&gt;

&lt;p&gt;⭐&lt;strong&gt;Definición y distribución de tareas.&lt;/strong&gt; Cada tarea necesita una descripción especifica de qué debe lograr el agente, una asignación que haga  matching con el agente adecuado según sus capacidades, y un nivel de prioridad que determine qué se ejecuta primero cuando hay margen de elección.&lt;/p&gt;

&lt;p&gt;⭐&lt;strong&gt;Gestión de dependencias.&lt;/strong&gt; Algunas tareas deben ejecutarse en un orden específico (dependencias secuenciales), mientras que las tareas independientes pueden correr en simultáneo (ejecución paralela). En la ejecución paralela tener en cuenta que el modelo de IA que se utilice debe permitirlo.&lt;br&gt;
Los &lt;em&gt;join points&lt;/em&gt; son los puntos donde varios caminos paralelos convergen antes de continuar. En el caso de uso elegido, los dos join points son &lt;code&gt;discovery_questions&lt;/code&gt; (espera a &lt;code&gt;company_research&lt;/code&gt; + &lt;code&gt;stakeholder_mapping&lt;/code&gt;) y &lt;code&gt;follow_up_actions&lt;/code&gt; (espera a &lt;code&gt;discovery_questions&lt;/code&gt; + &lt;code&gt;meeting_summary&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;⭐&lt;strong&gt;Flujo de información.&lt;/strong&gt; El mapeo de input/output conecta la salida de un agente con la entrada de otro, la preservación de contexto mantiene la información relevante a lo largo de todo el workflow, y la gestión de estado permite trackear el progreso general del workflow.&lt;/p&gt;

&lt;p&gt;Parámetros básicos del workflow:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;action&lt;/code&gt; → create, start, status (indica si hubo error o no), list, delete.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;workflow_id&lt;/code&gt; → identificador único del workflow.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tasks&lt;/code&gt; → lista de tareas con sus correspondientes propiedades&lt;/p&gt;

&lt;p&gt;Algunas propiedades dentro de cada task:&lt;br&gt;
&lt;code&gt;task_id&lt;/code&gt; : Obligatorio. Identificador o nombre de la tarea.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;description&lt;/code&gt;: Obligatorio. se completa con un resumen de lo que hace la tarea, es un pequeño prompt.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dependencies&lt;/code&gt;: Opcional. Encadenas una tarea con la otra para que no se ejecute si las dependencias aún no culminaron.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;system_prompt&lt;/code&gt;:  Obligatorio.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;priority&lt;/code&gt;: Propiedad numérica que indica prioridad en la ejecución por el modelo.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tools&lt;/code&gt;: Opcional. Pero si no lo especificás puede que en la inferencia el modelo decida usar cualquier tool nativa y entre en un loop infinito. &lt;br&gt;
Si no querés que la tarea ejecute ninguna tool se puede especificar cómo &lt;code&gt;"tools": NO_TOOLS&lt;/code&gt;, esto no es un valor oficial del SDK, es un workaround verificado empíricamente: se pasa un nombre de tool que no existe en el registry, forzando al sub-agente a correr sin tools. Pasar &lt;code&gt;"tools": []&lt;/code&gt; no funciona porque es falsy en Python y el workflow lo ignora heredando todo el toolset del agente padre.&lt;/p&gt;
&lt;h3&gt;
  
  
  ➡ Topologías de Secuencias y Workflow
&lt;/h3&gt;

&lt;p&gt;En Strands Agents, "Workflow" puede referirse a dos cosas distintas: una arquitectura secuencial que se implementa manualmente encadenando agentes en código Python, o la &lt;em&gt;Workflow Tool&lt;/em&gt; (&lt;code&gt;strands-agents-tools&lt;/code&gt;), que gestiona dependencias, paralelismo y pasaje de contexto automáticamente. &lt;/p&gt;
&lt;h4&gt;
  
  
  Workflow Secuencial
&lt;/h4&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyrsp016dpxtgsavpi0au.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyrsp016dpxtgsavpi0au.jpg" alt="Workflow-secuencial" width="800" height="162"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En la topología secuencial, cada agente es un objeto Python independiente y el desarrollador conecta las salidas manualmente. Es la forma más simple de encadenar agentes, pero no hay gestión automática de dependencias ni paralelismo.&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="c1"&gt;# Create specialized agents
&lt;/span&gt;&lt;span class="n"&gt;agent1&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;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;You are Agent 1. Perform the first step.&lt;/span&gt;&lt;span class="sh"&gt;"&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="n"&gt;agent2&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;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;You are Agent 2. Perform the second step.&lt;/span&gt;&lt;span class="sh"&gt;"&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="n"&gt;agent3&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;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;You are Agent 3. Perform the final step.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Sequential workflow processing
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_workflow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;step1_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;agent1&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;Process: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;input_data&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="n"&gt;step2_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;agent2&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;Process: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;step1_result&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="n"&gt;final_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;agent3&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;Process: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;step2_result&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;return&lt;/span&gt; &lt;span class="n"&gt;final_result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Workflow Tool
&lt;/h4&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpmzes5l1g4p3qcra6i1s.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpmzes5l1g4p3qcra6i1s.jpg" alt="Workflow-tool" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La &lt;em&gt;Workflow Tool&lt;/em&gt; gestiona dependencias, paralelismo y pasaje de contexto automáticamente. El desarrollador define la estructura (tasks + dependencies) y la tool se encarga del resto: qué corre en paralelo, qué espera, y qué contexto inyecta a cada tarea dependiente.&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="c1"&gt;# Create an agent with workflow capability
&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="c1"&gt;# Create a multi-agent workflow
&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;generic_workflow&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;task1&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;Description of the first task&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;You perform the first task.&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="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;task2&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;Description of the second task&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;task1&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;You perform the second task.&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="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;task3&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;Description of the third task&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;task2&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;You perform the third task.&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;2&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;# Execute workflow (parallel processing where possible)
&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;start&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;generic_workflow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Check results
&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&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;status&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;generic_workflow&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;
  
  
  ➡ Evaluaciones (Evals)
&lt;/h3&gt;

&lt;p&gt;Strands cuenta con una &lt;a href="https://strandsagents.com/docs/user-guide/evals-sdk/quickstart/" rel="noopener noreferrer"&gt;marco oficial&lt;/a&gt; para realizar evaluaciones de resultados simples hasta el análisis de interacciones de multiagentes complejos. &lt;/p&gt;

&lt;p&gt;Para evaluar el workflow usé &lt;code&gt;strands-agents-evals&lt;/code&gt; con el mismo provider configurado en &lt;code&gt;.env&lt;/code&gt; (Gemini u Ollama) como juez. En producción la práctica estándar es usar un modelo más capaz como judge (ej. el workflow corre con &lt;code&gt;gemini-2.0-flash&lt;/code&gt; y el judge usa &lt;code&gt;gemini-2.5-pro&lt;/code&gt;), pero para este proyecto educativo se unifica por simplicidad.&lt;/p&gt;

&lt;p&gt;Combiné dos tipos de evaluación:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LLM-as-a-judge&lt;/strong&gt; — el modelo evalúa la calidad del output final:&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é evalúa?&lt;/th&gt;
&lt;th&gt;Escala&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OutputEvaluator&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Verifica que &lt;code&gt;follow_up_actions&lt;/code&gt; tenga las 3 secciones obligatorias (email, next steps, gaps) y que el contenido esté fundamentado en los datos reales de la reunión.&lt;/td&gt;
&lt;td&gt;0 / 0.5 / 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;HelpfulnessEvaluator&lt;/code&gt;(1)&lt;/td&gt;
&lt;td&gt;Evalúa si el entregable final es útil para un Solutions Architect — no solo si tiene el formato correcto. (Es una instancia de &lt;em&gt;OutputEvaluator&lt;/em&gt; con rubric propio, no una clase separada del SDK!)&lt;/td&gt;
&lt;td&gt;0 / 0.5 / 1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(1)&lt;code&gt;HelpfulnessEvaluator&lt;/code&gt; del SDK requiere trazas OpenTelemetry que la Workflow Tool no provee. Se reemplazó por un segundo &lt;code&gt;OutputEvaluator&lt;/code&gt; con rubric de helpfulness.&lt;br&gt;
Cuando usé &lt;code&gt;HelpfulnessEvaluator&lt;/code&gt; del SDK, el score devolvió 0.0 con el error: "Trace parsing requires actual_trajectory to be a Session object, got NoneType". El evaluador espera un objeto Session de Strands (que debe contener trazas OpenTelemetry de la ejecución del agente), no solo el texto del output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Métricas deterministas&lt;/strong&gt; — evalúan propiedades estructurales del patrón, sin llamadas a LLM:&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;¿Qué mide?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;task_completion_rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fracción de tasks que completaron con &lt;code&gt;status="completed"&lt;/code&gt; en el JSON persistido.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;join_point_coverage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Verifica que ambos join points produjeron output no vacío, confirma que la inyección de contexto funcionó.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;required_sections&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check determinista de las 3 secciones del entregable (email, next steps, gaps).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stakeholder_coverage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Verifica que cada stakeholder del input aparece en &lt;code&gt;discovery_questions&lt;/code&gt;, confirma que el contexto de &lt;code&gt;stakeholder_mapping&lt;/code&gt; llegó al join point #1.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;keyword_presence&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Presencia de keywords de dominio (con soporte de sinónimos via `\&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Descripción de la solución
&lt;/h2&gt;

&lt;p&gt;📦 &lt;strong&gt;Repositorio GitHub:&lt;/strong&gt;  &lt;a href="https://github.com/reinalau/strands-workflow" rel="noopener noreferrer"&gt;github.com/reinalau/strands-workflow&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmb9dsyquvbfspszs1fat.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmb9dsyquvbfspszs1fat.jpg" alt="Workflow copiloto para Solutions Architects" width="800" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para la prueba de concepto utilicé dos opciones locales de ejecución que permitió analizar sus resultados, pero se puede utilizar solo una:&lt;br&gt;
a. Ollama + el pequeño modelo &lt;strong&gt;gemma4:e2b-it-qat&lt;/strong&gt; (que pesa poco mas de 4gb) ejecutando en Docker. &lt;br&gt;
En caso que quieras ejecutar realmente las task que son independientes en paralelo, hay que indicarselo a ollama (sino el comportamiento por default es secuencial) :&lt;br&gt;
{% raw %}&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;# Start the Ollama server with a persistent volume&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; ollama &lt;span class="nt"&gt;-p&lt;/span&gt; 11434:11434 &lt;span class="nt"&gt;-v&lt;/span&gt; ollama_data:/root/.ollama ollama/ollama

&lt;span class="c"&gt;# Start the Ollama server en parallel&lt;/span&gt;
&lt;span class="c"&gt;# docker run -d --name ollama -p 11434:11434 -v ollama_data:/root/.ollama -e OLLAMA_NUM_PARALLEL=3  ollama/ollama&lt;/span&gt;

&lt;span class="c"&gt;# Download the model&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; ollama ollama pull gemma4:e2b-it-qat

&lt;span class="c"&gt;# Test that the model responds&lt;/span&gt;
&lt;span class="c"&gt;# If the container has already been created and &lt;/span&gt;
&lt;span class="c"&gt;# is currently stopped, simply use: docker start ollama&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; ollama ollama run gemma4:e2b-it-qat

&lt;span class="c"&gt;# Verify that the model is running&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; ollama ollama ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;b. Api de gemini. La api key se puede generar de manera gratuita desde &lt;a href="https://aistudio.google.com/" rel="noopener noreferrer"&gt;aquí&lt;/a&gt; y utilizar algunos de estos modelos (experimentar con los que te permita):&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gemini-2.5-flash-lite
gemini-2.5-flash
gemini-3.5-flash
gemini-3.5-flash-lite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;El código está en Python&lt;/strong&gt; y la estructura del proyecto se diseñó de manera que sea más explicativo su lógica, todo el detalle lo encontrás en el &lt;code&gt;Readme.md&lt;/code&gt; del proyecto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;strands-workflow/
├── README.md
├── requirements.txt                  # strands-agents[litellm,ollama], strands-agents-tools, pydantic, pytest
├── .env.example                      # MODEL_PROVIDER, GEMINI_API_KEY, OLLAMA_MODEL_ID
├── .gitignore
├── pytest.ini                        # pythonpath=. so tests/ and evals/ resolve src./config imports
│
├── src/
│   ├── __init__.py
│   ├── config.py                     # get_model_config(): single global provider (Gemini or Ollama) via MODEL_PROVIDER env var
│   ├── main.py                       # Entry point: builds tasks, creates+starts the workflow, assembles the final .md
│   │
│   ├── agents/
│   │   ├── __init__.py
│   │   └── prompts.py                # System prompts per task_id (company_research, stakeholder_mapping, etc.)
│   │
│   ├── workflow/
│   │   ├── __init__.py
│   │   ├── builder.py                # build_tasks(): builds the task list — task_id, dependencies, priority
│   │   ├── deliverable.py            # Reads the persisted workflow JSON and assembles the final .md
│   │   └── instrumentation.py        # Timing hook: logs START/END + duration per task
│   │
│   ├── models/
│   │   └── schemas.py                # Pydantic: DiscoveryMeetingInput
│   │
│   └── utils/
│       ├── logging_config.py         # Tees stdout+stderr to logs/, optional DEBUG level
│       └── report_export.py          # Writes the final .md to outputs/
│
├── tests/
│   ├── __init__.py
│   ├── test_config.py                # get_model_config(): provider resolution + error if API key is missing
│   ├── test_workflow_structure.py    # Validates the workflow is well-formed: no cycles, existing dependencies, correct join points
│   ├── test_schemas.py               # Pydantic validation of DiscoveryMeetingInput
│   ├── test_report_export.py         # Generates the .md from a mocked workflow JSON
│   └── test_instrumentation.py       # Verifies install_task_timing_hooks() logs START/END with duration, without calling a real LLM
│
├── examples/
│   └── sample_input.json             # Sample input to run the workflow quickly
│
├── evals/
│   ├── __init__.py
│   ├── evaluate_workflow.py          # strands-evals runner: OutputEvaluator + HelpfulnessEvaluator + deterministic metrics
│   ├── metrics.py                    # Domain metrics: task completion rate, join point coverage, required sections, stakeholder coverage, keyword presence
│   └── test_cases/
│       └── workflow_eval_cases.json  # Input + expected properties per eval case
│
├── logs/
│   └── .gitkeep                      # logs/run_&amp;lt;timestamp&amp;gt;.log, gitignored otherwise
│
└── outputs/
    └── .gitkeep                      # .md generated per run, gitignored otherwise

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Ejecución Local
&lt;/h3&gt;

&lt;p&gt;Una vez que tengas el repositorio del código fuente clonado y el docker de ollama con el modelo gemma 4 o la api key de gemini generada, pasamos armar el ambiente.&lt;/p&gt;

&lt;p&gt;Los requerimientos que vas a encontrar en  &lt;code&gt;requirements.txt&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;strands-agents[litellm,ollama]==1.52.0
strands-agents-tools==0.8.6
ollama==0.5.1
pydantic==2.13.4
pytest==8.3.4
strands-agents-evals&amp;gt;=1.0.0
python-dotenv==1.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En &lt;code&gt;.env&lt;/code&gt; se necesitan estos valores. Notar que depende el modelo que elegimos podemos intercambiar el &lt;code&gt;MODEL_PROVIDER&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Select ONE provider: "ollama" or "gemini"
MODEL_PROVIDER=ollama

# --- Gemini (only required if MODEL_PROVIDER=gemini) ---
# Free tier key: https://aistudio.google.com/apikey
GEMINI_API_KEY=YourApiKey
GEMINI_MODEL_ID=gemini-3.5-flash-lite

# --- Ollama (only used if MODEL_PROVIDER=ollama) ---
# Must have the Ollama daemon running locally: https://ollama.com
# Pull the model first: ollama pull gemma4:e2b-it-qat
OLLAMA_MODEL_ID=gemma4:e2b-it-qat
OLLAMA_HOST=http://localhost:11434

# --- Workflow persistence ---
# Redirects strands_tools' internal workflow state JSON into logs/ instead of
# the default ~/.strands/workflows/ — keeps everything from one run together.
STRANDS_WORKFLOW_DIR=logs/workflows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;En &lt;code&gt;src/config.py&lt;/code&gt;&lt;/strong&gt; está la configuración de ejecución, en este caso de uso utilicé parámetros como la &lt;code&gt;temperature&lt;/code&gt; y  &lt;code&gt;max_tokens&lt;/code&gt; (limita los tokens que pueden gastar los agentes). &lt;br&gt;
→ Si optas  por ejecutar con el provider &lt;em&gt;ollama&lt;/em&gt; hay que tener en cuenta el valor de la ventana de contexto: &lt;code&gt;num_ctx&lt;/code&gt;.&lt;br&gt;
→ Si optas por ejecutar con la apikey de Gemini, en el SDK de Strands, el dispatcher &lt;code&gt;create_model&lt;/code&gt; que usa &lt;code&gt;workflow&lt;/code&gt;internamente soporta estos modelos solo vía &lt;strong&gt;litellm&lt;/strong&gt; (&lt;code&gt;"model_provider": "litellm"&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Las pruebas&lt;/strong&gt; están organizadas en &lt;code&gt;tests/&lt;/code&gt; en &lt;strong&gt;un solo nivel: ejecución rápida y determinista&lt;/strong&gt;. Validan la estructura del workflow (sin ciclos, dependencias existentes, join points correctos), el schema del input con Pydantic, la exportación del &lt;code&gt;.md&lt;/code&gt; desde un JSON mockeado, y los hooks de timing (sin llamadas al LLM).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;python -m pytest
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Ejecución del Workflow principal.&lt;/strong&gt; En &lt;code&gt;examples/sample_input.txt&lt;/code&gt;está el input de ejemplo en un formato json:&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;"company_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;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"known_challenges"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"stakeholders"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"raw_meeting_notes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;Se puede ejecutar contra el modelo ollama local o vía api de gemini comentado más arriba.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; src.main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎯 &lt;strong&gt;Un bug real&lt;/strong&gt;: durante las pruebas descubrí que usando Gemini con paralelismo real puede hacer crashear el proceso de forma intermitente (y no es un problema de Windows, ocurre también en Linux). La causa es un bug conocido y abierto en LiteLLM: el cliente HTTP que usa para el streaming de Gemini es un singleton global que no distingue entre los distintos &lt;em&gt;event loops&lt;/em&gt; que crea cada tarea del workflow al correr en paralelo. &lt;br&gt;
&lt;strong&gt;Solución aplicada:&lt;/strong&gt; limitar la ejecución a un hilo por vez cuando el proveedor es Gemini, vía la env var &lt;code&gt;STRANDS_WORKFLOW_MAX_THREADS=1&lt;/code&gt; de Strands. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;El log&lt;/strong&gt; de las llamadas del workflow está en &lt;code&gt;logs/&lt;/code&gt;. Contiene la data cruda que permite verificar si hubo paralelismo, cuanto tiempo tarda cada task, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;El resultado final&lt;/strong&gt; &lt;br&gt;
Es un  documento markdown con diferentes secciones como:&lt;br&gt;
Company Research - Stakeholder Mapping - Meeting Summary - Discovery Questions - Follow-up Deliverable and Gap&lt;br&gt;
 Se guarda en &lt;code&gt;outputs/discovery_meeting_yyyymmdd_hhmmss.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Por último,  hice una evaluación de pruebas sobre el workflow multiagente real con el concepto de &lt;em&gt;"Evals".&lt;/em&gt;&lt;/strong&gt; Ejecuta el workflow completo contra el modelo configurado, corre los evaluadores LLM-as-judge y las métricas deterministas, muestra los resultados en consola y guarda el reporte completo en &lt;code&gt;outputs/eval_report_&amp;lt;timestamp&amp;gt;.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python  &lt;span class="nt"&gt;-m&lt;/span&gt;  evals.evaluate_workflow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📝 &lt;strong&gt;Nota:&lt;/strong&gt; La explicación del código fuente y paso a paso de la ejecución se encuentra detallado en el &lt;code&gt;README.md&lt;/code&gt; en  &lt;a href="https://github.com/reinalau/strands-workflow" rel="noopener noreferrer"&gt;github&lt;/a&gt; .&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusiones y lecciones aprendidas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;El patrón Workflow en Strands resuelve en lineas generales lo que indica su documentación.&lt;/strong&gt; Dependency resolution, paralelismo real y context passing automático entre join points funcionaron correctamente y sin código extra de orquestación. El DAG se ejecuta en el orden correcto y los join points reciben el contexto inyectado de sus dependencias sin intervención manual.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;La Workflow Tool de Strands está en maduración activa.&lt;/strong&gt; &lt;code&gt;pause&lt;/code&gt;/&lt;code&gt;resume&lt;/code&gt; están documentados como &lt;em&gt;Advanced Features&lt;/em&gt; pero no implementados — al ejecutarlos devuelven &lt;code&gt;"🚧 Action '...' is not yet implemented"&lt;/code&gt;, verificado contra el código fuente instalado. El workflow puede quedar colgado indefinidamente si una task termina en &lt;code&gt;error&lt;/code&gt; (el loop interno gira para siempre esperando dependientes que nunca se vuelven "ready"). El resultado de cada task no se expone via API... hay que leer el JSON persistido directamente. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Algunos comportamientos requieren workarounds no oficiales.&lt;/strong&gt; La herencia de tools en sub-agentes no está documentada. Por defecto cada sub-agente hereda todo el toolset del padre, incluida la &lt;code&gt;workflow&lt;/code&gt; tool, lo que con modelos agénticos lleva a llamadas recursivas reales. &lt;code&gt;"tools": []&lt;/code&gt; (lista vacía) no funciona como restricción porque es falsy en Python — la solución es &lt;code&gt;NO_TOOLS&lt;/code&gt; (&lt;code&gt;["__no_tools__"]&lt;/code&gt;), un placeholder que fuerza &lt;code&gt;filtered_tools&lt;/code&gt; a quedar vacío. No encontré hooks públicos para observar timing por task, se requiere monkeypatch sobre &lt;code&gt;WorkflowManager.execute_task&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Los hooks de Strands tienen un mecanismo &lt;code&gt;LimitToolCounts&lt;/code&gt;&lt;/strong&gt; (via &lt;code&gt;BeforeToolCallEvent&lt;/code&gt;) que permite cancelar una tool cuando se invoca más veces de lo permitido, útil como guardrail contra loops. Sin embargo, no aplica al bug de herencia de la Workflow Tool, ese mecanismo actúa sobre el agente padre, pero los sub-agentes creados internamente por el framework son instancias separadas sobre las que no hay control para registrar hooks desde afuera. Por eso &lt;code&gt;"tools": NO_TOOLS&lt;/code&gt; sigue siendo la solución correcta.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Concurrencia del workflow tool con Gemini.&lt;/strong&gt; Cuando hay tasks en paralelo + LiteLLM/aiohttp, se produce un &lt;code&gt;RuntimeError: Task &amp;lt;...&amp;gt; got Future &amp;lt;...&amp;gt; attached to a different loop&lt;/code&gt; intermitente. Cada task corre en su propio thread con su propio &lt;code&gt;asyncio.run()&lt;/code&gt;, y el cierre del connector de aiohttp cae en el loop equivocado. Se solucionó colocando una variables de entorno de Strands &lt;code&gt;STRANDS_WORKFLOW_MAX_THREADS=1&lt;/code&gt;, limitando la concurrencia. (esto no sucede con Ollama-Gemma4 via Docker).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Curso Fundamentos (hacelos!)&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://skillbuilder.aws/learn/CFBFZNS11J/building-ai-agent-harnesses-with-strands-agents/D6YG8S6N7M" rel="noopener noreferrer"&gt;Building AI Agent Harnesses with Strands Agents&lt;/a&gt;&lt;br&gt;
&lt;a href="https://builder.aws.com/content/3GYPrAplMhl2IAl7jJW0yeKzXrR/building-ai-agent-harnesses-with-strands-agents-a-free-14-video-course/" rel="noopener noreferrer"&gt;Building AI Agent Harnesses – Video Course&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Documentación Strands Agents&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/concepts/multi-agent/workflow/" rel="noopener noreferrer"&gt;Agent Workflows&lt;/a&gt;&lt;br&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/evals-sdk/evaluators/" rel="noopener noreferrer"&gt;Evals&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Otros&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://builder.aws.com/content/3Ecodd9aFqsG1SAkQKmqZ2KxPiP/5-multi-agent-patterns-in-strands-agents-which-one-and-when" rel="noopener noreferrer"&gt;5 Multi-Agent Patterns in Strands&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>strandagents</category>
      <category>python</category>
      <category>agents</category>
    </item>
    <item>
      <title>Graph en Strands: Parallel + Aggregation y Feedback Loop para construir Knowledge Graphs</title>
      <dc:creator>Laura Bolaños</dc:creator>
      <pubDate>Wed, 12 Aug 2026 15:19:12 +0000</pubDate>
      <link>https://dev.to/aws-builders/graph-en-strands-parallel-aggregation-y-feedback-loop-para-construir-knowledge-graphs-22n0</link>
      <guid>https://dev.to/aws-builders/graph-en-strands-parallel-aggregation-y-feedback-loop-para-construir-knowledge-graphs-22n0</guid>
      <description>&lt;p&gt;Siguiendo con la serie de artículos sobre la orquestación de multiagentes en Strands, le toca el turno a &lt;em&gt;Graph Multi-Agent Pattern&lt;/em&gt;, ejemplificado con la construcción de un knowledge graph en base a un input de texto (abstract), donde los nodos de un grafo determinista tienen las responsabilidades divididas para lograrlo. Se probó de dos maneras: con Gemma4 + Ollama y con la API de Gemini en su capa gratuita.&lt;/p&gt;

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

&lt;p&gt;La información del presente artículo la podés seguir teniendo alguna noción sobre agentes y grafos. Pero te propongo hacer un curso de fundamentos del framework &lt;em&gt;Strands Agents&lt;/em&gt;, para que en futuros proyectos puedas dar mayor complejidad a tus agentes. La idea principal es experimentar con el patrón sin gastar en suscripciones pagas de tokens, dejando la implementación en AWS para otra instancia. &lt;/p&gt;

&lt;p&gt;✳ &lt;strong&gt;El caso de uso&lt;/strong&gt; que elegí para representar el patrón &lt;strong&gt;&lt;em&gt;Graph&lt;/em&gt;&lt;/strong&gt; es &lt;strong&gt;la Extracción de entidades/relaciones para formar un &lt;em&gt;Knowledge Graph&lt;/em&gt; con validación&lt;/strong&gt;.  Se me ocurrió hacer algo útil para la comunidad académica, un mecanismo de extracción de conocimiento que sea capaz de tomar parte de un artículo científico e ingestar lo más importante en un documento json, que pueda ser legible por humanos y sistemas. La solución es un pipeline de construcción de &lt;em&gt;knowledge graph&lt;/em&gt; a partir de un texto (ej. un abstract), usando la topología &lt;strong&gt;Parallel Processing→ with Aggregation→ Feedback Loop&lt;/strong&gt;.&lt;br&gt;
El diseño es una versión reducida de arquitecturas multi-agente real para enriquecimiento de &lt;em&gt;knowledge graph&lt;/em&gt; descritos en:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.researchgate.net/publication/389031530_Revolutionizing_Knowledge_Graphs_with_Multi-Agent_Systems_AI-Powered_Construction_Enrichment_and_Applications" rel="noopener noreferrer"&gt;Revolutionizing Knowledge Graphs with Multi-Agent Systems: AI-Powered Construction, Enrichment, and Applications&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Conceptos Claves
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;➡ Graph es un patrón de orquestación de agentes determinista&lt;/strong&gt; &lt;br&gt;
El desarrollador define explícitamente la topología de ejecución: qué nodos existen, en qué orden corren, y bajo qué condiciones se transita de uno a otro. Cada nodo es un agente y a diferencia de otros patrones como Swarm, donde el flujo surge de la negociación entre agentes, en Graph el flujo está fijado de antemano como un grafo dirigido (DAG o con ciclos controlados).&lt;/p&gt;

&lt;p&gt;Graph es apropiado cuando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;La estructura de dependencias es conocida de antemano&lt;/strong&gt;: sabés qué pasos necesitan el resultado de otros antes de poder ejecutarse.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hay paralelismo real que después converge&lt;/strong&gt;: varios agentes pueden trabajar al mismo tiempo sobre partes independientes del problema, y sus resultados se consolidan en un punto de agregación explícito.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Se necesita revisión con ciclos controlados&lt;/strong&gt;: un resultado puede requerir corrección y volver a un paso anterior (feedback loop), con límites de seguridad (&lt;code&gt;set_max_node_executions&lt;/code&gt;) que garantizan que el proceso termine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La trazabilidad y el control son prioritarios&lt;/strong&gt;: como cada nodo y cada transición son explícitos, es más fácil auditar, debuggear y predecir el comportamiento, es bueno en contextos con validación humana o compliance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;➡ Conceptos genéricos de Grafos Agenticos&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El framework propone una interfaz sencilla para la construcción de grafos, con 3 componentes base: &lt;strong&gt;GraphNode&lt;/strong&gt;, &lt;strong&gt;GraphEdge&lt;/strong&gt;, &lt;strong&gt;GraphBuilder&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1.Los nodos representan agentes, nodos personalizados o sistemas multiagente. Ej:&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&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GraphBuilder&lt;/span&gt;

&lt;span class="c1"&gt;# Build the graph
&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GraphBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Add nodes
&lt;/span&gt;&lt;span class="n"&gt;builder&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="n"&gt;agent_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[node_id_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;builder&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="n"&gt;agent_2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[node_id_2]&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;2.Las aristas definen las dependencias y el flujo de información entre los nodos. Además se puede agregar lógica condicional (no lo utilicé en el caso de uso analizado):&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;# Add edges (dependencies)
&lt;/span&gt;&lt;span class="n"&gt;builder&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;[node_id_1]&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;[node_id_2]&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;3.La ejecución sigue la estructura del grafo, respeta dependencias, la salida de un nodo se convierte en la entrada de su dependiente. Y en el caso de que varios nodos tienen aristas hacia un nodo de destino, el comportamiento predeterminado para la ejecución del destino puede variar según el SDK (Python utiliza semánticas &lt;strong&gt;OR&lt;/strong&gt;: el nodo destino se dispara en cuanto &lt;strong&gt;cualquiera&lt;/strong&gt; de sus dependencias completa).&lt;br&gt;
4.Compatibilidad con patrones anidados, un grafo como nodo dentro de otro grafo.&lt;br&gt;
5.&lt;strong&gt;Remote Agents vía A2A&lt;/strong&gt;: Graph soporta agentes remotos como nodos a través de &lt;code&gt;A2AAgent&lt;/code&gt;, para arquitecturas distribuidas donde la orquestación corre localmente pero tareas especializadas se ejecutan en servicios remotos.&lt;br&gt;
6.&lt;strong&gt;Cyclic graph&lt;/strong&gt; permite que un nodo sea revisitado dentro del mismo flujo, con límites de ejecución y gestión de estado.&lt;br&gt;
7.&lt;strong&gt;Custom Node Types&lt;/strong&gt;: Graph permite extender &lt;code&gt;MultiAgentBase&lt;/code&gt; para crear nodos que ejecutan funciones Python deterministas en lugar de llamar a un LLM, útil para lógica de negocio, validaciones y pipelines de procesamiento de datos dentro del mismo grafo.&lt;br&gt;
8.&lt;strong&gt;Graphs as a Tool&lt;/strong&gt;: mediante el paquete &lt;code&gt;strands_tools&lt;/code&gt;, un agente puede recibir el &lt;code&gt;graph&lt;/code&gt; como tool y construir/ejecutar su propia topología de nodos en tiempo real, dejando que el LLM decida dinámicamente la orquestación en lugar de definirla de antemano con &lt;code&gt;GraphBuilder&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;➡ Topologías comunes&lt;/strong&gt;&lt;br&gt;
Strands documenta 4 topologías base que cubren la mayoría de casos de uso, y se pueden combinar según la complejidad del problema. &lt;strong&gt;Sequential Pipeline&lt;/strong&gt; sirve cuando cada paso depende estrictamente del anterior. &lt;strong&gt;Parallel + Aggregation&lt;/strong&gt; es ideal cuando hay trabajo independiente que puede correr simultáneamente y luego converger en un solo resultado. &lt;strong&gt;Branching Logic&lt;/strong&gt; permite que el flujo tome caminos distintos según el contenido generado en un paso previo. Y &lt;strong&gt;Feedback Loop&lt;/strong&gt; habilita ciclos de revisión con vuelta atrás, para casos donde un resultado necesita validación antes de darse por definitivo.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmteh9ei94hn319omma04.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmteh9ei94hn319omma04.png" alt="graph_common_topologies" width="800" height="741"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;➡ Evaluaciones (Evals)&lt;/strong&gt;  Strands cuenta con una &lt;a href="https://strandsagents.com/docs/user-guide/evals-sdk/quickstart/" rel="noopener noreferrer"&gt;marco oficial&lt;/a&gt; para realizar evaluaciones de resultados simples hasta el análisis de interacciones de multiagentes complejos. &lt;br&gt;
En el caso de uso para la creación de &lt;em&gt;Knowledge Graph&lt;/em&gt;  se realizaron 6 evaluaciones (3 sin utilizar el framework). &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Evaluación / Métrica&lt;/th&gt;
&lt;th&gt;Tipo / Origen&lt;/th&gt;
&lt;th&gt;¿Qué evalúa?&lt;/th&gt;
&lt;th&gt;Escala&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OutputEvaluator&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;LLM-as-a-Judge (&lt;code&gt;strands-agents-evals&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Verifica que la salida del nodo &lt;em&gt;Publisher&lt;/em&gt; sea un JSON válido (&lt;code&gt;entities&lt;/code&gt; y &lt;code&gt;relations&lt;/code&gt;) y que las relaciones estén fundamentadas fácticamente en el texto original.&lt;/td&gt;
&lt;td&gt;0 / 0.5 / 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SemanticCorrectnessEvaluator&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;LLM-as-a-Judge (&lt;code&gt;strands-agents-evals&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Compara semánticamente el &lt;em&gt;knowledge graph&lt;/em&gt; generado contra las relaciones esperadas (&lt;em&gt;ground truth&lt;/em&gt;), dando crédito parcial a triples con distinto wording pero igual significado. Implementado como un segundo &lt;code&gt;OutputEvaluator&lt;/code&gt; con rúbrica de referencia, ya que el &lt;code&gt;CorrectnessEvaluator&lt;/code&gt; oficial requiere un objeto &lt;code&gt;Session&lt;/code&gt; de Strands (incompatible con grafos multi-agente).&lt;/td&gt;
&lt;td&gt;0 / 0.5 / 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Exact-Match (P / R / F1)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Métrica Determinista (&lt;code&gt;metrics.py&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Mide la coincidencia exacta (normalizada a minúsculas/espacios) entre las triples extraídas y las triples de referencia del caso de prueba.&lt;/td&gt;
&lt;td&gt;0.0 – 1.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Loop Iterations Count&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Métrica de Topología (&lt;code&gt;metrics.py&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Cuenta cuántas iteraciones de revisión ocurrieron en el &lt;em&gt;feedback loop&lt;/em&gt; entre el &lt;strong&gt;Validator&lt;/strong&gt; y el &lt;strong&gt;Aggregator&lt;/strong&gt; antes de la aprobación final.&lt;/td&gt;
&lt;td&gt;entero ≥ 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bias-Flag Rate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Métrica Determinista (&lt;code&gt;metrics.py&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Detecta si la respuesta del nodo &lt;strong&gt;Validator&lt;/strong&gt; levantó alguna objeción o alerta de sesgo/desbalance en las relaciones o entidades analizadas.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;true&lt;/code&gt; / &lt;code&gt;false&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Compound Entity Check&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Métrica Determinista (&lt;code&gt;metrics.py&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Chequeo de regresión: detecta entidades compuestas (ej. "entities and relations") que deberían haberse dividido en dos nodos separados, según la regla en &lt;code&gt;extractor_agent.py&lt;/code&gt;/&lt;code&gt;aggregator_agent.py&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;lista de entidades detectadas&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Para clarificar: una  &lt;strong&gt;triple&lt;/strong&gt;  es la unidad básica de información que compone un  &lt;em&gt;&lt;em&gt;Knowledge Graph&lt;/em&gt;&lt;/em&gt;. Representa una afirmación o hecho estructurado combinando tres elementos:&lt;/p&gt;

&lt;p&gt;$$\text{(Sujeto)} \xrightarrow{\quad\text{Relación / Predicado}\quad} \text{(Objeto)}$$&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Sujeto (&lt;em&gt;source&lt;/em&gt;)&lt;/strong&gt;: La entidad de origen (ej.  &lt;code&gt;"Laura Bolaños"&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Relación (&lt;em&gt;relation&lt;/em&gt;)&lt;/strong&gt;: El tipo de vínculo o conector (ej.  &lt;code&gt;"born_in"&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Objeto (&lt;em&gt;target&lt;/em&gt;)&lt;/strong&gt;: La entidad de destino (ej.  &lt;code&gt;"Argentina"&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Descripción de la solución
&lt;/h2&gt;

&lt;p&gt;📦 &lt;strong&gt;Repositorio GitHub:&lt;/strong&gt;  &lt;a href="https://github.com/reinalau/strands-graph" rel="noopener noreferrer"&gt;github.com/reinalau/strands-graph&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;⭐ El caso de uso combina dos topologías: &lt;strong&gt;paralelismo con agregación, seguido de un ciclo de feedback antes de publicar el resultado final&lt;/strong&gt;. Notar en la representación que Extractor es un agente que se instancia 3 veces, una única definición de agente, instanciada 3 veces (una por &lt;code&gt;chunk_number&lt;/code&gt;) dentro del loop en &lt;code&gt;builder.py&lt;/code&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F488r9eq02an0u0emeams.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F488r9eq02an0u0emeams.png" alt="Use Case Graph MultiAgent" width="800" height="771"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para la prueba de concepto utilicé dos opciones locales de ejecución que me permitió analizar sus resultados, pero podes utilizar solo una:&lt;br&gt;
a. Ollama + el pequeño modelo &lt;strong&gt;gemma4:e2b-it-qat&lt;/strong&gt; (que pesa poco mas de 4gb) ejecutando en Docker:&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;# Start the Ollama server with a persistent volume&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; ollama &lt;span class="nt"&gt;-p&lt;/span&gt; 11434:11434 &lt;span class="nt"&gt;-v&lt;/span&gt; ollama_data:/root/.ollama ollama/ollama

&lt;span class="c"&gt;# Download the model&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; ollama ollama pull gemma4:e2b-it-qat

&lt;span class="c"&gt;# Test that the model responds&lt;/span&gt;
&lt;span class="c"&gt;# If the container has already been created and &lt;/span&gt;
&lt;span class="c"&gt;# is currently stopped, simply use: docker start ollama&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; ollama ollama run gemma4:e2b-it-qat

&lt;span class="c"&gt;# Verify that the model is running&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; ollama ollama ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;b. Api de gemini. La api key se puede generar de manera gratuita desde &lt;a href="https://aistudio.google.com/" rel="noopener noreferrer"&gt;aquí&lt;/a&gt; y utilizar al menos estos modelos (experimentar con los que te permita):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gemini-2.5-flash-lite
gemini-2.5-flash
gemini-3.5-flash
gemini-3.5-flash-lite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;El código del multiagente Graph está en Python&lt;/strong&gt; y la estructura del proyecto se diseñó de manera que sea más explicativo su lógica:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;strands-graph/
├── README.md                        # Project overview, setup, and usage instructions
├── requirements.txt                  # Python dependencies (strands-agents[gemini,ollama], strands-agents-evals, pydantic, etc.)
├── .env.example                      # Template for API keys (GEMINI_API_KEY) and Ollama config
├── .gitignore
│
├── src/
│   ├── __init__.py
│   ├── config.py                     # Model configs: role -&amp;gt; provider mapping (Gemini/Ollama), get_model()
│   ├── main.py                       # Entry point: builds graph, runs on input, prints/exports result, saves run log
│   │
│   ├── agents/
│   │   ├── __init__.py               # Re-exports all create_*_agent() factories
│   │   ├── coordinator_agent.py      # Entry point: splits input text into chunks for parallel extraction
│   │   ├── extractor_agent.py        # Shared extractor logic, instantiated 3x (one per chunk); no compound entities
│   │   ├── aggregator_agent.py       # Conflict resolution: merges, deduplicates, splits any compound entities
│   │   ├── validator_agent.py        # structured_output_model: checks format, consistency, completeness, bias
│   │   └── publisher_agent.py        # Formats and exports the final validated KG
│   │
│   ├── graph/
│   │   ├── __init__.py
│   │   ├── builder.py                # GraphBuilder setup: 7 nodes, edges (incl. aggregator-&amp;gt;publisher), entry point
│   │   └── conditions.py             # Edge conditions: needs_revision, is_approved, all_dependencies_complete
│   │
│   ├── models/
│   │   └── schemas.py                # Pydantic schemas: Relation, KnowledgeGraph, ValidatorOutput
│   │
│   └── utils/
│       ├── logging_config.py         # Debug logging + tee_console_to_file (saves full run output to logs/)
│       └── kg_export.py              # Parses and exports final KG to JSON
│
├── tests/
│   ├── __init__.py
│   ├── test_conditions.py            # Unit tests for all_dependencies_complete, needs_revision, is_approved
│   ├── test_kg_export.py             # Unit tests for parse_publisher_output (valid/invalid JSON, schema mismatch)
│   ├── test_metrics.py               # Unit tests for evals/metrics.py, incl. find_compound_entities regression check
│   ├── test_schemas.py               # Unit tests for Relation/KnowledgeGraph/ValidatorOutput validation
│   └── test_settings.py              # Unit tests for get_model role/provider resolution and missing API key error
│
├── examples/
│   └── sample_input.txt              # Short input text used as the default demo run
│
├── evals/
│   ├── __init__.py
│   ├── test_cases/
│   │   └── kg_extraction_cases.json  # Input texts + expected relations for evaluation
│   ├── evaluators.py                 # Custom Strands Evals SDK evaluators (precision/recall, feedback loop)
│   ├── evaluate_graph.py             # Strands Evals SDK runner: Case/Experiment against test cases
│   └── metrics.py                    # Domain metrics: precision/recall, loop iterations, bias-flag rate, compound entities
│
├── logs/
│   └── .gitkeep                      # Full console output per run (logs/run_&amp;lt;timestamp&amp;gt;.log), gitignored otherwise
│
└── outputs/
    └── .gitkeep                      # Generated KG JSON + eval reports land here, gitignored otherwise

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Ejecución Local
&lt;/h3&gt;

&lt;p&gt;Una vez que tenemos el repositorio del código fuente clonado y el docker de ollama con el modelo gemma 4 o la api key de gemini generada, pasamos armar el ambiente.&lt;/p&gt;

&lt;p&gt;Los requerimientos que vamos a encontrar en  &lt;code&gt;requirements.txt&lt;/code&gt;:&lt;br&gt;
strands-agents[gemini,ollama]&amp;gt;=1.0.0&lt;br&gt;
strands-agents-tools&amp;gt;=0.1.0&lt;br&gt;
strands-agents-evals&lt;br&gt;
python-dotenv&amp;gt;=1.0.0&lt;br&gt;
pydantic&amp;gt;=2.0.0&lt;br&gt;
pytest&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; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En &lt;code&gt;.env&lt;/code&gt; se necesitan estos valores. Notar que depende el modelo que elegimos podemos intercambiar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#Gemini (required if any role uses provider="gemini") ---
GEMINI_API_KEY=your_gemini_api_key_here
GEMINI_MODEL_ID=gemini-2.5-flash
#Ollama (required if any role uses provider="ollama") ---
OLLAMA_HOST=http://localhost:11434
OLLAMA_MODEL_ID=gemma4:e2b-it-qat
#Per-role provider overrides (optional) ---
#Values: "gemini" or "ollama". Defaults are set in src/config.py.
MODEL_PROVIDER_COORDINATOR=ollama
MODEL_PROVIDER_EXTRACTOR=ollama
MODEL_PROVIDER_AGGREGATOR=ollama
MODEL_PROVIDER_VALIDATOR=ollama
MODEL_PROVIDER_PUBLISHER=ollama
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;En &lt;code&gt;src/config.py&lt;/code&gt;&lt;/strong&gt; está la configuración de despliegue, en este caso de ejecución local se encuentran parámetros como la &lt;code&gt;temperature&lt;/code&gt;, &lt;code&gt;top_p,&lt;/code&gt; &lt;code&gt;max_output_tokens&lt;/code&gt; (muy importante porque limita los tokens que pueden gastar los agentes).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Las constantes estructurales del grafo a nivel módulo&lt;/strong&gt;, se encuentran en &lt;strong&gt;&lt;code&gt;src/graph/builder.py&lt;/code&gt;&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;NUM_EXTRACTORS&lt;/code&gt;&lt;/strong&gt; → cantidad de nodos extractores paralelos (y de chunks que pide el Coordinator). Cambiarlo requiere que coincida con el &lt;code&gt;num_chunks&lt;/code&gt; pasado a &lt;code&gt;create_coordinator_agent()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;MAX_NODE_EXECUTIONS&lt;/code&gt;&lt;/strong&gt; → límite global de ejecuciones de nodos en todo el grafo, la salvaguarda contra el feedback loop infinito Validator↔Aggregator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;EXECUTION_TIMEOUT_SECONDS&lt;/code&gt;&lt;/strong&gt; → timeout global del grafo completo.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;NUM_EXTRACTORS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="n"&gt;MAX_NODE_EXECUTIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
&lt;span class="n"&gt;EXECUTION_TIMEOUT_SECONDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt; &lt;span class="c1"&gt;# (For local execution, it's advisable to have a high value)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Las pruebas&lt;/strong&gt; están organizadas en &lt;code&gt;tests/&lt;/code&gt; en &lt;strong&gt;un solo nivel: ejecución rápida y determinista&lt;/strong&gt;. Validan la lógica de las funciones de condición de Graph (&lt;code&gt;needs_revision&lt;/code&gt;, &lt;code&gt;is_approved&lt;/code&gt;, &lt;code&gt;all_dependencies_complete&lt;/code&gt;), el parsing/validación de schemas (&lt;code&gt;Relation&lt;/code&gt;, &lt;code&gt;KnowledgeGraph&lt;/code&gt;, &lt;code&gt;ValidatorOutput&lt;/code&gt;).&lt;br&gt;
Se testea, además de los posibles errores del runtime,  la resolución de provider/modelo por rol de &lt;code&gt;src/config.py&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;_resolve_provider()&lt;/code&gt;&lt;/strong&gt; — que cada rol (&lt;code&gt;coordinator&lt;/code&gt;, &lt;code&gt;extractor&lt;/code&gt;, &lt;code&gt;aggregator&lt;/code&gt;, &lt;code&gt;validator&lt;/code&gt;, &lt;code&gt;publisher&lt;/code&gt;) resuelva al provider correcto: el default (&lt;code&gt;ROLE_PROVIDER_DEFAULTS&lt;/code&gt;) cuando no hay override, y la variable de entorno (&lt;code&gt;MODEL_PROVIDER_VALIDATOR=ollama&lt;/code&gt;, etc.) cuando sí la hay.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;get_model()&lt;/code&gt;&lt;/strong&gt; — que devuelva una instancia de &lt;code&gt;OllamaModel&lt;/code&gt; cuando el provider es &lt;code&gt;"ollama"&lt;/code&gt;, y &lt;code&gt;GeminiModel&lt;/code&gt; cuando es &lt;code&gt;"gemini"&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Procedemos a ejecutar el Graph principal.&lt;/strong&gt; En &lt;code&gt;examples/sample_input.txt&lt;/code&gt;está el input de ejemplo.&lt;br&gt;
Se puede ejecutar contra el modelo ollama local o vía api de gemini comentado más arriba.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; src.main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;El log&lt;/strong&gt; de las llamadas entre el pipeline de agentes se visualiza en la carpeta &lt;code&gt;logs/&lt;/code&gt;. Contiene la data cruda que permite comparar comportamiento del modelo (cuántos turnos internos hace cada uno, cuánto tarda, si usa tool calls, etc.).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;El resultado final&lt;/strong&gt; &lt;br&gt;
Es un  Knowledge graph y  se guarda en un &lt;code&gt;.json&lt;/code&gt; en&lt;code&gt;outputs/&lt;/code&gt;.&lt;br&gt;
Se visualiza la &lt;strong&gt;representación estructurada&lt;/strong&gt; del texto de entrada, describiendo: qué conceptos existen (&lt;code&gt;entities&lt;/code&gt;) y cómo se relacionan entre sí (&lt;code&gt;relations&lt;/code&gt;), en formato sujeto-predicado-objeto.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Por último, hacemos una evaluación de pruebas sobre el grafo multiagente real con el concepto de &lt;em&gt;"Evals".&lt;/em&gt;&lt;/strong&gt; Esto es importante para todos los agentes futuros que construyas, tomar la documentación de Strands y descubrir cómo y para qué tenemos que evaluar nuestros agentes. El resultado se guarda en &lt;code&gt;outputs/eval_report.json&lt;/code&gt; (el detalle de las evaluaciones están en "&lt;em&gt;Conceptos Claves&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;python  &lt;span class="nt"&gt;-m&lt;/span&gt;  evals.evaluate_graph
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📝 &lt;strong&gt;Nota:&lt;/strong&gt; La explicación del código fuente y paso a paso de la ejecución se encuentra detallado en el &lt;code&gt;README.md&lt;/code&gt; en el repositorio de &lt;a href="https://github.com/reinalau/strands-graph" rel="noopener noreferrer"&gt;github&lt;/a&gt; .&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusiones y lecciones aprendidas
&lt;/h2&gt;

&lt;p&gt;Construir este pipeline con Graph me dejó varias lecciones que van más allá de la forma de crearlo según su documentación:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Las constantes estructurales del grafo no son de uso de común para todos los modelos.&lt;/strong&gt; &lt;code&gt;NUM_EXTRACTORS&lt;/code&gt;, &lt;code&gt;MAX_NODE_EXECUTIONS&lt;/code&gt; y &lt;code&gt;EXECUTION_TIMEOUT_SECONDS&lt;/code&gt; en &lt;code&gt;builder.py&lt;/code&gt; parecen valores de infraestructura, pero en la práctica están acoplados al comportamiento del modelo elegido. Con Gemma4 local, el cuello de botella fue el tiempo (&lt;code&gt;EXECUTION_TIMEOUT_SECONDS&lt;/code&gt; tuvo que subir a 1800s). Con Gemini, el cuello de botella fue la convergencia del feedback loop: con &lt;code&gt;temperature=0.3&lt;/code&gt; y &lt;code&gt;top_p=0.9&lt;/code&gt; el Aggregator regeneraba las triples con variación de wording en cada ronda, y el Validator nunca encontraba dos corridas iguales para aprobar, agotando &lt;code&gt;MAX_NODE_EXECUTIONS&lt;/code&gt; sin converger. Solución: bajar a &lt;code&gt;temperature=0&lt;/code&gt; y &lt;code&gt;top_p=0.5&lt;/code&gt; .&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Análisis del input propagation de Graph&lt;/strong&gt; Un nodo sólo recibe el resultado de sus dependencias directas + el input original con el que comienza el grafo. El Publisher, dependiendo solo del Validator, se quedó sin las triples reales porque el Validator fue instruido a responder con una confirmación corta, no a repetir los datos. La solución fue agregar &lt;code&gt;aggregator → publisher&lt;/code&gt; como dependencia adicional. Hay que diseñar edges, pensando qué datos necesita cada nodo, no solo qué controla el flujo.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Las semánticas OR de Python son una fuente de bugs silenciosos.&lt;/strong&gt; Sin &lt;code&gt;all_dependencies_complete()&lt;/code&gt;, el Aggregator se hubiera ejecutado 3 veces (una por extractor) en lugar de una vez con los 3 resultados completos. Cualquier nodo con múltiples edges de entrada en Python necesita esta verificación explícita si queremos esperar a todas las dependencias.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;La granularidad de extracción es responsabilidad del prompt, no del framework.&lt;/strong&gt; Ver entidades compuestas como "entities and relations" en lugar de dos nodos separados es variabilidad normal de un LLM con temperatura &amp;gt; 0. Hay que reforzar reglas en el prompt del Extractor y del Aggregator.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;structured_output_model&lt;/code&gt; requiere que el modelo soporte tool calling&lt;/strong&gt; (este parámetro se pasa en la creación del Agent. Hay que verificarlo (&lt;code&gt;ollama show &amp;lt;model&amp;gt;&lt;/code&gt; → &lt;code&gt;Capabilities: tools&lt;/code&gt;) antes de asumir que cualquier modelo local lo soporta.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Exact-match y LLM-as-judge miden cosas distintas.&lt;/strong&gt; En una corrida, el mismo caso obtuvo F1 exact-match = 0.0 pero SemanticCorrectnessEvaluator = 1.0, el pipeline había extraído el 100% del conocimiento correcto, solo con sinónimos distintos ("Aggregator node" vs "Aggregator"). Confiar solo en exact-match hubiera hecho pensar que el pipeline falló por completo.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Curso Fundamentos&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://skillbuilder.aws/learn/CFBFZNS11J/building-ai-agent-harnesses-with-strands-agents/D6YG8S6N7M" rel="noopener noreferrer"&gt;Building AI Agent Harnesses with Strands Agents&lt;/a&gt;&lt;br&gt;
&lt;a href="https://builder.aws.com/content/3GYPrAplMhl2IAl7jJW0yeKzXrR/building-ai-agent-harnesses-with-strands-agents-a-free-14-video-course/" rel="noopener noreferrer"&gt;Building AI Agent Harnesses – Video Course&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Documentación Strands Agents&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/concepts/multi-agent/graph/" rel="noopener noreferrer"&gt;Graph&lt;/a&gt;&lt;br&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/evals-sdk/evaluators/" rel="noopener noreferrer"&gt;Evals&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Otros&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://builder.aws.com/content/35nhh8YGNRwWHFhHL6zXPrOYzY3/multi-agent-systems-with-graph-pattern" rel="noopener noreferrer"&gt;Multi-Agent Systems with Graph Pattern&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dev.to/aws-espanol/como-detener-la-inyeccion-de-prompts-en-agentes-de-ia-que-leen-contenido-no-confiable-10h2"&gt;Inyección de prompts en agentes de IA&lt;/a&gt;&lt;br&gt;
&lt;a href="https://builder.aws.com/content/3Ecodd9aFqsG1SAkQKmqZ2KxPiP/5-multi-agent-patterns-in-strands-agents-which-one-and-when" rel="noopener noreferrer"&gt;5 Multi-Agent Patterns in Strands&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>strandsagents</category>
      <category>python</category>
    </item>
    <item>
      <title>Swarm en Strands: orquestación autónoma mediante handoffs emergentes</title>
      <dc:creator>Laura Bolaños</dc:creator>
      <pubDate>Mon, 03 Aug 2026 14:55:38 +0000</pubDate>
      <link>https://dev.to/aws-builders/swarm-en-strands-orquestacion-autonoma-mediante-handoffs-emergentes-11a9</link>
      <guid>https://dev.to/aws-builders/swarm-en-strands-orquestacion-autonoma-mediante-handoffs-emergentes-11a9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Siguiendo con la serie de artículos sobre la orquestación de multiagentes en Strands, en estas notas le toca turno a Swarm o Enjambre explicado con un ejemplo gamificado de cinco agentes especializados, donde cada uno es responsable de  diseñar una dimensión distinta de un videojuego. Testeado de dos maneras con Gemma4 + Ollama  y con la api de Gemini.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Para desarrollar y comprender agentes con el framework&amp;nbsp;Strands Agents&amp;nbsp;te propongo hacer un curso de fundamentos o leer su documentación (que es extensa). Los fundamentos son importantes! Es primordial conocer cómo dar complejidad a nuestros agentes, no esperar delegar toda la responsabilidad del diseño a la IA (recursos al pie de estas notas).&lt;br&gt;
Para la prueba de concepto utilicé dos opciones locales de ejecución que me permitió analizar sus resultados:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Ollama + el pequeño modelo&amp;nbsp;&lt;strong&gt;gemma4:e2b-it-qat&lt;/strong&gt;&amp;nbsp;(que pesa poco mas de 4gb) ejecutando en Docker.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Con la Api de gemini. La api key se puede generar de manera gratuita y utilizar el modelo &lt;strong&gt;"Gemini 2.5 Flash"&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🎮 &lt;strong&gt;El caso de uso que tomé para desarrollar el patrón&amp;nbsp;Swarm&lt;/strong&gt; es la creación de un Game Design Document (GDD) a partir de una premisa: "roguelike de cocina en un castillo maldito..."&lt;br&gt;
Participan cinco agentes especializados, cada uno responsable de una dimensión diferente del diseño del videojuego: mecánicas, narrativa, niveles, lore (historias, mitos, reglas, datos del pasado) y experiencia de juego. Todos colaboran para transformar una premisa simple en un &lt;strong&gt;Game Design Document (GDD)&lt;/strong&gt; coherente. &lt;/p&gt;

&lt;p&gt;A diferencia de un pipeline lineal, los agentes pueden detectar fricciones entre sus decisiones (una mecánica que contradice la narrativa, una regla de lore que rompe el balance) y reabrir la conversación con el agente correspondiente hasta llegar a un resultado consensuado.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conceptos Claves
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;➡ Swarm es un patrón de orquestación de agentes colaborativo&lt;/strong&gt;, donde múltiples agentes trabajan juntos como un equipo para resolver tareas complejas. A diferencia de los sistemas multiagente tradicionales, ya sean secuenciales o jerárquicos, un enjambre permite la coordinación autónoma entre agentes con &lt;strong&gt;contexto y memoria de trabajo compartidos&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Swarm es apropiado cuando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;La secuencia de pasos no se puede predefinir:&lt;/strong&gt; de antemano no sabés qué agente necesitará intervenir, porque depende del contenido que se vaya generando.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Puede haber correcciones o retrocesos:&lt;/strong&gt; el problema requiere ciclos de ida y vuelta entre especialistas (ejemplo: una decisión de un agente invalida el trabajo de otro).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hay múltiples dimensiones de expertise en tensión:&lt;/strong&gt; cada agente defiende una perspectiva distinta y el valor está justamente en que negocien entre sí, no en que trabajen aislados uno de otro.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Se necesita razonamiento colectivo emergente:&lt;/strong&gt; la solución final surge de la interacción entre agentes, no de que un solo agente (o un humano) coordine todo desde arriba.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;❗ No es el patrón ideal cuando el flujo de trabajo es fijo y predecible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;➡ Memoria / Contexto&lt;/strong&gt; &lt;br&gt;
Poseen memoria de trabajo compartida y contexto completo. Cuando el enjambre se ejecuta, el historial completo de mensajes (propuestas, objeciones y razonamientos de de los agentes anteriores) se transmite al nuevo agente que toma el control (&lt;em&gt;handoff&lt;/em&gt;). &lt;br&gt;
&lt;em&gt;Payload del Handoff&lt;/em&gt;: además del historial acumulado, cuando un agente ejecuta la herramienta de handoff, puede adjuntar un mensaje o nota explícita (&lt;code&gt;message&amp;nbsp;&lt;/code&gt;/&amp;nbsp;&lt;code&gt;context&lt;/code&gt;) explicando&amp;nbsp;por qué&amp;nbsp;le transfiere la tarea y qué espera que haga.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;➡ Nodos vs. Agentes&lt;/strong&gt;&lt;br&gt;
En Swarm, cada agente se registra como un "nodo" (&lt;code&gt;SwarmNode&lt;/code&gt;) que envuelve al &lt;code&gt;Agent&lt;/code&gt;. Esta distinción importa porque el swarm no razona en términos de "quién es quién" sino de "qué nodo ejecuta a continuación". El resultado final expone esto como &lt;code&gt;node_history&lt;/code&gt;, una lista de nodos ejecutados, no una conversación: es lo que permite reconstruir después la topología real que se ejecutó, sin haberla tenido que diseñar de antemano.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;➡ El mecanismo de Handoff es tool-calling, no un cambio de contexto mágico&lt;/strong&gt; &lt;br&gt;
Vale la pena aclarar explícitamente que &lt;code&gt;handoff_to_agent&lt;/code&gt;no es una función especial del framework fuera del paradigma de LLMs: es una tool más, inyectada automáticamente por Strands en cada agente del swarm, con el mismo mecanismo de function-calling que cualquier otra tool. La consecuente es &lt;strong&gt;la fiabilidad del patrón Swarm depende enteramente de qué tan bien el modelo subyacente hace tool-calling&lt;/strong&gt;. No es una limitación de Strands, es una dependencia estructural del patrón. Por eso, acompañando este artículo el caso de uso se testeo de dos maneras: con el pequeño modelo &lt;em&gt;Gemma4&lt;/em&gt; y con el modelo &lt;em&gt;Gemini 2.5 Flash&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;➡ Topología emergente vs. predefinida (el argumento central de "por qué Swarm")&lt;/strong&gt;&lt;br&gt;
En Swarm, la topología es un &lt;strong&gt;resultado&lt;/strong&gt;, no una entrada. Se descubre recién en la ejecución, y puede variar entre corridas con la misma premisa (ejemplo: tengo 5 agentes y sólo intervienen 4). Esto es una ventaja de adaptabilidad y un riesgo no-determinista al mismo tiempo.&lt;br&gt;
Se necesita indicar donde comienza el enjambre y cada punto de decisión lo toma el propio agente en base al contexto acumulado. Aquí un topología de ejemplo del caso de uso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mechanic_designer
      ↓
level_architect ←──────────┐
      ↓                    │
playtest_simulator ─────────┤ (iteration 1: objection → adjustment)
      ↓                    │
level_architect ──────────→┘
      ↓
playtest_simulator ─────────┐ (iteration 2: objection → adjustment)
      ↓                    │
level_architect ←───────────┘
      ↓
narrative_weaver
      ↓
lore_keeper
      ↓
playtest_simulator ─────────┐ (iteration 1: objection → adjustment)
      ↓                    │
level_architect ←───────────┘
      ↓
playtest_simulator
      ↓
[VERDICT: NO FRICTION DETECTED] → END
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;➡ Guardrails como parte del diseño&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;max_handoffs&lt;/code&gt;: Máximo de transferencias entre agentes permitidos en todo el swarm antes de forzar el corte. Es el límite global de "pasos" de la ejecución completa.&lt;br&gt;
&lt;code&gt;max_iterations&lt;/code&gt;: Máximo de iteraciones del swarm (ejecuciones de nodo) permitidas. En la práctica actúa junto con &lt;code&gt;max_handoffs&lt;/code&gt; como segunda red de seguridad contra ejecuciones descontroladas.&lt;br&gt;
&lt;code&gt;execution_timeout&lt;/code&gt;: Tiempo total en segundos que puede durar la ejecución completa del swarm, sumando todos los agentes y handoffs. Si se supera, el swarm corta con Status.FAILED aunque esté en medio de un turno.&lt;br&gt;
&lt;code&gt;node_timeout&lt;/code&gt;: Tiempo máximo (en segundos) que un agente individual puede tardar en un solo turno (una llamada al modelo + su respuesta). Protege contra que un nodo puntual se cuelgue sin afectar el límite global.&lt;br&gt;
&lt;code&gt;repetitive_handoff_detection_window&lt;/code&gt;: Tamaño de la "ventana" (cantidad de handoffs recientes) que Strands analiza para detectar patrones repetitivos. Con 6, mira los últimos 6 handoffs para decidir si hay un loop.&lt;br&gt;
&lt;code&gt;min_unique_agents&lt;/code&gt;: Dentro de esa ventana, cantidad mínima de agentes distintos que deben haber participado para considerar el flujo "sano". Si en los últimos 6 handoffs participaron menos de 3 agentes únicos (ej. solo 2 rebotando entre sí), Strands lo detecta como ping-pong y corta el swarm.&lt;br&gt;
Todos los guardrails anteriores no son opcionales para un swarm en producción, son necesarios para limitar la autonomía de los agentes y que el costo de tokens, tiempo o dinero en APIs pagas no se descontrole.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;➡ La importancia del diseño de prompts&lt;/strong&gt;&lt;br&gt;
Strands provee el mecanismo de handoff (la tool, el hook, los guardrails), pero no decide cuándo usarlo, eso está en el &lt;em&gt;system prompt&lt;/em&gt; de cada agente. En este caso de uso, cada uno de los 5 prompts define explícitamente tres cosas: el rol y criterios de evaluación del agente, a quién y bajo qué condición debe transferir el control, y reglas de convergencia para evitar que vuelva a objetar lo mismo una vez resuelto ("Convergence rule").&lt;/p&gt;

&lt;p&gt;Esta última fue necesaria escribirla ya que en las primeras ejecuciones algunos agentes re-abrían el mismo desacuerdo varias veces con distintas palabras, generando ciclos improductivos que ni los guardrails ni el hook podían distinguir de una negociación legítima. La solución fue de prompt, instruir explícitamente a cada agente a aceptar una corrección después de la primera vuelta, salvo que aparezca un motivo nuevo. Los guardrails protegen contra loops estructurales; esto resuelve los loops semánticos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;➡ Hooks como punto de extensión para mitigar limitaciones del modelo elegido&lt;/strong&gt;&lt;br&gt;
Strands permite engancharse al ciclo de ejecución del agente mediante &lt;em&gt;hooks&lt;/em&gt;, interceptando eventos como una llamada a herramienta exitosa. En el caso de uso se implementó &lt;code&gt;StopAfterHandoffHook&lt;/code&gt;, que fuerza al agente a detener su turno inmediatamente después de un handoff exitoso.&lt;/p&gt;

&lt;p&gt;Este mecanismos sirve para  cuando el prompt no alcanza para garantizar un comportamiento, evita que el modelo llame a la herramienta de handoff más de una vez por turno. Es una intervención determinista a nivel de código, sin depender del prompt engineering. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;➡ Evaluaciones (Evals)&lt;/strong&gt;&lt;br&gt;
Existe el package &lt;code&gt;strands-agents-evals&lt;/code&gt; que provee evaluadores reutilizables (por salida, por trayectoria de tool-calls, por trazas de ejecución, etc). En este caso de uso se implementaron dos evaluadores:&lt;br&gt;
&lt;strong&gt;- Evaluación por Trayectoria (Trajectory Evaluation)&lt;/strong&gt;. En vez de exigir una secuencia exacta de handoffs, valida propiedades estructurales de la ejecución. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;que el punto de entrada haya sido el correcto. &lt;/li&gt;
&lt;li&gt;que haya participado una cantidad mínima de agentes únicos, y que los agentes indispensables (como &lt;code&gt;mechanic_designer&lt;/code&gt;) hayan intervenido.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;- Evaluación por Salida (Output Evaluation)&lt;/strong&gt;. Verifica que el GDD final contenga las palabras clave esperadas del dominio del juego (ej. "curse", "ingredient", "castle"), como chequeo básico de que el contenido generado es relevante a la premisa de origen.&lt;/p&gt;

&lt;p&gt;⭐ Este enfoque refleja una diferencia clave frente a patrones más deterministas como agents-as-tools donde se puede evaluar contra un resultado esperado preciso (si se delegó al agente correcto); en cambio en Swarm hay que evaluar la &lt;strong&gt;forma&lt;/strong&gt; del comportamiento, no una secuencia fija.&lt;/p&gt;
&lt;h2&gt;
  
  
  Descripción de la solución
&lt;/h2&gt;

&lt;p&gt;📦&amp;nbsp;Repositorio GitHub:&amp;nbsp;&lt;a href="https://github.com/reinalau/strands-swarm" rel="noopener noreferrer"&gt;github.com/reinalau/strands-swarm&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feyb3nt953zbb9q9gzxd8.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feyb3nt953zbb9q9gzxd8.jpg" alt=" " width="800" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como comenté más arriba, utilicé 2 opciones de modelos para la ejecución y analizar el comportamiento, pero podes usar solo una de ellas:&lt;/p&gt;

&lt;p&gt;a. Docker Desktop, luego descargué la imagen de Ollama e instalé&amp;nbsp;gemma4:e2b-it-qat&amp;nbsp;(que pesa poco mas de 4GB). Todos los modelos gemma los podés encontrar&amp;nbsp;&lt;a href="https://ollama.com/library/gemma4/tags" rel="noopener noreferrer"&gt;aquí&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;&lt;span class="c"&gt;# Start the Ollama server with a persistent volume&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; ollama &lt;span class="nt"&gt;-p&lt;/span&gt; 11434:11434 &lt;span class="nt"&gt;-v&lt;/span&gt; ollama_data:/root/.ollama ollama/ollama

&lt;span class="c"&gt;# Download the model&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; ollama ollama pull gemma4:e2b-it-qat

&lt;span class="c"&gt;# Test that the model responds&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; ollama ollama run gemma4:e2b-it-qat

&lt;span class="c"&gt;# Verify that the model is running&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; ollama ollama ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;b. Generé una api key de gemini de capa gratuita, desde &lt;a href="https://aistudio.google.com/" rel="noopener noreferrer"&gt;aquí&lt;/a&gt;. Te permite usar el modelo &lt;strong&gt;Gemini 2.5 Flash&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;El código del multiagente está en Python&lt;/strong&gt; y la estructura del proyecto se diseñó de manera que sea más explicativo su lógica:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;strands-swarm/
├── README.md
├── requirements.txt
├── .env.example
├── .gitignore
│
├── src/
│   ├── __init__.py
│   ├── main.py                  # entry point, builds and runs the swarm
│   ├── config.py                # model provider config, timeouts, swarm limits
│   │
│   ├── agents/
│   │   ├── __init__.py
│   │   ├── _common.py           # shared model factory + prompt loader
│   │   ├── _hooks.py            # StopAfterHandoffHook
│   │   ├── mechanic_designer.py
│   │   ├── narrative_weaver.py
│   │   ├── level_architect.py
│   │   ├── lore_keeper.py
│   │   └── playtest_simulator.py
│   │
│   ├── prompts/
│   │   ├── mechanic_designer.md
│   │   ├── narrative_weaver.md
│   │   ├── level_architect.md
│   │   ├── lore_keeper.md
│   │   └── playtest_simulator.md
│   │
│   ├── swarm/
│   │   ├── __init__.py
│   │   └── build_swarm.py       # instantiates agents + configures Swarm (max_handoffs, etc.)
│   │
│   └── output/
│       ├── __init__.py
│       └── gdd_builder.py       # consolidates node_history/results into the final GDD
│
├── evals/
│   ├── __init__.py
│   ├── eval_cases.py            # EvalCase definitions (premise, expected agents, keywords)
│   └── run_evals.py             # trajectory + output evaluators over the swarm
│
├── examples/
│   └── example_premise.txt      # "cooking roguelike in a cursed castle"
│
├── outputs/
│   └── .gitkeep                 # generated GDDs land here after each run
│
├── logs/
│   └── .gitkeep                 # execution logs / handoff events
│
└── tests/
    ├── __init__.py
    └── test_swarm_flow.py

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Ejecución Local
&lt;/h3&gt;

&lt;p&gt;Una vez que tenemos el repositorio del código fuente clonado y el docker de ollama con el modelo gemma 4 o la api key de gemini generada, pasamos armar el ambiente.&lt;br&gt;
Los requerimientos que vamos a encontrar en &lt;code&gt;requirements.txt&lt;/code&gt;:&lt;br&gt;
strands-agents[ollama]&lt;br&gt;
strands-agents[gemini]&lt;br&gt;
strands-agents-evals&lt;br&gt;
python-dotenv&lt;br&gt;
pytest&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; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En &lt;code&gt;.env&lt;/code&gt; necesitamos:&lt;br&gt;
OLLAMA_HOST=&lt;a href="http://localhost:11434" rel="noopener noreferrer"&gt;http://localhost:11434&lt;/a&gt;&lt;br&gt;
MODEL_NAME=gemma4:e2b-it-qat&lt;br&gt;
MODEL_PROVIDER=gemini   # ollama&lt;br&gt;
GEMINI_API_KEY=tuapikeyOpcional&lt;br&gt;
GEMINI_MODEL_NAME=gemini-2.5-flash&lt;br&gt;
LOG_LEVEL=INFO&lt;br&gt;
MODEL_TEMPERATURE=0.5&lt;br&gt;
MODEL_MAX_TOKENS=3500&lt;br&gt;
MODEL_NUM_CTX=4096&lt;br&gt;
MAX_HANDOFFS=12&lt;br&gt;
MAX_ITERATIONS=12&lt;br&gt;
EXECUTION_TIMEOUT=5000&lt;br&gt;
NODE_TIMEOUT=1500&lt;br&gt;
REPETITIVE_HANDOFF_DETECTION_WINDOW=6&lt;br&gt;
REPETITIVE_HANDOFF_MIN_UNIQUE_AGENTS=3&lt;br&gt;
ENTRY_POINT_AGENT=mechanic_designer&lt;br&gt;
LOG_DIR=logs&lt;br&gt;
OUTPUT_DIR=outputs&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="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Las pruebas&lt;/strong&gt; están organizadas en un enfoque de 2 niveles (&lt;code&gt;tests/&lt;/code&gt;):&lt;br&gt;
&lt;strong&gt;Nivel 1 (Rápidas y deterministas):&lt;/strong&gt; Valida la estructura del swarm, la configuración de nodos/límites, la extracción de texto y la consolidación del GDD sin realizar llamadas reales al modelo. Usa cadenas simuladas como "Cooking roguelike in a cursed castle." o "Test Premise".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Nivel 2 (Test de integración end-to-end):&lt;/strong&gt; Ejecuta el swarm completo contra el modelo real (Gemma4 en Ollama o Gemini). Está deshabilitado por default, pytest lo salta automáticamente, y se activa así en esta ejecución:&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;RUN_INTEGRATION_TESTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest &lt;span class="nt"&gt;-m&lt;/span&gt; integration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📝 &lt;strong&gt;Nota:&lt;/strong&gt; el comportamiento de activación/desactivación explícita para tests que dependen de un LLM real es una práctica común cuando se integran estos flujos a pipelines de CI/CD, donde ejecutar llamadas reales a un modelo en cada commit sería muy lento.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Procedemos a ejecutar el Swarm principal&lt;/strong&gt;. Se puede ejecutar contra el modelo ollama local o vía api de gemini. Se configura en &lt;code&gt;.env&lt;/code&gt; , tener en cuenta de cambiar &lt;code&gt;MODEL_PROVIDER&lt;/code&gt; a "gemini" u "ollama". También recomiendo revisar valores default de &lt;code&gt;src/config.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; src.main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;El log&lt;/strong&gt; de las llamadas entre agentes se visualiza en la carpeta &lt;code&gt;logs/&lt;/code&gt;. &lt;br&gt;
&lt;strong&gt;El resultado final&lt;/strong&gt; del intercambio de mensajes entre los agentes del enjambre es el Game Design Document y se guarda en un documento markdown en la carpeta &lt;code&gt;outputs/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;⭐ Te invito analizar la topología de handoffs después de las ejecuciones para entender si los agentes necesitan ajustes ya sea de prompts, hooks o parámetros de configuración.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Por último, hacemos una evaluación de pruebas  sobre el agente real con el concepto de&amp;nbsp;"Evals"&lt;/strong&gt;.&amp;nbsp;Esto es importante para todos los agentes futuros que construyas, tomar la documentación de Strands y descubrir cómo y para qué tenemos que evaluar nuestros agentes.&lt;br&gt;
Se desactiva el recolector de telemetría&amp;nbsp;OpenTelemetry: &lt;code&gt;OTEL_SDK_DISABLED=true&lt;/code&gt;&amp;nbsp;para evitar cuelgues en un entorno local con el modelo en ollama (si internamente no alcanza un endpoint se cuelga indefinidamente).&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_SDK_DISABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; evals.run_evals
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📝&amp;nbsp;Nota-2:&amp;nbsp;La explicación del código y paso a paso de la ejecución se encuentra detallado en el README.md en el repositorio de &lt;a href="https://github.com/reinalau/strands-swarm" rel="noopener noreferrer"&gt;github&lt;/a&gt;.&lt;/p&gt;




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

&lt;p&gt;El patrón *&lt;em&gt;Swarm *&lt;/em&gt; cede el control del flujo al propio modelo. Una corrida puede resolverse en 4 pasos o en 11, en un minuto o en dos horas, según decisiones que toman los agentes que intervienen. &lt;br&gt;
Con un modelo chico experimenté loops de handoff, ping-pong entre agentes, transferencias narradas en texto en lugar de ejecutadas y para resolverlo tuve que agregar controles explícitos: límites de iteración, detección de repetición y un hook que corta el turno apenas el handoff se confirma.&lt;br&gt;
La autonomía de un swarm no reemplaza los guardarraíles de ingeniería, los vuelve indispensables.&lt;/p&gt;

&lt;p&gt;⭐ Y como trabajo futuro hay que pensar cómo implementar un proyecto multiagente Swarm productivo. El presente proyecto es educativo y sirve para entender el funcionamiento del patrón, pero es insuficiente para producción. AWS ya tiene un camino natural para eso: &lt;strong&gt;Amazon Bedrock AgentCore&lt;/strong&gt;, un runtime serverless que ejecuta agentes Strands con aislamiento por sesión, observabilidad, memoria administrada y escalado automático. Queda para un próximo artículo explorar esa migración.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Curso Fundamentos&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://skillbuilder.aws/learn/CFBFZNS11J/building-ai-agent-harnesses-with-strands-agents/D6YG8S6N7M" rel="noopener noreferrer"&gt;Building AI Agent Harnesses with Strands Agents&lt;/a&gt;&lt;br&gt;
&lt;a href="https://builder.aws.com/content/3GYPrAplMhl2IAl7jJW0yeKzXrR/building-ai-agent-harnesses-with-strands-agents-a-free-14-video-course/" rel="noopener noreferrer"&gt;Building AI Agent Harnesses – Video Course&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Documentación Strands Agents&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/concepts/multi-agent/swarm/" rel="noopener noreferrer"&gt;Swarm&lt;/a&gt;&lt;br&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/evals-sdk/evaluators/" rel="noopener noreferrer"&gt;Evals&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Otros&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://builder.aws.com/content/3GJxKulvQXV120QgfCSaq4QzG4I/blog-writer-agent-using-swarm-pattern/" rel="noopener noreferrer"&gt;Blog Writer Agent using Swarm Pattern&lt;/a&gt;&lt;br&gt;
&lt;a href="https://builder.aws.com/content/3GYPrAplMhl2IAl7jJW0yeKzXrR/building-ai-agent-harnesses-with-strands-agents-a-free-14-video-course/" rel="noopener noreferrer"&gt;Building AI Agent Harnesses – Video Course&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dev.to/aws-espanol/detener-alucinaciones-de-agentes-de-ia-validar-antes-de-que-el-agente-escriba-en-memoria-2ad3/"&gt;Detener alucinaciones de agentes de IA&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>strandsagents</category>
      <category>python</category>
    </item>
    <item>
      <title>Agents as Tools en Strands: orquestación jerárquica en local con Gemma 4 y Ollama</title>
      <dc:creator>Laura Bolaños</dc:creator>
      <pubDate>Mon, 27 Jul 2026 16:33:25 +0000</pubDate>
      <link>https://dev.to/aws-builders/agents-as-tools-en-strands-orquestacion-jerarquica-en-local-con-gemma-4-y-ollama-db4</link>
      <guid>https://dev.to/aws-builders/agents-as-tools-en-strands-orquestacion-jerarquica-en-local-con-gemma-4-y-ollama-db4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Hace tiempo vengo consumiendo cursos de orquestación de agentes de distintos frameworks y me propuse navegar por los diferentes patrones de Strands Agents de complejidad media, ejecutados localmente (con la idea de encarar más adelante el deploy en AWS). Este artículo está dedicado a Agents-as-Tools, corriendo con Ollama en una laptop con solo 16GB de RAM 💻.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Para desarrollar y comprender agentes con el framework Strands Agents  te propongo hacer un curso de fundamentos o leer su documentación (que es extensa). Los fundamentos son importantes! Es primordial conocer cómo dar complejidad a nuestros agentes, no esperar delegar toda la responsabilidad del diseño a la IA (recursos al pie de estas notas).&lt;/p&gt;

&lt;p&gt;Para que la prueba de concepto se pueda ejecutar localmente, utilicé Ollama +  el pequeño modelo &lt;strong&gt;gemma4:e2b-it-qat&lt;/strong&gt; (que pesa poco mas de 4gb) ejecutando en Docker. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;El caso de uso que tomé para desarrollar el patrón&lt;/strong&gt; es la automatización del soporte básico de TI:&lt;br&gt;
🛠 Se recibe un ticket de soporte y se determina si es un ticket de facturación, técnico o seguridad. Luego se envía al agente especializado para que lo resuelva.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conceptos claves
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;➡ Agents As Tools es un patrón de orquestación multiagente&lt;/strong&gt; que como se describe en la documentación es jerárquico (hub and spooke), donde un orquestador delega tareas a agentes especializados que tienen diferentes tools para resolver. El orquestador y cada agente poseen contexto aislado, no se contaminan entre sí. &lt;/p&gt;

&lt;p&gt;Para clarificar el funcionamiento: al delegar, &lt;strong&gt;hay dos agents loops anidados, no uno compartido:&lt;/strong&gt; el orquestador no "pausa" su razonamiento en espera del especialista, simplemente visualiza una tool call que devuelve texto, por detrás, esa tool call disparó un loop completo e independiente que ya terminó antes de volver.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Orchestrator loop
   └─ calls billing_agent&lt;span class="o"&gt;(&lt;/span&gt;query&lt;span class="o"&gt;)&lt;/span&gt; → triggers a new, isolated loop
         └─ Billing loop: check_invoice_status → process_refund → final response
   ← receives only the final string &lt;span class="o"&gt;(&lt;/span&gt;doesn&lt;span class="s1"&gt;'t see the internal tool calls)
   └─ continues its own loop: delegates to another specialist or replies to the user
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;➡&lt;/strong&gt; &lt;code&gt;@tool&lt;/code&gt; no distingue entre &lt;strong&gt;operación simple o integración vía APis y agente completo&lt;/strong&gt; , el patrón Agents-as-Tools explota que un agente &lt;strong&gt;cumpla la misma interfaz&lt;/strong&gt; que una tool (recibe input, devuelve output), para que se pueda componer recursivamente. Es lo que permite anidar agentes dentro de agentes sin que el nivel superior necesite saber la complejidad interna del nivel inferior.&lt;/p&gt;

&lt;p&gt;⭐ La descripción (docstring) de cada función &lt;code&gt;@tool&lt;/code&gt; no es un comentario decorativo: es el input que el LLM orquestador lee en tiempo de ejecución para decidir a qué especialista delegar. Distinto del parámetro &lt;code&gt;description=&lt;/code&gt; del propio &lt;code&gt;Agent&lt;/code&gt;, que es metadata informativa y no interviene en el routing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;➡ Memoria del orquestador (ConversationManager)&lt;/strong&gt;. Utilicé el concepto de compresión al hacer que el agente recuerde los últimos 10 mensajes o tickets de la sesión para mantener contexto entre interacciones consecutivas con &lt;code&gt;SlidingWindowConversationManager(window_size=10)&lt;/code&gt; &lt;br&gt;
Mantiene los mensajes en una lista dentro del objeto Python en RAM durante la ejecución del proceso.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;➡ Hook de dominio (SteeringHandler)&lt;/strong&gt;. Implementa el "&lt;em&gt;HookProvider&lt;/em&gt;" de Strands, en este caso de uso fuerza el orden de pasos del protocolo de respuesta ante incidentes de Seguridad. Si el LLM intenta llamar por ejemplo a &lt;code&gt;revoke_access_token&lt;/code&gt; sin haber auditado primero la IP, el hook lo bloquea con &lt;code&gt;event.cancel_tool&lt;/code&gt; antes de que la función se ejecute. El framework impone la lógica de negocio determinista, sin depender del criterio del modelo. Describe con precisión los tres eventos &lt;code&gt;BeforeInvocationEvent&lt;/code&gt;,&amp;nbsp;&lt;code&gt;AfterToolCallEvent&lt;/code&gt;,&amp;nbsp;&lt;code&gt;BeforeToolCallEvent&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Los hooks sirven en general para:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prevenir loops infinitos o excesivos de tool calls (control de costos/tokens)&lt;/li&gt;
&lt;li&gt;Guardrails de seguridad/negocio (ej. no permitir más de n transacciones, n consultas a una API con rate limit, etc.)&lt;/li&gt;
&lt;li&gt;Es el mecanismo estándar de Strands para inyectar lógica de control &lt;strong&gt;sin modificar el core del agent loop.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;➡ Evaluaciones (Evals):&lt;/strong&gt; evaluación de comportamiento del agente (con &lt;code&gt;Contains&lt;/code&gt;, no asserts tradicionales). &lt;/p&gt;

&lt;p&gt;Algunas consideraciones que tomé para poder ejecutar los evals sin que se cuelguen en un entorno local con el modelo en ollama: Desactivé OTLP de Opentelemetry,  este si internamente no alcanza un endpoint (&lt;code&gt;localhost:4318&lt;/code&gt;) se cuelga indefinidamente. &lt;/p&gt;

&lt;p&gt;En el caso de uso del repositorio se hacen evaluaciones deterministas ya que se utiliza solo un LLM, no se abarcan todas las evaluaciones pero sí dos importantes descritas debajo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output Evaluation&lt;/strong&gt;. Evalúa el &lt;strong&gt;resultado final&lt;/strong&gt; (el texto que recibe el usuario), sin importar el camino interno que tomó el agente para llegar ahí. Es un chequeo determinista de &lt;strong&gt;keyword matching&lt;/strong&gt;: la respuesta debe contener ciertas palabras clave esperadas (&lt;code&gt;INV-1002&lt;/code&gt;, &lt;code&gt;USR-884&lt;/code&gt;, etc.).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trajectory Evaluation&lt;/strong&gt;. No evalúa qué respondió el agente, sino cómo llegó ahí, &lt;strong&gt;qué tools/sub-agentes invocó durante su razonamiento&lt;/strong&gt;. En el código, &lt;code&gt;extract_called_tools()&lt;/code&gt; inspecciona &lt;code&gt;agent.messages&lt;/code&gt; buscando bloques &lt;code&gt;toolUse&lt;/code&gt;, y &lt;code&gt;evaluate_trajectory()&lt;/code&gt; verifica que el sub-agente esperado (ej. &lt;code&gt;BillingAgent&lt;/code&gt;) haya sido efectivamente el delegado. Para la ejecución en entorno local con Ollama, la función &lt;code&gt;extract_called_tools&lt;/code&gt; extrae directamente los nombres de las herramientas en agent.messages y comprueba en milisegundos si se invocó a &lt;code&gt;case.expected_agent&lt;/code&gt;, sin necesidad de instanciar un modelo juez adicional ni hacer peticiones extra a Bedrock/Ollama (sin cuelgues asíncronos).&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;PASS / FAIL a nivel dimensión, en el output:&lt;/strong&gt; cada &lt;code&gt;EvalCase&lt;/code&gt; se marca como aprobado (&lt;code&gt;case_passed&lt;/code&gt;) solo si ambas dimensiones pasan (&lt;code&gt;traj_eval.test_pass&lt;/code&gt; and &lt;code&gt;out_eval.test_pass&lt;/code&gt;).  Un agente puede delegar al especialista correcto (trayectoria OK) pero responder sin las keywords esperadas (output FAIL), y el caso se marca como fallido igual. &lt;strong&gt;Una sola dimensión de evaluación no alcanza&lt;/strong&gt;: el agente puede "hacer lo correcto" pero "comunicarlo mal", o viceversa.&lt;/p&gt;
&lt;h2&gt;
  
  
  Descripción de la solución
&lt;/h2&gt;

&lt;p&gt;📦 Repositorio GitHub: &lt;a href="https://github.com/reinalau/strands-agents-as-tools" rel="noopener noreferrer"&gt;github.com/reinalau/strands-agents-as-tools&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fquhnii87fqcf4urzghj4.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fquhnii87fqcf4urzghj4.png" alt="Soporte TI - Agents-as-Tools" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para alojar el LLM utilicé Docker Desktop, luego descargué la imagen de Ollama e instalé &lt;strong&gt;gemma4:e2b-it-qat&lt;/strong&gt; (que pesa poco mas de 4GB). Todos los modelos gemma los podés encontrar &lt;a href="https://ollama.com/library/gemma4/tags" rel="noopener noreferrer"&gt;aquí&lt;/a&gt; pero este en particular funciona OK en una laptop con 16GB de RAM. &lt;/p&gt;

&lt;p&gt;Como condición necesaria asegurate de dar memoria suficiente a Docker.&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;# Start the Ollama server with a persistent volume&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; ollama &lt;span class="nt"&gt;-p&lt;/span&gt; 11434:11434 &lt;span class="nt"&gt;-v&lt;/span&gt; ollama_data:/root/.ollama ollama/ollama
&lt;span class="c"&gt;# Download the model&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; ollama ollama pull gemma4:e2b-it-qat
&lt;span class="c"&gt;# Test that the model responds&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; ollama ollama run gemma4:e2b-it-qat
&lt;span class="c"&gt;# Verify that the model is running&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; ollama ollama ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Importante!&lt;/strong&gt; Testear el modelo gemma 4 en la terminal haciendo alguna pregunta para corroborar que responda.&lt;/p&gt;

&lt;p&gt;El código del multiagente está en Python y la estructura del proyecto se diseñó de manera que sea más explicativo su lógica:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;strands-agents-as-tools
├── README.md
├── requirements.txt
├── .env.example
├── .gitignore
│
├── src/
│   ├── __init__.py
│   ├── main.py
│   ├── config.py
│   │
│   ├── agents/
│   │   ├── __init__.py
│   │   ├── orchestrator.py
│   │   ├── billing_agent.py
│   │   ├── technical_agent.py
│   │   └── security_agent.py
│   │
│   ├── tools/
│   │   ├── __init__.py
│   │   ├── billing_tools.py
│   │   ├── technical_tools.py
│   │   └── security_tools.py
│   │
│   └── hooks/
│       ├── __init__.py
│       └── security_verification_hook.py   # Domain SteeringHandler
│
├── examples/
│   └── sample_tickets.py
│
├── evals/
│   ├── __init__.py
│   ├── eval_cases.py                 # Cases with inputs, expected agent and keywords
│   └── run_evals.py                  # Dual evaluator: Trajectory (Tool Call) + Output (Keywords)
│
└── tests/
    └── test_agents.py                # Unit tests for tools (no LLM)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Ejecución local
&lt;/h3&gt;

&lt;p&gt;Una vez que tenemos el repositorio del código fuente clonado y el docker de ollama con el modelo gemma 4 ejecutando, pasamos armar el ambiente.&lt;/p&gt;

&lt;p&gt;Los requerimientos que vamos a encontrar en &lt;code&gt;requirements.txt&lt;/code&gt;:&lt;br&gt;
strands-agents&lt;br&gt;
strands-agents-evals&lt;br&gt;
ollama&lt;br&gt;
python-dotenv&lt;br&gt;
pytest&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; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En el .env vamos a necesitar:&lt;br&gt;
OLLAMA_HOST=&lt;a href="http://localhost:11434" rel="noopener noreferrer"&gt;http://localhost:11434&lt;/a&gt;&lt;br&gt;
MODEL_NAME=gemma4:e2b-it-qat&lt;br&gt;
LOG_LEVEL=INFO&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="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Los test son &lt;strong&gt;pruebas unitarias deterministas&lt;/strong&gt; (&lt;code&gt;tests/&lt;/code&gt;).&amp;nbsp;Permite validar rápidamente y de forma aislada la lógica de código puro de las herramientas (tools) de negocio (facturación, soporte técnico y seguridad), sin requerir inferencia de LLM. Garantizan que las funciones auxiliares retornen los estados y formatos esperados antes de ser expuestas como capacidades&amp;nbsp;&lt;code&gt;@tool&lt;/code&gt;&amp;nbsp;al sistema multiagente.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest tests/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Procedemos a ejecutar el flujo principal demostrativo donde se ve al orquestador delegar tickets&lt;/strong&gt; a los especialistas procesados por Ollama. Puede ser algo lento debido al modelo de ollama elegido pero finalmente termina el flujo con los ejemplos de tickets. Se utilizan ejemplos de soportes dentro de main con &lt;code&gt;from examples.sample_tickets import SAMPLE_TICKETS&lt;/code&gt; ...te invito a cambiarlos!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; src.main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por último, hacemos una evaluación de pruebas de calidad y precisión ejecutadas sobre el agente real con el concepto de &lt;em&gt;"Evals"&lt;/em&gt;. Esto es importante para todos los agentes futuros que construyas: hay que tomar la documentación de Strands y descubrir cómo y para qué tenemos que evaluar nuestros agentes.&lt;/p&gt;

&lt;p&gt;También recomiendo establecer &lt;code&gt;OTEL_SDK_DISABLED=true&lt;/code&gt; para desactivar el recolector de telemetría OpenTelemetry (explicado en Conceptos Claves)&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_SDK_DISABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; evals.run_evals
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este último script ejecuta dos metodologías de evaluación según las especificaciones de &lt;strong&gt;Strands Agents: Output / Keyword Evaluation y Tool-Call Trajectory Evaluation&lt;/strong&gt; (explicado en Conceptos Claves) &lt;/p&gt;

&lt;p&gt;📝 &lt;strong&gt;Nota:&lt;/strong&gt; Todo la explicación del código del repositorio y paso a paso de la ejecución se encuentra en el &lt;code&gt;README.md&lt;/code&gt;&lt;/p&gt;




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

&lt;p&gt;Implementar Agents-as-Tools con un modelo local vía Ollama fue una buena forma de entender el patrón de orquestación sin gastar, pero también descubrí que modelos chicos como &lt;code&gt;gemma4:e2b-it-qat&lt;/code&gt; pueden ser inconsistentes en su razonamiento (llamadas duplicadas a una misma tool, dudas antes de delegar) a diferencia de un modelo más grande (Bedrock/Claude/GPT-4) que probablemente sea más preciso. Esto no invalida el patrón, todo lo contrario: reafirma por qué los guardrails deterministas (el hook de seguridad) y las evaluaciones automáticas es la forma de detectar el comportamiento errático antes de que llegue a producción.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitaciones del ejemplo
&lt;/h3&gt;

&lt;p&gt;Este repositorio es educativo, y tiene varias simplificaciones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tools hardcodeadas con datos mock:&lt;/strong&gt; &lt;code&gt;check_invoice_status&lt;/code&gt;, &lt;code&gt;check_system_status&lt;/code&gt;, &lt;code&gt;audit_ip_address&lt;/code&gt;, etc. devuelven datos fijos en el código, no consultan ningún sistema real.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Un solo LLM local:&lt;/strong&gt; no se probó el comportamiento con otros modelos para comparar consistencia de routing/tool-calling entre modelos de distinto tamaño.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluación:&lt;/strong&gt; solo se cubrieron Output y Trajectory de forma determinista; no se probó chaos testing, LLM-as-judge, ni simulación multi-turno (mirar la documentación oficial). &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pensando en este mismo Multiagente productivo 🤯
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Conectar tools a APIs/sistemas reales:&lt;/strong&gt; reemplazar los mocks por llamadas a una API de facturación real, un endpoint de status de infraestructura (ej. AWS CloudWatch, Datadog), y un servicio real de gestión de identidad (ej. IAM, Okta) para revocación de tokens. Varios de estos ya existen como servidores MCP mantenidos por la comunidad/AWS (conectarlos vía &lt;code&gt;MCPClient&lt;/code&gt; sería más rápido que escribir cada tool a mano).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Persistencia real de sesión:&lt;/strong&gt; en este ejemplo cada ticket es stateless entre ejecuciones de &lt;code&gt;main.py&lt;/code&gt;; sumar un &lt;code&gt;SessionManager&lt;/code&gt;(&lt;code&gt;FileSessionManager&lt;/code&gt;o &lt;code&gt;DynamoDBSessionManager&lt;/code&gt;) para simular conversaciones multi-turno reales de un mismo usuario.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Comparar modelos:&lt;/strong&gt; correr el mismo test suite contra un modelo de Bedrock o la API de Anthropic, y comparar tasa de errores de routing/tool-calling contra el modelo local, cuantificar el trade-off costo/latencia vs. confiabilidad.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://strandsagents.com/docs/user-guide/evals-sdk/chaos_testing/" rel="noopener noreferrer"&gt;Chaos testing&lt;/a&gt;:&lt;/strong&gt; simular caídas de las APIs reales (una vez conectadas) con &lt;code&gt;ChaosPlugin&lt;/code&gt;, para validar que el agente degrada con gracia en vez de alucinar una respuesta. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⭐ &lt;strong&gt;Migrar a &lt;a href="https://strandsagents.com/docs/user-guide/deploy/deploy_to_bedrock_agentcore/" rel="noopener noreferrer"&gt;Bedrock AgentCore&lt;/a&gt;&lt;/strong&gt;: envolver el orquestador con &lt;code&gt;BedrockAgentCoreApp&lt;/code&gt;para exponerlo vía el runtime administrado de AWS, adicionando &lt;code&gt;AgentCoreMemorySessionManager&lt;/code&gt;para memoria semántica de usuario (facts/preferences) en vez de solo &lt;code&gt;SlidingWindowConversationManager&lt;/code&gt; &lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cursos Fundamentos&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://skillbuilder.aws/learn/CFBFZNS11J/building-ai-agent-harnesses-with-strands-agents/D6YG8S6N7M" rel="noopener noreferrer"&gt;Building AI Agent Harnesses with Strands Agents&lt;/a&gt;&lt;br&gt;
&lt;a href="https://builder.aws.com/content/3GYPrAplMhl2IAl7jJW0yeKzXrR/building-ai-agent-harnesses-with-strands-agents-a-free-14-video-course/" rel="noopener noreferrer"&gt;Building AI Agent Harnesses – Video Course&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Documentación Strands Agents&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/concepts/multi-agent/agents-as-tools/" rel="noopener noreferrer"&gt;Agents as Tools&lt;/a&gt;&lt;br&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/evals-sdk/evaluators/trajectory_evaluator/" rel="noopener noreferrer"&gt;Trajectory Evaluator&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Artículos de Builders y AWS Developer Advocate&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://builder.aws.com/content/3AUxm8HafsZjZiOb3qrPDIH2MlD/aws-strands-agents-simplifica-la-creacion-de-agentes-de-ia" rel="noopener noreferrer"&gt;AWS Strands Agents: simplifica la creación de agentes de IA&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dev.to/aws-espanol/detener-alucinaciones-de-agentes-de-ia-validar-antes-de-que-el-agente-escriba-en-memoria-2ad3/"&gt;Detener alucinaciones de agentes de IA&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>strandsagents</category>
      <category>python</category>
    </item>
    <item>
      <title>Apuntes para la certificación AWS CloudOps Engineer Associate (SOA-C03) - Parte 2</title>
      <dc:creator>Laura Bolaños</dc:creator>
      <pubDate>Thu, 04 Jun 2026 13:41:05 +0000</pubDate>
      <link>https://dev.to/aws-builders/apuntes-para-la-certificacion-aws-cloudops-engineer-associate-soa-c03-parte-2-2e04</link>
      <guid>https://dev.to/aws-builders/apuntes-para-la-certificacion-aws-cloudops-engineer-associate-soa-c03-parte-2-2e04</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Segunda parte de las notas de examen. La idea es que identifiques los principales temas para el examen de certificación y conocer algunos tips de estudio para alcanzar con éxito tu meta 🎓&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;⬅&lt;/strong&gt; &lt;a href="https://dev.to/reinalau/apuntes-para-la-certificacion-aws-cloudops-engineer-associate-soa-c03-parte-1-2dji"&gt;&lt;strong&gt;&lt;em&gt;Apuntes para la certificación AWS CloudOps Engineer Associate (SOA-C03) - Parte 1&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Temas que son claves conocer para el examen - Segunda Parte&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  AWS Config
&lt;/h3&gt;

&lt;p&gt;Es prioritario revisar todas sus casos de uso y su integración con acciones de remediación con SSM y sus runbook. La remediación puede ser manual o automática.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;La integración importante es con EventBridge para realizar una acción. Algo a saber es que los avisos a un equipo de soporte se hacen a través de SNS.
&lt;/li&gt;
&lt;li&gt;Data Aggregation for AWS Config (Multi-Account - Multi-Region) ➡  recopila datos de configuración y cumplimiento de varias cuentas y varias regiones de AWS. Una organización en AWS Organizations y todas las cuentas de esa organización que tengan AWS Config habilitado.
&lt;/li&gt;
&lt;li&gt;Reglas de configuración comunes en AWS Config:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Qué chequea&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cloudtrail-enabled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CloudTrail habilitado en la cuenta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mfa-enabled-for-iam-console-access&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Los usuarios de IAM con acceso a la consola tienen MFA habilitado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;s3-bucket-public-read-prohibited&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Los buckets de S3 no permiten acceso de lectura pública&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;encrypted-volumes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Los volúmenes de EBS están encriptados&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rds-storage-encrypted&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Las instancias RDS tienen encriptación del storage habilitada&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;iam-password-policy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;La política de contraseñas cumple con los requisitos especificados&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;restricted-ssh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Los Security Groups no permiten acceso SSH sin restricciones (puerto 22)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;root-account-mfa-enabled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;La cuenta root tiene MFA habilitado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vpc-flow-logs-enabled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;VPC Flow Logs está activado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cloud-trail-encryption-enabled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Los logs de CloudTrail están encriptados con KMS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;s3-bucket-ssl-requests-only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;S3 solo acepta requests HTTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ec2-instances-in-vpc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Las instancias EC2 están dentro de una VPC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;access-keys-rotated&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Las access keys de IAM se rotan dentro de N días&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Control Tower
&lt;/h3&gt;

&lt;p&gt;Relacionado con casos de uso de Organization ➡ Proporciona una landing zone para múltiples cuentas. Automatiza la configuración de AWS Organizations, la venta de cuentas, las medidas de seguridad (controles preventivos y de detección) y un registro de auditoría centralizado.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Hub - WAF - Detective - Inspector -  GuardDuty
&lt;/h3&gt;

&lt;p&gt;Es útil realizar cuadro comparativo con los servicios Detective, Inspector y GuardDuty para visualizar sus diferencias y en qué casos de uso son más adecuados. La mayoría se utilizan para controles de detección.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security Hub:&lt;/strong&gt; gestiona la postura o posición de seguridad de una Organización. Automatiza las comprobaciones de mejores prácticas y agrega alertas de Seguridad.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WAF:&lt;/strong&gt; los casos de uso referidos son sencillos de identificar, detección de ataques sql injection - bloqueo de ip de países-rate limiting por IP, etc. Se gestiona mediante &lt;strong&gt;Web ACLs&lt;/strong&gt; con reglas propias o &lt;strong&gt;AWS Managed Rules&lt;/strong&gt; (reglas preconfiguradas por AWS)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspector:&lt;/strong&gt; Evalúa automáticamente vulnerabilidades en EC2, ECR y funciones Lambda ➡ prevención y detección proactiva.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detective:&lt;/strong&gt; simplifica el proceso de &lt;strong&gt;analizar, investigar e identificar la causa raíz&lt;/strong&gt; de los hallazgos de seguridad o actividades sospechosas en las cuentas de AWS. Recopila registros de CloudTrail (frase clave es log de APIs), VPC Flows, GuardDuty, entre otros  ➡ Actúa en el post-incidente.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GuardDuty:&lt;/strong&gt; Detección de amenazas continua mediante análisis de &lt;strong&gt;CloudTrail, VPC Flow Logs y DNS logs&lt;/strong&gt;. Usa ML para identificar comportamiento anómalo (ej: instancia EC2 haciendo crypto mining, credenciales filtradas usadas desde IP sospechosa). Es el &lt;strong&gt;detector en tiempo real,&lt;/strong&gt; genera findings que pueden disparar acciones en EventBridge → Lambda/SNS.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conceptos básicos de Networking
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NACL&lt;/strong&gt;: Actúan a nivel de subnet y no tienen estado. Hay que agregar reglas de entrada y de salida. Las reglas se evalúan de manera ascendente. Es el primer mecanismo de defensa a nivel subred, útil para &lt;strong&gt;bloquear IPs específicas.&lt;/strong&gt; Ejemplo de resolución de problema: una instancia EC2 (en una private subnet) no tiene salida a internet? ➡ Las reglas de entrada y salida deben permitir el tráfico (incluidos los puertos de retorno efímeros 1024–65535).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security groups&lt;/strong&gt;: Actúan a nivel de instancia o servicio. Tienen estado, todo lo que se permite entrar- se permite salir (salvo que indiques lo contrario en las reglas de salida).

&lt;ul&gt;
&lt;li&gt;Resolución de conexiones vía SSH o RDP a instancias EC2. Qué revisar? En principio las reglas de entrada del Security Group a los puertos correspondientes.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Nat Gateway:&lt;/strong&gt; es escalable mejor que &lt;em&gt;Instance Nat Gateway.&lt;/em&gt; Asociada a una subnet pública. Solo para IPV4. Asegurar que la &lt;em&gt;Private subnet route table&lt;/em&gt; debe tener
&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Egress-Only Internet Gateway (EIGW):&lt;/strong&gt;  permite que los recursos en una subred privada se comuniquen con el exterior a través de &lt;strong&gt;IPv6&lt;/strong&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  VPC FlowLogs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;VPC &amp;gt; Your VPCs &amp;gt; Flow logs &amp;gt; Create Flow Logs (se necesita un rol para escribir en CloudWatch). Monitorea todo el trafico de la VPC determinada aparece en &lt;strong&gt;&lt;em&gt;CloudWatch &amp;gt; Logs &amp;gt; Log Management&lt;/em&gt;&lt;/strong&gt; .
&lt;/li&gt;
&lt;li&gt;Otros destinos de logs son S3 y Kinesis Data Firehose. Se puede realizar consultas sql mediante &lt;strong&gt;Athena&lt;/strong&gt; tomando como fuente S3.
&lt;/li&gt;
&lt;li&gt;Conocer el formato de los registros Default VPC Flow Logs y Custom VPC Flow Log Format. Cómo configurar y qué soportes permite resolver.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ejemplo cuando falta una regla en Nacl o Security Group para permitir una conexión:&lt;/p&gt;

&lt;p&gt;2 123456789012 eni-abc123 10.0.1.4 10.0.2.5 43234 443 6 12 1400 1624387215 1624387275 ACCEPT OK&lt;/p&gt;

&lt;p&gt;2 123456789012 eni-abc123 172.31.16.139 172.31.16.21 80 42333 6 24 2456 1624387215 1624387275 REJECT OK&lt;/p&gt;

&lt;h3&gt;
  
  
  Route 53
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Hosted zone ➡ pública y privada. Comprender el concepto de casos de uso: El cliente externo consulta la &lt;strong&gt;zona pública&lt;/strong&gt; de Route 53 y llega a la &lt;strong&gt;versión pública&lt;/strong&gt; de la app y el cliente interno dentro de la VPC consulta la &lt;strong&gt;zona privada&lt;/strong&gt; de Route 53 y llega a la &lt;strong&gt;versión interna&lt;/strong&gt; de la app ➡ ambos pueden tener el mismo nombre laub.com pero devuelven destinos diferentes de acuerdo de donde se lo consulte.
&lt;/li&gt;
&lt;li&gt;Es primordial conocer los tipos de registros y casos de usos:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tipo&lt;/th&gt;
&lt;th&gt;Qué contiene&lt;/th&gt;
&lt;th&gt;Uso&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;A&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;IPv4&lt;/td&gt;
&lt;td&gt;
&lt;a href="http://www.laub.com" rel="noopener noreferrer"&gt;www.laub.com&lt;/a&gt; → 52.10.1.100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AAAA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;IPv6&lt;/td&gt;
&lt;td&gt;
&lt;a href="http://www.laub.com" rel="noopener noreferrer"&gt;www.laub.com&lt;/a&gt; → 2001:db8::1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CNAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Otro hostname&lt;/td&gt;
&lt;td&gt;www → otro.dominio.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Alias&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Recurso AWS&lt;/td&gt;
&lt;td&gt;laub.com → mi-alb.us-east-1.elb.amazonaws.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name servers de la zona&lt;/td&gt;
&lt;td&gt;Delegación de subdominios&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SOA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Metadatos de la zona&lt;/td&gt;
&lt;td&gt;Se crea automático&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Servidor de correo&lt;/td&gt;
&lt;td&gt;laub.com → mail.empresa.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TXT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Texto libre&lt;/td&gt;
&lt;td&gt;Verificación de dominio, SPF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SRV&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Servicio + puerto&lt;/td&gt;
&lt;td&gt;Usado por algunos protocolos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PTR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DNS inverso&lt;/td&gt;
&lt;td&gt;IP → hostname&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Route 53 Resolver:&lt;/strong&gt; define cómo y hacia dónde se hacen las consultas DNS.

&lt;ul&gt;
&lt;li&gt;En esta feature se da la relación de entornos híbridos, DNS de on-premises resuelto por Route 53 (Outbound endpoint) o un DNS en AWS (en VPC) que lo resuelve on-premises (Inbound endpoint).
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Route 53 routing policies&lt;/strong&gt;, hay preguntas de examen sobre estas features relacionados con estrategias de deployment o failover:&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Política&lt;/th&gt;
&lt;th&gt;Descripción&lt;/th&gt;
&lt;th&gt;Health Checks&lt;/th&gt;
&lt;th&gt;Caso de Uso&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Simple&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Un único servidor. Sin lógica ni failover.&lt;/td&gt;
&lt;td&gt;No soporta&lt;/td&gt;
&lt;td&gt;Un solo recurso sin redundancia&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Weighted&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Distribución de tráfico por pesos.&lt;/td&gt;
&lt;td&gt;Soportado — si falla un destino su peso es ignorado&lt;/td&gt;
&lt;td&gt;Deployment canary, A/B testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dirige el tráfico al recurso con menor latencia.&lt;/td&gt;
&lt;td&gt;Soportado&lt;/td&gt;
&lt;td&gt;Usuarios globales que necesitan baja latencia&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Geolocation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Enruta según la ubicación geográfica del origen de la consulta DNS (continente, país o estado).&lt;/td&gt;
&lt;td&gt;Soportado&lt;/td&gt;
&lt;td&gt;Contenido regionalizado, cumplimiento normativo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Geoproximity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Enruta según ubicación geográfica de los recursos con valor de sesgo opcional. Requiere &lt;strong&gt;Route 53 Traffic Flow&lt;/strong&gt;.&lt;/td&gt;
&lt;td&gt;Soportado&lt;/td&gt;
&lt;td&gt;Balanceo geográfico con control fino de fronteras&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Failover&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Enruta a recurso principal; si falla va al secundario. Recursos en VPC privada requieren CloudWatch Alarm como intermediario.&lt;/td&gt;
&lt;td&gt;Requerido&lt;/td&gt;
&lt;td&gt;Alta disponibilidad activo-pasivo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IP-Based&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Enruta según la IP del remitente de la consulta DNS.&lt;/td&gt;
&lt;td&gt;Soportado&lt;/td&gt;
&lt;td&gt;Segmentación por ISP o rango de IPs corporativas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multivalue Answer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Devuelve hasta 8 registros saludables al azar.&lt;/td&gt;
&lt;td&gt;Soportado&lt;/td&gt;
&lt;td&gt;Distribución simple sin necesidad de ELB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross Account&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;La zona padre delega un subdominio creando registros NS que apuntan a la zona hija en otra cuenta.&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Gestión de DNS entre múltiples cuentas AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Query Logging:&lt;/strong&gt; Registra todas las consultas DNS está disponible para zonas públicas., con esto podemos resolver problemas.  Auditar consultas DNS de una zona privada ➡ no es Route 53 query logging, es &lt;em&gt;Route 53 Resolver Query Logs&lt;/em&gt; (servicio diferente).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tipos de Health checks:&lt;/strong&gt; Es un concepto básico a retener ➡Endpoint health check, Calculated health check y CloudWatch alarm health check.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cálculo de CIDR para un Subnet
&lt;/h3&gt;

&lt;p&gt;En una VPC no se puede superponer el bloque de direcciones cidr de sus subnet ➡ ejemplo encontrar el tamaño mínimo de la subred que tenga 30 direcciones IP disponibles.&lt;/p&gt;

&lt;p&gt;Total de direcciones IP disponibles = 2^(32-X) – 5 (Reservadas por AWS)&lt;/p&gt;

&lt;p&gt;Donde X es la notación CIDR.&lt;/p&gt;

&lt;p&gt;Para el bloque CIDR 192.168.1.0/&lt;strong&gt;26&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Total de direcciones IP disponibles = 2^(32-26) – 5 = 59 direcciones IP.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Global Accelerator
&lt;/h3&gt;

&lt;p&gt;Si enfrentas alguna pregunta que necesitas tiempos de milisegundos con direcciones IP anycast estáticas ➡ la respuesta seguramente es Global Accelerator.&lt;/p&gt;

&lt;p&gt;Frases claves para este servicio: aceleración  del tráfico TCP/UDP.&lt;/p&gt;

&lt;h3&gt;
  
  
  VPC Peering - Transit Gateway
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;VPC peering:&lt;/strong&gt;  Conocer las limitaciones de  ➡ hasta 10 interconexiones de VPC es potable. No hay transitividad en las conexiones. Es conveniente realizar laboratorio para entender cómo se configuran las Route Tables. Tener presente que se puede realizar conexiones entre diferentes regiones y cuentas.

&lt;ul&gt;
&lt;li&gt;Solucionar conexion de VPC Peering entre dos instancias de EC2 en dos VPC diferentes. Se configuró correctamente el VPC peering entre ambas. Una instancia A puede hacer ping a instancia B. Pero la instancia B no puede hacer ping a la instancia A ➡ Posiblemente para solucionar este problema es necesario actualizar el grupo de seguridad de la instancia A para permitir el tráfico ICMP entrante.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Transit Gateway:&lt;/strong&gt; actúa como una hub de redes eliminando la necesidad de complejas redes de interconexión. A diferencia de VPC peering tiene ruteo transitivo. Importante! conocer el concepto de adjuntar una VPC (Attach a VPC).

&lt;ul&gt;
&lt;li&gt;Soporta &lt;strong&gt;multicast&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Se puede usar con &lt;strong&gt;VPN y Direct Connect&lt;/strong&gt; como hub central, simplificando arquitecturas híbridas.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TGW Route Tables&lt;/strong&gt; permiten segmentar el tráfico entre VPCs conectadas.Ejemplo ➡ aislar producción de desarrollo.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cloudfront
&lt;/h3&gt;

&lt;p&gt;Comprender cómo configurar los diferentes &lt;strong&gt;orígenes.&lt;/strong&gt; El más sencillo y natural que conocemos, si has rendido alguna certificación, es S3. Pero vas a tener que preparar ELB y el caso especial de EC2:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Origin Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Clave para el examen&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Amazon S3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Contenido estático&lt;/td&gt;
&lt;td&gt;OAC reemplaza OAI (legacy). Sin OAC, el bucket puede quedar público&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Application Load Balancer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Contenido dinámico&lt;/td&gt;
&lt;td&gt;El ELB debe ser &lt;strong&gt;público&lt;/strong&gt;. CloudFront no puede llegar a un ALB interno. Usá &lt;strong&gt;security group del ALB restringido a los IPs de CloudFront&lt;/strong&gt; para que el tráfico solo entre por CloudFront&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EC2 instance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Servidores web custom&lt;/td&gt;
&lt;td&gt;La instancia debe ser &lt;strong&gt;pública&lt;/strong&gt; y con security group que permita IPs de CloudFront&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Gateway&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;REST o HTTP APIs&lt;/td&gt;
&lt;td&gt;Podés deshabilitar el endpoint público de APIGW y forzar acceso solo vía CloudFront&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Custom HTTP origin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Servidores on-premise u otros&lt;/td&gt;
&lt;td&gt;Requiere HTTPS con certificado válido si usás HTTPS entre CloudFront y el origen&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Qué pasa cuando hay pocos cache hit (&lt;strong&gt;Cache Hit bajo&lt;/strong&gt;)?

&lt;ul&gt;
&lt;li&gt;Revisar el &lt;strong&gt;TTL&lt;/strong&gt; , si es muy bajo, los objetos expiran rápido y van al origen frecuentemente.
&lt;/li&gt;
&lt;li&gt;Revisar la &lt;strong&gt;Cache Key&lt;/strong&gt; , si incluye headers, cookies o query strings innecesarios, fragmenta el caché y baja el hit rate. La solución es usar &lt;strong&gt;Cache Policies&lt;/strong&gt; para controlar qué forma parte de la cache key.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Qué hacer cuando lo cacheado quedó antiguo, cómo resolver inmediatamente sin esperar que expire el TTL?

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Invalidación de caché&lt;/strong&gt; (&lt;code&gt;CreateInvalidation&lt;/code&gt;) hace que CloudFront descarte el objeto antes del TTL.
&lt;/li&gt;
&lt;li&gt;Alternativa más eficiente para el examen: &lt;strong&gt;versionado de archivos&lt;/strong&gt; en el nombre (&lt;code&gt;app.v2.js&lt;/code&gt;),  evita invalidaciones y es la práctica recomendada por AWS.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Behavior (comportamiento): analizar las variantes. Es útil realizar laboratorios.

&lt;ul&gt;
&lt;li&gt;Permiten definir reglas distintas por &lt;strong&gt;path pattern&lt;/strong&gt; (&lt;code&gt;/api/*&lt;/code&gt;, &lt;code&gt;/images/*&lt;/code&gt;, &lt;code&gt;/static/*&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;Cada behavior puede tener su propio origen, TTL, política de caché y restricciones de acceso.
&lt;/li&gt;
&lt;li&gt;Caso de uso común: &lt;code&gt;/api/*&lt;/code&gt; apunta a ALB (dinámico, sin caché) y &lt;code&gt;/*&lt;/code&gt; apunta a S3 (estático, con caché larga).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  VPC Endpoints
&lt;/h3&gt;

&lt;p&gt;Es clave conocer los tipos de punto final para aplicar seguridad a la conexión de servicios aws (sin pasar por internet).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gateway Endpoint :&lt;/strong&gt; Es sin costo, solo para conectar con S3, DynamoDB. Importante para sortear preguntas, solo tiene utilidad dentro de una VPC, no tiene la capacidad de acceso externo. También admite políticas de punto final para controlar el acceso.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interface Endpoint (PrivateLink):&lt;/strong&gt; Tiene costo. Crea una ENI con IP privada en tu VPC para conectarse a servicios de AWS (y servicios de terceros). Permite el acceso desde on-premise si existe conectividad previa (VPN/Direct Connect). Admite políticas de punto final para controlar qué entidades principales pueden usarlo.

&lt;ul&gt;
&lt;li&gt;Al crearlo AWS genera DNS privados para el servicio, si &lt;strong&gt;Enable Private DNS&lt;/strong&gt; está activado, las llamadas al endpoint público se resuelven automáticamente a la ENI privada. Resolución de problemas: si el tráfico sigue saliendo a internet a pesar de tener un Interface Endpoint → verificar que &lt;strong&gt;Enable Private DNS&lt;/strong&gt; esté activado.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;⬅&lt;/strong&gt; &lt;a href="https://dev.to/reinalau/apuntes-para-la-certificacion-aws-cloudops-engineer-associate-soa-c03-parte-1-2dji"&gt;&lt;strong&gt;&lt;em&gt;Apuntes para la certificación AWS CloudOps Engineer Associate (SOA-C03) - Parte 1&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sysops</category>
      <category>aws</category>
      <category>certification</category>
    </item>
    <item>
      <title>Apuntes para la certificación AWS CloudOps Engineer Associate (SOA-C03) - Parte 1</title>
      <dc:creator>Laura Bolaños</dc:creator>
      <pubDate>Thu, 04 Jun 2026 03:00:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/apuntes-para-la-certificacion-aws-cloudops-engineer-associate-soa-c03-parte-1-2dji</link>
      <guid>https://dev.to/aws-builders/apuntes-para-la-certificacion-aws-cloudops-engineer-associate-soa-c03-parte-1-2dji</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Primera parte de los apuntes del examen. La idea es que identifiques los principales temas para el examen de certificación y conocer algunos tips de estudio para alcanzar con éxito tu meta 🎓&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Características generales&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;La complejidad es de &lt;strong&gt;nivel medio&lt;/strong&gt;. A mi criterio lo más complejo son las diferentes formas de soporte vía SSM.&lt;/p&gt;

&lt;p&gt;Consta de &lt;a href="https://docs.aws.amazon.com/es_es/aws-certification/latest/sysops-administrator-associate-03/sysops-administrator-associate-03.html" rel="noopener noreferrer"&gt;5 dominios&lt;/a&gt; y toda la información detallada la podemos encontrar &lt;a href="https://aws.amazon.com/certification/certified-cloudops-engineer-associate/" rel="noopener noreferrer"&gt;aquí&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Solo se puede rendir en inglés, con lo cual es prioritario estudiar con apuntes, entrenamiento de preguntas y laboratorios en este idioma. Se aprueba con 720 puntos.&lt;/p&gt;

&lt;p&gt;Es una certificación que te invita a conocer en profundidad las arquitecturas mas tradicionales de AWS , la resolución de problemas en producción y el mantenimiento evolutivo al momento de actualizar infraestructura para la continuidad del negocio.&lt;/p&gt;

&lt;p&gt;⭐ Se superponen varios temas con Solutions Architect Associate y Developer Associate. Si ya rendiste alguna, te va a resultar familiar un % del contenido.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Método de estudio&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Estudiar para una certificación no tiene fórmulas mágicas, se necesita apartar tiempo de nuestro dia y constancia. Tener en claro que nos permite profundizar en conceptos o prácticas que tal vez no vemos en el trabajo diario...esa será nuestras motivación.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tu punto de partida debe ser la &lt;a href="https://docs.aws.amazon.com/es_es/aws-certification/latest/sysops-administrator-associate-03/sysops-administrator-associate-03.html" rel="noopener noreferrer"&gt;guia de examen&lt;/a&gt; siempre.
&lt;/li&gt;
&lt;li&gt;Conviene hacer la &lt;a href="https://skillbuilder.aws/learning-plan/S427S3T7B5/exam-prep-plan-aws-certified-cloudops-engineer--associate-soac03--english/" rel="noopener noreferrer"&gt;preparación de examen de SkillBuilder&lt;/a&gt;, te da un paneo general de temas y asi podes identificar las brechas de conocimientos en las que debes profundizar.
&lt;/li&gt;
&lt;li&gt;Mi forma favorita de estudiar es hacer una especie de RAG con &lt;em&gt;NotebookLM&lt;/em&gt; con la documentación oficial de AWS pertinente (aws permite  bajar los pdf).
&lt;/li&gt;
&lt;li&gt;La novedad es que hay mcp de AWS disponibles referidos a la documentación. Por ejemplo, utilizando los temas de la guia de examen como contexto, se puede generar apuntes. Utilice &lt;a href="https://awslabs.github.io/mcp/servers/aws-documentation-mcp-server" rel="noopener noreferrer"&gt;este mcp&lt;/a&gt; , pero tambien hay uno que no he probado que es mucho más amplio y contiene temas de arquitectura &lt;a href="https://awslabs.github.io/mcp/servers/aws-knowledge-mcp-server" rel="noopener noreferrer"&gt;aquí&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;⭐ &lt;a href="https://github.com/reinalau/AWS-Certified/tree/main/SOA-C03" rel="noopener noreferrer"&gt;Resumen de los Dominios&lt;/a&gt; de examen generado por kiro con el mcp de la documentación de AWS.&lt;/p&gt;

&lt;p&gt;⭐ Utilicé &lt;em&gt;Tutorials Dojo&lt;/em&gt; como banco de preguntas para practicar, pero mi percepción es que las preguntas en el examen eran más difíciles.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Arquitecturas importantes&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Te invito a pensar en sus variantes para lograr alta disponibilidad, escalabilidad y resiliencia. Los temas de networking son importantes en el examen: configuración de VPC, subnet,  tablas de ruteo, Security Groups entre otros, especialmente troubleshooting de conectividad.&lt;/p&gt;

&lt;h3&gt;
  
  
  High Available (Route53)
&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%2F6pxvkecvcbk261xjtr9x.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%2F6pxvkecvcbk261xjtr9x.jpg" alt="High Available (Route53)" width="800" height="674"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  High Available and Scalable Web App (Cloudfront - ALB)
&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%2F07tv409rfqmmofmwby85.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%2F07tv409rfqmmofmwby85.jpg" alt="High Available and Scalable Web App (Cloudfront - ALB)" width="800" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Worker Fleet - SQS
&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%2Fw929qpaixg5uymehebtd.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%2Fw929qpaixg5uymehebtd.jpg" alt="Worker Fleet and SQS" width="799" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  VPC Peering
&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%2Foytcdcx1ziccwewrus2o.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%2Foytcdcx1ziccwewrus2o.jpg" alt="VPC Peering" width="800" height="835"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Transit Gateway
&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%2F6yc5hz6wi6yylky0wd9n.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%2F6yc5hz6wi6yylky0wd9n.jpg" alt="Transit Gateway" width="800" height="835"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Cloudfront - OAC
&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%2F3vpon37thqefrpuhu9f9.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%2F3vpon37thqefrpuhu9f9.jpg" alt="Cloudfront - OAC" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Config
&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%2Fyhwaqua5spp8bl15y6ld.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%2Fyhwaqua5spp8bl15y6ld.jpg" alt="Config" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Temas que son claves conocer para el examen&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Las preguntas de examen se centran en la resolución de problemas con menos carga operativa  y  la elección de arquitecturas más lógica.&lt;/p&gt;

&lt;h3&gt;
  
  
  CloudWatch
&lt;/h3&gt;

&lt;p&gt;Familiarizarse con las features de la consola de CloudWatch porque vas a enfrentarte a varias preguntas sobre la administración de logs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cómo visualizó los logs comunes? &lt;em&gt;**CloudWatch &amp;gt; Logs &amp;gt; Log Management&lt;/em&gt;&lt;em&gt;. Aquí se encuentran todos los "Logs Groups" como aws/lambda/..., aws/apigateway/...,ecs/..., ec2..&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Cómo creo un Log Group? Desde la CLI de AWS. Cómo inserto Log en CloudWatch? Desde la Api con Create
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch metrics&lt;/strong&gt;, tener en claro las diferentes tipos y circunstancias a utilizar. Cada métrica tiene un nombre, namespace y dimensiones (opcional).

&lt;ul&gt;
&lt;li&gt;Basic Monitoring (5 min, default).
&lt;/li&gt;
&lt;li&gt;Detailed Monitoring (1 min, se habilita manualmente)
&lt;/li&gt;
&lt;li&gt;High Resolution para métricas custom (hasta 1 segundo).
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Metric Filters&lt;/strong&gt; ➡ Definís un patrón de búsqueda sobre un Log Group y CloudWatch genera una métrica custom a partir de las coincidencias.

&lt;ul&gt;
&lt;li&gt;El caso de uso típico a resolver: detectar cantidad de errores 500 en logs de una aplicación. Metric Filter sobre el Log Group (métrica custom) → alarma de CloudWatch sobre esa métrica y acción de remediación automática.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;CloudWatch Agent&lt;/strong&gt; ➡ es uno de las features más importantes que nos va a permitir enviar Logs a CloudWatch desde las instancias de EC2, server onprem, EKS cluster (recopilar métricas y registros a nivel de nodo de los contenedores).

&lt;ul&gt;
&lt;li&gt;Comprender cómo se configura y se modifica el archivo de configuración del agente (sin reiniciar las instancias de EC2): &lt;strong&gt;amazon-cloudwatch-agent-ctl command with the fetch-config&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;CloudWatch Logs Insights:&lt;/strong&gt; sintaxis y comandos para el análisis de logs ➡ &lt;code&gt;fields&lt;/code&gt; , &lt;code&gt;display&lt;/code&gt; , &lt;code&gt;filter&lt;/code&gt; , &lt;code&gt;stats&lt;/code&gt; , &lt;code&gt;sort&lt;/code&gt; , &lt;code&gt;limit&lt;/code&gt; ...&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Escalabilidad y Elasticidad
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Administración de Fleet de EC2:  Estrategias para continuar procesamiento de trabajos minimizando las interrupciones de form costo-efectiva ➡ &lt;strong&gt;capacity-optimized&lt;/strong&gt; para minimizar interrupciones o &lt;strong&gt;price-capacity-optimized&lt;/strong&gt; para balance entre costo y disponibilidad. (Combinando instancias Spot y On-Demand con el parámetro  &lt;code&gt;OnDemandBaseCapacity&lt;/code&gt; se especifica cuántas instancias base On-Demand y el resto se cubre con Spot).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EC2 Auto Scaling Group:&lt;/strong&gt; puede aparecer pregunta puntual sobre administrar ASG, pero por lo general no aparece como concepto aislado sino asociado a un balanceador de carga (&lt;strong&gt;ALB/NLB&lt;/strong&gt;) y en arquitecturas multi-región con &lt;strong&gt;Route 53&lt;/strong&gt;.

&lt;ul&gt;
&lt;li&gt;Capacidad mínima, máxima y deseada.
&lt;/li&gt;
&lt;li&gt;Definición de Launch template, cómo aplicar una nueva configuración a todas las instancias del ASG sin &lt;em&gt;downtime&lt;/em&gt;? ➡ Cuando se actualiza un Launch Template, las instancias existentes &lt;strong&gt;no se reemplazan automáticamente,&lt;/strong&gt; siguen ejecutando la versión anterior. Las nuevas instancias que escalen usarán la versión nueva. Para forzar el reemplazo se usa &lt;strong&gt;Instance Refresh&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Distribución en AZ.
&lt;/li&gt;
&lt;li&gt;Health Checks de EC2 o si health checks desde un ELB  determina que las instancias que no están en buen estado se reemplazan automáticamente. &lt;strong&gt;Profundizar en este punto&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Es clave entender las siguientes políticas de escalamiento para responder sobre mejora de performance. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tipo de Política&lt;/th&gt;
&lt;th&gt;Cómo Funciona&lt;/th&gt;
&lt;th&gt;Caso de Uso&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Target Tracking&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mantiene una métrica en un valor objetivo (ej: CPU al 50%). AWS gestiona las alarmas de CloudWatch automáticamente.&lt;/td&gt;
&lt;td&gt;Aplicaciones web generales donde querés simplicidad sin configuración compleja.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Step Scaling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ajusta la capacidad en pasos según cuánto se desvía una métrica del umbral. Permite diferentes tamaños de paso según la magnitud.&lt;/td&gt;
&lt;td&gt;Workloads con picos abruptos donde necesitás agregar más instancias cuanto mayor es el desvío.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Simple Scaling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agrega o quita una cantidad fija de instancias cuando se dispara una alarma. Incluye período de cooldown antes de la siguiente acción.&lt;/td&gt;
&lt;td&gt;Legacy, no recomendada. Target Tracking la reemplaza en casi todos los casos.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scheduled Scaling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Escala la capacidad en un horario específico usando expresiones cron.&lt;/td&gt;
&lt;td&gt;Cargas predecibles: horario comercial, cierre de mes, campañas programadas.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Predictive Scaling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Usa ML para predecir la carga futura y ajusta la capacidad de forma proactiva.&lt;/td&gt;
&lt;td&gt;E-commerce con patrones cíclicos (ej: picos semanales o estacionales recurrentes).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LifeCycle Hooks:&lt;/strong&gt; En Auto Scaling Group para analizar errores de las instancias antes de que se finalicen para ser reemplazadas se puede pausar la finalización de la instancia. También, este mecanismo, al inicio permite acciones como instalación de software. La instancia permanece en estado Pendiente
&lt;/li&gt;
&lt;li&gt;Conocer el término &lt;strong&gt;Warmup,&lt;/strong&gt; define un tiempo de calentamiento de la instancia. Es el período posterior al lanzamiento de una nueva instancia antes de que sus métricas contribuyan a la métrica de escalado. Evita el escalado por falsas métricas hasta que la instancia se estabilice.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application AutoScaling (no confundir con ASG):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;ECS, cantidad de task.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda&lt;/strong&gt;. Entender la diferencia entre Provisioned Concurrency y Reserved Concurrency. La primera es la que permite "autoescalar" ➡ muy probable que te planteen una situación con Lambda para resolver temas de concurrencia.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aurora réplicas&lt;/strong&gt;. Permite liberar la instancia del cluster, teniendo réplicas de lectura (no de escrituras).
&lt;/li&gt;
&lt;li&gt;DynamoDB, puede crear una política de AutoScaling. Luego DynamoDB pública métricas de capacidad consumida CloudWatch ➡ La alarma de CloudWatch invoca a AutoScaling de aplicaciones para evaluar la política de escalado, emite una solicitud &lt;code&gt;UpdateTable&lt;/code&gt; para ajustar el rendimiento aprovisionado de la tabla.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  ELB - High availability
&lt;/h3&gt;

&lt;p&gt;Con el siguiente cuadro podés responder varios casos de uso:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Load Balancer&lt;/th&gt;
&lt;th&gt;Capa&lt;/th&gt;
&lt;th&gt;Protocolos&lt;/th&gt;
&lt;th&gt;Características Clave&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Application Load Balancer (ALB)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Layer 7 (HTTP/HTTPS)&lt;/td&gt;
&lt;td&gt;HTTP, HTTPS, gRPC, WebSocket&lt;/td&gt;
&lt;td&gt;Enrutamiento basado en contenido (path, host, headers, query strings), targets Lambda, integración con WAF, sticky sessions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Network Load Balancer (NLB)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Layer 4 (TCP/UDP)&lt;/td&gt;
&lt;td&gt;TCP, UDP, TLS&lt;/td&gt;
&lt;td&gt;Ultra-baja latencia, IP estática por AZ, soporte Elastic IP, preserva la IP de origen, maneja millones de requests/segundo.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gateway Load Balancer (GWLB)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Layer 3 (IP)&lt;/td&gt;
&lt;td&gt;IP&lt;/td&gt;
&lt;td&gt;Despliega, escala y gestiona appliances virtuales de terceros (firewalls, IDS/IPS). Usa protocolo GENEVE.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Classic Load Balancer (CLB)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Layer 4 y 7&lt;/td&gt;
&lt;td&gt;HTTP, HTTPS, TCP, SSL&lt;/td&gt;
&lt;td&gt;Legacy; no recomendado para nuevos despliegues.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Dónde encontrar información detallada como la &lt;strong&gt;dirección IP del cliente, latencias, rutas de solicitud y respuestas del servidor? ➡&lt;/strong&gt; La respuesta es habilitar los &lt;strong&gt;ELB Access Logs&lt;/strong&gt; y almacenarlos en un bucket de S3.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Problemas de conectividad.&lt;/strong&gt; Una web inalcanzable con grupos de seguridad correctos ➡ La solución es &lt;strong&gt;permitir puertos efímeros (1024-65535)&lt;/strong&gt; en la &lt;strong&gt;Network ACL (NACL)&lt;/strong&gt;, porque ELB utiliza este rango para comunicarse con los clientes.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sticky Sessions:&lt;/strong&gt; Caso de uso donde una sola instancia EC2 tiene un uso de CPU del 95% mientras las otras están al 10% ➡ ajustar la expiración de las &lt;strong&gt;cookies de la aplicación&lt;/strong&gt; para rebalancear la carga.
&lt;/li&gt;
&lt;li&gt;Conocer &lt;strong&gt;Cross-Zone Load Balancing p&lt;/strong&gt;ara asegurar una distribución uniforme del tráfico en todas las zonas de disponibilidad habilitadas.
&lt;/li&gt;
&lt;li&gt;Integración de ALB con WAF pero no así con NLB.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Placement Group&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cluster:&lt;/strong&gt; Agrupa las instancias muy cerca unas de otras en &lt;strong&gt;una única zona de disponibilidad&lt;/strong&gt;. Proporciona una red de baja latencia y alto ancho de banda (hasta 100 Gbps con EFA). &lt;strong&gt;Casos de uso ➡ HPC, Machine Learning,&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Partition:&lt;/strong&gt; Divide las instancias en particiones lógicas, cada una en racks separados con alimentación y red independientes. Hasta 7 particiones por zona de disponibilidad. Caso de uso ➡ base de datos Distribuidas: Cassandra,HDFS, Kafka&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spread:&lt;/strong&gt; Cada instancia se ubica en un hardware distinto (racks separados). Máximo 7 instancias por zona de disponibilidad por grupo. Caso de Uso ➡ grupos de instancias  que deben aislarse entre sí en hardware separado.&lt;/p&gt;

&lt;h3&gt;
  
  
  Estrategias de Disaster Recovery
&lt;/h3&gt;

&lt;p&gt;Conceptos principales RTO (Recovery Time Objective), RPO (Recovery Point Objective) ➡ mientras mas pequeños mas costosos. &lt;a href="https://docs.aws.amazon.com/whitepapers/latest/disaster-recovery-workloads-on-aws/disaster-recovery-options-in-the-cloud.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Este enlace&lt;/strong&gt;&lt;/a&gt; tiene información detallada que vale la pena revisar de las siguientes estrategias:&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;RTO&lt;/th&gt;
&lt;th&gt;RPO&lt;/th&gt;
&lt;th&gt;Costo&lt;/th&gt;
&lt;th&gt;Caso de Uso&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Backup and Restore&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Horas&lt;/td&gt;
&lt;td&gt;Horas&lt;/td&gt;
&lt;td&gt;$&lt;/td&gt;
&lt;td&gt;Dev/test, no-crítico&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pilot Light&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Varios minutos&lt;/td&gt;
&lt;td&gt;Minutos&lt;/td&gt;
&lt;td&gt;$$&lt;/td&gt;
&lt;td&gt;Apps internas, criticidad moderada&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Warm Standby&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Minutos&lt;/td&gt;
&lt;td&gt;Segundos–minutos&lt;/td&gt;
&lt;td&gt;$$$&lt;/td&gt;
&lt;td&gt;Apps de negocio críticas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multi-Site Active/Active&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Segundos&lt;/td&gt;
&lt;td&gt;Segundos&lt;/td&gt;
&lt;td&gt;$$$$&lt;/td&gt;
&lt;td&gt;Crítico, zero-downtime&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Clases de S3 -  Versiones - Eventos
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Hacer un pequeño cuadro comparativo de versiones de S3 y que casos de uso se utiliza para cada uno. Ejemplo ➡ cuando los patrones de acceso son &lt;strong&gt;impredecibles o cambiantes&lt;/strong&gt; y no querés gestionar manualmente las políticas de ciclo de vida: S3 Intelligent-Tiering
&lt;/li&gt;
&lt;li&gt;S3 Object Lock ➡ cuando se usa Governance mode o Compliance mode.
&lt;/li&gt;
&lt;li&gt;Activación de versionado. Relación con CRR (&lt;em&gt;Cross Region Replication&lt;/em&gt;) y SRR (replicación en misma región pero diferentes cuentas).

&lt;ul&gt;
&lt;li&gt;Resolución de problemas de &lt;strong&gt;latencia o errores&lt;/strong&gt; cuando el S3 es origen de CloudFront: Si hay millones de versiones y las operaciones de listado o ciclo de vida se vuelven pesadas ➡ Configurar &lt;strong&gt;S3 Lifecycle Policies&lt;/strong&gt; para expirar versiones antiguas automáticamente, usando &lt;code&gt;NoncurrentVersionExpiration&lt;/code&gt; para eliminar versiones no actuales después de cierta cantidad de días.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;em&gt;Event notifications&lt;/em&gt; para creación, eliminación, update de objetos. Integración con Lambda, SNS y Eventbridge. &lt;strong&gt;EventBridge&lt;/strong&gt; permite filtrar eventos por prefijo, sufijo, tamaño, etc. y enrutarlos a más de 20 destinos distintos.
&lt;/li&gt;

&lt;li&gt;Conocer las políticas de bucket para responder preguntas sobre que habilita o deniega una policy. Su alcance. Entender que un &lt;strong&gt;Deny explícito siempre gana&lt;/strong&gt;, incluso si hay un Allow en otra política.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  ElastiCache
&lt;/h3&gt;

&lt;p&gt;Conocer las llamadas básicas de la API es básico ➡ saber como crear un cluster &lt;em&gt;CreateCacheCluster&lt;/em&gt;, parámetros importantes como &lt;em&gt;CacheNodeType&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ElastiCache - Memcached&lt;/strong&gt;:  caso de uso que recibe alertas relacionadas con un alto uso de CPU en un clúster de Amazon ElastiCache Memcached, cómo solucionarlo? ➡ Agregar nodos adicionales proporcionará mayor capacidad al clúster o bien según la versión de Memcached se puede escalar verticalmente cambiando el tipo de nodo sin necesidad de crear un nuevo cluster. &lt;strong&gt;No tiene replicación, ni persistencia.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ElastiCache - Redis:&lt;/strong&gt; El concepto más importante de esta opción es permitir &lt;strong&gt;Alta Disponibilidad&lt;/strong&gt; a través de sus réplicas: &lt;strong&gt;Multi-AZ con failover automático&lt;/strong&gt;. División en sharding. Compatible con PUB/SUB. Adecuado para conservar sesiones. El soporte más común a resolver por performance es cambiar el tipo de nodo.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  SSM (Simple Service Manager)
&lt;/h3&gt;

&lt;p&gt;Si no trabajas todos los días con este servicio, es necesario que no solo estudies teoría en cuanto a sus features, sino que también hagas laboratorios.&lt;/p&gt;

&lt;p&gt;Las preguntas que aparecen en el examen son muy variadas en cuanto a la administración de EC2, aplicación de parches, auto aprobaciones de releases y algunas que involucran webhooks con github.&lt;/p&gt;

&lt;p&gt;Recomiendo mirar la sintaxis de los documentos en JSON o Yaml que definen acciones y flujos de trabajo automatizados ➡ hay preguntas sencillas sobre este punto en el examen.&lt;/p&gt;

&lt;p&gt;Enumero algunas situaciones o casos de uso:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un concepto sencillo útil: todas las ejecuciones se puede trackear su estatus y log de salidas desde la consola de &lt;em&gt;System Manager&lt;/em&gt;.
&lt;/li&gt;
&lt;li&gt;Se quiere automatizar la creación de Amazon Machine Images (AMIs) personalizadas. Se detecta errores timeout sobre el pipeline de EC2 Image Builder ➡ El agente de AWS Systems Manager (agente SSM) en la instancia de compilación no se está ejecutando o no está accesible.
&lt;/li&gt;
&lt;li&gt;¿Cómo dividimos los parches entre servidores de diferentes cuentas/entornos y SO? ➡ La solución combina &lt;strong&gt;Patch Baselines&lt;/strong&gt; (definen qué parches aplicar, con aprobaciones automáticas y listas de rechazo, por SO) + &lt;strong&gt;Patch Groups&lt;/strong&gt; (agrupan nodos mediante tags y los asocian a una Baseline específica).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Runbook&lt;/strong&gt;: Tipo específico de documento de Systems Manager (SSM) que define las acciones exactas que realiza el servicio Automation. Define yaml o json para orquestar un workflow de ejecución.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Por lo general, se usa para la remediación asociado con &lt;em&gt;AWS Config.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Usando AWS Organizations se necesita automatizar el proceso de stopping de instancias EC2 no productivas fuera del horario de negocio de todos las cuentas miembro ➡ Crear roles de IAM en cada cuenta con los permisos adecuados, utilizar Systems Manager Automation con una cuenta de administrador delegado para ejecutar un runbook que dirija los recursos de toda la organización según los tags.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Patch Policies:&lt;/strong&gt; se utiliza para Organizations. Tener en claro que centraliza el control para definir patching schedules y baselines para múltiples cuentas y regiones
&lt;/li&gt;
&lt;li&gt;Revisar los documentos de ejecuciones más importantes en SSM.
&lt;/li&gt;
&lt;li&gt;Integraciones por eventos externos, tener claro cómo trabaja en los diferentes casos webhooks (con avisos de software de terceros). Que flujos podemos tener:

&lt;ul&gt;
&lt;li&gt;Webhook → API GW → Lambda → SSM
&lt;/li&gt;
&lt;li&gt;GitHub Actions → SSM (puedes filtrar exactamente por eventos RELEASED o PRERELEASED).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon EventBridge Partner Events → GitHub&lt;/strong&gt; como fuente nativa de eventos → SSM Automation o Lambda como destino.
&lt;/li&gt;
&lt;li&gt;EventBridge Scheduler → Lambda consulta GitHub API → escribe versión en SSM Parameter Store (como fuente de verdad para actualizaciones)
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;AWS RunRemoteScript: Documento SSM que permite ejecutar scripts alojados en S3 o GitHub directamente en los nodos, sin necesidad de copiarlos previamente.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Servicios y estrategias de Deployment
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Casos de uso de AWS Code Pipeline, y los otros servicios que involucra ➡ Code Commit o GitHUb, Code Build, Code Deploy o CloudFormation. Entender donde se integran los test y tener en cuenta que se puede agregar una &lt;strong&gt;aprobación&lt;/strong&gt; vía sns antes de realizar deploy.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CodePipeline&lt;/strong&gt; puede implementar CloudFormation StackSets, lo que permite implementaciones en múltiples cuentas y regiones como parte de una canalización de CI/CD.
&lt;/li&gt;
&lt;li&gt;Estrategias de deployment:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In-place (All-at-once):&lt;/strong&gt; Implementa en todas las instancias simultáneamente. Downtime Posible. Es Lento (redeploy versión antigua). Se usa en entornos de desarrollo, cuando el downtime es aceptable
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rolling:&lt;/strong&gt; Reemplazar instancias en lotes. Downtime Mínimo. Se usa cuando querés ahorrar costos sin aprovisionar nueva infraestructura.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rolling with additional batch:&lt;/strong&gt; Agrega nuevas instancias antes de eliminar las antiguas. Downtime mejor que el anterior. Se usa cuando necesitas mantener capacidad completa durante el deploy
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immutable:&lt;/strong&gt; Inicia instancias completamente nuevas; intercambia cuando estén en buen estado. Se usa en producción donde el rollback rápido es crítico.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blue/Green:&lt;/strong&gt; Ejecuta dos entornos idénticos; cambia el tráfico. Downtime mínimo.  Se usa cuando necesitás rollback instantáneo y podés asumir el costo de doble infraestructura.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canary:&lt;/strong&gt; Desvía primero un pequeño % del tráfico; lo va ampliando si todo está OK. Es rápido. Se usa para validar un release con un subconjunto de usuarios antes de rollout completo.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cloudformation
&lt;/h3&gt;

&lt;p&gt;Es un servicio fundamental en el examen. Conocer las secciones principales de la plantilla (JSON o YAmL) : Metadata, Parameters, Mappings (caso de uso de parametrización de Amazon Machine Images (AMIs) ), Conditions, Transform (SAM), Resources.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Una buena práctica para familiarizarse con la sintaxis de los templates es construir arquitecturas clásicas que utilicen EC2, ASG, ALB, RDS con Secret Manager, entre otras.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comandos CLI importantes:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;aws cloudformation create-stack&lt;/code&gt; ➡ crea un stack nuevo
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aws cloudformation deploy&lt;/code&gt; ➡ crea o actualiza un stack usando un template empaquetado (más usado en CI/CD)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aws cloudformation detect-drift&lt;/code&gt; ➡ inicia la detección de drift en un stack
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aws cloudformation describe-stack-drift-detection-status&lt;/code&gt; ➡ consulta el resultado de la detección
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aws cloudformation cancel-update-stack&lt;/code&gt; ➡ cancela una actualización en curso
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;StackSet&lt;/strong&gt; ➡ caso de uso importante de cuentas asociadas en Organizations. Gestiona de manera  centralizada para implementar cambios en las stack en varias cuentas. Se puede usar StackSets para crear, actualizar o eliminar stacks en varias cuentas y regiones de AWS con una sola operación.
&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Intrinsic functions:&lt;/strong&gt; comandos que se utilizan dentro de las plantillas para asignar valores a propiedades que no están disponibles hasta el momento de la implementación (en tiempo de ejecución) ➡ revisar implementaciones de las mas importantes: &lt;code&gt;Ref&lt;/code&gt; &lt;strong&gt;,&lt;/strong&gt; &lt;code&gt;Fn::GetAtt&lt;/code&gt;&lt;strong&gt;,&lt;/strong&gt; &lt;code&gt;Fn::ImportValue&lt;/code&gt;&lt;strong&gt;,&lt;/strong&gt; &lt;code&gt;Fn::FindInMap&lt;/code&gt;&lt;strong&gt;,&lt;/strong&gt; &lt;code&gt;Fn::ForEach&lt;/code&gt;&lt;strong&gt;,&lt;/strong&gt; &lt;code&gt;Fn::Transform ...&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;NestedStack:&lt;/strong&gt; &lt;code&gt;AWS::CloudFormation::Stack&lt;/code&gt; &lt;strong&gt;➡ mecanismo&lt;/strong&gt; para crear infraestructura &lt;strong&gt;modular, reutilizable y gestionable&lt;/strong&gt; mediante la integración de una pila dentro de otra. En lugar de crear una plantilla única y masiva que resulta difícil de mantener, se puede dividir la arquitectura en componentes lógicos (por ejemplo, VPC, base de datos, capa de aplicación) y referenciarlos como pilas separadas dentro de una plantilla "raíz" o "padre".
&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Mecanismos de protección en CloudFormation:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stack Policy:&lt;/strong&gt; documento JSON que restringe qué recursos pueden ser actualizados. Ideal para proteger RDS de producción de cambios accidentales.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Termination Protection:&lt;/strong&gt; protección a nivel stack contra eliminación accidental. Se hereda a los nested stacks.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DeletionPolicy:&lt;/strong&gt; &lt;code&gt;Retain&lt;/code&gt; conserva el recurso, &lt;code&gt;Snapshot&lt;/code&gt; genera snapshot antes de eliminar (soportado en RDS, EBS). La situación típica que se plantea, como conservar una RDS si se elimina el stack de cloudformation?
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UpdateReplacePolicy:&lt;/strong&gt; igual que DeletionPolicy pero aplica cuando un update requiere reemplazar el recurso físico.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drift Detection:&lt;/strong&gt; detecta recursos cuya configuración real difiere del template, útil para identificar cambios manuales que bypasearon el pipeline.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Profundizar en los Script Auxiliares más importantes, mecanismos para sincronizar la creación o actualización de recursos, para que el stack espere hasta que la configuración esté lista antes de continuar.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cfn-init&lt;/code&gt; → lee y ejecuta la sección &lt;code&gt;AWS::CloudFormation::Init&lt;/code&gt; del template (instala paquetes, archivos, servicios). Se ejecuta durante el User Data.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cfn-signal&lt;/code&gt; ➡ Generalmente se implementa al final de un script de &lt;em&gt;User Data&lt;/em&gt;. Un patrón común es ejecutarlo después de &lt;code&gt;cfn-init&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Tener en cuenta los atributos que requieren señales como &lt;strong&gt;CreationPolicy&lt;/strong&gt; (puede estar asociados con &lt;code&gt;AWS::EC2::Instance&lt;/code&gt; o &lt;code&gt;AWS::AutoScaling::AutoScalingGroup&lt;/code&gt; ) y los tipos de &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-attribute-updatepolicy.html" rel="noopener noreferrer"&gt;&lt;strong&gt;UpdatePolicy&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;(con WaitOnResourceSignals).&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;CloudFormation Traffic Shifting&lt;/strong&gt;  ➡ se implementa definiendo &lt;code&gt;DeploymentPreference&lt;/code&gt; en el template. CodeDeploy ejecuta el shifting según la estrategia elegida (Canary, Linear, AllAtOnce).&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Storage
&lt;/h3&gt;

&lt;p&gt;Evaluar y seleccionar que tipo de Storage es adecuado a un caso de uso. Si estudiaste para la certificación de SAA son preguntas similares.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Storage en entornos híbridos ➡ &lt;strong&gt;Storage Gateway&lt;/strong&gt; y todas sus variantes

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S3 File Gateway:&lt;/strong&gt; Acceso NFS/SMB a objetos en S3. Los archivos se almacenan como objetos nativos. Caso de uso: migración de archivos on-premise a S3.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Volume Gateway:&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Cached mode:&lt;/em&gt; datos principales en S3, caché local de los más accedidos.
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Stored mode:&lt;/em&gt; datos principales on-premise, backup asíncrono a S3 como EBS snapshots.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tape Gateway:&lt;/strong&gt; Reemplaza cintas físicas. Importante es que se expande automáticamente, esta última característica puede ser un caso de uso que aparezca en el examen.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FSx File Gateway:&lt;/strong&gt; Caché local de FSx for Windows File Server para acceso de baja latencia desde on-premise.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;FSx for Windows FileServer y su otras variantes, cuando usarlo?

&lt;ul&gt;
&lt;li&gt;Es MultiAZ, es una feature importante.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shadow Copies:&lt;/strong&gt; los usuarios finales puedan ver y restaurar por sí mismos versiones anteriores de archivos o carpetas individuales sin depender de otro mecanismo.
&lt;/li&gt;
&lt;li&gt;Usado con Windows SMB, Active Directory.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;FSx for Lustre ➡ HPC, ML, high throughput
&lt;/li&gt;

&lt;li&gt;EFS (Elastic File System) comprender el caso de uso que puede compartir storage entre varias EC2, Linux NFS.

&lt;ul&gt;
&lt;li&gt;Multi-AZ
&lt;/li&gt;
&lt;li&gt;Performance modes: general Purpose y MaxIO
&lt;/li&gt;
&lt;li&gt;Throughput Modes: Elastic (recomendado), Provisioned, Bursting.
&lt;/li&gt;
&lt;li&gt;Conocer qué EFS tiene Storage Class y  Lifecycle Policies ➡ aunque suele no aparecer en el examen.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;EBS&lt;/strong&gt; ➡ siempre en el contexto de EC2 y la elección del disco para optimizar las cargas de trabajo: &lt;strong&gt;gp3&lt;/strong&gt; sobre gp2 sin incrementar los costos, &lt;strong&gt;gp3&lt;/strong&gt; permite configurar IOPS y throughput &lt;strong&gt;independientemente&lt;/strong&gt; del tamaño del volumen. Para transacciones, persistencia y durabilidad es &lt;strong&gt;io2&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Data Lifecycle Manager (DLM)&lt;/strong&gt;: Automatiza la creación, retención y eliminación de instantáneas de EBS y AMI respaldadas por EBS mediante políticas de ciclo de vida.
&lt;/li&gt;
&lt;li&gt;EBS Snapshot: Una copia de seguridad puntual de un volumen EBS almacenado en S3 (gestionado por AWS). Los snapshot son incrementales.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  RDS y Aurora
&lt;/h3&gt;

&lt;p&gt;Los accesos se deben guardar en Secrets Manager y luego las aplicaciones acceden a eso. Característica importante &lt;em&gt;Cross-Region backup replication.&lt;/em&gt; Tanto RDS como Aurora tienen PITR (Point-in-Time Restore).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RDS Multi-AZ&lt;/strong&gt; ➡ standby síncrono en otra AZ, failover automático pero más lento &lt;em&gt;que Aurora.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Se puede asociar con el servicio &lt;strong&gt;RDS Proxy&lt;/strong&gt; para el manejo de conexiones.
&lt;/li&gt;
&lt;li&gt;Aurora ➡  Almacenamiento distribuido que se replica automáticamente en 3 AZs, hasta 15 réplicas de lectura, failover automático en segundos.
&lt;/li&gt;
&lt;li&gt;Aurora Serverless v2 ➡ la característica importante es que escala a "cero" y no genera costo.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  DynamoDB
&lt;/h3&gt;

&lt;p&gt;Las preguntas se centran en resolver problemas de performance; cómo volver a un recovery point. Ejemplo:  La tabla experimenta picos de tráfico incrementales durante el horario laboral, con patrones predecibles, cuando se producen millones de lecturas y escrituras. Hay que configurar una solución para aumentar la capacidad de procesamiento ➡ Habilitar el escalado automático en la tabla de DynamoDB. Configurar una política de escalado automático para ajustar el tamaño en función de las unidades de capacidad de lectura y escritura.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Point-in-Time Recovery (PITR) ➡&lt;/strong&gt; backup continuo, permite restaurar a cualquier &lt;strong&gt;segundo&lt;/strong&gt; de los últimos 35 días.
&lt;/li&gt;
&lt;li&gt;Para esto debemos conocer los tipos de Lectura &lt;em&gt;Eventually Consistent Read&lt;/em&gt; (por defecto) y &lt;em&gt;Strongly Consistent Read&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Los diferentes modos de capacidad &lt;em&gt;On-Demand y Provisioned&lt;/em&gt; &lt;strong&gt;(profundizar en estos dos conceptos).&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB Global Tables&lt;/strong&gt; &lt;em&gt;➡&lt;/em&gt; replicación multirregional y multiactiva. Cada región puede aceptar lecturas y escrituras, y los cambios se replican de forma asíncrona. Esto permite la escalabilidad global y la recuperación ante desastres.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Encriptación en reposo, tránsito y uso de KMS
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Encriptación con S3 de KMS: &lt;strong&gt;SSE-S3&lt;/strong&gt; (clave gestionada por AWS), &lt;strong&gt;SSE-KMS&lt;/strong&gt; (usás una KMS key propia, permite auditoría via CloudTrail y control de acceso granular) y &lt;strong&gt;SSE-C&lt;/strong&gt; (el cliente provee la clave, AWS no la almacena). Si la pregunta menciona &lt;strong&gt;auditoría de uso de claves o control granular&lt;/strong&gt; ➡ SSE-KMS.
&lt;/li&gt;
&lt;li&gt;Clave simetrica y asimetrica son básicos. Aunque en el examen no hay preguntas profundas sobre este punto.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Policies:&lt;/strong&gt; Cada clave KMS tiene una política de claves. Una política basada en recursos que controla quién puede usar y administrar la clave. A diferencia de las políticas de IAM, la política de claves es el principal mecanismo de control de acceso para las claves KMS.
&lt;/li&gt;
&lt;li&gt;Rotación de Key, hay 3 variantes:

&lt;ul&gt;
&lt;li&gt;automática, para customer managed keys puede habilitar la rotación automática
&lt;/li&gt;
&lt;li&gt;manual u On-demand por medio de trigger.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Importación de material de clave de un cliente en KMS, pero no rota automáticamente.
&lt;/li&gt;

&lt;li&gt;Replicación AWS KMS Multi-Region Keys: Una clave KMS que existe en una región diferente, pero comparte el mismo ID de clave y material de clave que su clave principal.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  AWS Certificate Manager (ACM)
&lt;/h3&gt;

&lt;p&gt;Las preguntas son sencillas, si se habla que servicio proporciona  certificados SSL/TLS la respuesta es  ACM.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Revisar los casos de uso de como integrar ACM con ELB, CloudFront, ApiGateway, Elastic Beanstalk, Cognito.
&lt;/li&gt;
&lt;li&gt;Tipos de certificados ➡ públicos, privados e importación de terceros. Identificar los casos de uso para poder resolver situaciones.
&lt;/li&gt;
&lt;li&gt;Mecanismo de renovación e importación de certificados de terceros.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Seguridad IAM
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Pensar que todo servicio para ejecutar algo debe tener un rol de IAM asociado. En ciertas ocasiones para permitir que usuarios utilicen servicios o listen ciertas features de manera temporal podemos agregar inline policies y luego eliminarlas.
&lt;/li&gt;
&lt;li&gt;Casos de uso de &lt;code&gt;iam:PassRole&lt;/code&gt;. Entender por qué es una capa de seguridad importante para proteger el entorno de AWS de actividades no deseadas y de la escalada de privilegios.
&lt;/li&gt;
&lt;li&gt;Caso de uso de credenciales temporales&lt;code&gt;iam:AssumeRole.&lt;/code&gt; Ejemplo ➡ la entidad en la Cuenta A utiliza para ejecutar servicios en la Cuenta B como si fuera un usuario local en esa cuenta.
&lt;/li&gt;
&lt;li&gt;Federación de Identidades: Permite a los usuarios autenticados por un proveedor de identidad externo (IdP) acceder a AWS sin crear usuarios de IAM individuales. Conocer los casos de uso:

&lt;ul&gt;
&lt;li&gt;SAML 2.0: se integra con Microsoft Active Directory Federation Services (ADFS) u Okta. Los usuarios se autentican con sus credenciales corporativas y reciben credenciales temporales de AWS.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;OpenID Connect (OIDC): &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;para aplicaciones web y móviles&lt;/strong&gt;. Admite proveedores de identidad como Google, Facebook o cualquier proveedor compatible con OIDC. Solución de error "Not authorized to perform sts:AssumeRoleWithWebIdentity" ➡ agregar el ARN del proveedor de identidad en el elemento principal de la política de confianza (trust policy).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AWS Organizations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OU (Organizational Units):&lt;/strong&gt; las cuentas se agrupan en OUs y los SCPs se aplican a nivel OU o cuenta individual, heredando hacia abajo en la jerarquía.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delegated Administrator:&lt;/strong&gt; permite que una cuenta miembro (no la cuenta de administración) gestione servicios específicos como Security Hub, GuardDuty o Config para toda la organización.
&lt;/li&gt;
&lt;li&gt;Relaciona el concepto de administrar muchas cuentas con &lt;strong&gt;Service control policies&lt;/strong&gt; (SCPs). Recordar que &lt;strong&gt;&lt;em&gt;afectan únicamente a los usuarios y roles de IAM&lt;/em&gt;&lt;/strong&gt; gestionados por las cuentas que forman parte de la organización. SCPs no afectan directamente a las políticas basadas en recursos.
&lt;/li&gt;
&lt;li&gt;Políticas de tagging. Se puede asegurar que los usuarios no puedan crear recursos sin una etiqueta específica.
&lt;/li&gt;
&lt;li&gt;Integración con &lt;strong&gt;&lt;em&gt;CloudFormation StackSet.&lt;/em&gt;&lt;/strong&gt; Definir un StackSet que implementa una plantilla base (almacenada en S3) en toda una Unidad Organizativa (OU) en varias regiones utilizando &lt;em&gt;Service-Managed permission model&lt;/em&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consolidated Billing:&lt;/strong&gt; todas las cuentas miembro consolidan el pago en la cuenta de administración, permite aprovechar descuentos por volumen y Reserved Instances compartidas entre cuentas.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;AWS Artifact&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Su caso de uso es acceder &lt;strong&gt;a informes de auditoría.&lt;/strong&gt; Se puede descargar bajo demanda certificaciones de cumplimiento normativo (como SOC 1/2/3, ISO/IEC, y PCI DSS).&lt;/p&gt;




&lt;p&gt;Este apunte continua en &lt;strong&gt;➡&lt;/strong&gt; &lt;a href="https://dev.to/reinalau/apuntes-para-la-certificacion-aws-cloudops-engineer-associate-soa-c03-parte-2-2e04"&gt;&lt;strong&gt;&lt;em&gt;Apuntes para la certificación AWS CloudOps Engineer Associate (SOA-C03) - Parte 2&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sysops</category>
      <category>aws</category>
      <category>certification</category>
    </item>
    <item>
      <title>Patrones de acceso seguro a S3 🌐</title>
      <dc:creator>Laura Bolaños</dc:creator>
      <pubDate>Mon, 27 Apr 2026 14:09:23 +0000</pubDate>
      <link>https://dev.to/aws-builders/patrones-de-acceso-seguro-a-s3-l7c</link>
      <guid>https://dev.to/aws-builders/patrones-de-acceso-seguro-a-s3-l7c</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Este análisis se propone clarificar algunos casos de uso donde se requiere servir contenido de S3 de manera segura, identificando las brechas de seguridad a las que debemos enfrentarnos y explorar posibles mejoras. El desafío real no está en analizar cada servicio por separado, sino en combinarlos correctamente.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  1. CloudFront + OAC con presigned url
&lt;/h2&gt;

&lt;p&gt;Caso de uso frecuente: Un sitio web que muestra imágenes/vídeos de una temática determinada. Una parte de los objetos multimedia puede restringirse a que solo lo vean usuarios que iniciaron sesión vía Cognito. Aquí la solución más sencilla:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upload de Multimedia (escritura)&lt;/strong&gt;&lt;br&gt;
El frontend solicita una Presigned URL a un endpoint protegido por Cognito (una api). La api se integra con una función Lambda que genera una URL temporal (válida por x minutos) firmada con permisos de "s3:PutObject" para el bucket privado de imágenes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Visualización de Multimedia (lectura)&lt;/strong&gt;&lt;br&gt;
El bucket de S3 tiene el bloqueo de acceso público activado.&lt;br&gt;
Los elementos multimedia se sirven a través de CloudFront, utilizando OAC (Origin Access Control) para firmar internamente la petición (usando el protocolo SigV4) y solicitar el objeto a S3 de forma segura.&lt;br&gt;
La política (Bucket Policy) del bucket S3 solo permite la acción "s3:GetObject" si la petición proviene específicamente del ARN de la distribución de CloudFront ("Condition: AWS:SourceArn").&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%2Fx0j2r3afdmhfgjhewpj5.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%2Fx0j2r3afdmhfgjhewpj5.jpg" alt="CloudFront + OAC con presigned url" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Conceptualmente una &lt;strong&gt;Presigned URL&lt;/strong&gt; se puede utilizar tanto para cargar como para leer objetos, en el ejemplo anterior notar que solo se usa para cargar los objetos a S3*.* Una presigned URL es simplemente una URL con la firma embebida ➡ cualquiera que la tenga puede usarla hasta que expire, sin autenticación. Por eso debemos aplicar diferentes técnicas de seguridad.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;La url puede tener una duración desde 1 minuto hasta 12 horas, si alguien no autorizado consigue la url y ya caduco ➡ NO la puede utilizar.&lt;/li&gt;
&lt;li&gt;Con el uso de Cognito, el token JWT tiene una duración y se puede aplicar este control con &lt;strong&gt;Cognito JWT Authorizer&lt;/strong&gt; (nativo de API Gateway). La otra posibilidad es tener Lambda Authorizer en API Gateway. Esta última tiene lógica de autorización custom.&lt;/li&gt;
&lt;li&gt;Cuando generamos una presigned url para upload, se puede controlar que tipos de objetos cargar y su peso máximo. Algunos ejemplos:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Generate presigned URL for PUT
# ContentType specifies the types of objects and controls their size on the frontend
&lt;/span&gt;&lt;span class="n"&gt;upload_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_presigned_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;put_object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Params&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;Bucket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;IMAGES_BUCKET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="n"&gt;imagen_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ContentType&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_content_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;ExpiresIn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PRESIGNED_URL_TTL&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# the frontend makes a multipart POST request
&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_presigned_post&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;IMAGES_BUCKET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;imagen_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Fields&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;Content-Type&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_content_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;Conditions&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;Content-Type&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_content_type&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;content-length-range&lt;/span&gt;&lt;span class="sh"&gt;"&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="mi"&gt;5242880&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# máx 5MB
&lt;/span&gt;    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;ExpiresIn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PRESIGNED_URL_TTL&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;
  
  
  1.2 Caso de uso complementario - Validación de acceso en el edge (Lambda@Edge)
&lt;/h3&gt;

&lt;p&gt;Podemos enfrentarnos a dos requerimientos no funcionales: minimizar la latencia y asegurar quién puede hacer request a Cloudfront.&lt;br&gt;
El caso anterior asegura que solo CloudFront puede acceder a S3, pero no controla quién puede hacer requests a CloudFront. Con Lambda@Edge es posible agregar lógica de autorización en los nodos de la CDN, antes de que el request llegue al origen. La secuencia es la siguiente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cuando un usuario solicita un objeto de un bucket, CloudFront invoca una función Lambda@Edge en el nodo más cercano geográficamente.&lt;/li&gt;
&lt;li&gt;La función valida el JWT de Cognito (o cualquier token de sesión) sin necesidad de redirigir el request al backend.&lt;/li&gt;
&lt;li&gt;Si el token es válido, el request continúa hacia S3 a través de OAC normalmente.&lt;/li&gt;
&lt;li&gt;Si no es válido, Lambda@Edge retorna un &lt;code&gt;401&lt;/code&gt; o redirige al login desde el edge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⭐ La ventaja es la &lt;strong&gt;baja latencia&lt;/strong&gt;, la validación ocurre en el edge node más cercano al usuario, sin necesidad de viajar hasta la región de AWS donde vive el backend. Cada request de contenido protegido se resuelve en milisegundos en lugar de sumar el tiempo de ida y vuelta a tu servidor.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Descarga temporal de un archivo privado - S3 Presigned URLs
&lt;/h2&gt;

&lt;p&gt;Para esta opción el casos de uso típico: Un usuario necesita descargar un reporte generado, una factura o un documento legal guardado en bucket S3. No se utiliza Cloudfront porque no necesitamos cachear contenido de la petició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%2Fougq75ayilzbgr5wpfaq.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%2Fougq75ayilzbgr5wpfaq.jpg" alt="S3 Presigned URLs" width="800" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Se produce la petición del usuario, el backend valida que ese usuario tiene permiso sobre ese archivo específico.&lt;/li&gt;
&lt;li&gt;Lambda genera una &lt;strong&gt;presigned URL&lt;/strong&gt; con &lt;code&gt;GetObject&lt;/code&gt; que expire entre 5-15 min. La lógica de validación de lambda es idéntica del caso de uso 1 de Upload.&lt;/li&gt;
&lt;li&gt;El browser descarga directamente desde S3 usando esa URL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔐 &lt;strong&gt;Brechas de seguridad detectadas&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;&lt;strong&gt;Riesgos de seguridad&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Mitigación posible&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;El usuario la copia y la comparte intencionalmente o no.&lt;/td&gt;
&lt;td&gt;TTL muy corto (1-2 minutos si es descarga inmediata)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Queda en logs del browser, proxies o herramientas de red&lt;/td&gt;
&lt;td&gt;Generar la URL y redirigir directamente (&lt;code&gt;302&lt;/code&gt;) en lugar de devolverla al frontend (nunca queda expuesta en el browser)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Se filtra por un &lt;code&gt;Referer&lt;/code&gt; header en un redirect&lt;/td&gt;
&lt;td&gt;Incluir el &lt;code&gt;userId&lt;/code&gt; en la key del objeto (&lt;code&gt;uploads/{userId}/{filename}&lt;/code&gt;) para que aunque alguien obtenga la URL, solo pueda acceder al archivo de ese usuario específico.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  3. Distribución segura con cookies por sesión - CloudFront Signed Cookies
&lt;/h2&gt;

&lt;p&gt;Caso de uso típico: Una plataforma web con contenido que se accede por suscripción, los usuarios se loguean pero cada sesión debe tener acceso acotado en el tiempo. En lugar de firmar URL por URL, el backend emite una &lt;strong&gt;Signed Cookie de CloudFront&lt;/strong&gt; al momento del login, que el browser envía automáticamente en cada request de objetos multimedia.&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%2Fd4ofyfbqg1jfvgil5q39.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%2Fd4ofyfbqg1jfvgil5q39.jpg" alt="CloudFront Signed Cookies" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;El backend verifica la sesión y envía tres headers &lt;code&gt;Set-Cookie&lt;/code&gt; al browser (CloudFront requiere exactamente tres pares clave-valor para una Signed Cookie). Estos headers deben enviarse &lt;strong&gt;antes&lt;/strong&gt; de que el usuario solicite el contenido privado.&lt;/li&gt;
&lt;li&gt;Si se configura un tiempo de expiración corto, el backend puede reenviar tres nuevos headers &lt;code&gt;Set-Cookie&lt;/code&gt; en requests posteriores para mantener el acceso activo.&lt;/li&gt;
&lt;li&gt;CloudFront valida las cookies en cada request antes de servir los objetos multimedia. La distribución debe tener al menos dos comportamientos de &lt;strong&gt;cache&lt;/strong&gt;: uno público y uno que requiere autenticación. La sección privada redirige al login si no hay cookie válida.&lt;/li&gt;
&lt;li&gt;Al cerrar sesión o expirar las cookies, el acceso se revoca sin cambiar las URLs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⭐ Esta opción es Ideal cuando un usuario accede a múltiples objetos multimedia de una misma sección sin generar una firma por cada archivo.&lt;br&gt;
⭐ Si CloudFront se configura para cachear basándose en cookies, NO generará entradas de caché separadas por los atributos de las &lt;em&gt;Signed Cookies&lt;/em&gt;. Es decir, todos los usuarios autorizados compartirán la misma entrada de caché para un objeto determinado— CloudFront valida la cookie para permitir o denegar el acceso, pero la ignora al construir la cache key.&lt;/p&gt;




&lt;h2&gt;
  
  
  Aspectos generales a tener en cuenta
&lt;/h2&gt;

&lt;p&gt;➡ &lt;strong&gt;Diseño&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Considerar Patrón BFF (Backend for Frontend)&lt;/strong&gt;. Para el caso de descarga privada, se puede interponer una capa BFF entre el frontend y API Gateway. Esta capa maneja la sesión del usuario y puede ejecutar un redirect &lt;code&gt;302&lt;/code&gt; directo hacia S3, evitando que la presigned URL quede expuesta en el browser.&lt;/p&gt;

&lt;p&gt;➡ &lt;strong&gt;Performance/Costo&lt;/strong&gt;&lt;br&gt;
Invalidaciones de CloudFront (incurre costo después de 1000/mes). Además del costo analizar tiempo de cacheo de datos.&lt;/p&gt;

&lt;p&gt;S3 request rates (3500 PUT/5500 GET por segundo por prefix, si el volumen es alto distribuir con prefijos).&lt;/p&gt;

&lt;p&gt;Costo de Lambda por invocación para generar presigned URLs a escala.&lt;/p&gt;

&lt;p&gt;➡ &lt;strong&gt;Seguridad&lt;/strong&gt;&lt;br&gt;
Rotación de claves KMS si el bucket usa SSE-KMS.&lt;/p&gt;

&lt;p&gt;Versionado de objetos en S3 para evitar sobreescritura accidental.&lt;/p&gt;

&lt;p&gt;➡ &lt;strong&gt;Operacional&lt;/strong&gt;&lt;br&gt;
TTL de presigned URLs vs tiempo de expiración del JWT — si el JWT expira antes que la URL, tenemos un problema!&lt;/p&gt;

&lt;p&gt;Estrategia de nombres de objetos (&lt;code&gt;userId/filename&lt;/code&gt;) para evitar colisiones y facilitar políticas IAM por prefijo.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>cloudfront</category>
      <category>s3</category>
    </item>
    <item>
      <title>Pensar en Serverless para crear bellos sueños 🦋</title>
      <dc:creator>Laura Bolaños</dc:creator>
      <pubDate>Fri, 10 Apr 2026 12:56:18 +0000</pubDate>
      <link>https://dev.to/aws-builders/pensar-en-serverless-para-crear-bellos-suenos-390</link>
      <guid>https://dev.to/aws-builders/pensar-en-serverless-para-crear-bellos-suenos-390</guid>
      <description>&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%2Fjkt4xky3sc2hei44br55.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%2Fjkt4xky3sc2hei44br55.jpg" alt="Mariposa Monarca" width="688" height="742"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;En estas líneas te explico una sencilla arquitectura para desplegar un sitio web íntegramente utilizando servicios Serverless que podría servir, como en mi caso, para mostrarle al mundo mi interés por la ecología (con repositorio github incluido)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;En un mundo digital donde se crean productos financieros, web que muestran dashboard de eficiencias, p&amp;amp;L y todo tipo de temáticas asociada al dinero ...y que está muy bien! olvidamos que nuestro hábitat natural necesita ayuda.&lt;br&gt;
Hace unos años me percate de esto y comencé a leer/hablar de árboles, arbustos y plantas mal llamadas malezas - nativas de mi región que son fuente de alimento de mariposas e insectos que son fundamentales para la biodiversidad - que están desapareciendo con el avance de las especies exóticas, los agroquímicos y la destrucción de humedales.&lt;br&gt;
La mayoría de las personas cuando comentas la problemática te dice "OK estoy de acuerdo contigo" pero no activan en tener un pequeño jardín de nativas o a la hora de elegir un árbol para su vereda priorizan especies exóticas solo por estetica, sin saber que muchas nativas son igual de hermosas y además sostienen la fauna local (hablo especialmente de la Provincia de Buenos Aires, Argentina).&lt;/p&gt;

&lt;p&gt;....Y qué pasaría si cambio mi discurso en mi afán de convencer y digo: &lt;strong&gt;"si no ayudamos plantando nativas en nuestros hogares las mariposas pueden desaparecer"&lt;/strong&gt; ¿Suena drástico? pero en gran parte es cierto. Estudios afirman que la población ha disminuido muy rápido, especialmente la mariposa &lt;strong&gt;Bandera Argentina&lt;/strong&gt; debido a que no encuentra su planta nutricia (Coronillo) donde desovar sus huevos 💔 Cabe aclarar que la disminución de mariposas afecta directamente a la polinización y con esto atenta contra la seguridad alimentaria.&lt;br&gt;
Por todo lo anterior y para bajar un poco el dramatismo decidí hacer algo: crear un proyecto web que muestre mariposas e indique la planta nativa nutricia que alimenta sus orugas! Y que los usuarios registrados puedan cargar avistamientos de mariposas en la región. Para acompañar esta idea pensé en una arquitectura Serverless, que reduce la huella de carbono y acompaña mi pensamiento ecológico 🌱.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comencemos analizando la arquitectura
&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%2Fksxqm8z5i19gaiscwxpi.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%2Fksxqm8z5i19gaiscwxpi.png" alt="Diagrama de Arquitectura" width="800" height="509"&gt;&lt;/a&gt;&lt;br&gt;
Como podés observar el laboratorio es de un nivel entre 100 y 200 (si necesitas ayuda me comentas).&lt;br&gt;
Implementa una arquitectura Serverless de &lt;strong&gt;3 capas&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Web Tier:&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;S3-Sitio Web + CloudFront&lt;/strong&gt; ➡ contiene todo el sitio web creado con react. El código está preparado para integrar con los servicios &lt;strong&gt;Apigateway y Cognito&lt;/strong&gt;. Cloudfront servicio para servir el sitio web de S3. Este s3 no es público, solo se accede a través de Cloudfront.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;App Tier:&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Cognito + API Gateway + Lambda&lt;/strong&gt; ➡ Cognito realiza la autenticación del usuario que se inicia sesión. Se aclara que los usuarios se deben crear manualmente por la Consola de AWS o por cli (explicado en el Readme de repositorio). Las Lambdas de crear imagen, get imagen, presigned_URL y delete imagen(funcionalidad no desarrollada aún desde el frontend) se invocan via Apigateway. Solo los usuarios autorizados por Cognito pueden acceder a estas funcionalidades.&lt;br&gt;
Las lambdas están codificadas en Python.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Data Tier:&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;DynamoDB + S3 - imágenes&lt;/strong&gt; ➡ Toda la información de la fichas de las imágenes de mariposas se guarda en DynamoDB incluyendo la presigned-URL de S3-imagenes. En este último bucket se guardarán las imágenes de mariposas subidas por el usuario.&lt;br&gt;
Aunque &lt;strong&gt;Route53&lt;/strong&gt; aparece en el diagrama, en el repo no lo vas a ver codificado en la IaC. Por qué? porque para el entorno educativo del laboratorio no es necesario, con la url de cloudfront el sitio web queda totalmente funcional.&lt;/p&gt;

&lt;p&gt;⭐ El objetivo es construir una aplicación real con autenticación, subida de archivos y CRUD completo utilizando solo servicios serverless de AWS, con la opción de deployar en CloudFormation o Terraform. Porque también es muy importante aprender las diferentes formas de desplegar sin tocar el Console Home.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como utilizar el repositorio de código
&lt;/h2&gt;

&lt;p&gt;✅ Ten en cuenta que como requisito para comenzar con este ejercicio necesitas instalado Python, Terraform, AWS Cli (todo explicadisimo en el repo 🤓)&lt;/p&gt;

&lt;p&gt;El código se encuentra en una url de un repositorio padre &lt;strong&gt;labs-aws-llb&lt;/strong&gt; donde hay otros laboratorios, pero el que nos interesa es: &lt;a href="https://github.com/reinalau/labs-aws-llb/tree/main/webAppServerless" rel="noopener noreferrer"&gt;webAppServerles&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Comenzar leyendo Arquitectura.md que explica un poco la secuencia de las acciones en los componentes y las decisiones de Arquitectura, como por ejemplo por qué no pasar las imagenes por el payload de Lambda.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Leer el &lt;strong&gt;Readme.md&lt;/strong&gt; principal: contiene el paso a paso que debes hacer y si decides hacer el deploy con terraform o cloudformation te indicará ir a su correspondiente directorio. Cada directorio también contiene su Readme.md.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Leer &lt;strong&gt;Integration.md&lt;/strong&gt; : te vas a encontrar que una vez deployada la IaC en AWS, solo necesitarás tomar los outputs de terraform o el descripción del stack de cloudformation y configurar el archivo &lt;strong&gt;aws-config.ts&lt;/strong&gt; . Luego volverás al &lt;strong&gt;Readme principal&lt;/strong&gt; para seguir el paso a paso ➡ volver a subir la web buildeada a S3 y crear una invalidación de CloudFront para actualizar la web integrada con el backend.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;El laboratorio tiene un costo mínimo (si estás en la capa gratuita cero) Como siempre recomiendo limpiar todo lo desplegado. Si estas pensando en crear una web funcional estable ten en cuenta los costos. Información detallada en Readme principal.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Funcionalidades
&lt;/h2&gt;

&lt;p&gt;Una vez deployado el sitio web siguiendo las indicaciones, tenemos las siguientes funcionalidades.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Usuario anónimo&lt;/th&gt;
&lt;th&gt;Usuario logueado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ver catálogo de mariposas precargadas&lt;/td&gt;
&lt;td&gt;🦋&lt;/td&gt;
&lt;td&gt;🦋&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ver galería de avistamientos de usuarios&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;🦋&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subir foto + metadata de avistamiento&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;🦋&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filtrar por ecorregión&lt;/td&gt;
&lt;td&gt;🦋&lt;/td&gt;
&lt;td&gt;🦋&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ver ficha detallada de mariposa&lt;/td&gt;
&lt;td&gt;🦋&lt;/td&gt;
&lt;td&gt;🦋&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Después de todo el trabajo... con la url de Cloudfront descubrirás esta hermosa web funcional de mariposas bonaerenses 💗 sustentada íntegramente con servicios serverless.&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%2Fmh01boamvwd9gosnvxog.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%2Fmh01boamvwd9gosnvxog.jpg" alt="Imagen sitio web" width="800" height="953"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En el título hablo de sueños porque realmente lo es, sueño que mis vecinos tengan aunque sea solo un ejemplar nativo. No hay estadística clara sobre la vegetación nativa, pero sabemos que las especies van retrocediendo y con esto también las mariposas.&lt;br&gt;
&lt;strong&gt;La foto principal es una Monarca del Sur recién nacida en mi jardín, ya que generé las condiciones para que eso pasara.&lt;/strong&gt;&lt;br&gt;
Este laboratorio sirve para mostrar que casi sin costo podemos difundir información sobre temas que nos favorecen a todos y a todas. Y en tu región, ¿qué mariposas habitan?&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>aws</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Claves para lograr la certificación AWS Data Engineer Associate</title>
      <dc:creator>Laura Bolaños</dc:creator>
      <pubDate>Fri, 20 Mar 2026 03:33:59 +0000</pubDate>
      <link>https://dev.to/aws-builders/claves-para-lograr-la-certificacion-aws-data-engineer-associate-bb7</link>
      <guid>https://dev.to/aws-builders/claves-para-lograr-la-certificacion-aws-data-engineer-associate-bb7</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;En este artículo encontrarás una guía para descubrir qué conocimientos necesitás antes de rendir el examen, identificar tus brechas y entender en qué temas profundizar. 🤓&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Información general del examen
&lt;/h3&gt;

&lt;p&gt;La certificación esta orientada los Ingenieros de datos pero también a todos aquellos que están construyendo su perfil de Arquitecto Cloud.&lt;br&gt;
&lt;a href="https://aws.amazon.com/es/certification/certified-data-engineer-associate/" rel="noopener noreferrer"&gt;Aquí&lt;/a&gt;  podes encontrar datos generales del examen, intuyo que ya conoces esta información.&lt;br&gt;
La guia de examen consta de 4 dominios, es super necesario leer y revisar continuamente mientras estudias para la certificación: &lt;a href="https://docs.aws.amazon.com/aws-certification/latest/data-engineer-associate-01/data-engineer-associate-01.html" rel="noopener noreferrer"&gt;link &lt;/a&gt;&lt;br&gt;
El dato importante: el examen no esta disponible en español. Para todos los que tenemos dificultad con el idioma puede parecer un impedimento pero no te preocupes! estudiando y armando apuntes en ingles lo puedes lograr 🏆 Las preguntas se pueden comprender perfectamente porque se alcanza un nivel de familiaridad con frases claves del examen.&lt;br&gt;
⭐ Tip: lengua no nativa ingles ➡ pedir los 30 minutos adicionales (Exam acommodations) en la plataforma &lt;a href="https://www.aws.training/certification" rel="noopener noreferrer"&gt;Training And Certificacion&lt;/a&gt;  antes de agendar el examen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Complejidad
&lt;/h3&gt;

&lt;p&gt;Si no trabajas como Data Engineer pero contas con un background en bases de datos, consultas sql complejas y performance tuning los contenidos te van a resultar familiares y sencillos de incorporar. Además, si contás con la certificación Solutions Architect Associate una parte del contenido ya lo habrás estudiado.&lt;/p&gt;

&lt;p&gt;⭐ Es una certificación importante y muy amena para estudiar, de complejidad media.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cursos de preparación y laboratorios básicos
&lt;/h3&gt;

&lt;p&gt;➡ Curso en Skillbuilder de &lt;em&gt;&lt;a href="https://skillbuilder.aws/learn/6BP61KB1FJ/data-engineering-on-aws--foundations/KXCN4PJD9Y?parentId=7UPVWWCC45" rel="noopener noreferrer"&gt;Data Engineer Foundations&lt;/a&gt;&lt;/em&gt; . Es gratis y básico para esta certificación.&lt;br&gt;
➡ Curso en &lt;a href="https://skillbuilder.aws/learning-plan/QYZWVSMX4B/exam-prep-plan-aws-certified-data-engineer--associate-deac01--english/YTMBK7R698" rel="noopener noreferrer"&gt;SkillBuilder de preparación del examen&lt;/a&gt;. Es esencial ya que explica en que consiste el examen y lo que debemos estudiar. Si no contas con la suscripción haz todo el contenido libre, basicamente es la explicación de los 4 dominios.&lt;br&gt;
➡ Es necesario hacer laboratorios de diferentes casos de uso, tanto de Data Lakes como de DataWarehouse. Los laboratorios los encontras en SkillBuilder (ejemplo CloudQuest Data Analytics) o invertir unos dolares en la consola. Comparto laboratorio básico de mi &lt;a href="https://github.com/reinalau/labs-aws-llb/tree/main/serverless-aws-datalake-1" rel="noopener noreferrer"&gt;github &lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Método de estudio sugerido
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Armar un NotebookLM con tus apuntes y links a pdf de la &lt;a href="https://docs.aws.amazon.com/" rel="noopener noreferrer"&gt;documentación oficial de AWS&lt;/a&gt;. Particularmente recomiendo Developer Guide y/o User Guide de servicios como Glue, Redshift, Kinesis, Athena, QuickSigth, OpenSearch, Amazon MSK, LakeFormation, DataSync y todas las url que consideres. De esta manera construyes una especie de RAG para tener precisión a la hora de preguntar dudas conceptuales o de casos de uso.&lt;/li&gt;
&lt;li&gt;En el momento de practicar preguntas no te fies ciegamente de las respuestas de las IA, el último tiempo noté que algunos modelos tienen dificultades con temas de elección de arquitecturas. Verificar siempre con la documentación oficial, por eso es importante el punto anterior.&lt;/li&gt;
&lt;li&gt;Buscar un banco de preguntas para entrenar y conocer el estilo de examen. En mi caso utilicé &lt;a href="https://tutorialsdojo.com/" rel="noopener noreferrer"&gt;Tutoriales Dojo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Recursos útiles (lo utilicé en la preparación) :&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=s2DzF8avQnY" rel="noopener noreferrer"&gt;video banco de preguntas &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=s2DzF8avQnY" rel="noopener noreferrer"&gt;video casos de uso &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/reinalau/AWS-Certified/tree/main/DEA-C01" rel="noopener noreferrer"&gt;github llb&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Conocimientos fundamentales para presentar el examen
&lt;/h3&gt;

&lt;p&gt;Durante el examen prestar atención a palabras claves, pensar cual solución es mas lógica, más costo- efectiva, con menor esfuerzo operacional y con performance aceptable.&lt;br&gt;
El listado a continuación no cubre todo el temario, pero dominar estos temas es fundamental para aprobar:&lt;/p&gt;

&lt;h4&gt;
  
  
  Generales
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Datalake ➡ S3: raw, curation, consumption.&lt;/li&gt;
&lt;li&gt;DataWarehouse : Redshift. Siempre relacionado con queries complejas.&lt;/li&gt;
&lt;li&gt;Data Lakehouse.&lt;/li&gt;
&lt;li&gt;Patrones de ingesta de datos: Streaming y Batch.&lt;/li&gt;
&lt;li&gt;Atención en las preguntas donde se haga alusión a data lake o datawarehouse porque puede ser la clave para elegir el almacenamiento.&lt;/li&gt;
&lt;li&gt;Formatos de archivos para alimentar un Data Lake en S3: Json, Parquet, Avro, ORC, Csv. Sus casos de uso. Comprender mecanismo de construcción de su metadata. Formatos de Compresión gzip (ratio), snappy (rápidez), zlib.&lt;/li&gt;
&lt;li&gt;Elección de servicio para migrar archivos/datos desde el onprem. Probablemente la elección es DataSync.&lt;/li&gt;
&lt;li&gt;Data Skew: referido a una distribución desigual de los datos entre las diferentes unidades de procesamiento provocando un desequilibrio de carga.&lt;/li&gt;
&lt;li&gt;Métodos principales de orquestación de pipeline de datos: Step Functions, Glue Workflows (nativo para job de glue) y MWAA (dependencias complejas, portabilidad). Se pueden crear flujos y orquestarlos con Event Bridge.&lt;/li&gt;
&lt;li&gt;DynamoDb Stream. DynamoDB TTL.&lt;/li&gt;
&lt;li&gt;Conexion a RDS, Aurora, Redshift via api desde Lambda. Revisar ejemplos.&lt;/li&gt;
&lt;li&gt;Comprender el concepto DAGs (Directed Acyclic Graphs) y con qué servicios esta relacionado. Amazon MWAA&lt;/li&gt;
&lt;li&gt;Visualizacion-Dashboard ➡ QuickSigth . QuickSigth SPICE (relacion con Athena). Analizar diferentes casos de uso.&lt;/li&gt;
&lt;li&gt;QuickSigth librerias built-in functions and operators: utilidad en casos de uso que necesitamos Rolling Averages, Pivoting and Grouping y Machine Learning (ML) Insights.&lt;/li&gt;
&lt;li&gt;Opensearch: Creación de indices, monitorización de logs en tiempo real. UltraWarm en OpenSearch ➡ Conocer su caso de uso.&lt;/li&gt;
&lt;li&gt;Secrets Manager es nuestro servicio estrella para guardar accesos a base de dados de manera segura.&lt;/li&gt;
&lt;li&gt;Casos de uso de MSK (Kafka). Se asocia con Glue para transformar.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Amazon S3
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;S3 Storage Lens: Si tengo que hacer una auditoría de bucket, identificar versiones antiguas de objetos, partes incompletas, etc ➡ cuento con un dashboard para clarificar estas falencias.&lt;/li&gt;
&lt;li&gt;Tener claro todos los tipos o layers de S3, su tiempo de recuperación de datos y costos: S3 Standar, IA, Glacier Flexible, Glacier Deep Archive, Intelligent tearing y los otros que no son de alta disponibilidad. Ciclo de vida. Versionamiento. Dependiendo el caso de uso hay que optar.&lt;/li&gt;
&lt;li&gt;Cómo aplico encriptación en reposo en S3, DynamoDB. Siempre pensar en KMS pero NO con una clave proporcionada por el cliente porque es MÁS carga operacional (es lo que debemos evitar): en S3 ➡ SSE-KMS (claves gestionadas por AWS KMS con auditoría)&lt;/li&gt;
&lt;li&gt;Cifrado doble en S3 ➡ SE-KMS (cifrado de doble capa del lado del servidor con AWS KMS).&lt;/li&gt;
&lt;li&gt;VPC Endpoint GATEWAY . No tiene costo en S3 y otorga seguridad sin pasar por internet. Desde Glue se puede crear una conexión de red, especificar vpc/subnet. Mismo caso para conectar una lambda de manera segura a S3.&lt;/li&gt;
&lt;li&gt;Si tengo varias aplicaciones que consumen diferentes prefix de S3 cual es la mejor manera de brindar seguridad para que una aplicación no pueda ver los datos de otra? Probablemente la solución sea S3 Access Point&lt;/li&gt;
&lt;li&gt;Casos de uso de EDA (Event-Driven Architecture). Especialmente de S3, reconocer cuando conviene registrar un EventBridge y cuando reaccionar a una lambda (sin intermediarios).&lt;/li&gt;
&lt;li&gt;Modificación de objetos (datos) en S3 al momento de la recuperación, ejemplo: un caso de uso que deba enmascarar información ➡ Por medio de S3 Object Lambda.&lt;/li&gt;
&lt;li&gt;Técnicas de Performance en datos almacenados en S3: Bucketing y Partition (ver cardinalidades). Entender las diferencias y cuando se usa según el caso para poder responder con seguridad.&lt;/li&gt;
&lt;li&gt;Amazon Macie: tener claro que hace escaneo, descubrimiento y clasificacion de información sensible cuando los datos ya están alojados en S3. Puede integrarse con EventBridge para realizar acciones sobre los hallazgos.&lt;/li&gt;
&lt;li&gt;SQL S3 ➡ solo puede consultar un unico archivo. Util para consultas adhoc de un dato en particular.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Amazon Glue
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Glue Engine: Spark, Ray (computación paralela/intensiva) y Python Shell (ejecuciones sencillas).&lt;/li&gt;
&lt;li&gt;Glue ETLs. Glue Crawler (identificación de metadata en varios origenes). Programación de cron. DynamicFrames.&lt;/li&gt;
&lt;li&gt;Glue Dynamic Schema (Schema Evolution). Relacion con AVRO.&lt;/li&gt;
&lt;li&gt;Glue Data Catalog (recordar que no solo se usa en el scope de Glue sino que también se integra con EMR). Schema version history.&lt;/li&gt;
&lt;li&gt;Glue DataBrew - relacionado con Data Quality pero es herramienta visual no-code. Conocer la sintaxis de rules. Conviene hacer laboratorios o al menos revisar la consola de AWS. En qué casos de uso podemos arreglar los datos. Recordar que está vinculado con SageMaker AI en la preparación de datos para entrenar modelos.&lt;/li&gt;
&lt;li&gt;Permite hacer Custom rules.&lt;/li&gt;
&lt;li&gt;AWS Glue Data Quality. Es una capacidad diferentes al punto anterior. Utiliza DQDL para definir reglas de validación.&lt;/li&gt;
&lt;li&gt;Glue Workflows (usar cuando orquestamos solo job de Glue, si vemos que tenemos que orquestar Lambdas la opción correcta probablemente sea Step Functions).&lt;/li&gt;
&lt;li&gt;Glue Bookmark. Si lo tengo activado y el job vuelve a procesar los datos nuevamente...que puede estar pasando? falta job.commit().&lt;/li&gt;
&lt;li&gt;Desde un bucket policy se puede denegar ejecuciones de glue que no esten desde un rango horario determinado, ejemplo: "StringNotEquals":{},"NumericGreaterThan":{"aws:CurrentTime":"11:00:00Z"},"NumericLessThan":{"aws:CurrentTime":"09:00:00Z"}} . Ver casos de policy en S3, es la parte de seguridad mas importante del examen.&lt;/li&gt;
&lt;li&gt;Comprender permisos de ejecucion en Glue mediante roles y politica de confianza (Trust Policy): AWSServiceRole (tiene las politicas básicas).&lt;/li&gt;
&lt;li&gt;Si tengo que monitorear job de Glue, a donde voy? lo hago en Glue Studio ➡Job monitoring dashboard (no desde Cloudwatch). Hacer laboratorio con Glue y revisar las métricas, ejecuciones anteriores. Este analisis sirve para ajustar dpu o workers.&lt;/li&gt;
&lt;li&gt;Glue transformación de datos JSON a Tabular : Relationalize Transform.&lt;/li&gt;
&lt;li&gt;Feature Transformación Detect PII (o Detect Sensitive Data): Accion de enmascarar, redactar o eliminar.&lt;/li&gt;
&lt;li&gt;Glue Interactive Sessions.&lt;/li&gt;
&lt;li&gt;Glue Flex. Usa capacidad de computación ociosa por lo tanto es menos costoso.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Amazon Redshift
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Redshift sintaxis sql: practicar select, agrupaciones, rank() y funciones más comunes. Sintaxis UDF (se invocan en la proyeccción). Entender que las búsquedas con expresiones regulares son más lentas.&lt;/li&gt;
&lt;li&gt;Redshift comandos: COPY (se usa para traer rápidamente datos desde S3) ➡Option: PARALLEL y COMPUPDATE. Unload (se usa para descargar datos a S3, generalmente históricos).&lt;/li&gt;
&lt;li&gt;Redshift leader nodes y compute nodes. Entender cómo trabaja para tener la capacidad de responder preguntas de performance.&lt;/li&gt;
&lt;li&gt;Redshift Spectrum, está pensando para consultar S3 Y Redshift. No confundir con consultas federadas. Por lo general se usa Spectrum cuando redshift descarga registros antiguos en s3 para abaratar costos y se requiere algún informe que cruce con datos históricos.&lt;/li&gt;
&lt;li&gt;Redshift consultas federadas vs Athena federated queries. Se puede presentar una pregunta con estas dos opciones. Leer el caso de uso y tener en claro que soporta cada una. Ejemplo, Redshift queries federadas no soportas consultas en TimeStream.&lt;/li&gt;
&lt;li&gt;Redshift Distribution Style : entender que es Auto Distribution, ALL, Even, Key. Esto se aplica para hacer performantes las querys. Relacionar con Star Schemas.&lt;/li&gt;
&lt;li&gt;Redshift: Ingesta de datos en formato JSON, cómo se almacena/consulta de forma performante? Con el tipo de dato SUPER, se crea una tabla con una única columna y se puede realizar consultas anidadas (se usa PARTITION BY y funciones como like json_extract_path_text).&lt;/li&gt;
&lt;li&gt;Data Sharing: Redshift comparte información entre cluster. Ver casos de uso.&lt;/li&gt;
&lt;li&gt;Vistas materializadas. Como las construye. En qué casos de uso se utilizan. Cómo se refrescan.&lt;/li&gt;
&lt;li&gt;Redshift troubleshooting: Tabla de sistema STL_ALERT_EVENT_LOG. Maximum Transmission Unit (MTU). VACCUM, VACCUM FULL, VACCUM DELETE ONLY y VACCUM REINDEX.&lt;/li&gt;
&lt;li&gt;Redshift Workload Management. Determina como se ejecutan las consultas en el cluster (query queues and route queries).&lt;/li&gt;
&lt;li&gt;MPP (Massively Parallel Processing) y Windows Functions.&lt;/li&gt;
&lt;li&gt;Dynamic Data Masking - DDM (CREATE MASKING POLICY) ➡ Permite ocultar, ofuscar o seudonimizar datos. Se pueden restringir a usuarios o a roles específicos.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Kinesis Data Stream - Kinesis Firehose - Amazon Managed Service for Apache Flink (Ex Kinesis Data Analytics)
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Tener claro conceptualmente los 3 servicios del título. Real time y Near real time.&lt;/li&gt;
&lt;li&gt;Amazon Managed Service for Apache Flink: Detectar anomalias o casos de uso de Fraude. Windows Operations (defining time-based or row-based windows to manage data streams)&lt;/li&gt;
&lt;li&gt;Mirar todos los casos de ingesta Kinesis Data Stream (pensar que siempre es real time). Puede ingestar a Redshift directamente con Redshift Streaming (es zero etl).&lt;/li&gt;
&lt;li&gt;Analizar Casos de Uso de streaming de IoT. Velocidad de Transmisión de datos- Registros por segundo.&lt;/li&gt;
&lt;li&gt;Performance en streaming de datos: Kinesis Producer Library (KPL) y Kinesis Client Library (KCL). Shared Fan-out&lt;/li&gt;
&lt;li&gt;Partition key de alta cardinalidad para distribuir la carga de forma equitativa en los diferentes shard.&lt;/li&gt;
&lt;li&gt;Resolución de problemas en Kinesis Data Stream ➡ si la velocidad de los datos supera la capacidad de los fragmentos actuales: ProvisionedThroughput - Exceeded Exception.&lt;/li&gt;
&lt;li&gt;Caso de uso Kinesis Firehose + Lambda para transformación de datos.&lt;/li&gt;
&lt;li&gt;Integración Kinesis Firehose con CloudTrail.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Lake Formation
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Lake Formation, si hay preguntas sobre permisos granulares considerar que la respuesta es esta. Tener en cuenta todo lo que puede gobernar Lake Formation (qué fuentes de datos).&lt;/li&gt;
&lt;li&gt;Fine-Grained Access Control (FGAC). Tag-Based Access Control (LF-TBAC). Credential Vending.&lt;/li&gt;
&lt;li&gt;Blueprints and Workflows: Templates que ayudan a ingestar datos desde varias fuentes para crear automaticamente AWS Glue crawlers, jobs, and triggers.&lt;/li&gt;
&lt;li&gt;Auditoría via CloudTrail.&lt;/li&gt;
&lt;li&gt;CloudTrail Lake: observalidad de como interactúa el ecosistema de AWS. Centralidad de logs. SQL Engine. Seguridad Granular.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  EMR
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Comprender los casos de uso EMR. Es la opcion de procesamiento de datos mas costosa para mantener (para que viva la metadata hay que tener prendido el cluster). Puede ser la elección adecuado si se requiere control profundo sobre la infraestructura, configuraciones personalizadas (flexibilidad) o el uso del ecosistema completo de Hadoop para migraciones de datos.&lt;/li&gt;
&lt;li&gt;Relación con BigData. Procesamiento Distribuido.&lt;/li&gt;
&lt;li&gt;EMR puede usar Glue Catalog.&lt;/li&gt;
&lt;li&gt;Mirar la combinación de cómputo recomendada para brindar alta disponibilidad y cost effective. Instancias Spot y Procesadores Graviton.&lt;/li&gt;
&lt;li&gt;EMR: feature security configuration para setear encriptación tanto en reposo como en tránsito.&lt;/li&gt;
&lt;li&gt;EMR Serverless. Casos de uso. Entender en cual caso conviene utilizar en lugar de EMR Standard.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Amazon Athena
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;"Analizar datos con sql" la respuesta es Athena.&lt;/li&gt;
&lt;li&gt;Tablas en Athena que se pueden modificar : Apache Iceberg.&lt;/li&gt;
&lt;li&gt;Athena predicate pushdown*&lt;em&gt;.&lt;/em&gt;*&lt;/li&gt;
&lt;li&gt;Athena Parallel Execution.&lt;/li&gt;
&lt;li&gt;Consultas anidadas en Athena (nested), como hacer performante y fácil. View por cada consulta.&lt;/li&gt;
&lt;li&gt;Athena federated Query.&lt;/li&gt;
&lt;li&gt;Athena ➡ queries lentas, están mal las particiones que estoy tomando para filtrar. Uso Partition Projection en base a los accesos.&lt;/li&gt;
&lt;li&gt;Glue Notebooks y Athena Notebooks: cuando se usa cada uno?&lt;/li&gt;
&lt;li&gt;CTAS (Create Table as Select)&lt;/li&gt;
&lt;li&gt;Athena Federated Query por medio de Lambda.&lt;/li&gt;
&lt;li&gt;Athena Workgroups &lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  DMS - STC -CDC
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Migrar a una base de datos en la nube o desde onprem, la mejor elección es DMS. Si no hay homogeneidad en la base de datos origen con la de destino se debe usar STC (AWS Schema Conversion tool). Para replicar datos evitando downtime en la base de datos se replica con CDC (change data capture). Este último se realiza con los logs full de transacciones activado en la base de datos origen.&lt;/li&gt;
&lt;li&gt;En los casos de uso de migración de bases de datos homogéneas solo se usa DMS + DMS Schema Conversion y cdc (change data capture) para los cambios.&lt;/li&gt;
&lt;li&gt;Cuando tengo dos regiones y se quiere migrar una base de datos en la región A con destino en la región B, DMS debe ubicarse idealmente en la región donde está la base de datos de destino o en una región cercana para minimizar la latencia de escritura.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Cloudwatch - CloudTrail
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Comprender cuándo utilizar CloudWatch, ejemplo ➡ si necesito analizar/buscar con lenguaje sql uso Amazon CloudWatch Logs Insights. Metrics Filters y Subscription Filter.&lt;/li&gt;
&lt;li&gt;Creación de Train en CloudTrail. Mecanismo de envío de datos (a S3) y consultas.&lt;/li&gt;
&lt;li&gt;En CloudTrail puedo direccionar el logueo de la actividad de cuentas y/o servicios a un S3 y analizar los datos mediante Athena.&lt;/li&gt;
&lt;li&gt;CloudTrail caso de uso testigo: a. CloudTrail capta eventos de Redshift, b. por medio de Reglas de EventBridge guarda esos logs en DynamoDB. También EventBridge Pipes puede filtrar contenido de eventos.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>aws</category>
      <category>dataengineering</category>
      <category>awscertified</category>
    </item>
    <item>
      <title>Camino a lograr AWS Certified Developer - Associate (DVA-C02)</title>
      <dc:creator>Laura Bolaños</dc:creator>
      <pubDate>Tue, 17 Mar 2026 14:23:20 +0000</pubDate>
      <link>https://dev.to/aws-builders/camino-a-lograr-aws-certified-developer-associate-dva-c02-4o19</link>
      <guid>https://dev.to/aws-builders/camino-a-lograr-aws-certified-developer-associate-dva-c02-4o19</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Estas notas no intentan ser una guía detallada, sino un recorrido por temas importantes y tips que debemos tener en cuenta si nos aventuramos a estudiar esta certificación. También es importante aclarar que se trata de una visión subjetiva acerca de la complejidad del examen, basada en mi experiencia después de haber obtenido 3 certificaciones previas de AWS.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Información general del examen
&lt;/h2&gt;

&lt;p&gt;No me voy a detener en esta descripción porque está claramente escrito en este &lt;a href="https://aws.amazon.com/es/certification/certified-developer-associate/" rel="noopener noreferrer"&gt;enlace que debes leer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;Tip&lt;/strong&gt;: si tu lengua no es ingles nativa, pedir 30 minutos adicionales (Exam acommodations) en la plataforma &lt;em&gt;&lt;a href="https://www.aws.training/certification" rel="noopener noreferrer"&gt;Training And Certificacion&lt;/a&gt;&lt;/em&gt; antes de agendar el examen, sin importar si se rinde en ingles o español.&lt;br&gt;
Esta selección se realiza en la opción de menú marcada y queda seleccionada por default para todas las futuras certificaciones de AWS.&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%2Fpopczz0kjfyxxqieunl0.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%2Fpopczz0kjfyxxqieunl0.png" alt="tc" width="215" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Complejidad
&lt;/h2&gt;

&lt;p&gt;AWS Developer Associate &amp;lt; AWS Solutions Architect Associate&lt;br&gt;
AWS Developer Associate &amp;gt; AWS Cloud Practtioner&lt;br&gt;
AWS Developer Associate &amp;lt; AWS AI Practitioner&lt;/p&gt;

&lt;h2&gt;
  
  
  Relación con certificaciones anteriores
&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%2F13uk85wqrzw15ryy8sn3.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%2F13uk85wqrzw15ryy8sn3.png" alt="comparativa certificaciones" width="720" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cuál es tu punto de partida?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Leer la guía de examen para tener una visión general del contenido que debemos abordar de los 4 dominios, cada dominio tiene un % diferente de cobertura en la certificación. No te desanimes si a priori pensas que tu perfil no es el del candidato que se describe en la guía, yo tampoco lo soy 100%, pero con ganas y entusiasmo para aprender → llegarás a la meta!!.&lt;br&gt;
&lt;a href="https://d1.awsstatic.com/es_ES/training-and-certification/docs-dev-associate/AWS-Certified-Developer-Associate_Exam-Guide.pdf" rel="noopener noreferrer"&gt;guía del examen&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Learning plan de preparación de AWS SkillBuilder. No importa si no contas con la suscripción, haz lo que este sin costo. Es una forma de nivelar tus conocimientos.&lt;br&gt;
Lo bueno de la suscripción es que accedes a varios laboratorios útiles pero no es un impedimento. Si haces practicas de laboratorios por cuenta propia necesitarás una inversión mínima en la consola de AWS de 1 a 3 usd.&lt;br&gt;
&lt;a href="https://skillbuilder.aws/learning-plan/ZWHYYQBTUK/exam-prep-plan-aws-certified-developer--associate-dvac02--espaol-latam/UD82VTBREP" rel="noopener noreferrer"&gt;Exam Prep Plan: AWS Certified Developer — Associate&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;⭐ Cuando comiences el punto 2, recomiendo armar planilla de calculo de “brechas de conocimiento” e indicar como vas a cubrir ese gap (con laboratorios, documentación de AWS o practicando preguntas de examen).&lt;/p&gt;

&lt;h2&gt;
  
  
  Claves fundamentales para presentar el examen
&lt;/h2&gt;

&lt;p&gt;En esta sección se listan los conocimientos clave que debemos profundizar para alcanzar la certificación. Como siempre, es fundamental tener una perspectiva de seguridad para los diferentes servicios de AWS: quién puede invocar cada servicio y qué acciones puede realizar una vez invocado → aplicando el Principio del Mínimo Privilegio.&lt;/p&gt;

&lt;p&gt;☁ &lt;strong&gt;Desarrollo con Lambda:&lt;/strong&gt; si crees que por lanzar una lambda via consola o cloudformation ya tenes todo el conocimiento…pues no es así. Hay que profundizar y tener practica de laboratorios pasando por la mayoría de sus features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Versiones Lambda — Alias Lambda-Integraciones (como se usan?). Pruebas de dev y prod con alias. Alias ponderados (como usar?).&lt;/li&gt;
&lt;li&gt;Tiempo de Espera Lambda.&lt;/li&gt;
&lt;li&gt;Variables de Entorno (se cifran? ).&lt;/li&gt;
&lt;li&gt;Concurrencia Aprovisionada y Concurrencia Reservada. (diferencia y sus casos de uso).&lt;/li&gt;
&lt;li&gt;DLQ (cuando se usa).&lt;/li&gt;
&lt;li&gt;Triggers. Lambda Source Events (EventSourceMaping).&lt;/li&gt;
&lt;li&gt;Logueo en &lt;strong&gt;CloudWatch&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Limites y quotas Lambda&lt;/li&gt;
&lt;li&gt;Lambda layers (para que se usan).&lt;/li&gt;
&lt;li&gt;Lambda VPC (cuando y como se usa).&lt;/li&gt;
&lt;li&gt;Eficiencia Lambda (memoria? codigo?)&lt;/li&gt;
&lt;li&gt;Almacenamiento interno /tmp (limites y en que casos es util).&lt;/li&gt;
&lt;li&gt;Manejo de errores. Retroceso exponencial.&lt;/li&gt;
&lt;li&gt;Payload de entrada y de salida.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⭐ Tip: para cubrir la mayoría de teoría de lambda con demostraciones, ver &lt;a href="https://youtu.be/JIUdRraTZRw?si=S5X6S8j65sd2rwuc" rel="noopener noreferrer"&gt;video Joan Amengual&lt;/a&gt;.&lt;br&gt;
⭐ No dejes de hacer laboratorios o pequeñas pruebas tomando un lenguaje de programación soportado, en mi caso elegí Python.&lt;/p&gt;

&lt;p&gt;☁ &lt;strong&gt;DynamoDB:&lt;/strong&gt; Conocimiento profundo del servicio.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clave sencilla (único atributo) y compuesta (2 atributos).&lt;/li&gt;
&lt;li&gt;Importancia de cardinalidad en claves de partición. Índices de ordenación.&lt;/li&gt;
&lt;li&gt;Índices LSI y GSI. Qué índice se puede crear en una tabla ya activa y con datos. Limites de índices.
*Recuperar items y en qué casos de uso se utilizan: GetItem, Scan (no performante), Query y BatchGetItem. Consultas condicionales.&lt;/li&gt;
&lt;li&gt;Lecturas eventualmente consistentes y fuertemente consistentes. Transacciones en DynamoDB. Consumo de RCU y WCU de cada una. Tra&lt;/li&gt;
&lt;li&gt;Conocimiento para calcular RCU-WCU en un caso de uso de lecturas/escrituras por segundo de acuerdo al tipo de requerimiento de consistencia.&lt;/li&gt;
&lt;li&gt;Capacidad aprovisionada o a demanda. En qué casos de uso conviene utilizar cada una.&lt;/li&gt;
&lt;li&gt;TTL. Configuración para eliminación de ítem por medio de atributo marca de tiempo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⭐&lt;strong&gt;Tip: Hacer laboratorios de Lambda invocando programáticamente a DynamoDB&lt;/strong&gt;, con diferentes funcionalidades de recupero de ítems utilizando el SDK de AWS.&lt;/p&gt;

&lt;p&gt;☁ &lt;strong&gt;DynamoDB Stream:&lt;/strong&gt; Cómo integramos los avisos de cambios de DynamoDB (insert, modify, delete ) con los diferentes servicios .&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FilterCriteria. Revisar ejemplos.&lt;/li&gt;
&lt;li&gt;Cómo evito publicar información sensible por medio de StreamViewType (KEYS_ONLY).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⭐Tip: Recomiendo revisar diferentes casos de uso Lambda, Amazon Kinesis Data Stream, Step Functions.&lt;/p&gt;

&lt;p&gt;☁ &lt;strong&gt;Desarrollo Api Gateway:&lt;/strong&gt; Contar con conocimiento profundo del servicio.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tipos de APIs. Correspondencia con casos de uso.&lt;/li&gt;
&lt;li&gt;Integración con lambda, alias-versiones.&lt;/li&gt;
&lt;li&gt;Stages. Ambientes dev-prod. Variables de stage.&lt;/li&gt;
&lt;li&gt;Integraciones simuladas. Mocks.&lt;/li&gt;
&lt;li&gt;Validación de headers, request y body. Por ejemplo, podemos validar antes de llamar a un backend Lambda.&lt;/li&gt;
&lt;li&gt;Autenticación y Autorización. Roles de IAM. Api key. Cognito (app web y mobile). Lambda Authotizer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⭐ Este servicio es de los mas importantes junto con Lambda. Es necesario hacer diferentes laboratorios por medio de la Consola de AWS y también por SAM (Serverless Application Model).&lt;/p&gt;

&lt;p&gt;☁ Autenticación y Autorización Cognito:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User Pools e Identity Pools. Comprender el flujo básico de autenticación.&lt;/li&gt;
&lt;li&gt;Casos en que solo con User Pools podemos tener autenticación. Adicionando Identity Pool para implemetar Roles de IAM acceder a servicios aws (credenciales temporales).&lt;/li&gt;
&lt;li&gt;Acceso a usuarios autenticados y no autenticados. Casos de uso y diferencias.&lt;/li&gt;
&lt;li&gt;IAM roles y Trust relationships. Su relación con la configuración en Cognito.&lt;/li&gt;
&lt;li&gt;Grupos de usuarios de Cognito (dentro de User Pools). Asignación de Roles de IAM.&lt;/li&gt;
&lt;li&gt;Federación de identidades (Facebook, google, etc). Identity Provider. Protocolos SAML o OpenID Connect (OIDC).&lt;/li&gt;
&lt;li&gt;Flujos de autenticación personalizados con Lambda (Custom authentication challenge Lambda trigger).
*Amazon Cognito Sync para sincronización de perfiles en todos los dispositos mobile y web.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⭐ Es importante repasar ejemplos y si es posible hacer pequeños laboratorios de: app web con ALB-Cognito, flujos de autorizaciones personalizadas Lambda, Autenticación Cognito en Apigateway.&lt;br&gt;
⭐Ayuda revisar las diferentes configuraciones en la consola de AWS.&lt;/p&gt;

&lt;p&gt;☁ &lt;strong&gt;ALB-ASG-EC2:&lt;/strong&gt; Conocer los casos de uso típicos de aplicaciones web con esta arquitectura de alta disponibilidad y cómo aplicar seguridad y certificados (ACM).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perfiles de instancia de EC2 y asunción de roles. Casos de uso que necesito acceder a una RDS a escribir.&lt;/li&gt;
&lt;li&gt;Cómo aplico Seguridad por VPC, Security groups, ACL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;☁ &lt;strong&gt;Contenedores ECS, EKS:&lt;/strong&gt; Componentes fundamentales, como aplicar monitoreo y observabilidad en los dos casos para solucionar problemas (Cloudwtach y X-Ray).&lt;/p&gt;

&lt;p&gt;☁ &lt;strong&gt;Cloudfront:&lt;/strong&gt; Además de conocer frente a que servicios puede estar (ALB, S3, lambdaEdge..) centrarse en la seguridad. Ver casos de uso para aplicar seguridad entre el lector-cloudfront y cloudfront-origen.&lt;/p&gt;

&lt;p&gt;☁ &lt;strong&gt;SAM (Serveless Application Model):&lt;/strong&gt; Conocer los comandos básicos de la Cli de SAM. Hacer pequeños laboratorios con Lambda-ApiGateway- DynamoDB (caso de uso CRUD), comandos para testear localmente e implementar.&lt;/p&gt;

&lt;p&gt;☁ &lt;strong&gt;CI/CD:&lt;/strong&gt; Conocer comandos básicos y cómo se integran CodeBuild, CodeDeploy, CodePipeline.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Archivos necesarios en la raiz local de cada proyecto: buildspec.yml y appspec.yml (gestiona los hooks necesarios para la implementación).&lt;/li&gt;
&lt;li&gt;Verificación con Hooks de CodeDeploy: PreTraffic y PostTraffic.&lt;/li&gt;
&lt;li&gt;En cual podemos agregar pruebas automatizadas. En dónde podemos agregar aprobación manual.&lt;/li&gt;
&lt;li&gt;Cómo lo integro con SAM? Para separar ambientes de dev-staging-prod que opciones tengo para guardar esa parametría (configuración local, ParameterStore).&lt;/li&gt;
&lt;li&gt;Importancia de las implementaciones Canary y el desvío de tráfico por porcentaje entre producción actual y la nueva implementación .&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;☁ &lt;strong&gt;EventBridge:&lt;/strong&gt; Revisar casos de uso de Arquitecturas orientadas a eventos donde intervenga este servicio.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.linkedin.com/posts/theburningmonk_sns-eventbridge-have-many-overlaps-and-activity-7392206683923664896-jigu?utm_source=share&amp;amp;utm_medium=member_desktop&amp;amp;rcm=ACoAAAG0JN8B2SSu7fYsiTll9cyKiq6IrBpqnWc" rel="noopener noreferrer"&gt;Comprender la diferencia de utilizar Amazon Eventbridge o SNS&lt;/a&gt;. Revisar diferentes casos de uso.&lt;/li&gt;
&lt;li&gt;Capacidad de transformación de el payload para cada destino.&lt;/li&gt;
&lt;li&gt;Filtrado de eventos. Reglas complejas basadas en el payload del evento.&lt;/li&gt;
&lt;li&gt;Casos de uso de avisos relacionados a cumplimiento, seguridad o controles generales. Relación con Security Hub, avisos de Vencimientos de certificados (ACM).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⭐ Recomiendo hacer laboratorio de un ejemplo similar a: venta/pedidos de producto, donde tengo un bus de eventos y diferentes reglas que ejecutan Lambdas: “registrar pedido”, “preparar pedido” y “entregar pedido”. Cada lambda con un evento posterior avisa para el siguiente paso al bus de eventos.&lt;/p&gt;

&lt;p&gt;☁ &lt;strong&gt;SQS:&lt;/strong&gt; Elemental entender sus casos de uso con Lambda, SNS y EventBridge.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tipos Standard y FIFO. Performance de cada uno.&lt;/li&gt;
&lt;li&gt;DLQ (dead letter queue).&lt;/li&gt;
&lt;li&gt;Visibilidad de mensajes. Máximos.&lt;/li&gt;
&lt;li&gt;Retardos de entrega de mensajes: Delay. No confundir con visbilidad de mensajes.&lt;/li&gt;
&lt;li&gt;Reintentos de entrega de mensajes. Permanencia de mensajes.&lt;/li&gt;
&lt;li&gt;Borrado automático de mensajes cuando se realiza sondeo o pooling y cuando es necesario eliminar programáticamente.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;☁ &lt;strong&gt;SNS:&lt;/strong&gt; Comprender sus casos de uso con Lambda, SQS y EventBridge.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Filtrado de eventos para diferentes consumidores. Uso de atributos para el filtrado.&lt;/li&gt;
&lt;li&gt;Diferencias con SQS. SNS tiene la capacidad de entregar el mismo mensaje a diferentes consumidores.&lt;/li&gt;
&lt;li&gt;Reintentos de entrega de mensaje. Protocolos y Políticas de entrega.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;☁ &lt;strong&gt;S3:&lt;/strong&gt; Conocimiento general de todos los tipos y el tiempo de recuperación de información.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Casos de uso comunes de carga de objetos y que eventos puede disparar, ya sea en una SQS, SNS o simplemente la ejecución de una Lambda.&lt;/li&gt;
&lt;li&gt;Conocimientos de manejo de políticas de bucket (políticas basadas en recursos) no solo para permitir qué hacer a nivel bucket sino también a nivel objeto.&lt;/li&gt;
&lt;li&gt;Políticas de recursos para forzar conexiones cifradas (https), forzar realizar put ítem donde elemento ya venga encriptado. Cómo hacer para que un usuario determinado solo vea sus objetos en un bucket general.&lt;/li&gt;
&lt;li&gt;URL pre-firmadas. Duración máxima y seteo de expiración time.&lt;/li&gt;
&lt;li&gt;Sitios web estaticos. Como hacer para que sólo se puede invocar desde Cloudfront (OAC-Origin Access Control).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⭐ Es muy útil la practica de comandos/acciones sobre S3 con AWS CLI.&lt;/p&gt;

&lt;p&gt;☁ &lt;strong&gt;ElasticBeanstalk:&lt;/strong&gt; que tipos de casos de uso puedo desarrollar. Comandos ClI EB. Por qué se Beanstalk (ventajas) ? En qué casos nos ayuda?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tipos de deployment, (hacer cuadro comparativo de All at once, Rolling, Immutable, Traffic splitting, Blue/green).&lt;/li&gt;
&lt;li&gt;Componentes centrales Enviroments y Applications-Application versions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;☁ &lt;strong&gt;StepFunctions:&lt;/strong&gt; Conocer los estados y condiciones.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cómo hace los reintentos. Revisar ejemplos del tipo sistema de pagos.&lt;/li&gt;
&lt;li&gt;Comprender que no siempre utilizar SF es la solución mas sencilla.&lt;/li&gt;
&lt;li&gt;Relación con &lt;strong&gt;Lambda, SQS y Eventbridge&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;☁ &lt;strong&gt;CDK:&lt;/strong&gt; No confundir con SDK.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uso de AWS CDK Construct Library y AWS CDK Tookit.&lt;/li&gt;
&lt;li&gt;El modelo de Programación de Constructs. Clase CDK Aspects.&lt;/li&gt;
&lt;li&gt;Aserciones de CDK . Pruebas unitarias.&lt;/li&gt;
&lt;li&gt;Traducción a plantillas Cloudformation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;☁ &lt;strong&gt;CloudFormation:&lt;/strong&gt; Estructura básica. Parametría. Rollback. Diferencias entre Ref o GetAtt. Pilas anidadas. La importancia de los Outputs. Fn::ImportValue.&lt;/p&gt;

&lt;p&gt;☁ &lt;strong&gt;CloudWatch:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Métricas personalizas. Grupos de logs. Logueo personalizado.&lt;/li&gt;
&lt;li&gt;Query en los logs.&lt;/li&gt;
&lt;li&gt;Uso Formato de Métrica Integrada (EMF, Embedded Metric Format).&lt;/li&gt;
&lt;li&gt;Agente cloudwatch. Su relacion con ECS, EKS y servidores on-prem. Qué recopila.&lt;/li&gt;
&lt;li&gt;Filtros Cloudwatch. Contadores. Avisos. Desde qué momento tiene vigencia.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;☁ &lt;strong&gt;CloudTrail — XRay&lt;/strong&gt; Diferencias conceptuales entre ambos. Particularmente para el caso de AWS Developer tiene mas utilidad X-Ray, entender el por qué.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consultas en CloudTrail, relación con Amazon Athena.&lt;/li&gt;
&lt;li&gt;Auditoria de KMS en CloudTrail.&lt;/li&gt;
&lt;li&gt;Analisis/Solución cuellos de botella. Latencia.&lt;/li&gt;
&lt;li&gt;Cómo integrar &lt;strong&gt;X-Ray en ECS o EKS&lt;/strong&gt;. Sidecar X-Ray.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;☁ &lt;strong&gt;Seguridad de datos y secretos:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claves simétricas y asimétricas. Cifrado en transito y reposo.&lt;/li&gt;
&lt;li&gt;Cómo puedo usar &lt;strong&gt;KMS&lt;/strong&gt; (claves administrados, material de clave importados, etc).&lt;/li&gt;
&lt;li&gt;Casos de uso en los que un desarrollador necesita rotar secretos de password de base de datos usando Secret Manager.&lt;/li&gt;
&lt;li&gt;Casos de uso que podemos utilizar &lt;strong&gt;Parameter Store - Secure String. Cifrado en transito y reposo&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;☁ &lt;strong&gt;STS:&lt;/strong&gt; Comprender los mecanismos de obtención de credenciales: AssumeRole, AssumeRoleWebIndentity, AssumeRoleWithSAML, GetFederationToken, GetSessionToken..&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cómo hacer para probar asumir un rol desde AWS CLI y solucionar un problema.&lt;/li&gt;
&lt;li&gt;STS en &lt;strong&gt;Cognito&lt;/strong&gt;. Relaciones de Confianza.&lt;/li&gt;
&lt;li&gt;Invocaciones AssumeRoleWithWebIdentity por qué no requieren el uso de credenciales de seguridad de AWS.&lt;/li&gt;
&lt;li&gt;Etiquetas de Sesión (Tags).&lt;/li&gt;
&lt;li&gt;Endpoint STS Global y Regional. Su relación con la latencia.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Alguna manera útil de estudiar teoría y buscar apoyo de colegas?
&lt;/h2&gt;

&lt;p&gt;⭐ En casos de uso de servicios que mas dudas tengas, lo recomendable siempre es hacer pequeños laboratorios..seguro ya lo dije pero es muy importante!!&lt;/p&gt;

&lt;p&gt;⭐ Cuidado con las IA y la explicación profunda de los servicios o casos de uso, tienen alucinaciones!&lt;br&gt;
Recomiendo estudiar de las &lt;a href="https://docs.aws.amazon.com/es_es/" rel="noopener noreferrer"&gt;documentación oficial de Aws&lt;/a&gt;. En mi caso usé: Developer Guide y API Reference en pdf/html de los servicios para CHATEAR con ellas por medio de un cuaderno de &lt;a href="https://notebooklm.google.com/notebook/7b4cf4a5-6341-4928-a190-9cb95a32b244?authuser=1" rel="noopener noreferrer"&gt;Google NotebookLM&lt;/a&gt; (te permite agregar hasta 50 fuentes con usuario sin suscripción, adición de notas y +…es espectacular!).&lt;/p&gt;

&lt;p&gt;⭐ Practicar muchas preguntas de examen es lo que te asegura el éxito.&lt;br&gt;
En AWS SkillBuilder hay un &lt;a href="https://skillbuilder.aws/learn/VHBKSAVY53/official-practice-question-set-aws-certified-developer--associate-dvac02--espaol-latam/J88736CU3U" rel="noopener noreferrer"&gt;set gratuito&lt;/a&gt; y uno &lt;a href="https://skillbuilder.aws/learn/W6VKWVHMDN/official-practice-exam-aws-certified-developer--associate-dvac02--espaol-latam/BUY8KKYZEN" rel="noopener noreferrer"&gt;con suscripción&lt;/a&gt;.&lt;br&gt;
&lt;a href="https://www.examtopics.com/exams/amazon/aws-certified-developer-associate-dva-c02/view/" rel="noopener noreferrer"&gt;Exam Topics&lt;/a&gt; (10 gratuitas).&lt;br&gt;
Tutoriales Dojo (&lt;a href="https://portal.tutorialsdojo.com/courses/free-aws-certified-developer-associate-practice-exams-sampler/" rel="noopener noreferrer"&gt;gratuitas&lt;/a&gt;).&lt;br&gt;
Revisar algún set de Practica de Udemy. &lt;a href="https://www.udemy.com/course/dva-c02-2/" rel="noopener noreferrer"&gt;En mi caso utilice set de un instructor muy poco conocido&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;⭐ Todo proceso de certificación conviene transitarlo con apoyo de las comunidades con intereses parecidos a los nuestros. Te invito a unirte:&lt;br&gt;
📚 &lt;a href="https://www.linkedin.com/company/aws-wic-buenos-aires/posts/?feedView=all" rel="noopener noreferrer"&gt;AWS Women In Cloud Buenos Aires&lt;/a&gt; → donde ayudamos a cerrar la brecha de género en aws.&lt;br&gt;
📚 &lt;a href="https://www.linkedin.com/in/maxcloud/" rel="noopener noreferrer"&gt;La comunidad de MaxCloud — El Capitan&lt;/a&gt;&lt;br&gt;
📚 &lt;a href="https://www.linkedin.com/company/aws-women-colombia/posts/?feedView=all" rel="noopener noreferrer"&gt;AWS Women Colombia&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>developercertified</category>
      <category>awscertified</category>
    </item>
  </channel>
</rss>
