# How to Implement Agent Memory and User Profile Tracking in Agno

> Learn to implement agent memory and user profile tracking in Agno. Discover how Agno separates memories and profiles for persistent, LLM-driven updates across sessions.

- Repository: [Agno/agno](https://github.com/agno-agi/agno)
- Tags: how-to-guide
- Published: 2026-02-23

---

**Agno separates unstructured short-term memories handled by `MemoryManager` from structured long-term user profiles managed by `UserProfileStore`, enabling agents to persist facts across sessions through a configurable `LearningMachine` that exposes LLM-driven update tools.**

The open-source Agno framework (agno-agi/agno) provides a dual-memory architecture that lets AI agents remember both free-form observations and structured user attributes. By implementing agent memory and user profile tracking in Agno, you create persistent, personalized experiences where the LLM automatically updates profiles using generated tools while storing contextual memories in a database backend.

## Architectural Overview

Agno implements two distinct but complementary subsystems for persistence: **unstructured memories** for arbitrary observations and **structured user profiles** for typed, searchable facts.

### MemoryManager for Unstructured Memories

The `MemoryManager` class in [`libs/agno/agno/memory/manager.py`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/memory/manager.py) provides the core CRUD and search API for user memories. It handles arbitrary text entries with topics and timestamps, offering methods like `add_user_memory`, `replace_user_memory`, and `search_user_memories`. This subsystem is ideal for capturing short-term facts, conversation history, and contextual observations that do not fit a rigid schema.

### UserProfileStore for Structured Profiles

Located in [`libs/agno/agno/learn/stores/user_profile.py`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/learn/stores/user_profile.py), the `UserProfileStore` implements the `LearningStore` protocol to persist typed data according to the `UserProfile` schema defined in [`libs/agno/agno/learn/schemas.py`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/learn/schemas.py). Unlike free-form memories, the profile store maintains fields such as `name`, `preferred_name`, and `location` across sessions. It dynamically builds tool signatures (like `update_profile`) from the schema, allowing the LLM to invoke structured updates when users provide personal information.

### LearningMachine Orchestration

The `LearningMachine` class in [`libs/agno/agno/learn/machine.py`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/learn/machine.py) serves as the central orchestrator. When instantiated with `user_profile=True` or a `UserProfileConfig` object, it creates a `UserProfileStore` and registers its tools with every attached agent. The machine also controls context injection via `UserProfileStore.build_context()`, which inserts a `<user_profile>` block into the agent's system prompt, making stored fields available without explicit memory subsystem calls.

## Implementation Steps

Follow these steps to enable agent memory and user profile tracking in your Agno application:

1. **Choose a database backend** that implements `BaseDb` or `AsyncBaseDb` (SQLite, Postgres, MongoDB, etc.).

2. **Create a LearningMachine** with user-profile enabled:

   ```python
   from agno.learn.machine import LearningMachine
   from agno.learn.config import UserProfileConfig, LearningMode

   lm = LearningMachine(
       db=my_db,                     # any BaseDb implementation

       model=my_model,               # OpenAIChat, Claude, etc.

       user_profile=UserProfileConfig(
           mode=LearningMode.ALWAYS, # automatic extraction after each turn

           enable_agent_tools=True,  # expose `update_profile`

       ),
   )
   ```

3. **Attach the learning machine to an agent**:

   ```python
   from agno.agent import Agent

   agent = Agent(
       db=my_db,
       model=my_model,
       learning_machine=lm,
   )
   ```

4. **Access the profile programmatically** using the store's getter:

   ```python
   profile = agent.learning_machine.user_profile_store.get(user_id="alice@example.com")
   # profile is an instance of the configured schema (e.g., UserProfile)

   ```

5. **Enable LLM-driven profile updates** by setting `enable_agent_tools=True` or using `LearningMode.AGENTIC`. The agent will see a function signature like:

   ```python
   def update_profile(
       *, name: Optional[str] = None,
           preferred_name: Optional[str] = None,
           location: Optional[str] = None,
           ... # all schema fields

   ) -> str: ...
   ```

   The LLM invokes this tool when parsing statements like "My name is Alice, but call me Ali," automatically persisting changes via `upsert_learning`.

6. **Manage unstructured memories** using `MemoryManager` for observations that don't fit the profile schema:

   ```python
   from agno.memory.manager import MemoryManager

   mem_mgr = MemoryManager(
       db=my_db,
       model=my_model,
       delete_memories=False,
       clear_memories=False,
   )
   mem_mgr.add_user_memory(
       memory=UserMemory(
           user_id="alice@example.com",
           memory="Alice loves hiking on weekends",
           topics=["hobbies", "weekend"],
       )
   )
   ```

## Code Examples

### Setting Up an Agent with Automatic Profile Tracking

This complete example demonstrates an agent that automatically extracts and persists user profile information during conversation:

```python
from agno.agent import Agent
from agno.learn.machine import LearningMachine
from agno.learn.config import UserProfileConfig, LearningMode
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIChat

db = SqliteDb(filename="demo.db")
model = OpenAIChat(id="gpt-4o")

lm = LearningMachine(
    db=db,
    model=model,
    user_profile=UserProfileConfig(
        mode=LearningMode.ALWAYS,
        enable_agent_tools=True,
        enable_update_profile=True,   # expose update_profile tool

    ),
)

agent = Agent(
    db=db,
    model=model,
    learning_machine=lm,
)

# Run a conversation

response = agent.run(
    "Hi, I'm Bob. I just moved to Seattle and love sushi.",
    user_id="bob@example.com"
)
print(response)

# The LLM will call `update_profile(name="Bob", location="Seattle")` automatically.

```

### Manually Updating a Profile from Application Code

For scenarios requiring programmatic updates outside of LLM inference:

```python
store = lm.user_profile_store
store.update_profile = store._build_update_profile_tool(user_id="bob@example.com")
store.update_profile(name="Bob", preferred_name="Bobby", location="Seattle")

# Profile now persisted; next agent turn will see it in the system prompt.

```

### Managing Short-Term Memories with MemoryManager

Capture and retrieve transient facts that don't belong in the structured profile:

```python
from agno.memory.manager import MemoryManager
from agno.models.message import Message

mem_mgr = MemoryManager(
    db=db,
    model=model,
    add_memories=True,
    update_memories=True,
)

# Capture a memory from a user utterance

msg = Message(role="user", content="I adopted a golden retriever named Max.")
mem_mgr.create_user_memories(
    messages=[msg],
    user_id="bob@example.com",
    db=db,
)

# Retrieve the last two memories

last_two = mem_mgr._get_last_n_memories(user_id="bob@example.com", limit=2)
for m in last_two:
    print(m.memory)   # → "I adopted a golden retriever named Max."

```

## Key Source Files and Classes

Understanding these core files is essential for implementing agent memory and user profile tracking in Agno:

- **[`libs/agno/agno/memory/manager.py`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/memory/manager.py)** – Contains `MemoryManager`, which provides the full CRUD and search API for unstructured user memories including `add_user_memory` and `search_user_memories`.

- **[`libs/agno/agno/learn/stores/user_profile.py`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/learn/stores/user_profile.py)** – Implements `UserProfileStore` with schema-aware persistence, tool generation via `_build_update_profile_tool`, and the `build_context()` method for system prompt injection.

- **[`libs/agno/agno/learn/machine.py`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/learn/machine.py)** – Defines `LearningMachine`, the orchestrator that instantiates stores and manages the learning lifecycle across agent sessions.

- **[`libs/agno/agno/learn/config.py`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/learn/config.py)** – Provides configuration objects including `UserProfileConfig` and the `LearningMode` enum (ALWAYS, AGENTIC, NEVER) that control when and how profile extraction occurs.

- **[`libs/agno/agno/os/routers/memory/memory.py`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/os/routers/memory/memory.py)** – HTTP API router exposing memory endpoints for external services integrating with the memory subsystem.

- **[`cookbook/08_learning/07_patterns/personal_assistant.py`](https://github.com/agno-agi/agno/blob/main/cookbook/08_learning/07_patterns/personal_assistant.py)** – Reference implementation showing an end-to-end personal assistant that combines profile tracking with memory management.

## Summary

- **Agno uses two distinct persistence systems**: `MemoryManager` for unstructured observations and `UserProfileStore` for typed, schema-backed profiles.

- **The `LearningMachine` orchestrates both systems**, automatically injecting profile context into agent prompts and exposing update tools when configured with `enable_agent_tools=True`.

- **LLM-driven updates occur through dynamically generated tools**: The `update_profile` function is built from the `UserProfile` schema and invoked by the agent when users provide personal information.

- **Database flexibility**: Any implementation of `BaseDb` or `AsyncBaseDb` (SQLite, Postgres, MongoDB) can back both memory and profile stores.

- **Manual access is always available**: Developers can retrieve profiles via `user_profile_store.get()` and manage memories via `MemoryManager` methods for hybrid human-AI workflows.

## Frequently Asked Questions

### What is the difference between MemoryManager and UserProfileStore in Agno?

`MemoryManager` handles unstructured, free-form text memories with topics and timestamps, suitable for observations like "User mentioned they have a meeting tomorrow." `UserProfileStore` maintains a structured, typed schema (name, location, preferences) that persists across sessions and supports LLM-driven updates through generated tools. According to the agno-agi/agno source code, they are implemented in separate modules ([`memory/manager.py`](https://github.com/agno-agi/agno/blob/main/memory/manager.py) vs [`learn/stores/user_profile.py`](https://github.com/agno-agi/agno/blob/main/learn/stores/user_profile.py)) to maintain clean separation between transient context and permanent user attributes.

### How does the LLM automatically update user profiles?

When `UserProfileConfig` has `enable_agent_tools=True` or mode set to `LearningMode.AGENTIC`, the `UserProfileStore.get_agent_tools()` method returns an `update_profile` callable. The store dynamically constructs this tool's signature from the `UserProfile` schema fields. The agent's LLM sees this function in its tool registry and can invoke it when parsing user statements containing profile information, such as "I moved to Boston." The tool then calls `upsert_learning` to persist changes to the database.

### Can I use custom database backends for memory and profile storage?

Yes. Both `MemoryManager` and `UserProfileStore` accept any database implementation that follows the `BaseDb` or `AsyncBaseDb` interface. The examples above use `SqliteDb`, but you can substitute Postgres, MongoDB, or other supported backends by passing the appropriate `db` instance to `LearningMachine` and `Agent` constructors.

### How do I retrieve stored memories or profiles programmatically?

Access structured profiles through the learning machine's store: `agent.learning_machine.user_profile_store.get(user_id="...")`. For unstructured memories, use `MemoryManager` methods like `_get_last_n_memories(user_id="...", limit=5)` or `search_user_memories(query="...")`. These methods query the underlying database directly, returning `UserProfile` objects or `UserMemory` instances respectively.