Conversation Variables#
Variables provide a flexible way to store, share, and inject contextual information between agents and tools. Unlike hardcoded configurations, variables allow you to dynamically pass state—like API keys, session data, or user preferences—during runtime without exposing them to the underlying LLM.
Passing Variables to a Conversation#
You can pass variables at different levels depending on how long they need to persist and which parts of the system need access to them.
Agent Variables#
When you want a variable to be available across all interactions with a specific agent, you can initialize the agent with default variables.
Conversation Variables#
If a variable is only relevant for a specific conversation, you can pass it directly when calling the ask method.
Mixed Variables#
When both agent-level and call-level variables are provided, they are merged. If there is a key collision, the variables provided during the ask call will override the agent's default variables.
Context Variables Access#
The most straightforward way to access variables inside a tool is by requesting the Context object. By adding an argument annotated with Context, the framework will automatically inject the current execution context, which contains the .variables dictionary.
Special Variable Access#
Instead of passing the entire Context object, you can instruct the framework to inject specific variables directly into your tool's arguments using the Variable annotation. This makes your tool's signature cleaner and more explicit.
If the name of the argument in your function doesn't match the key in the variables dictionary, you can specify the key explicitly:
Note
Important remark: these annotations have no effect on the tool schema for the LLM. They are only used for the framework to inject the value into the function.
Variables with Default Values#
Sometimes a variable might not be provided by the user. You can define fallback behaviors directly within the Variable annotation using either default or default_factory.
Static Defaults#
Use default for simple, immutable fallback values.
Dynamic Defaults#
For mutable objects (like lists or dictionaries) or values that need to be computed at runtime, use default_factory. The framework ensures that the factory function is only called once per execution context, preserving state across multiple tool calls in the same turn.
Updating Variables in Tools#
Variables are mutable. If a tool updates the context.variables dictionary, that change is preserved in the context and will be available to subsequent tool calls within the same conversation.
This is highly useful for sharing state or caching results between different tools without requiring the LLM to pass the data back and forth.
Variables Over a Protocol Connection#
When an agent is served over — or delegates through — A2A, NLIP, A2UI or AG-UI, the conversation's variables are synced with the peer on every message. An inbound payload is merged into the turn's variables before the agent runs, and the turn's variables are sent back to the peer with the reply.
That makes variables a channel, not just local state: the peer reads what you put there, and writes what the turn sees.
Reserved namespaces are never authored by a peer
Variable keys beginning with ag: or a2a: are the framework's own control-plane state — the approval_required() allow-always bypass, the A2A context-id bookkeeping, the per-call tenant override. They are stripped from every payload crossing a protocol boundary, in both directions: a caller cannot pre-approve a gated tool by sending one, and a peer is not shown yours. Do not use those prefixes for your own variables; they will not survive the wire.
Everything else round-trips. Treat a variable as readable by the peer and writable by it: keep secrets in dependencies instead, which are never synced, and do not let a variable a peer can set decide something the peer should not control.