# User Profile (/learning/stores/user-profile)



The User Profile Store captures structured fields about users: name, preferred name, and custom fields you define.

| Aspect              | Value                                    |
| ------------------- | ---------------------------------------- |
| **Scope**           | Per user                                 |
| **Persistence**     | Forever (updated as new info is learned) |
| **Default mode**    | Always                                   |
| **Supported modes** | Always, Agentic                          |

## Setup [#setup]

```bash
pip install agno openai sqlalchemy "psycopg[binary]" pgvector
export OPENAI_API_KEY="your-api-key"
```

The examples use a local PostgreSQL database on port 5532. With Docker running, start it using:

<Steps>
    <Step title="Run PgVector">
      <CodeBlockTabs defaultValue="macOS / Linux">
        <CodeBlockTabsList>
          <CodeBlockTabsTrigger value="macOS / Linux">
            macOS / Linux
          </CodeBlockTabsTrigger>

          <CodeBlockTabsTrigger value="Windows">
            Windows
          </CodeBlockTabsTrigger>
        </CodeBlockTabsList>

        <CodeBlockTab value="macOS / Linux">
          ```bash
          docker run -d \
            -e POSTGRES_DB=ai \
            -e POSTGRES_USER=ai \
            -e POSTGRES_PASSWORD=ai \
            -e PGDATA=/var/lib/postgresql \
            -v pgvolume:/var/lib/postgresql \
            -p 5532:5432 \
            --name pgvector \
            agnohq/pgvector:18
          ```
        </CodeBlockTab>

        <CodeBlockTab value="Windows">
          ```powershell
          docker run -d `
            -e POSTGRES_DB=ai `
            -e POSTGRES_USER=ai `
            -e POSTGRES_PASSWORD=ai `
            -e PGDATA=/var/lib/postgresql `
            -v pgvolume:/var/lib/postgresql `
            -p 5532:5432 `
            --name pgvector `
            agnohq/pgvector:18
          ```
        </CodeBlockTab>
      </CodeBlockTabs>
    </Step>
</Steps>

Later configuration fragments reuse the imports and `db` from the first complete example.

## Basic Usage [#basic-usage]

```python
from agno.agent import Agent
from agno.db.postgres import PostgresDb
from agno.learn import LearningMachine
from agno.models.openai import OpenAIResponses

db = PostgresDb(db_url="postgresql+psycopg://ai:ai@localhost:5532/ai")

agent = Agent(
    model=OpenAIResponses(id="gpt-5.2"),
    db=db,
    learning=LearningMachine(user_profile=True),
)

# Session 1: Share information
agent.print_response(
    "Hi! I'm Alice Chen, but please call me Ali.",
    user_id="alice@example.com",
    session_id="session_1",
)

# Session 2: Profile is recalled automatically
agent.print_response(
    "What's my name?",
    user_id="alice@example.com",
    session_id="session_2",
)
```

## Always Mode [#always-mode]

Extraction starts concurrently with the main model call using the current input snapshot. No tools are visible to the agent.

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

agent = Agent(
    model=OpenAIResponses(id="gpt-5.2"),
    db=db,
    learning=LearningMachine(
        user_profile=UserProfileConfig(mode=LearningMode.ALWAYS),
    ),
)
```

The tradeoff is an extra LLM call per interaction.

## Agentic Mode [#agentic-mode]

The agent receives an `update_profile` tool and decides when to update.

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

agent = Agent(
    model=OpenAIResponses(id="gpt-5.2"),
    db=db,
    learning=LearningMachine(
        user_profile=UserProfileConfig(mode=LearningMode.AGENTIC),
    ),
)

agent.print_response(
    "Please remember that my name is Bob Smith.",
    user_id="bob@example.com",
)
```

The tradeoff is that the agent may miss implicit profile info.

## Default Fields [#default-fields]

| Field            | Description                   |
| ---------------- | ----------------------------- |
| `name`           | Full name                     |
| `preferred_name` | Name they prefer to be called |

## Custom Schemas [#custom-schemas]

Extend the base schema for your domain:

```python
from dataclasses import dataclass, field
from typing import Optional
from agno.learn.schemas import UserProfile

@dataclass
class CustomerProfile(UserProfile):
    company: Optional[str] = field(
        default=None,
        metadata={"description": "Company or organization"}
    )
    plan_tier: Optional[str] = field(
        default=None,
        metadata={"description": "Subscription tier: free | pro | enterprise"}
    )
    role: Optional[str] = field(
        default=None,
        metadata={"description": "Job title or role"}
    )
    timezone: Optional[str] = field(
        default=None,
        metadata={"description": "User's timezone"}
    )

agent = Agent(
    model=OpenAIResponses(id="gpt-5.2"),
    db=db,
    learning=LearningMachine(
        user_profile=UserProfileConfig(schema=CustomerProfile),
    ),
)
```

The `metadata["description"]` tells the LLM what each field captures.

## Accessing Profile Data [#accessing-profile-data]

```python
lm = agent.learning_machine

# Get profile
profile = lm.user_profile_store.get(user_id="alice@example.com")
if profile is not None:
    print(profile.name)
    print(profile.preferred_name)

# Debug output
lm.user_profile_store.print(user_id="alice@example.com")
```

## Context Injection [#context-injection]

Profiles are automatically injected into the system prompt:

```
<user_profile>
Name: Alice Chen
Preferred Name: Ali
Company: Acme Corp
Role: Data Scientist
</user_profile>
```

No manual context building is needed.

## User Profile vs User Memory [#user-profile-vs-user-memory]

| User Profile            | User Memory                               |
| ----------------------- | ----------------------------------------- |
| Structured fields       | Unstructured text                         |
| Fixed schema            | Flexible observations                     |
| Updated in place        | Appended over time                        |
| Keyed recall by user ID | Keyed recall of the user's memory entries |

**Use User Profile for:** name, company, role, preferences with defined values.

**Use User Memory for:** observations like "prefers detailed explanations" or "works on ML projects."
