Skip to main content
The Taskboard agent is a working task list that lives in one session. State persists across messages in the session. The agent updates it directly.
from agno.agent import Agent

taskboard = Agent(
    id="taskboard",
    model=MODEL,
    db=agent_db,
    tools=[add_task, update_task_status, list_tasks, remove_task, get_summary],
    session_state={
        "tasks": [],
        "categories": ["general", "work", "personal"],
    },
    enable_agentic_state=True,         # agent can mutate session_state
    add_session_state_to_context=True, # state is visible in the prompt
)
Three flags do the work:
FlagBehavior
session_state={...}Initial state. Persisted to agno_sessions per (user_id, session_id).
enable_agentic_state=TrueAgent gets update_session_state tool. Can mutate the dict directly.
add_session_state_to_context=TrueState gets injected into the system prompt every turn.

Session state vs memory vs dependencies

TypeScopeMutabilityPersistence
session_stateOne (user_id, session_id)Mutable, agent updatesYes, in agno_sessions
MemoryPer user_id across sessionsMutable, agent or extractor updatesYes, in agno_memories
KnowledgeGlobal (filterable)Mutable, you load/upsertYes, in agno_knowledge
DependenciesOne runRead-onlyNo, ephemeral
Session state holds what the user is working on right now. Memory holds what’s true about the user across sessions. Dependencies carry config for the current request.

CRUD via tools

The Taskboard agent ships its own CRUD tools instead of relying on update_session_state directly. This gives the model cleaner intent labels and lets you validate updates:
from agno.tools import tool

@tool
def add_task(session_state: dict, title: str, category: str = "general") -> str:
    """Add a task to the board."""
    if category not in session_state["categories"]:
        return f"Unknown category: {category}"
    task = {"id": len(session_state["tasks"]) + 1, "title": title, "status": "open"}
    session_state["tasks"].append(task)
    return f"Added task #{task['id']}"

@tool
def update_task_status(session_state: dict, task_id: int, status: str) -> str:
    """Mark a task as open, in_progress, or done."""
    for t in session_state["tasks"]:
        if t["id"] == task_id:
            t["status"] = status
            return f"Task #{task_id}{status}"
    return f"Task #{task_id} not found"
session_state gets injected into tool functions automatically (same way RunContext does for Injector). Mutations persist.

When to use session state

Use caseUse
Working list the user is editing (tasks, items, choices)session_state
Multi-turn form fillingsession_state
Per-conversation scratchpadsession_state
Cross-session preferencesMemory or user profile
Per-request config (tenant, flags)Dependencies
If state should survive the user closing the chat and coming back next week, it belongs in memory or a database table the agent reads from, not in session state.

See it in action

@Taskboard add a task: review the Q2 roadmap, category: work
@Taskboard add a task: book dentist appointment, category: personal
@Taskboard show me my tasks
@Taskboard mark task #1 as in_progress
@Taskboard summarize what I've got open
Each turn updates session_state. The next turn sees the updated state in context. Refreshing the chat page reloads the state from agno_sessions. Source: agents/taskboard/agent.py

Next

Human-in-the-Loop →