State Management
A compact guide to session.state and scoped persistence
Within each Session (our conversation thread), the state attribute acts like the agent's dedicated scratchpad for that specific interaction. While session.events holds the full history, session.state is where the agent stores and updates dynamic details needed during the conversation.
What is session.state?
Conceptually, session.state is a collection (dictionary or Map) holding key-value pairs. It's designed for information the agent needs to recall or track to make the current conversation effective:
- Personalize Interaction: Remember user preferences mentioned earlier (e.g.,
'user_preference_theme': 'dark'). - Track Task Progress: Keep tabs on steps in a multi-turn process (e.g.,
'booking_step': 'confirm_payment'). - Accumulate Information: Build lists or summaries (e.g.,
'shopping_cart_items': ['book', 'pen']). - Make Informed Decisions: Store flags or values influencing the next response (e.g.,
'user_is_authenticated': true).
Key Characteristics of State
- Structure: string keys → JSON-serializable values (no functions, sockets, class instances)
- Mutability: changes throughout the turn
- Persistence: depends on the SessionService (in-memory vs database vs cloud)
Serialization Only
Store only JSON-serializable data. Save large blobs/files as artifacts and reference by name in state.
Organizing State with Prefixes: Scope Matters
- No prefix: session-only (e.g.,
current_step) user:user-wide across sessions (e.g.,user:theme)app:app-wide across users (e.g.,app:feature_flags)temp:transient for the current invocation (not persisted)
Accessing Session State in Agent Instructions
Use {key} placeholders inside LlmAgent.instruction (auto-injected). Add ? to make a key optional.
const agent = new LlmAgent({
name: 'Guide',
model: 'gemini-2.0-flash',
instruction: 'Welcome back! Theme: {user:theme?}. Step: {current_step}.'
});Using {key} Templating
{key}requires a value and throws if missing{key?}is optional and injects empty string if missing- Supports
artifact.filenameviainjectSessionStatehelper in InstructionProvider
Important Considerations
- Keys must exist (or use
?) - Non-string values are converted to string
- To bypass auto-injection, pass a function to
instructionand inject manually:
import { injectSessionState } from '@iqai/adk';
async function provider(ctx: ReadonlyContext) {
return injectSessionState('Hello {user:name?}', ctx);
}How State is Updated: Recommended Methods
- Agent outputKey: save final response directly to
session.state
const agent = new LlmAgent({ name: 'Greeter', model: 'gemini-2.0-flash', outputKey: 'last_greeting' });- Event actions: use
EventActions.stateDeltafor explicit bulk updates
const event = new Event({ actions: new EventActions({ stateDelta: { 'user:login_count': 1 } }) });
await sessionService.appendEvent(session, event);- Callback/Tool contexts: mutate
context.state(delta-aware helpers)
// Callback
beforeModelCallback: ({ callbackContext }) => {
const n = callbackContext.state.get('user:count', 0);
callbackContext.state.set('user:count', n + 1);
callbackContext.state['temp:flag'] = true; // bracket access works too
return null;
}
// Tool
const addItem = createTool({
name: 'add_item',
schema: z.object({ item: z.string(), qty: z.number().default(1) }),
fn: ({ item, qty }, context) => {
const list = context.state.get('items', []);
list.push({ item, qty });
context.state.set('items', list);
return { ok: true };
}
});⚠️ A Warning About Direct State Modification
Avoid mutating session.state on a retrieved Session outside of events/callbacks/tools. It bypasses event history, may not persist, and is not thread-safe. Prefer:
outputKeyfor agent responsesEventActions.stateDeltawithappendEventcallbackContext.stateortoolContext.state
Best Practices for State Design Recap
- Use clear prefixes (
user:,app:,temp:) and descriptive keys - Keep values small and serializable; use artifacts for large blobs
- Batch updates via
stateDeltawhere possible - Read state directly; write via events, callbacks, or tools for persistence
How is this guide?