# How Agno's Agent Class Manages Session State and User Context Across Runs

> Learn how Agno's Agent class manages session state and user context across runs. Discover its explicit attributes and dedicated API for seamless data handling.

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

---

**Agno's Agent class manages session state and user context across runs by separating the runnable agent logic from persistent session data, providing explicit attributes like `session_id` and `session_state`, and exposing a public API that allows both developers and LLMs to read and mutate state stored in the database.**

The `agno-agi/agno` repository provides a robust framework for building AI agents that maintain context across multiple interactions. Understanding how the Agno Agent session state system works is crucial for developing applications that require memory persistence and user-specific context management. The architecture cleanly separates the Agent's execution logic from the Session's data layer, enabling both programmatic and agentic state manipulation.

## Core Session State Concepts

The session state system is built around several key attributes defined in [`libs/agno/agno/agent/agent.py`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/agent/agent.py) that control how data persists and flows through the agent.

### Session Identification with `session_id`

Every agent interaction occurs within a session identified by a UUID. In [`libs/agno/agno/agent/agent.py`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/agent/agent.py) (lines 73-84), the `session_id` attribute stores this identifier. If omitted during agent initialization, the system auto-generates a unique session ID, ensuring that stateless runs remain isolated while stateful runs can retrieve their historical context from the database.

### User Context Storage via `session_state`

The `session_state` attribute (lines 83-86) holds an arbitrary JSON-serializable dictionary that users can supply when calling `run()`. This dictionary merges with any persisted state retrieved from the database, allowing applications to inject user preferences, counters, or metadata into the agent's context. The merging behavior respects the `overwrite_db_session_state` flag (lines 89-91), which when set to `True`, replaces the database state entirely rather than performing a shallow merge.

### Context Injection with `add_session_state_to_context`

When the `add_session_state_to_context` attribute is set to `True` (lines 84-86), the current `session_state` dictionary is automatically serialized and injected into the system message or context. This makes the session data visible to the LLM during inference, enabling the model to reference previous user preferences or application state without requiring explicit retrieval logic in the prompt.

### Agentic State Management via `enable_agentic_state`

The `enable_agentic_state` attribute (lines 87-89) grants the LLM itself the ability to modify session state. When enabled, the agent exposes a tool named `_session.update_session_state` that the model can invoke during a run. This tool delegates to `Agent.update_session_state`, writing changes directly to the database and updating the cached session object, effectively allowing the agent to "remember" facts or update counters autonomously.

## The Session State API

The `Agent` class exposes a public API for state management that wraps low-level utilities defined in [`libs/agno/agno/agent/_session.py`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/agent/_session.py). These methods handle both synchronous and asynchronous execution patterns.

### Reading State with `get_session_state`

To retrieve the current persisted state, the agent provides:

```python
def get_session_state(self, session_id: Optional[str] = None) -> Dict[str, Any]:
    session_id = session_id or self.session_id
    if session_id is None:
        raise Exception("Session ID is not set")
    return get_session_state_util(self, session_id=session_id)

```

*Source:* [`libs/agno/agno/agent/_session.py`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/agent/_session.py) (lines 73-86)

The async variant `aget_session_state` provides non-blocking access for asynchronous applications.

### Updating State with `update_session_state`

Programmatic updates merge new data into the existing state:

```python
def update_session_state(
    self, 
    session_state_updates: Dict[str, Any], 
    session_id: Optional[str] = None
) -> str:
    # Merges updates into DB state (or overwrites if flag set)

    # Returns new version ID

    ...

```

This method handles the logic for merging versus overwriting based on the `overwrite_db_session_state` configuration. When called by the LLM via the agentic state tool, it updates the database and refreshes the `_cached_session` object if session caching is enabled.

### Persisting Sessions with `save_session`

To persist the full session object including runs and metadata:

```python
def save_session(self, session: AgentSession) -> None:
    # Persists to database via agno.session models

    ...

```

The `get_session` and `aget_session` methods retrieve full `AgentSession` objects, applying the in-memory cache (`_cached_session`) when `cache_session=True` is set during agent initialization (lines 134-136 in [`agent.py`](https://github.com/agno-agi/agno/blob/main/agent.py)).

## How State Flows Across Runs

The session state lifecycle follows a predictable path through each agent invocation:

1. **Run Initialization** – When `Agent.run()` is called, it accepts an optional `session_state` dictionary from the user.

2. **Session Resolution** – The `_session.get_session` utility fetches an existing `AgentSession` from the database or creates a new one using the `session_id`. If `cache_session=True`, the result is stored in `self._cached_session` to speed up subsequent lookups.

3. **State Merging** – The provided `session_state` merges with the persisted state from the database. The merge strategy depends on `overwrite_db_session_state`: when `False` (default), it performs a shallow merge; when `True`, the supplied state replaces the database entry entirely.

4. **Context Injection** – If `add_session_state_to_context=True`, the merged `session_state` is serialized and injected into the system message via `_messages.get_system_message`, making the data available to the LLM.

5. **Agentic Updates** – During inference, if `enable_agentic_state=True`, the model may invoke the `_session.update_session_state` tool to modify state mid-run. This writes immediately to the database and updates the cache.

6. **Persistence** – Upon completion, `Agent.save_session` persists the updated `AgentSession` (including any new runs and the final `session_state`) to the database, ensuring the next run retrieves the current context.

## Practical Implementation Example

The following example demonstrates a counter that persists across multiple agent invocations using the session state system:

```python
from agno import Agent, Model

# Initialize agent with session state enabled

agent = Agent(
    model=Model("gpt-4o-mini"),
    session_id="demo-123",
    add_session_state_to_context=True,
    enable_agentic_state=True
)

# First call initializes the counter

agent.run({"counter": 0})

# The system prompt now contains: {"counter": 0}

# Model can update state via tool call:

# _session.update_session_state({"counter": 5})

# Second call automatically merges with persisted state

agent.run({"extra": "info"})

# System prompt now contains: {"counter": 5, "extra": "info"}

```

*Sources:* `Agent` constructor arguments ([`libs/agno/agno/agent/agent.py`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/agent/agent.py) lines 70-90); session-state helpers ([`libs/agno/agno/agent/_session.py`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/agent/_session.py) lines 73-102).

## Summary

- **Session Isolation**: Every `Agent` instance uses a `session_id` (UUID) to isolate context between conversations, auto-generating one if not provided.
- **Explicit State Management**: The `session_state` dictionary provides a JSON-serializable container for user context that merges with database-persisted state on each run.
- **LLM Visibility**: Setting `add_session_state_to_context=True` injects the state dictionary into the system message, allowing the model to reference user preferences and application data.
- **Agentic Mutation**: With `enable_agentic_state=True`, the LLM can modify its own session state mid-run via the `_session.update_session_state` tool, enabling autonomous memory updates.
- **Persistence Layer**: The [`_session.py`](https://github.com/agno-agi/agno/blob/main/_session.py) utilities handle database operations, while optional in-memory caching (`cache_session=True`) improves performance for repeated lookups.

## Frequently Asked Questions

### How does Agno handle session state when the same session_id is used across different Agent instances?

When multiple `Agent` instances share the same `session_id`, they access the same underlying database record. Each instance retrieves the current `session_state` from the database at the start of `run()`, merges it with any locally provided state, and persists changes at the end. If `cache_session=True`, each instance maintains its own in-memory cache (`_cached_session`), so simultaneous modifications from different processes could lead to stale reads unless the database is the source of truth.

### Can the LLM modify session state without explicit user intervention?

Yes, when `enable_agentic_state=True`, the agent exposes a tool named `_session.update_session_state` that the LLM can invoke during inference. This tool delegates to `Agent.update_session_state`, which writes changes directly to the database and updates the cached session object. This allows the model to autonomously "remember" facts, update counters, or modify user preferences based on conversation flow without requiring the user to manually pass state in subsequent calls.

### What is the difference between session_state and the Agent's memory or storage systems?

The `session_state` is a specific JSON-serializable dictionary designed for structured user context and application data that persists across runs within a single session. It is distinct from the agent's **memory** (which typically handles conversation history and RAG contexts) and **storage** (which refers to the database backend where sessions are persisted). While memory systems handle what the model "remembers" conversationally, `session_state` provides a programmatic key-value store that developers can explicitly control and inject into the model's context via `add_session_state_to_context`.

### How does state merging work when providing session_state to run()?

When calling `agent.run(session_state={"key": "value"})`, the provided dictionary merges with the existing state retrieved from the database. By default (`overwrite_db_session_state=False`), this performs a shallow merge where top-level keys in the provided dictionary overwrite those in the stored state, but nested structures may require careful handling. If `overwrite_db_session_state=True`, the provided `session_state` completely replaces the database entry, effectively resetting the session context to the provided values. This merge occurs during the session resolution phase at the start of each run, ensuring the agent operates with the most current combined state.