How to Implement Agent Memory and User Profile Tracking in Agno

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 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, the UserProfileStore implements the LearningStore protocol to persist typed data according to the UserProfile schema defined in 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 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:

    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:

    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:

    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:

    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:

    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:

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:

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:

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:

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 vs 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →