Skip to content

Context

The context is a mutable object passed throughout an agent run.

Basic Context

from dataclasses import dataclass
from agents import Agent, Runner

@dataclass
class MyContext:
    user_id: str
    session_id: str
    request_count: int = 0

context = MyContext(user_id="123", session_id="abc")
result = await Runner.run(agent, "Hello!", context=context)

Accessing Context in Tools

from agents import function_tool, RunContextWrapper

@function_tool
def get_user_data(ctx: RunContextWrapper[MyContext]) -> str:
    """Get data for the current user."""
    user_id = ctx.context.user_id
    return f"Data for user {user_id}"

@function_tool
def increment_count(ctx: RunContextWrapper[MyContext]) -> str:
    """Increment request count."""
    ctx.context.request_count += 1
    return f"Count: {ctx.context.request_count}"

Context in Instructions

Dynamic instructions based on context:

from agents import Agent, RunContextWrapper

def dynamic_instructions(ctx: RunContextWrapper[MyContext], agent: Agent) -> str:
    user = ctx.context.user_id
    count = ctx.context.request_count
    return f"""You are helping user {user}.
This is request #{count} in this session.
Be concise and helpful."""

agent = Agent(
    name="contextual",
    instructions=dynamic_instructions,
)

Context in Guardrails

from agents import input_guardrail, InputGuardrailResult, RunContextWrapper

@input_guardrail
async def check_permissions(
    ctx: RunContextWrapper[MyContext],
    agent,
    input_text: str
) -> InputGuardrailResult:
    """Check user permissions."""
    if ctx.context.user_id not in allowed_users:
        return InputGuardrailResult(
            tripwire_triggered=True,
            output_info="User not authorized."
        )
    return InputGuardrailResult(tripwire_triggered=False)

Context in Handoffs

from agents import handoff, Handoff, RunContextWrapper

@handoff
def conditional_handoff(ctx: RunContextWrapper[MyContext]) -> Handoff | None:
    """Hand off based on context."""
    if ctx.context.user_id.startswith("vip_"):
        return Handoff(target=vip_agent)
    return None

RunContextWrapper

The wrapper provides additional functionality:

from agents import RunContextWrapper

@function_tool
def example(ctx: RunContextWrapper[MyContext]) -> str:
    # Access your context
    user = ctx.context.user_id

    # Access run metadata
    run_id = ctx.run_id

    # Check current agent
    agent_name = ctx.current_agent.name

    return f"User: {user}, Run: {run_id}, Agent: {agent_name}"

Context Types

from dataclasses import dataclass, field

@dataclass
class AppContext:
    user_id: str
    permissions: list[str] = field(default_factory=list)
    metadata: dict = field(default_factory=dict)

Pydantic Model

from pydantic import BaseModel

class AppContext(BaseModel):
    user_id: str
    permissions: list[str] = []

    class Config:
        extra = "allow"  # Allow additional fields

Simple Dict

# Works but not type-safe
context = {"user_id": "123", "data": {}}
result = await Runner.run(agent, "Hello!", context=context)

Context Mutation

Context can be modified during the run:

@function_tool
def update_context(ctx: RunContextWrapper[MyContext], key: str, value: str) -> str:
    """Update context metadata."""
    ctx.context.metadata[key] = value
    return f"Updated {key}"

Context Persistence

For multi-turn conversations:

# Store context between requests
contexts: dict[str, MyContext] = {}

async def handle_message(session_id: str, message: str):
    # Get or create context
    if session_id not in contexts:
        contexts[session_id] = MyContext(
            user_id="user_123",
            session_id=session_id,
        )

    context = contexts[session_id]
    result = await Runner.run(agent, message, context=context)

    # Context is updated in place
    return result.final_output

See Also