Skip to content

Commit 7f866d9

Browse files
authored
feat: add AgentCore Memory Session Manager with Strands Agents (#65)
* feat: add Strands AgentCore Memory integration - Add AgentCoreMemorySessionManager for persistent conversation storage - Implement bedrock_converter for message format transformation - Add configuration management for memory settings - Include comprehensive unit and integration tests - Add strands-agents optional dependency in pyproject.toml - Provide documentation and examples for STM/LTM support * readme updates * use native message parsing from SessionMessage and updating naming of converter class * move integration under strands folder to allow for other providers in the future * move integration under strands folder to allow for other providers in the future * update formatting so that `uv run pre-commit run --all-file` passes
1 parent 1a85ebe commit 7f866d9

File tree

16 files changed

+2341
-1
lines changed

16 files changed

+2341
-1
lines changed

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,8 @@ dev = [
144144
"wheel>=0.45.1",
145145
"strands-agents>=1.1.0",
146146
]
147+
148+
[project.optional-dependencies]
149+
strands-agents = [
150+
"strands-agents>=1.1.0"
151+
]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Memory integrations for Bedrock AgentCore."""
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
# Strands AgentCore Memory Examples
2+
3+
This directory contains comprehensive examples demonstrating how to use the Strands AgentCoreMemorySessionManager with Amazon Bedrock AgentCore Memory for persistent conversation storage and intelligent retrieval (Supports STM and LTM).
4+
5+
## Quick Setup
6+
7+
```bash
8+
pip install 'bedrock-agentcore[strands-agents]'
9+
```
10+
11+
or to develop locally:
12+
```bash
13+
git clone https://github.com/aws/bedrock-agentcore-sdk-python.git
14+
cd bedrock-agentcore-sdk-python
15+
uv sync
16+
source .venv/bin/activate
17+
```
18+
19+
## Examples Overview
20+
21+
### 1. Short-Term Memory (STM)
22+
Basic memory functionality for conversation persistence within a session.
23+
24+
### 2. Long-Term Memory (LTM)
25+
Advanced memory with multiple strategies for user preferences, facts, and session summaries.
26+
27+
---
28+
29+
## Short-Term Memory Example
30+
31+
### Basic Setup
32+
33+
```python
34+
import uuid
35+
import boto3
36+
from datetime import date
37+
from strands import Agent
38+
from bedrock_agentcore.memory import MemoryClient
39+
from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig, RetrievalConfig
40+
from bedrock_agentcore.memory.integrations.strands.session_manager import AgentCoreMemorySessionManager
41+
```
42+
43+
### Create a Basic Memory
44+
45+
```python
46+
client = MemoryClient(region_name="us-east-1")
47+
basic_memory = client.create_memory(
48+
name="BasicTestMemory",
49+
description="Basic memory for testing short-term functionality"
50+
)
51+
print(basic_memory.get('id'))
52+
```
53+
54+
### Configure and Use Agent
55+
56+
```python
57+
MEM_ID = basic_memory.get('id')
58+
ACTOR_ID = "actor_id_test_%s" % datetime.now().strftime("%Y%m%d%H%M%S")
59+
SESSION_ID = "testing_session_id_%s" % datetime.now().strftime("%Y%m%d%H%M%S")
60+
61+
62+
# Configure memory
63+
agentcore_memory_config = AgentCoreMemoryConfig(
64+
memory_id=MEM_ID,
65+
session_id=SESSION_ID,
66+
actor_id=ACTOR_ID
67+
)
68+
69+
# Create session manager
70+
session_manager = AgentCoreMemorySessionManager(
71+
agentcore_memory_config=agentcore_memory_config,
72+
region_name="us-east-1"
73+
)
74+
75+
# Create agent
76+
agent = Agent(
77+
system_prompt="You are a helpful assistant. Use all you know about the user to provide helpful responses.",
78+
session_manager=session_manager,
79+
)
80+
```
81+
82+
### Example Conversation
83+
84+
```python
85+
agent("I like sushi with tuna")
86+
# Agent remembers this preference
87+
88+
agent("I like pizza")
89+
# Agent acknowledges both preferences
90+
91+
agent("What should I buy for lunch today?")
92+
# Agent suggests options based on remembered preferences
93+
```
94+
95+
---
96+
97+
## Long-Term Memory Example
98+
99+
### Create LTM Memory with Strategies
100+
101+
```python
102+
from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig, RetrievalConfig
103+
from bedrock_agentcore.memory.integrations.strands.session_manager import AgentCoreMemorySessionManager
104+
from datetime import datetime
105+
106+
# Create comprehensive memory with all built-in strategies
107+
client = MemoryClient(region_name="us-east-1")
108+
comprehensive_memory = client.create_memory_and_wait(
109+
name="ComprehensiveAgentMemory",
110+
description="Full-featured memory with all built-in strategies",
111+
strategies=[
112+
{
113+
"summaryMemoryStrategy": {
114+
"name": "SessionSummarizer",
115+
"namespaces": ["/summaries/{actorId}/{sessionId}"]
116+
}
117+
},
118+
{
119+
"userPreferenceMemoryStrategy": {
120+
"name": "PreferenceLearner",
121+
"namespaces": ["/preferences/{actorId}"]
122+
}
123+
},
124+
{
125+
"semanticMemoryStrategy": {
126+
"name": "FactExtractor",
127+
"namespaces": ["/facts/{actorId}"]
128+
}
129+
}
130+
]
131+
)
132+
MEM_ID = comprehensive_memory.get('id')
133+
ACTOR_ID = "actor_id_test_%s" % datetime.now().strftime("%Y%m%d%H%M%S")
134+
SESSION_ID = "testing_session_id_%s" % datetime.now().strftime("%Y%m%d%H%M%S")
135+
136+
```
137+
138+
### Single Namespace Retrieval
139+
140+
```python
141+
config = AgentCoreMemoryConfig(
142+
memory_id=MEM_ID,
143+
session_id=SESSION_ID,
144+
actor_id=ACTOR_ID,
145+
retrieval_config={
146+
"/preferences/{actorId}": RetrievalConfig(
147+
top_k=5,
148+
relevance_score=0.7
149+
)
150+
}
151+
)
152+
session_manager = AgentCoreMemorySessionManager(config, region_name='us-east-1')
153+
ltm_agent = Agent(session_manager=session_manager)
154+
```
155+
156+
### Multiple Namespace Retrieval
157+
158+
```python
159+
config = AgentCoreMemoryConfig(
160+
memory_id=MEM_ID,
161+
session_id=SESSION_ID,
162+
actor_id=ACTOR_ID,
163+
retrieval_config={
164+
"/preferences/{actorId}": RetrievalConfig(
165+
top_k=5,
166+
relevance_score=0.7
167+
),
168+
"/facts/{actorId}": RetrievalConfig(
169+
top_k=10,
170+
relevance_score=0.3
171+
),
172+
"/summaries/{actorId}/{sessionId}": RetrievalConfig(
173+
top_k=5,
174+
relevance_score=0.5
175+
)
176+
}
177+
)
178+
session_manager = AgentCoreMemorySessionManager(config, region_name='us-east-1')
179+
agent_with_multiple_namespaces = Agent(session_manager=session_manager)
180+
```
181+
182+
---
183+
184+
## Large Payload example processing an Image using the [strands_tools](https://github.com/strands-agents/tools) library
185+
186+
### Agent with Image Processing
187+
188+
```python
189+
from strands import Agent, tool
190+
from strands_tools import generate_image, image_reader
191+
192+
ACTOR_ID = "actor_id_test_%s" % datetime.now().strftime("%Y%m%d%H%M%S")
193+
SESSION_ID = "testing_session_id_%s" % datetime.now().strftime("%Y%m%d%H%M%S")
194+
195+
config = AgentCoreMemoryConfig(
196+
memory_id=MEM_ID,
197+
session_id=SESSION_ID,
198+
actor_id=ACTOR_ID,
199+
)
200+
session_manager = AgentCoreMemorySessionManager(config, region_name='us-east-1')
201+
agent_with_tools = Agent(
202+
tools=[image_reader],
203+
system_prompt="You will be provided with a filesystem path to an image. Describe the image in detail.",
204+
session_manager=session_manager,
205+
agent_id='my_test_agent_id'
206+
)
207+
# Use with image
208+
result = agent_with_tools("/path/to/image.png")
209+
```
210+
211+
---
212+
213+
## Key Configuration Options
214+
215+
### AgentCoreMemoryConfig Parameters
216+
217+
- `memory_id`: ID of the Bedrock AgentCore Memory resource
218+
- `session_id`: Unique identifier for the conversation session
219+
- `actor_id`: Unique identifier for the user/actor
220+
- `retrieval_config`: Dictionary mapping namespaces to RetrievalConfig objects
221+
222+
### RetrievalConfig Parameters
223+
224+
- `top_k`: Number of top results to retrieve (default: 5)
225+
- `relevance_score`: Minimum relevance threshold (0.0-1.0)
226+
227+
### Memory Strategies
228+
https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/memory-strategies.html
229+
230+
1. **summaryMemoryStrategy**: Summarizes conversation sessions
231+
2. **userPreferenceMemoryStrategy**: Learns and stores user preferences
232+
3. **semanticMemoryStrategy**: Extracts and stores factual information
233+
234+
### Namespace Patterns
235+
236+
- `/preferences/{actorId}`: User-specific preferences
237+
- `/facts/{actorId}`: User-specific facts
238+
- `/summaries/{actorId}/{sessionId}`: Session-specific summaries
239+
240+
241+
---
242+
243+
## Important Notes
244+
245+
### Session Management
246+
- Only **one** agent per session is currently supported
247+
- Creating multiple agents with the same session will show a warning
248+
249+
### Memory Types
250+
- **STM (Short-Term Memory)**: Basic conversation persistence within a session
251+
- **LTM (Long-Term Memory)**: Advanced memory with multiple strategies for learning user preferences, facts, and summaries
252+
253+
### Best Practices
254+
- Use unique `session_id` for each conversation
255+
- Use consistent `actor_id` for the same user across sessions
256+
- Configure appropriate `relevance_score` thresholds for your use case
257+
- Test with different `top_k` values to optimize retrieval performance
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Strands integration for Bedrock AgentCore Memory."""
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
"""Bedrock AgentCore Memory conversion utilities."""
2+
3+
import json
4+
import logging
5+
from typing import Any, Tuple
6+
7+
from strands.types.session import SessionMessage
8+
9+
logger = logging.getLogger(__name__)
10+
11+
CONVERSATIONAL_MAX_SIZE = 9000
12+
13+
14+
class AgentCoreMemoryConverter:
15+
"""Handles conversion between Strands and Bedrock AgentCore Memory formats."""
16+
17+
@staticmethod
18+
def message_to_payload(session_message: SessionMessage) -> list[Tuple[str, str]]:
19+
"""Convert a SessionMessage to Bedrock AgentCore Memory message format.
20+
21+
Args:
22+
session_message (SessionMessage): The session message to convert.
23+
24+
Returns:
25+
list[Tuple[str, str]]: list of (text, role) tuples for Bedrock AgentCore Memory.
26+
"""
27+
session_dict = session_message.to_dict()
28+
return [(json.dumps(session_dict), session_message.message["role"])]
29+
30+
@staticmethod
31+
def events_to_messages(events: list[dict[str, Any]]) -> list[SessionMessage]:
32+
"""Convert Bedrock AgentCore Memory events to SessionMessages.
33+
34+
Args:
35+
events (list[dict[str, Any]]): list of events from Bedrock AgentCore Memory.
36+
Each individual event looks as follows:
37+
```
38+
{
39+
"memoryId": "unique_mem_id",
40+
"actorId": "actor_id",
41+
"sessionId": "session_id",
42+
"eventId": "0000001756147154000#ffa53e54",
43+
"eventTimestamp": datetime.datetime(2025, 8, 25, 15, 12, 34, tzinfo=tzlocal()),
44+
"payload": [
45+
{
46+
"conversational": {
47+
"content": {"text": "What is the weather?"},
48+
"role": "USER",
49+
}
50+
}
51+
],
52+
"branch": {"name": "main"},
53+
}
54+
```
55+
56+
Returns:
57+
list[SessionMessage]: list of SessionMessage objects.
58+
"""
59+
messages = []
60+
for event in events:
61+
for payload_item in event.get("payload", []):
62+
if "conversational" in payload_item:
63+
conv = payload_item["conversational"]
64+
messages.append(SessionMessage.from_dict(json.loads(conv["content"]["text"])))
65+
elif "blob" in payload_item:
66+
try:
67+
blob_data = json.loads(payload_item["blob"])
68+
if isinstance(blob_data, (tuple, list)) and len(blob_data) == 2:
69+
try:
70+
messages.append(SessionMessage.from_dict(json.loads(blob_data[0])))
71+
except (json.JSONDecodeError, ValueError):
72+
logger.error("This is not a SessionMessage but just a blob message. Ignoring")
73+
except (json.JSONDecodeError, ValueError):
74+
logger.error("Failed to parse blob content: %s", payload_item)
75+
return list(reversed(messages))
76+
77+
@staticmethod
78+
def total_length(message: tuple[str, str]) -> int:
79+
"""Calculate total length of a message tuple."""
80+
return sum(len(text) for text in message)
81+
82+
@staticmethod
83+
def exceeds_conversational_limit(message: tuple[str, str]) -> bool:
84+
"""Check if message exceeds conversational size limit."""
85+
return AgentCoreMemoryConverter.total_length(message) >= CONVERSATIONAL_MAX_SIZE
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Configuration for AgentCore Memory Session Manager."""
2+
3+
from typing import Dict, Optional
4+
5+
from pydantic import BaseModel, Field
6+
7+
8+
class RetrievalConfig(BaseModel):
9+
"""Configuration for memory retrieval operations.
10+
11+
Attributes:
12+
top_k: Number of top-scoring records to return from semantic search (default: 10)
13+
relevance_score: Relevance score to filter responses from semantic search (default: 0.2)
14+
strategy_id: Optional parameter to filter memory strategies (default: None)
15+
initialization_query: Optional custom query for initialization retrieval (default: None)
16+
"""
17+
18+
top_k: int = Field(default=10, gt=0, le=1000)
19+
relevance_score: float = Field(default=0.2, ge=0.0, le=1.0)
20+
strategy_id: Optional[str] = None
21+
initialization_query: Optional[str] = None
22+
23+
24+
class AgentCoreMemoryConfig(BaseModel):
25+
"""Configuration for AgentCore Memory Session Manager.
26+
27+
Attributes:
28+
memory_id: Required Bedrock AgentCore Memory ID
29+
session_id: Required unique ID for the session
30+
actor_id: Required unique ID for the agent instance/user
31+
retrieval_config: Optional dictionary mapping namespaces to retrieval configurations
32+
"""
33+
34+
memory_id: str = Field(min_length=1)
35+
session_id: str = Field(min_length=1)
36+
actor_id: str = Field(min_length=1)
37+
retrieval_config: Optional[Dict[str, RetrievalConfig]] = None

src/bedrock_agentcore/memory/integrations/strands/py.typed

Whitespace-only changes.

0 commit comments

Comments
 (0)