Ir al contenido

Implementar y configurar el agente Story

El Agente Story es un agente de Strands generado con --protocol=AG-UI en el Módulo 1, para que la interfaz de usuario pueda transmitir desde él a través del protocolo Agent-User Interaction mediante CopilotKit. Utiliza el Servidor MCP de Inventario para gestionar los objetos del jugador, y el S3SessionManager integrado de Strands para persistir el historial de conversación en el bucket de sesiones que aprovisionamos en el Módulo 2.

Actualiza los siguientes archivos en packages/story/dungeon_adventure_story/agent:

import logging
import os
import uuid
from functools import cache
from typing import Any, cast
from ag_ui.core import RunAgentInput
from ag_ui_strands import StrandsAgent, StrandsAgentConfig, create_strands_app
from aws_lambda_powertools.utilities import parameters
from dungeon_adventure_agent_connection import session_id_context
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
from strands.session import FileSessionManager, S3SessionManager, SessionManager
from .agent import get_agent
logging.basicConfig(level=logging.INFO)
SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
@cache
def _resolve_sessions_bucket() -> str:
"""Read the conversation-history bucket name from runtime config.
Resolved lazily (and memoised) so `fastapi dev` can import this module
before ``RUNTIME_CONFIG_APP_ID`` is in the environment.
"""
application = os.environ.get("RUNTIME_CONFIG_APP_ID")
if not application:
raise RuntimeError("RUNTIME_CONFIG_APP_ID is not set — cannot resolve the StorySessions bucket.")
provider = parameters.AppConfigProvider(environment="default", application=application)
buckets = cast(dict[str, Any], provider.get("buckets", transform="json"))
return buckets["StorySessions"]["bucketName"]
def _session_manager_provider(input_data: RunAgentInput) -> SessionManager:
"""Create a session manager keyed by the AG-UI thread_id.
- In AgentCore (and `agent-serve`), persist to the shared ``StorySessions``
S3 bucket — the same bucket the Game API reads from to rebuild
conversation history on revisit.
- In `agent-dev` (`LOCAL_DEV=true`), persist to a temp
directory so the agent can run fully offline against the local MCP
server without any AWS calls.
"""
session_id = input_data.thread_id or "default"
if os.environ.get("LOCAL_DEV") == "true":
return FileSessionManager(session_id=session_id, storage_dir="/tmp/strands-sessions")
return S3SessionManager(session_id=session_id, bucket=_resolve_sessions_bucket())
# The template Agent is cloned per thread_id by ``StrandsAgent`` — we plug in
# a ``session_manager_provider`` so each thread gets its own session manager
# and conversation history is replayed on subsequent turns and survives agent
# restarts.
_agent_ctx = get_agent()
_agent = _agent_ctx.__enter__()
agui_agent = StrandsAgent(
agent=_agent,
name="StoryAgent",
description="A Strands Agent exposed via the AG-UI protocol.",
config=StrandsAgentConfig(session_manager_provider=_session_manager_provider),
)
class _SessionIdMiddleware(BaseHTTPMiddleware):
"""Bind the session ID for this request so downstream MCP / A2A clients forward it on outbound calls."""
async def dispatch(self, request: Request, call_next):
session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
with session_id_context(session_id):
return await call_next(request)
app = create_strands_app(agui_agent, path="/invocations")
app.add_middleware(_SessionIdMiddleware)

Los cambios son:

  • main.py añade un session_manager_provider que crea un S3SessionManager por thread_id cuando se despliega, y recurre a un FileSessionManager en disco bajo /tmp/strands-sessions cuando se ejecuta bajo agent-serve-local (SERVE_LOCAL=true). En despliegue, el bucket de S3 es el mismo del que lee queryActions de la API del juego, por lo que el navegador puede reconstruir las transcripciones al volver a visitar; localmente el agente persiste las sesiones en disco y se comunica con el servidor MCP local, sin necesidad de un despliegue.
  • agent.py elimina la herramienta de ejemplo subtract y cambia el prompt del sistema por uno de maestro de mazmorras que invita al primer mensaje del usuario a indicar el nombre del jugador y el género, y utiliza las herramientas del Servidor MCP de Inventario.

Para construir el código:

Terminal window
pnpm build

El target generado agent-chat abre un REPL interactivo contra tu agente. Se ejecuta de forma independiente y se conecta a tu agente en ejecución local, así que primero inicia el servidor local del agente en una terminal:

Terminal window
pnpm nx agent-dev story

Luego, en una segunda terminal, inicia el chat:

Terminal window
pnpm nx agent-chat story

Tu primer mensaje debe indicarle al agente el nombre de tu héroe y el género (por ejemplo: Mi nombre es Alice. Comienza mi aventura zombie.) y la historia se transmitirá de vuelta.

¡Felicitaciones! ¡Has construido y probado tu primer Agente Strands localmente! 🎉🎉🎉