Agent Session State
Manage persistent state in agents across multiple runs within a session
Your Agents often need to access certain data during a run/session. It could be a todo list, the user's profile, or anything else.
When this data needs to remain accessible across runs, or needs to be updated during the session, you want to consider it session state.
Session state is accessible from tool calls, pre-hooks and post-hooks, and other functions that are part of the Agent run. You are also able to use it in the system message, to ultimately present it to the Model.
Session state is also persisted in the database, if one is available to the Agent, and is automatically loaded when the session is continued.
Understanding Agent "Statelessness": Agents in Kern don't maintain working state directly on the Agent object in memory. Instead they provide state management capabilities:
- The
session.get_session_state(session_id=session_id)method retrieves the session state of a particular session from the database - The
session_stateparameter onAgentprovides the default state data for new sessions - Working state is managed per run and persisted to the database per session
- The agent instance (or attributes thereof) itself is not modified during runs
State Management
Now that we understand what session state is, let's see how it works:
- You can set the Agent's
session_stateparameter with a dictionary of default state variables. This will be the initial state. - You can pass
session_statetoagent.run(). This will take precedence over the Agent's default state for that run. - You can access the session state in tool calls and other functions, via
run_context.session_state. - The
session_statewill be stored in your database. Subsequent runs within the same session will load the state from the database. See the guide for more information. - You can use any data in your
session_statein the system message, by referencing it in thedescriptionandinstructionsparameters. See the guide for more information. - You can have your Agent automatically update the session state by setting the
enable_agentic_stateparameter toTrue. See the guide for more information.
Here's an example where an Agent is managing a shopping list:
1from kern.agent import Agent2from kern.db.sqlite import SqliteDb3from kern.models.openai import OpenAIResponses4from kern.run import RunContext56# Define a tool that adds an item to the shopping list7def add_item(run_context: RunContext, item: str) -> str:8 """Add an item to the shopping list."""910 # We access the session state via run_context.session_state11 run_context.session_state["shopping_list"].append(item)1213 return f"The shopping list is now {run_context.session_state['shopping_list']}"141516# Create an Agent that maintains state17agent = Agent(18 model=OpenAIResponses(id="gpt-5.2"),19 # Database to store sessions and their state20 db=SqliteDb(db_file="tmp/agents.db"),21 # Initialize the session state with an empty shopping list. This will be the default state for all sessions.22 session_state={"shopping_list": []},23 tools=[add_item],24 # You can use variables from the session state in the instructions25 instructions="Current state (shopping list) is: {shopping_list}",26 markdown=True,27)2829# Example usage30agent.print_response("Add milk, eggs, and bread to the shopping list", stream=True)31print(f"Final session state: {agent.get_session_state()}")The RunContext object is automatically passed to the tool as an argument.
Any updates to run_context.session_state will automatically be persisted in the database and reflected in the session state.
See the RunContext schema for more information.
Session state is also shared between members of a team when using Team. See Teams for more information.
Maintaining state across multiple runs
A big advantage of sessions is the ability to maintain state across multiple runs within the same session. For example, let's say the agent is helping a user keep track of their shopping list.
You have to configure your storage via the db parameter for state to be persisted across runs. See Storage for more information.
1from textwrap import dedent23from kern.agent import Agent4from kern.db.sqlite import SqliteDb5from kern.models.openai import OpenAIResponses6from kern.run import RunContext789# Define tools to manage our shopping list10def add_item(run_context: RunContext, item: str) -> str:11 """Add an item to the shopping list and return confirmation."""12 # Add the item if it's not already in the list13 if item.lower() not in [i.lower() for i in run_context.session_state["shopping_list"]]:14 run_context.session_state["shopping_list"].append(item) # type: ignore15 return f"Added '{item}' to the shopping list"16 else:17 return f"'{item}' is already in the shopping list"181920def remove_item(run_context: RunContext, item: str) -> str:21 """Remove an item from the shopping list by name."""22 # Case-insensitive search23 for i, list_item in enumerate(run_context.session_state["shopping_list"]):24 if list_item.lower() == item.lower():25 run_context.session_state["shopping_list"].pop(i)26 return f"Removed '{list_item}' from the shopping list"2728 return f"'{item}' was not found in the shopping list"293031def list_items(run_context: RunContext) -> str:32 """List all items in the shopping list."""33 shopping_list = run_context.session_state["shopping_list"]3435 if not shopping_list:36 return "The shopping list is empty."3738 items_text = "\n".join([f"- {item}" for item in shopping_list])39 return f"Current shopping list:\n{items_text}"404142# Create a Shopping List Manager Agent that maintains state43agent = Agent(44 model=OpenAIResponses(id="gpt-5.2"),45 # Initialize the session state with an empty shopping list (default session state for all sessions)46 session_state={"shopping_list": []},47 db=SqliteDb(db_file="tmp/example.db"),48 tools=[add_item, remove_item, list_items],49 # You can use variables from the session state in the instructions50 instructions=dedent("""\51 Your job is to manage a shopping list.5253 The shopping list starts empty. You can add items, remove items by name, and list all items.5455 Current shopping list: {shopping_list}56 """),57 markdown=True,58)5960# Example usage61agent.print_response("Add milk, eggs, and bread to the shopping list", stream=True)62print(f"Session state: {agent.get_session_state()}")6364agent.print_response("I got bread", stream=True)65print(f"Session state: {agent.get_session_state()}")6667agent.print_response("I need apples and oranges", stream=True)68print(f"Session state: {agent.get_session_state()}")6970agent.print_response("whats on my list?", stream=True)71print(f"Session state: {agent.get_session_state()}")7273agent.print_response(74 "Clear everything from my list and start over with just bananas and yogurt",75 stream=True,76)77print(f"Session state: {agent.get_session_state()}")Agentic Session State
Kern provides a way to allow the Agent to automatically update the session state.
Simply set the enable_agentic_state parameter to True.
1from kern.agent import Agent2from kern.models.openai import OpenAIResponses3from kern.db.sqlite import SqliteDb45agent = Agent(6 db=SqliteDb(db_file="tmp/agents.db"),7 model=OpenAIResponses(id="gpt-5.2"),8 session_state={"shopping_list": []},9 add_session_state_to_context=True, # Required so the agent is aware of the session state10 enable_agentic_state=True, # Adds a tool to manage the session state11)1213agent.print_response("Add milk, eggs, and bread to the shopping list", stream=True)14print(f"Session state: {agent.get_session_state()}")Don't forget to set add_session_state_to_context=True to make the session
state available to the agent's context.
Using state in instructions
You can reference variables from the session state in your instructions.
Don't use the f-string syntax in the instructions. Directly use the {key}
syntax, Kern substitutes the values for you.
1from textwrap import dedent23from kern.agent import Agent4from kern.models.openai import OpenAIResponses5from kern.db.sqlite import SqliteDb678agent = Agent(9 db=SqliteDb(db_file="tmp/agents.db"),10 model=OpenAIResponses(id="gpt-5.2"),11 # Initialize the session state with a variable12 session_state={"user_name": "John"},13 # You can use variables from the session state in the instructions14 instructions="Users name is {user_name}",15 markdown=True,16)1718agent.print_response("What is my name?", stream=True)Changing state on run
When you pass session_id to the agent on agent.run(), the run will be part of the session with the given session_id. The state loaded from the database will be the state for that session.
This is useful when you want to continue a session for a specific user.
1from kern.agent import Agent2from kern.models.openai import OpenAIResponses3from kern.db.sqlite import SqliteDb45agent = Agent(6 db=SqliteDb(db_file="tmp/agents.db"),7 model=OpenAIResponses(id="gpt-5.2"),8 instructions="Users name is {user_name} and age is {age}",9)1011# Sets the session state for the session with the id "user_1_session_1"12agent.print_response("What is my name?", session_id="user_1_session_1", user_id="user_1", session_state={"user_name": "John", "age": 30})1314# Will load the session state from the session with the id "user_1_session_1"15agent.print_response("How old am I?", session_id="user_1_session_1", user_id="user_1")1617# Sets the session state for the session with the id "user_2_session_1"18agent.print_response("What is my name?", session_id="user_2_session_1", user_id="user_2", session_state={"user_name": "Jane", "age": 25})1920# Will load the session state from the session with the id "user_2_session_1"21agent.print_response("How old am I?", session_id="user_2_session_1", user_id="user_2")Overwriting the state in the db
By default, if you pass session_state to the run methods, this new state will be merged with the session_state in the db.
You can change that behavior if you want to overwrite the session_state in the db:
1from kern.agent import Agent2from kern.db.sqlite import SqliteDb3from kern.models.openai import OpenAIResponses45# Create an Agent that maintains state6agent = Agent(7 model=OpenAIResponses(id="gpt-5.2"),8 db=SqliteDb(db_file="tmp/agents.db"),9 markdown=True,10 # Set the default session_state. The values set here won't be overwritten - they are the initial state for all sessions.11 session_state={},12 # Adding the session_state to context for the agent to easily access it13 add_session_state_to_context=True,14 # Allow overwriting the stored session state with the session state provided in the run15 overwrite_db_session_state=True,16)1718# Let's run the agent providing a session_state. This session_state will be stored in the database.19agent.print_response(20 "Can you tell me what's in your session_state?",21 session_state={"shopping_list": ["Potatoes"]},22 stream=True,23)24print(f"Stored session state: {agent.get_session_state()}")2526# Now if we pass a new session_state, it will overwrite the stored session_state.27agent.print_response(28 "Can you tell me what is in your session_state?",29 session_state={"secret_number": 43},30 stream=True,31)32print(f"Stored session state: {agent.get_session_state()}")Developer Resources
- View the Agent schema
- View the RunContext schema
- View Cookbook