Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
164 changes: 25 additions & 139 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,8 @@
"import logging\n",
"from boto3.session import Session\n",
"\n",
"\n",
"# Import AgentCore Memory client and Hook events from strands\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 @@ -212,15 +204,17 @@
"metadata": {},
"outputs": [],
"source": [
"import random\n",
"import string\n",
"from lab_helpers.lab2_memory import ACTOR_ID\n",
"\n",
"# 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",
"\n",
"# Seed with previous customer interactions\n",
"CUSTOMER_ID = \"customer_001\"\n",
"\n",
"previous_interactions = [\n",
" (\"I'm having issues with my MacBook Pro overheating during video editing.\", \"USER\"),\n",
" (\n",
Expand Down Expand Up @@ -250,7 +244,7 @@
" try:\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 +291,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 +347,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 @@ -373,133 +367,17 @@
"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",
"## Step 4: Create a Customer Support Agent with memory\n",
"\n",
"To create the hooks we will extend the `HookProvider` class:\n"
"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."
]
},
{
"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."
]
"source": []
},
{
"cell_type": "code",
Expand All @@ -511,6 +389,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 +401,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=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 +484,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
27 changes: 18 additions & 9 deletions 01-tutorials/07-AgentCore-E2E/lab-04-agentcore-runtime.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@
"import boto3\n",
"from strands.models import BedrockModel\n",
"from lab_helpers.utils import get_ssm_parameter\n",
"from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig, RetrievalConfig\n",
"from bedrock_agentcore.memory.integrations.strands.session_manager import AgentCoreMemorySessionManager\n",
"from lab_helpers.lab1_strands_agent import (\n",
" get_return_policy,\n",
" get_product_info,\n",
Expand All @@ -146,7 +148,6 @@
")\n",
"\n",
"from lab_helpers.lab2_memory import (\n",
" CustomerSupportMemoryHooks,\n",
" memory_client,\n",
" ACTOR_ID,\n",
" SESSION_ID,\n",
Expand All @@ -161,11 +162,18 @@
"# Lab1 import: Create the Bedrock model\n",
"model = BedrockModel(model_id=MODEL_ID)\n",
"\n",
"# Lab2 import : Initialize memory via hooks\n",
"# Lab2 import : Initialize memory config\n",
"memory_id = get_ssm_parameter(\"/app/customersupport/agentcore/memory_id\")\n",
"memory_hooks = CustomerSupportMemoryHooks(\n",
" memory_id, memory_client, ACTOR_ID, SESSION_ID\n",
")\n",
"\n",
"memory_config = AgentCoreMemoryConfig(\n",
" memory_id=memory_id,\n",
" session_id=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 AgentCore Runtime App\n",
"app = BedrockAgentCoreApp() #### AGENTCORE RUNTIME - LINE 2 ####\n",
Expand Down Expand Up @@ -218,9 +226,9 @@
" # Create the agent with all customer support tools\n",
" agent = Agent(\n",
" model=model,\n",
" session_manager=AgentCoreMemorySessionManager(memory_config, REGION),\n",
" tools=tools,\n",
" system_prompt=SYSTEM_PROMPT,\n",
" hooks=[memory_hooks],\n",
" )\n",
" # Invoke the agent\n",
" response = agent(user_input)\n",
Expand Down Expand Up @@ -470,6 +478,7 @@
"outputs": [],
"source": [
"import uuid\n",
"from IPython.display import display, Markdown\n",
"\n",
"# Create a session ID for demonstrating session continuity\n",
"session_id = uuid.uuid4()\n",
Expand All @@ -483,7 +492,7 @@
" session_id=str(session_id),\n",
")\n",
"\n",
"print(response[\"response\"])"
"display(Markdown(response[\"response\"].replace('\\\\n', '\\n')))"
]
},
{
Expand All @@ -509,7 +518,7 @@
" bearer_token=access_token[\"bearer_token\"],\n",
" session_id=str(session_id),\n",
")\n",
"print(response[\"response\"])"
"display(Markdown(response[\"response\"].replace('\\\\n', '\\n')))"
]
},
{
Expand Down Expand Up @@ -537,7 +546,7 @@
" bearer_token=access_token[\"bearer_token\"],\n",
" session_id=str(session_id2),\n",
")\n",
"print(response[\"response\"])"
"display(Markdown(response[\"response\"].replace('\\\\n', '\\n')))"
]
},
{
Expand Down
1 change: 1 addition & 0 deletions 01-tutorials/07-AgentCore-E2E/lab_helpers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ def create_agentcore_runtime_execution_role():
"Effect": "Allow",
"Action": [
"bedrock-agentcore:CreateEvent",
"bedrock-agentcore:ListEvents",
"bedrock-agentcore:GetMemoryRecord",
"bedrock-agentcore:GetMemory",
"bedrock-agentcore:RetrieveMemoryRecords",
Expand Down