This [example](https://github.com/strands-agents/harness-sdk/blob/main/site/docs/examples/python/memory_agent.py) demonstrates how to create a Strands agent that leverages [mem0.ai](https://mem0.ai) to maintain context across conversations and provide personalized responses. It showcases how to store, retrieve, and utilize memories to create more intelligent and contextual AI interactions.

## Overview

| Feature | Description |
| --- | --- |
| **Tools Used** | mem0\_memory, use\_llm |
| **Complexity** | Intermediate |
| **Agent Type** | Single agent with Memory Management |
| **Interaction** | Command Line Interface |
| **Key Focus** | Memory Operations & Contextual Responses |

## Tool Overview

The memory agent utilizes two primary tools:

1.  **memory**: Enables storing and retrieving information with capabilities for:
    
    -   Storing user-specific information persistently
    -   Retrieving memories based on semantic relevance
    -   Listing all stored memories for a user
    -   Setting relevance thresholds and result limits
2.  **use\_llm**: Provides language model capabilities for:
    
    -   Generating conversational responses based on retrieved memories
    -   Creating natural, contextual answers using memory context

## Memory-Enhanced Response Generation Workflow

> \[!NOTE\] By default, mem0 persists memories to local storage at `~/.mem0/`. See [mem0 vector store configuration](https://docs.mem0.ai/components/vectordbs/config) for options on using a different backend for production use.

This example demonstrates a workflow where memories are used to generate contextually relevant responses:

```mermaid
flowchart TD
    UserQuery["User Query"] --> CommandClassification["Command Classification<br />(store/retrieve/list)"]
    CommandClassification --> ConditionalExecution["Conditional Execution<br />Based on Command Type"]

    ConditionalExecution --> ActionContainer["Memory Operations"]

    subgraph ActionContainer[Memory Operations]
        StoreAction["Store Action<br /><br />mem0()<br />(store)"]
        ListAction["List Action<br /><br />mem0()<br />(list)"]
        RetrieveAction["Retrieve Action<br /><br />mem0()<br />(retrieve)"]
    end

    RetrieveAction --> UseLLM["use_llm()"]
```

### Key Workflow Components

1.  **Command Classification Layer**
    
    The workflow begins by classifying the user’s input to determine the appropriate memory operation:
    
    ```python
    def process_input(self, user_input: str) -> str:
        # Check if this is a memory storage request
        if user_input.lower().startswith(("remember ", "note that ", "i want you to know ")):
            content = user_input.split(" ", 1)[1]
            self.store_memory(content)
            return f"I've stored that information in my memory."
    
        # Check if this is a request to list all memories
        if "show" in user_input.lower() and "memories" in user_input.lower():
            all_memories = self.list_all_memories()
            # ... process and return memories list ...
    
        # Otherwise, retrieve relevant memories and generate a response
        relevant_memories = self.retrieve_memories(user_input)
        return self.generate_answer_from_memories(user_input, relevant_memories)
    ```
    
    This classification examines patterns in the user’s input to determine whether to store new information, list existing memories, or retrieve relevant memories to answer a question.
    
2.  **Memory Retrieval and Response Generation**
    
    The workflow’s most powerful feature is its ability to retrieve relevant memories and use them to generate contextual responses:
    
    ```python
    def generate_answer_from_memories(self, query: str, memories: List[Dict[str, Any]]) -> str:
        # Format memories into a string for the LLM
        memories_str = "\n".join([f"- {mem['memory']}" for mem in memories])
    
        # Create a prompt that includes user context
        prompt = f"""
    User ID: {self.user_id}
    User question: "{query}"
    
    Relevant memories for user {self.user_id}:
    {memories_str}
    
    Please generate a helpful response using only the memories related to the question.
    Try to answer to the point.
    """
    
        # Use the LLM to generate a response based on memories
        response = self.agent.tool.use_llm(
            prompt=prompt,
            system_prompt=ANSWER_SYSTEM_PROMPT
        )
    
        return str(response['content'][0]['text'])
    ```
    
    This two-step process:
    
    1.  First retrieves the most semantically relevant memories using the memory tool
    2.  Then feeds those memories to an LLM to generate a natural, conversational response
3.  **Tool Chaining for Enhanced Responses**
    
    The retrieval path demonstrates tool chaining, where memory retrieval and LLM response generation work together:
    

```mermaid
flowchart LR
    UserQuery["User Query"] --> MemoryRetrieval["memory() Retrieval<br />(Finds relevant memories)"]
    MemoryRetrieval --> UseLLM["use_llm()<br />(Generates natural<br />language answer)"]
    UseLLM --> Response["Response"]
```

This chaining allows the agent to:

1.  First retrieve memories that are semantically relevant to the user’s query
2.  Then process those memories to generate a natural, conversational response that directly addresses the query

## Implementation Benefits

### 1\. Object-Oriented Design

The Memory Agent is implemented as a class, providing encapsulation and clean organization of functionality:

```python
class MemoryAssistant:
    def __init__(self, user_id: str = "demo_user"):
        self.user_id = user_id
        self.agent = Agent(
            system_prompt=MEMORY_SYSTEM_PROMPT,
            tools=[mem0_memory, use_llm],
        )

    def store_memory(self, content: str) -> Dict[str, Any]:
        # Implementation...

    def retrieve_memories(self, query: str, min_score: float = 0.3, max_results: int = 5) -> List[Dict[str, Any]]:
        # Implementation...

    def list_all_memories(self) -> List[Dict[str, Any]]:
        # Implementation...

    def generate_answer_from_memories(self, query: str, memories: List[Dict[str, Any]]) -> str:
        # Implementation...

    def process_input(self, user_input: str) -> str:
        # Implementation...
```

This design provides:

-   Clear separation of concerns
-   Reusable components
-   Easy extensibility
-   Clean interface for interacting with memory operations

### 2\. Specialized System Prompts

The code uses specialized system prompts for different tasks:

1.  **Memory Agent System Prompt**: Focuses on general memory operations
    
    ```python
    MEMORY_SYSTEM_PROMPT = """You are a memory specialist agent. You help users store,
    retrieve, and manage memories. You maintain context across conversations by remembering
    important information about users and their preferences...
    ```
    
2.  **Answer Generation System Prompt**: Specialized for generating responses from memories
    
    ```python
    ANSWER_SYSTEM_PROMPT = """You are an assistant that creates helpful responses based on retrieved memories.
    Use the provided memories to create a natural, conversational response to the user's question...
    ```
    

This specialization improves performance by focusing each prompt on a specific task rather than using a general-purpose prompt.

### 3\. Explicit Memory Structure

The agent initializes with structured memories to demonstrate memory capabilities:

```python
def initialize_demo_memories(self) -> None:
    init_memories = "My name is Alex. I like to travel and stay in Airbnbs rather than hotels. I am planning a trip to Japan next spring. I enjoy hiking and outdoor photography as hobbies. I have a dog named Max. My favorite cuisine is Italian food."
    self.store_memory(init_memories)
```

These memories provide:

-   Examples of what can be stored
-   Demonstration data for retrieval operations
-   A baseline for testing functionality

## Important Requirements

The memory tool requires either a `user_id` or `agent_id` for most operations:

1.  **Required for**:
    
    -   Storing new memories
    -   Listing all memories
    -   Retrieving memories via semantic search
2.  **Not required for**:
    
    -   Getting a specific memory by ID
    -   Deleting a specific memory
    -   Getting memory history

This ensures that memories are properly associated with specific users or agents and maintains data isolation between different users.

## Example Interactions

**Interaction 1**: Storing Information

```plaintext
> Remember that I prefer window seats on flights

I've stored that information in my memory.
```

**Interaction 2**: Retrieving Information

```plaintext
> What do you know about my travel preferences?

Based on my memory, you prefer to travel and stay in Airbnbs rather than hotels instead of traditional accommodations. You're also planning a trip to Japan next spring. Additionally, you prefer window seats on flights for your travels.
```

**Interaction 3**: Listing All Memories

```plaintext
> Show me all my memories

Here's everything I remember:
1. My name is Alex. I like to travel and stay in Airbnbs rather than hotels. I am planning a trip to Japan next spring. I enjoy hiking and outdoor photography as hobbies. I have a dog named Max. My favorite cuisine is Italian food.
2. I prefer window seats on flights
```

## Extending the Example

Here are some ways to extend this memory agent:

1.  **Memory Categories**: Implement tagging or categorization of memories for better organization
2.  **Memory Prioritization**: Add importance levels to memories to emphasize critical information
3.  **Memory Expiration**: Implement time-based relevance for memories that may change over time
4.  **Multi-User Support**: Enhance the system to manage memories for multiple users simultaneously
5.  **Memory Visualization**: Create a visual interface to browse and manage memories
6.  **Proactive Memory Usage**: Have the agent proactively suggest relevant memories in conversations

For more advanced memory management features and detailed documentation, visit [Mem0 documentation](https://docs.mem0.ai).