Skip to content

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.

1
2
3
4
5
6
7
from autogen.beta import Agent

# This variable will be available in all tool calls executed by this agent
agent = Agent(
    name="WeatherBot",
    variables={"api_key": "your_global_api_key"}
)

Conversation Variables#

If a variable is only relevant for a specific conversation, you can pass it directly when calling the ask method.

1
2
3
4
await agent.ask(
    "What is the weather?",
    variables={"session_id": "12345"},
)

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.

agent = Agent(
    name="Bot",
    variables={"global_param": "A", "override_me": "AgentLevel"}
)

# Inside the tool, variables will be:
# {"global_param": "A", "override_me": "CallLevel", "call_param": "B"}
await agent.ask(
    "Hello!",
    variables={"override_me": "CallLevel", "call_param": "B"}
)

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.

1
2
3
4
5
6
7
8
9
from autogen.beta import Context, tool

@tool
def process_data(context: Context) -> str:
    # Access variables directly from the context dictionary
    api_key = context.variables.get("api_key")
    session = context.variables.get("session_id")

    return f"Processed using key: {api_key}"

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.

from typing import Annotated
from autogen.beta import Variable, tool

@tool
def fetch_user_data(
    user_id: str,
    # The framework automatically looks for a variable named "api_key"
    # and injects its value here.
    api_key: Annotated[str, Variable()],
) -> str:
    return f"Fetching {user_id} using {api_key}"

If the name of the argument in your function doesn't match the key in the variables dictionary, you can specify the key explicitly:

1
2
3
4
5
6
7
@tool
def fetch_user_data(
    user_id: str,
    # Looks for "api_key" in variables, but binds it to the "key" argument
    key: Annotated[str, Variable("api_key")],
) -> str:
    return f"Fetching {user_id} using {key}"

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.

1
2
3
4
5
6
@tool
def get_settings(
    # If "theme" is not provided in the variables, it defaults to "dark"
    theme: Annotated[str, Variable(default="dark")],
) -> str:
    return f"Using theme: {theme}"

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.

def create_default_state() -> dict:
    return {"status": "init"}

@tool
def update_status(
    state: Annotated[
        dict[str, str],
        # Uses the dynamically created dictionary if "state" wasn't provided
        Variable(default_factory=create_default_state),
    ],
) -> str:
    state["status"] = "running"
    return "Status updated"

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.

@tool
def authenticate(context: Context) -> str:
    # Generate and store a token in the context variables
    context.variables["auth_token"] = "abc-123"
    return "Successfully authenticated."

@tool
def fetch_secure_data(
    auth_token: Annotated[str | None, Variable(default=None)],
) -> str:
    if not auth_token:
        return "Error: Not authenticated."
    return f"Data fetched with token {auth_token}"