Jeu de Donjon d'IA Agentique
Module 3 : Implémentation de l’agent narratif
Section intitulée « Module 3 : Implémentation de l’agent narratif »Le Story Agent est un agent Strands qui, étant donné un Game
et une liste d’Action
s comme contexte, fait progresser une histoire. Nous configurerons l’agent pour interagir avec notre Inventory MCP Server afin de gérer les objets disponibles du joueur.
Implémentation de l’agent
Section intitulée « Implémentation de l’agent »Implémentons notre agent. Mettez à jour les fichiers suivants dans packages/story/dungeon_adventure_story/agent
:
import osfrom bedrock_agentcore.runtime import BedrockAgentCoreApp
from .agent import get_agent
app = BedrockAgentCoreApp()
PORT = int(os.environ.get("PORT", "8080"))
@app.entrypointasync def invoke(payload, context): """Handler for agent invocation""" player_name = payload.get("playerName") genre = payload.get("genre") actions = payload.get("actions")
messages = [{"role": "user", "content": [{"text": "Continue or create a new story..."}]}] for action in actions: messages.append({"role": action["role"], "content": [{"text": action["content"]}]})
with get_agent(player_name, genre, session_id=context.session_id) as agent: stream = agent.stream_async(messages) async for event in stream: print(event) yield (event)
if __name__ == "__main__": app.run(port=PORT)
import osfrom bedrock_agentcore.runtime import BedrockAgentCoreApp
from .agent import get_agent
app = BedrockAgentCoreApp()
PORT = int(os.environ.get("PORT", "8080"))
@app.entrypointasync def invoke(payload, context): """Handler for agent invocation""" prompt = payload.get( "prompt", "No prompt found in input, please guide the user " "to create a json payload with prompt key" ) player_name = payload.get("playerName") genre = payload.get("genre") actions = payload.get("actions")
with get_agent(session_id=context.session_id) as agent: stream = agent.stream_async(prompt) messages = [{"role": "user", "content": [{"text": "Continue or create a new story..."}]}] for action in actions: messages.append({"role": action["role"], "content": [{"text": action["content"]}]})
with get_agent(player_name, genre, session_id=context.session_id) as agent: stream = agent.stream_async(messages) async for event in stream: print(event) yield (event)
if __name__ == "__main__": app.run() app.run(port=PORT)
import osfrom contextlib import contextmanager
import boto3from strands import Agent
from .agentcore_mcp_client import AgentCoreMCPClient
# Obtain the region and credentialsregion = os.environ["AWS_REGION"]boto_session = boto3.Session(region_name=region)credentials = boto_session.get_credentials()
@contextmanagerdef get_agent(player_name: str, genre: str, session_id: str): mcp_client = AgentCoreMCPClient.with_iam_auth( agent_runtime_arn=os.environ["INVENTORY_MCP_ARN"], credentials=credentials, region=region, session_id=session_id, ) with mcp_client: yield Agent( system_prompt=f"""You are running a text adventure game in the genre <genre>{genre}</genre> for player <player>{player_name}</player>.Construct a scenario and give the player decisions to make.Use the tools to manage the player's inventory as items are obtained or lost.When adding, removing or updating items in the inventory, always list items to check the current state,and be careful to match item names exactly. Item names in the inventory must be Title Case.Ensure you specify a suitable emoji when adding items if available.When starting a game, populate the inventory with a few initial items. Items should be a key part of the narrative.Keep responses under 100 words.""", tools=[*mcp_client.list_tools_sync()], )
import osfrom contextlib import contextmanager
from strands import Agent, toolfrom strands_tools import current_timeimport boto3from strands import Agent
from .agentcore_mcp_client import AgentCoreMCPClient
# Define a custom tool@tooldef add(a: int, b: int) -> int: return a + b# Obtain the region and credentialsregion = os.environ["AWS_REGION"]boto_session = boto3.Session(region_name=region)credentials = boto_session.get_credentials()
@contextmanagerdef get_agent(session_id: str): yield Agent( system_prompt="""You are an addition wizard.Use the 'add' tool for addition tasks.Refer to tools as your 'spellbook'.""", tools=[add, current_time],def get_agent(player_name: str, genre: str, session_id: str): mcp_client = AgentCoreMCPClient.with_iam_auth( agent_runtime_arn=os.environ["INVENTORY_MCP_ARN"], credentials=credentials, region=region, session_id=session_id, ) with mcp_client: yield Agent( system_prompt=f"""You are running a text adventure game in the genre <genre>{genre}</genre> for player <player>{player_name}</player>.Construct a scenario and give the player decisions to make.Use the tools to manage the player's inventory as items are obtained or lost.When adding, removing or updating items in the inventory, always list items to check the current state,and be careful to match item names exactly. Item names in the inventory must be Title Case.Ensure you specify a suitable emoji when adding items if available.When starting a game, populate the inventory with a few initial items. Items should be a key part of the narrative.Keep responses under 100 words.""", tools=[*mcp_client.list_tools_sync()], )
Cette configuration permet :
- L’extraction du joueur, du genre et des actions depuis le payload de l’agent
- La construction d’un client que l’agent peut utiliser pour invoquer notre serveur MCP avec une authentification SigV4
- La construction de l’agent avec un prompt système et les outils du serveur MCP
Déploiement et tests
Section intitulée « Déploiement et tests »D’abord, compilons le codebase :
pnpm nx run-many --target build --all
yarn nx run-many --target build --all
npx nx run-many --target build --all
bunx nx run-many --target build --all
Déployez maintenant l’application avec :
pnpm nx deploy infra dungeon-adventure-infra-sandbox/*
yarn nx deploy infra dungeon-adventure-infra-sandbox/*
npx nx deploy infra dungeon-adventure-infra-sandbox/*
bunx nx deploy infra dungeon-adventure-infra-sandbox/*
Ce déploiement prendra environ 2 minutes.
Une fois terminé, vous verrez des sorties similaires à ceci (valeurs masquées) :
dungeon-adventure-infra-sandbox-Applicationdungeon-adventure-infra-sandbox-Application: deploying... [2/2]
✅ dungeon-adventure-infra-sandbox-Application
✨ Temps de déploiement : 354s
Outputs:dungeon-adventure-infra-sandbox-Application.ElectroDbTableTableNameXXX = dungeon-adventure-infra-sandbox-Application-ElectroDbTableXXX-YYYdungeon-adventure-infra-sandbox-Application.GameApiEndpointXXX = https://xxx.execute-api.region.amazonaws.com/prod/dungeon-adventure-infra-sandbox-Application.GameUIDistributionDomainNameXXX = xxx.cloudfront.netdungeon-adventure-infra-sandbox-Application.InventoryMcpArn = arn:aws:bedrock-agentcore:region:xxxxxxx:runtime/dungeonadventureventoryMcpServerXXXX-YYYYdungeon-adventure-infra-sandbox-Application.StoryAgentArn = arn:aws:bedrock-agentcore:region:xxxxxxx:runtime/dungeonadventurecationStoryAgentXXXX-YYYYdungeon-adventure-infra-sandbox-Application.UserIdentityUserIdentityIdentityPoolIdXXX = region:xxxdungeon-adventure-infra-sandbox-Application.UserIdentityUserIdentityUserPoolIdXXX = region_xxx
Testez l’API via :
- Un serveur Agent local avec
curl
- L’API déployée avec un token JWT
Démarrez le serveur local avec :
PORT=9999 INVENTORY_MCP_ARN=arn:aws:bedrock-agentcore:region:xxxxxxx:runtime/dungeonadventureventoryMcpServerXXXX-YYYY AWS_REGION=<region> pnpm nx run dungeon_adventure.story:agent-serve
PORT=9999 INVENTORY_MCP_ARN=arn:aws:bedrock-agentcore:region:xxxxxxx:runtime/dungeonadventureventoryMcpServerXXXX-YYYY AWS_REGION=<region> yarn nx run dungeon_adventure.story:agent-serve
PORT=9999 INVENTORY_MCP_ARN=arn:aws:bedrock-agentcore:region:xxxxxxx:runtime/dungeonadventureventoryMcpServerXXXX-YYYY AWS_REGION=<region> npx nx run dungeon_adventure.story:agent-serve
PORT=9999 INVENTORY_MCP_ARN=arn:aws:bedrock-agentcore:region:xxxxxxx:runtime/dungeonadventureventoryMcpServerXXXX-YYYY AWS_REGION=<region> bunx nx run dungeon_adventure.story:agent-serve
Invoquez le serveur avec :
curl -N -X POST http://127.0.0.1:9999/invocations \ -d '{"genre":"superhero", "actions":[], "playerName":"UnnamedHero"}' \ -H "Content-Type: application/json" \ -H "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id: abcdefghijklmnopqrstuvwxyz-123456789"
Authentifiez-vous avec Cognito pour obtenir un token JWT :
export POOL_ID="<UserPoolId des sorties CDK>"export CLIENT_ID="<UserPoolClientId des sorties CDK>"export REGION="<votre-région>"
Créez un utilisateur test :
aws cognito-idp admin-create-user \ --user-pool-id $POOL_ID \ --username "testuser" \ --temporary-password "TempPass123!" \ --region $REGION \ --message-action SUPPRESS > /dev/null
aws cognito-idp admin-set-user-password \ --user-pool-id $POOL_ID \ --username "testuser" \ --password "PermanentPass123!" \ --region $REGION \ --permanent > /dev/null
export BEARER_TOKEN=$(aws cognito-idp initiate-auth \ --client-id "$CLIENT_ID" \ --auth-flow USER_PASSWORD_AUTH \ --auth-parameters USERNAME='testuser',PASSWORD='PermanentPass123!' \ --region $REGION | jq -r '.AuthenticationResult.IdToken')
Invoquez l’agent déployé :
export AGENT_ARN="<StoryAgentArn des sorties CDK>"export ENCODED_ARN=$(echo $AGENT_ARN | sed 's/:/%3A/g' | sed 's/\//%2F/g')export MCP_URL="https://bedrock-agentcore.$REGION.amazonaws.com/runtimes/$ENCODED_ARN/invocations?qualifier=DEFAULT"
curl -N -X POST "$MCP_URL" \ -H "authorization: Bearer $BEARER_TOKEN" \ -H "Content-Type: application/json" \ -H "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id: abcdefghijklmnopqrstuvwxyz-123456789" \ -d '{"genre":"superhero", "actions":[], "playerName":"UnnamedHero"}'
En cas de succès, vous verrez un flux d’événements similaire à :
data: {"init_event_loop": true}
data: {"start": true}
data: {"start_event_loop": true}
data: {"event": {"messageStart": {"role": "assistant"}}}
data: {"event": {"contentBlockDelta": {"delta": {"text": "Welcome"}, "contentBlockIndex": 0}}}
...
Félicitations. Vous avez déployé votre premier agent Strands sur Bedrock AgentCore Runtime ! 🎉🎉🎉