← Todas las guías
Guía interactiva · 10 niveles

Tu primer agente con Strands + Python

De tu primer agente en 10 líneas de código con OpenAI, hasta una arquitectura de producción con orquestador multi-agente y balanceo de carga. Con código real del Strands Agents SDK (open source, AWS).

0 / 10 completado
Nivel 1 · Fundamentos

Tu primer agente

Objetivo: instalar el Strands Agents SDK, configurar tu API key de OpenAI y correr tu primer agente en menos de 10 líneas de código.

El Strands Agents SDK es un framework open source (creado por AWS) para construir agentes de IA con un enfoque "model-driven": el modelo decide qué hacer en cada paso, y el SDK maneja el bucle de ejecución (el agent loop) por vos. Funciona con cualquier proveedor — Bedrock, Anthropic, y también OpenAI directamente.

1. Instala las dependencias

TERMINAL
pip install strands-agents strands-agents-tools export OPENAI_API_KEY="sk-tu-api-key-aqui"

2. Escribe tu primer agente

Un agente en Strands necesita solo dos cosas: un modelo y (opcionalmente) herramientas. Así se ve el "Hello World":

agente.py
from strands import Agent from strands.models.openai import OpenAIModel model = OpenAIModel(model_id="gpt-4o") agent = Agent(model=model) respuesta = agent("¿Qué es un agente de IA, en una frase?") print(respuesta)

Corrés python agente.py y ya tenés un agente conversacional funcionando. No hay bucles manuales, no hay parsing de function calls a mano — el SDK se encarga de todo el ciclo pregunta → razonamiento → respuesta.

Reto

Cambiá el model_id a otro modelo de OpenAI (por ejemplo uno más económico) y compará la velocidad de respuesta. Después cambiá el prompt para pedirle al agente que responda siempre en formato de lista.

Nivel 2 · Personalidad

System prompt y el bucle del agente

Objetivo: entender el "agent loop" y controlar el comportamiento del agente con un system prompt.

Cada vez que le hablás a un agente, Strands ejecuta el agent loop: envía tu mensaje + historial + herramientas disponibles al modelo, el modelo decide si responde directamente o llama una herramienta, y el ciclo se repite hasta que el modelo da una respuesta final. El system_prompt es lo que moldea cómo piensa el agente en cada vuelta de ese ciclo.

agente.py
from strands import Agent from strands.models.openai import OpenAIModel model = OpenAIModel( model_id="gpt-4o", params={"temperature": 0.3, "max_tokens": 400}, ) agent = Agent( model=model, system_prompt=( "Sos un asistente técnico de soporte para una empresa de software. " "Respondé siempre en español, de forma breve y con pasos numerados. " "Si no sabés algo, decilo explícitamente en vez de inventar." ), ) print(agent("Un cliente dice que la app se cierra al abrir la cámara."))

Parámetros que vas a usar seguido

  • temperature — más bajo (0.0–0.3) para tareas precisas/soporte, más alto (0.7+) para creatividad
  • max_tokens — límite de longitud de respuesta, importante para controlar costo
  • model_id — podés mezclar un modelo económico para tareas simples y uno más potente para razonamiento complejo
Reto

Escribí un system prompt para un agente que solo responde preguntas sobre un tema específico de tu elección, y que rechaza cortésmente cualquier otra pregunta.

Nivel 3 · Herramientas

Dale herramientas (Tools) a tu agente

Objetivo: crear una herramienta custom con el decorador @tool y usar herramientas ya construidas del paquete strands_tools.

Un agente sin herramientas solo puede "hablar". Con herramientas, puede actuar: consultar una API, hacer un cálculo, leer un archivo. En Strands, cualquier función de Python se vuelve una herramienta con el decorador @tool — los type hints se convierten en el schema, y el docstring en la descripción que el modelo usa para decidir cuándo llamarla.

agente.py
from strands import Agent, tool from strands.models.openai import OpenAIModel from strands_tools import calculator @tool def clima_actual(ciudad: str) -> str: """Devuelve el clima actual reportado para una ciudad dada. Args: ciudad: nombre de la ciudad, ej. "Ciudad de Guatemala" """ # Aquí llamarías a una API real de clima (OpenWeather, etc.) datos = {"Ciudad de Guatemala": "22°C, parcialmente nublado"} return datos.get(ciudad, "No tengo datos para esa ciudad") model = OpenAIModel(model_id="gpt-4o") agent = Agent(model=model, tools=[clima_actual, calculator]) print(agent("¿Cómo está el clima en Ciudad de Guatemala y cuánto es 340*12?"))

El agente decide solo, sin que se lo digas explícitamente, que necesita llamar clima_actual para la primera parte y calculator (una herramienta prebuilt de strands_tools) para la segunda — y combina ambos resultados en una sola respuesta.

Reto

Creá una herramienta buscar_producto(nombre: str) que devuelva precio/stock desde un diccionario en memoria, y probá que el agente la use correctamente en una conversación con varias preguntas seguidas.

Nivel 4 · Memoria

Memoria conversacional y sesiones

Objetivo: mantener contexto entre múltiples turnos de conversación y entender cómo Strands gestiona el historial.

Un objeto Agent ya mantiene su historial de conversación mientras viva en memoria — cada llamada nueva se agrega al mismo hilo. Para conversaciones largas, Strands ofrece conversation managers que deciden cómo truncar o resumir el historial para no exceder la ventana de contexto del modelo.

agente.py
from strands import Agent from strands.models.openai import OpenAIModel from strands.agent.conversation_manager import SlidingWindowConversationManager model = OpenAIModel(model_id="gpt-4o") agent = Agent( model=model, system_prompt="Sos un asistente de ventas para una tienda de tecnología.", conversation_manager=SlidingWindowConversationManager(window_size=20), ) agent("Hola, busco una laptop para diseño gráfico") agent("¿Cuál es la más barata de las que me mencionaste?") agent("¿Y esa tiene buena batería?") # El agente recuerda las laptops mencionadas antes sin que se las repitas

El SlidingWindowConversationManager conserva los últimos N mensajes y descarta los más viejos automáticamente — útil para bots de soporte o ventas que corren indefinidamente sin acumular contexto infinito (y costo infinito).

Reto

Simulá una conversación de 5 turnos y comprobá qué pasa cuando reducís el window_size a un número muy chico (ej. 2) — vas a notar que el agente "olvida" el inicio de la charla.

Nivel 5 · Salida estructurada

Respuestas tipadas con Pydantic

Objetivo: forzar que el agente devuelva datos estructurados y validados en vez de texto libre.

Cuando vas a conectar el agente a otro sistema (una base de datos, un CRM, un frontend), no querés parsear texto libre — querés JSON validado. Strands soporta esto de forma nativa usando modelos de Pydantic como esquema de salida.

agente.py
from pydantic import BaseModel, Field from strands import Agent from strands.models.openai import OpenAIModel class TicketSoporte(BaseModel): categoria: str = Field(description="bug, pregunta, o solicitud de feature") urgencia: str = Field(description="baja, media, o alta") resumen: str = Field(description="resumen en una frase del problema") model = OpenAIModel(model_id="gpt-4o") agent = Agent(model=model) resultado: TicketSoporte = agent.structured_output( TicketSoporte, "La app se me cierra sola cada vez que subo una foto de perfil, es urgente" ) print(resultado.categoria, resultado.urgencia, resultado.resumen) # -> "bug" "alta" "La app se cierra al subir foto de perfil"

Esto es la base para automatizar clasificación de tickets, extracción de datos de documentos, o cualquier flujo donde la salida del agente alimenta código, no un humano leyendo texto.

Reto

Definí un modelo Pydantic para extraer datos de una reseña de producto (rating del 1-5, sentimiento, y si menciona un problema específico) y probalo con 3 reseñas de ejemplo distintas.

Nivel 6 · Streaming y async

Respuestas en tiempo real

Objetivo: usar invocación asíncrona y manejar eventos del agente en tiempo real, clave para UIs tipo chat.

Para una API o un chat en producción no querés esperar a que el agente termine de "pensar" para mostrar algo — querés ir mostrando tokens a medida que llegan. Strands expone invoke_async() y un sistema de callback handlers para esto.

agente_async.py
import asyncio from strands import Agent from strands.models.openai import OpenAIModel model = OpenAIModel(model_id="gpt-4o") agent = Agent(model=model) async def main(): async for evento in agent.stream_async("Explicame qué es RAG en 3 pasos"): if "data" in evento: print(evento["data"], end="", flush=True) asyncio.run(main())

Si estás construyendo una API (FastAPI, por ejemplo) y no necesitás ver el streaming en la terminal sino solo procesar eventos internamente, podés desactivar la salida en vivo con callback_handler=None al crear el agente, y manejar los eventos vos mismo.

Reto

Envolvé el agente en un endpoint de FastAPI que devuelva la respuesta como StreamingResponse, reusando el generador async de arriba.

Nivel 7 · Multi-agente

Agents-as-Tools: tu primer equipo de agentes

Objetivo: combinar varios agentes especializados usando el patrón "agente como herramienta".

Cuando un solo agente empieza a tener demasiadas responsabilidades (investigar, escribir, validar), conviene dividirlo en agentes especializados. El patrón más simple de Strands para esto es Agents-as-Tools: envolvés un agente entero dentro de una función @tool, y otro agente lo puede "llamar" como si fuera una herramienta más.

equipo_agentes.py
from strands import Agent, tool from strands.models.openai import OpenAIModel model = OpenAIModel(model_id="gpt-4o") agente_investigador = Agent( model=model, system_prompt="Investigás datos técnicos y das respuestas factuales y concisas.", ) @tool def investigar(pregunta: str) -> str: """Delega una pregunta de investigación al agente investigador especializado.""" return str(agente_investigador(pregunta)) agente_redactor = Agent( model=model, system_prompt=( "Sos un redactor de blog técnico. Cuando necesites datos factuales, " "usá la herramienta 'investigar' antes de escribir." ), tools=[investigar], ) print(agente_redactor("Escribí un párrafo sobre por qué RAG reduce las alucinaciones"))

El agente redactor decide cuándo delegar en el investigador — vos no orquestás el flujo paso a paso, el modelo lo hace. Este patrón consume pocos tokens extra porque la coordinación es determinista (una llamada a función normal).

Reto

Agregá un tercer agente "revisor" que reciba el texto del redactor y devuelva una versión corregida, encadenando los tres roles.

Nivel 8 · Orquestador

Graph y Swarm: patrones de orquestación

Objetivo: elegir el patrón multi-agente correcto y construir un orquestador que enruta tareas a especialistas.

Strands trae tres formas de coordinar múltiples agentes, cada una para un caso distinto:

  • Agents-as-Tools (nivel anterior) — delegación simple y determinista, ideal cuando ya sabés qué agente hace qué
  • Graph — un grafo dirigido y determinista: los agentes son nodos, las conexiones definen el flujo de datos entre ellos. Ideal para pipelines predecibles (ej. investigar → redactar → revisar, siempre en ese orden)
  • Swarm — los agentes deciden dinámicamente a quién pasarle la tarea entre ellos. Más flexible, pero consume más tokens porque el modelo "razona" sobre a quién delegar
orquestador.py
from strands import Agent, tool from strands.models.openai import OpenAIModel model = OpenAIModel(model_id="gpt-4o") agente_ventas = Agent(model=model, system_prompt="Especialista en preguntas de ventas y precios.") agente_soporte = Agent(model=model, system_prompt="Especialista en soporte técnico y bugs.") agente_facturacion = Agent(model=model, system_prompt="Especialista en facturación y pagos.") @tool def consultar_ventas(pregunta: str) -> str: """Usar para preguntas sobre precios, productos o el proceso de compra.""" return str(agente_ventas(pregunta)) @tool def consultar_soporte(pregunta: str) -> str: """Usar para preguntas sobre errores, bugs o problemas técnicos.""" return str(agente_soporte(pregunta)) @tool def consultar_facturacion(pregunta: str) -> str: """Usar para preguntas sobre facturas, pagos o reembolsos.""" return str(agente_facturacion(pregunta)) orquestador = Agent( model=model, system_prompt=( "Sos el punto de entrada de soporte al cliente. Analizá cada mensaje " "y delegá SIEMPRE al especialista correcto usando las herramientas disponibles. " "No respondas preguntas de dominio directamente vos mismo." ), tools=[consultar_ventas, consultar_soporte, consultar_facturacion], ) print(orquestador("Me cobraron dos veces la suscripción este mes")) # -> el orquestador enruta esto a consultar_facturacion automáticamente

Este es el corazón de una arquitectura de agentes en producción: un orquestador (router) que nunca resuelve el dominio él mismo, solo decide a quién delegar — igual que un dispatcher humano en un call center.

Reto

Agregá logging dentro de cada herramienta consultar_* para registrar cuántas veces se enruta a cada especialista — es el primer paso hacia observabilidad real.

Nivel 9 · Producción

Observabilidad, errores y guardrails

Objetivo: instrumentar el agente con trazas, manejar errores de forma robusta y poner límites de costo.

Antes de poner esto en producción, tres cosas dejan de ser opcionales: saber qué hizo el agente (trazas), qué pasa cuando algo falla (errores/reintentos), y cuánto puede llegar a costar (límites).

Observabilidad con OpenTelemetry

Strands emite trazas usando el estándar OpenTelemetry de forma nativa — cada llamada al modelo, cada uso de herramienta y cada paso del event loop queda registrado, compatible con Jaeger, Grafana Tempo, AWS X-Ray o Datadog.

TERMINAL
export STRANDS_OTEL_ENABLED=true export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318" python orquestador.py # Ahora cada request queda trazado end-to-end en tu backend de observabilidad

Manejo de errores y reintentos

agente_robusto.py
import time def invocar_con_reintentos(agent, mensaje, intentos=3): for intento in range(1, intentos + 1): try: return agent(mensaje) except Exception as e: if intento == intentos: raise espera = 2 ** intento # backoff exponencial print(f"Error ({e}), reintentando en {espera}s...") time.sleep(espera)

Guardrails básicos

  • Límite de max_tokens por respuesta para evitar respuestas descontroladamente largas
  • Validar que las herramientas críticas (ej. las que escriben en una base de datos) requieran confirmación explícita
  • Un límite de "pasos" máximos del event loop, para evitar que un agente entre en un ciclo de llamadas a herramientas infinito
Reto

Agregá un contador de tokens usados por sesión y cortá la conversación (con un mensaje claro al usuario) si se supera un presupuesto que definas.

Nivel 10 · Arquitectura final

Orquestador + load balancer en producción

Objetivo: ensamblar todo lo anterior en una arquitectura escalable: agentes como servicios, balanceados, detrás de un orquestador.

Llegado este punto tenés un orquestador que delega en agentes especialistas — pero corriendo todos en un solo proceso Python, un pico de tráfico tumba todo. El último paso es separar cada agente en su propio servicio, y repartir la carga entre múltiples instancias.

1. Cada agente como su propio servicio

Envolvés cada agente especialista en un microservicio con FastAPI, desplegable de forma independiente (Docker, Lambda, Fargate o EKS — Strands soporta los cuatro out of the box):

servicio_soporte.py
from fastapi import FastAPI from pydantic import BaseModel from strands import Agent from strands.models.openai import OpenAIModel app = FastAPI() model = OpenAIModel(model_id="gpt-4o") agente_soporte = Agent(model=model, system_prompt="Especialista en soporte técnico.") class Consulta(BaseModel): mensaje: str @app.post("/consultar") async def consultar(c: Consulta): respuesta = await agente_soporte.invoke_async(c.mensaje) return {"respuesta": str(respuesta)} # uvicorn servicio_soporte:app --host 0.0.0.0 --port 8001

2. Múltiples workers detrás de un load balancer

Corrés varias réplicas de cada servicio (ej. 3 instancias de servicio_soporte) y ponés un load balancer (nginx, o uno gestionado como AWS ALB) repartiendo tráfico entre ellas — así ningún agente especialista es un punto único de falla:

nginx.conf
upstream agentes_soporte { least_conn; # manda cada request al worker con menos conexiones activas server soporte-1:8001; server soporte-2:8001; server soporte-3:8001; } server { listen 80; location /soporte/ { proxy_pass http://agentes_soporte/; } }

3. Cola de trabajos para desacoplar el orquestador

Para picos de tráfico o tareas largas, el orquestador no llama a los especialistas directamente — encola el trabajo (Redis, SQS) y los workers lo consumen a su propio ritmo. Esto evita que un agente lento bloquee a todos los demás:

arquitectura final
Cliente │ ▼ Orquestador (Agent + tools de routing) │ ▼ Cola de trabajos (Redis / SQS) │ ├──▶ Load Balancer ──▶ [Worker Ventas x3] ├──▶ Load Balancer ──▶ [Worker Soporte x3] └──▶ Load Balancer ──▶ [Worker Facturación x3] │ ▼ OpenTelemetry (trazas de todo el flujo)

Con esto tenés el camino completo: de un agent("hola") de una línea en el Nivel 1, a una arquitectura de producción real con enrutamiento inteligente, escalado horizontal y observabilidad de punta a punta.

Reto final

Tomá el orquestador del Nivel 8, separá cada especialista en su propio servicio FastAPI, y montá 2 instancias de uno de ellos detrás de nginx con least_conn. Ese es tu primer sistema multi-agente en producción.