Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 32 additions & 182 deletions 01-tutorials/07-AgentCore-E2E/lab-02-agentcore-memory.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,9 @@
"import logging\n",
"from boto3.session import Session\n",
"\n",
"\n",
"# Import AgentCore Memory client and Hook events from strands\n",
"from bedrock_agentcore_starter_toolkit.operations.memory.manager import MemoryManager\n",
"from bedrock_agentcore.memory import MemoryClient\n",
"from bedrock_agentcore.memory.constants import StrategyType\n",
"from strands.hooks import (\n",
" AfterInvocationEvent,\n",
" HookProvider,\n",
" HookRegistry,\n",
" MessageAddedEvent,\n",
")\n",
"\n",
"from lab_helpers.utils import get_ssm_parameter, put_ssm_parameter\n",
"\n",
Expand Down Expand Up @@ -123,18 +116,12 @@
"metadata": {},
"outputs": [],
"source": [
"memory_client = MemoryClient(region_name=REGION)\n",
"memory_name = \"CustomerSupportMemory\"\n",
"\n",
"\n",
"def create_or_get_memory_resource():\n",
" try:\n",
" memory_id = get_ssm_parameter(\"/app/customersupport/agentcore/memory_id\")\n",
" memory_client.gmcp_client.get_memory(memoryId=memory_id)\n",
" return memory_id\n",
" except Exception:\n",
" try:\n",
" strategies = [\n",
"memory_manager = MemoryManager(region_name=REGION)\n",
"memory = memory_manager.get_or_create_memory(\n",
" name=memory_name,\n",
" strategies=[\n",
" {\n",
" StrategyType.USER_PREFERENCE.value: {\n",
" \"name\": \"CustomerPreferences\",\n",
Expand All @@ -150,28 +137,9 @@
" }\n",
" },\n",
" ]\n",
" print(\"Creating AgentCore Memory resources. This will take 2-3 minutes...\")\n",
" print(\"While we wait, let's understand what's happening behind the scenes:\")\n",
" print(\"• Setting up managed vector databases for semantic search\")\n",
" print(\"• Configuring memory extraction pipelines\")\n",
" print(\"• Provisioning secure, multi-tenant storage\")\n",
" print(\"• Establishing namespace isolation for customer data\")\n",
" # *** AGENTCORE MEMORY USAGE *** - Create memory resource with semantic strategy\n",
" response = memory_client.create_memory_and_wait(\n",
" name=memory_name,\n",
" description=\"Customer support agent memory\",\n",
" strategies=strategies,\n",
" event_expiry_days=90, # Memories expire after 90 days\n",
" )\n",
" memory_id = response[\"id\"]\n",
" try:\n",
" put_ssm_parameter(\"/app/customersupport/agentcore/memory_id\", memory_id)\n",
" except Exception as e:\n",
" raise e\n",
" return memory_id\n",
" except Exception as e:\n",
" print(f\"Failed to create memory resource: {e}\")\n",
" return None"
")\n",
"memory_id = memory[\"id\"]\n",
"put_ssm_parameter(\"/app/customersupport/agentcore/memory_id\", memory_id)"
]
},
{
Expand All @@ -180,7 +148,6 @@
"metadata": {},
"outputs": [],
"source": [
"memory_id = create_or_get_memory_resource()\n",
"if memory_id:\n",
" print(\"✅ AgentCore Memory created successfully!\")\n",
" print(f\"Memory ID: {memory_id}\")\n",
Expand Down Expand Up @@ -212,15 +179,12 @@
"metadata": {},
"outputs": [],
"source": [
"# List existing memory resources\n",
"for memory in memory_client.list_memories():\n",
" print(f\"Memory Arn: {memory.get('arn')}\")\n",
" print(f\"Memory ID: {memory.get('id')}\")\n",
" print(\"--------------------------------------------------------------------\")\n",
"import random\n",
"import string\n",
"from lab_helpers.lab2_memory import ACTOR_ID\n",
"\n",
"# Seed with previous customer interactions\n",
"CUSTOMER_ID = \"customer_001\"\n",
"\n",
"# Seed with previous customer interactions\n",
"previous_interactions = [\n",
" (\"I'm having issues with my MacBook Pro overheating during video editing.\", \"USER\"),\n",
" (\n",
Expand Down Expand Up @@ -248,9 +212,10 @@
"# Save previous interactions\n",
"if memory_id:\n",
" try:\n",
" memory_client = MemoryClient(region_name=REGION)\n",
" memory_client.create_event(\n",
" memory_id=memory_id,\n",
" actor_id=CUSTOMER_ID,\n",
" actor_id=ACTOR_ID,\n",
" session_id=\"previous_session\",\n",
" messages=previous_interactions,\n",
" )\n",
Expand Down Expand Up @@ -297,7 +262,7 @@
"while retries < max_retries:\n",
" memories = memory_client.retrieve_memories(\n",
" memory_id=memory_id,\n",
" namespace=f\"support/customer/{CUSTOMER_ID}/preferences\",\n",
" namespace=f\"support/customer/{ACTOR_ID}/preferences\",\n",
" query=\"can you summarize the support issue\",\n",
" )\n",
"\n",
Expand Down Expand Up @@ -353,7 +318,7 @@
"while True:\n",
" semantic_memories = memory_client.retrieve_memories(\n",
" memory_id=memory_id,\n",
" namespace=f\"support/customer/{CUSTOMER_ID}/semantic\",\n",
" namespace=f\"support/customer/{ACTOR_ID}/semantic\",\n",
" query=\"information on the technical support issue\",\n",
" )\n",
" print(\"🧠 AgentCore Memory identified these factual details from conversations:\")\n",
Expand All @@ -369,136 +334,13 @@
" print(f\" {i}. {text}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 3: Implement Strands Hooks to save and retrieve agent interactions\n",
"\n",
"Now we'll integrate AgentCore Memory with our agent using Strands' hook system. This creates an automatic memory layer that works seamlessly with any agent conversation.\n",
"\n",
"- **MessageAddedEvent**: Triggered when messages are added to the conversation, allowing us to retrieve and inject customer context\n",
"- **AfterInvocationEvent**: Fired after agent responses, enabling automatic storage of interactions to memory\n",
"\n",
"The hook system ensures memory operations happen automatically without manual intervention, creating a seamless experience where customer context is preserved across conversations.\n",
"\n",
"To create the hooks we will extend the `HookProvider` class:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class CustomerSupportMemoryHooks(HookProvider):\n",
" \"\"\"Memory hooks for customer support agent\"\"\"\n",
"\n",
" def __init__(\n",
" self, memory_id: str, client: MemoryClient, actor_id: str, session_id: str\n",
" ):\n",
" self.memory_id = memory_id\n",
" self.client = client\n",
" self.actor_id = actor_id\n",
" self.session_id = session_id\n",
" self.namespaces = {\n",
" i[\"type\"]: i[\"namespaces\"][0]\n",
" for i in self.client.get_memory_strategies(self.memory_id)\n",
" }\n",
"\n",
" def retrieve_customer_context(self, event: MessageAddedEvent):\n",
" \"\"\"Retrieve customer context before processing support query\"\"\"\n",
" messages = event.agent.messages\n",
" if (\n",
" messages[-1][\"role\"] == \"user\"\n",
" and \"toolResult\" not in messages[-1][\"content\"][0]\n",
" ):\n",
" user_query = messages[-1][\"content\"][0][\"text\"]\n",
"\n",
" try:\n",
" all_context = []\n",
"\n",
" for context_type, namespace in self.namespaces.items():\n",
" # *** AGENTCORE MEMORY USAGE *** - Retrieve customer context from each namespace\n",
" memories = self.client.retrieve_memories(\n",
" memory_id=self.memory_id,\n",
" namespace=namespace.format(actorId=self.actor_id),\n",
" query=user_query,\n",
" top_k=3,\n",
" )\n",
" # Post-processing: Format memories into context strings\n",
" for memory in memories:\n",
" if isinstance(memory, dict):\n",
" content = memory.get(\"content\", {})\n",
" if isinstance(content, dict):\n",
" text = content.get(\"text\", \"\").strip()\n",
" if text:\n",
" all_context.append(\n",
" f\"[{context_type.upper()}] {text}\"\n",
" )\n",
"\n",
" # Inject customer context into the query\n",
" if all_context:\n",
" context_text = \"\\n\".join(all_context)\n",
" original_text = messages[-1][\"content\"][0][\"text\"]\n",
" messages[-1][\"content\"][0][\"text\"] = (\n",
" f\"Customer Context:\\n{context_text}\\n\\n{original_text}\"\n",
" )\n",
" logger.info(f\"Retrieved {len(all_context)} customer context items\")\n",
"\n",
" except Exception as e:\n",
" logger.error(f\"Failed to retrieve customer context: {e}\")\n",
"\n",
" def save_support_interaction(self, event: AfterInvocationEvent):\n",
" \"\"\"Save customer support interaction after agent response\"\"\"\n",
" try:\n",
" messages = event.agent.messages\n",
" if len(messages) >= 2 and messages[-1][\"role\"] == \"assistant\":\n",
" # Get last customer query and agent response\n",
" customer_query = None\n",
" agent_response = None\n",
"\n",
" for msg in reversed(messages):\n",
" if msg[\"role\"] == \"assistant\" and not agent_response:\n",
" agent_response = msg[\"content\"][0][\"text\"]\n",
" elif (\n",
" msg[\"role\"] == \"user\"\n",
" and not customer_query\n",
" and \"toolResult\" not in msg[\"content\"][0]\n",
" ):\n",
" customer_query = msg[\"content\"][0][\"text\"]\n",
" break\n",
"\n",
" if customer_query and agent_response:\n",
" # *** AGENTCORE MEMORY USAGE *** - Save the support interaction\n",
" self.client.create_event(\n",
" memory_id=self.memory_id,\n",
" actor_id=self.actor_id,\n",
" session_id=self.session_id,\n",
" messages=[\n",
" (customer_query, \"USER\"),\n",
" (agent_response, \"ASSISTANT\"),\n",
" ],\n",
" )\n",
" logger.info(\"Saved support interaction to memory\")\n",
"\n",
" except Exception as e:\n",
" logger.error(f\"Failed to save support interaction: {e}\")\n",
"\n",
" def register_hooks(self, registry: HookRegistry) -> None:\n",
" \"\"\"Register customer support memory hooks\"\"\"\n",
" registry.add_callback(MessageAddedEvent, self.retrieve_customer_context)\n",
" registry.add_callback(AfterInvocationEvent, self.save_support_interaction)\n",
" logger.info(\"Customer support memory hooks registered\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 4: Create a Customer Support Agent with memory\n",
"\n",
"Next, we will implement the Customer Support Agent just as we did in Lab 1, but this time we instantiate the class `CustomerSupportMemoryHooks` and we pass the memory hook to the agent contructor."
"Next, we will implement the Customer Support Agent just as we did in Lab 1, but this time we will add AgentCoreMemoryConfig to the session manager of the Agent. It will let the agent to create new events and retrieve memories."
]
},
{
Expand All @@ -511,6 +353,8 @@
"\n",
"from strands import Agent\n",
"from strands.models import BedrockModel\n",
"from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig, RetrievalConfig\n",
"from bedrock_agentcore.memory.integrations.strands.session_manager import AgentCoreMemorySessionManager\n",
"\n",
"from lab_helpers.lab1_strands_agent import (\n",
" SYSTEM_PROMPT,\n",
Expand All @@ -521,19 +365,25 @@
" MODEL_ID,\n",
")\n",
"\n",
"SESSION_ID = str(uuid.uuid4())\n",
"memory_hooks = CustomerSupportMemoryHooks(\n",
" memory_id, memory_client, CUSTOMER_ID, SESSION_ID\n",
")\n",
"session_id = uuid.uuid4()\n",
"\n",
"memory_config = AgentCoreMemoryConfig(\n",
" memory_id=memory_id,\n",
" session_id=str(session_id),\n",
" actor_id=ACTOR_ID,\n",
" retrieval_config={\n",
" \"support/customer/{actorId}/semantic\": RetrievalConfig(top_k=3, relevance_score=0.2),\n",
" \"support/customer/{actorId}/preferences\": RetrievalConfig(top_k=3, relevance_score=0.2)\n",
" }\n",
" )\n",
"\n",
"# Initialize the Bedrock model (Anthropic Claude 3.7 Sonnet)\n",
"model = BedrockModel(model_id=MODEL_ID, region_name=REGION)\n",
"\n",
"# Create the customer support agent with all 5 tools\n",
"agent = Agent(\n",
" model=model,\n",
" hooks=[memory_hooks], # Pass Memory Hooks\n",
" session_manager=AgentCoreMemorySessionManager(memory_config, REGION),\n",
" tools=[\n",
" get_product_info, # Tool 1: Simple product information lookup\n",
" get_return_policy, # Tool 2: Simple return policy lookup\n",
Expand Down Expand Up @@ -598,14 +448,14 @@
"\n",
"- Created a serverless managed memory with Amazon Bedrock AgentCore Memory\n",
"- Implemented long-term memory to store User-Preferences and Semantic (Factual) information.\n",
"- Integrated AgentCore Memory with the customer support Agent using the hook mechanism provided by Strands Agents\n",
"- Integrated AgentCore Memory with the customer support Agent using the sesssion management mechanism provided by Strands Agents\n",
"\n",
"##### Next Up [Lab 3 - Scaling with Gateway and Identity →](lab-03-agentcore-gateway.ipynb)\n",
"\n",
"## Resources\n",
"- [Amazon Bedrock Agent Core Memory](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/memory.html)\n",
"- [Amazon Bedrock AgentCore Memory Deep Dive blog](https://aws.amazon.com/blogs/machine-learning/amazon-bedrock-agentcore-memory-building-context-aware-agents/)\n",
"- [Strands Agents Hooks Documentation](https://strandsagents.com/latest/documentation/docs/user-guide/concepts/agents/hooks/?h=hooks)"
"- [Strands Agent SDK - AgentCore Memory examples](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/strands-sdk-memory.html)"
]
}
],
Expand Down
28 changes: 19 additions & 9 deletions 01-tutorials/07-AgentCore-E2E/lab-03-agentcore-gateway.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -439,21 +439,31 @@
" get_technical_support,\n",
" SYSTEM_PROMPT,\n",
")\n",
"from lab_helpers.lab2_memory import (\n",
" CustomerSupportMemoryHooks,\n",
" create_or_get_memory_resource,\n",
")\n",
"\n",
"import uuid\n",
"from bedrock_agentcore.memory import MemoryClient\n",
"from bedrock_agentcore_starter_toolkit.operations.memory.manager import MemoryManager\n",
"from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig, RetrievalConfig\n",
"from bedrock_agentcore.memory.integrations.strands.session_manager import AgentCoreMemorySessionManager\n",
"\n",
"memory_client = MemoryClient(region_name=REGION)\n",
"memory_manager = MemoryManager(region_name=REGION)\n",
"memory_name = \"CustomerSupportMemory\"\n",
"memory = memory_manager.get_or_create_memory(name=memory_name)\n",
"\n",
"memory_id = create_or_get_memory_resource()\n",
"SESSION_ID = str(uuid.uuid4())\n",
"CUSTOMER_ID = \"customer_001\"\n",
"memory_hooks = CustomerSupportMemoryHooks(\n",
" memory_id, memory_client, CUSTOMER_ID, SESSION_ID\n",
")\n",
"\n",
"memory_id = memory[\"id\"]\n",
"memory_config = AgentCoreMemoryConfig(\n",
" memory_id=memory_id,\n",
" session_id=str(SESSION_ID),\n",
" actor_id=CUSTOMER_ID,\n",
" retrieval_config={\n",
" \"support/customer/{actorId}/semantic\": RetrievalConfig(top_k=3, relevance_score=0.2),\n",
" \"support/customer/{actorId}/preferences\": RetrievalConfig(top_k=3, relevance_score=0.2)\n",
" }\n",
" )\n",
"\n",
"# Initialize the Bedrock model\n",
"model_id = \"us.anthropic.claude-3-7-sonnet-20250219-v1:0\"\n",
Expand All @@ -477,8 +487,8 @@
" agent = Agent(\n",
" model=model,\n",
" tools=tools,\n",
" hooks=[memory_hooks],\n",
" system_prompt=SYSTEM_PROMPT,\n",
" session_manager=AgentCoreMemorySessionManager(memory_config, REGION),\n",
" )\n",
" response = agent(prompt)\n",
" return response\n",
Expand Down
Loading