Instructions from a function
Dynamic instructions are an Agent's system prompt built by a function instead of a fixed string: Agent(instructions=func) calls func(ctx, agent) each run and uses what it returns.
Last updated: 28 Sep, 2026 · openai-agents 0.22.3
A fixed instructions string says the same thing to every caller. When the prompt should mention the customer's name or tier, you compute it per run from the context you already pass in.
instructions as a function
Set instructions to a function of two arguments: the run context and the agent. It returns the string the SDK uses as the system prompt for that one run.
def build_instructions(ctx: RunContextWrapper[Customer], agent: Agent) -> str:
return f"Help {ctx.context.name}." # the system prompt for this run
agent = Agent(name="Greeter", instructions=build_instructions, model=model)The function signature
The function receives the same RunContextWrapper your tools get, and the agent itself. Read ctx.context to shape the prompt.
def build_instructions(ctx: RunContextWrapper[Customer], agent: Agent) -> str:
c = ctx.context # the data passed to this run
return f"You are helping {c.name}, a {c.tier} customer. Greet them by name."Passing the function, not a string
A string is fixed for every run. A function is called on each run, so the prompt can change with the context.
# instructions can be a str or a function; here it is a function
agent = Agent(name="Greeter", instructions=build_instructions,
model=EchoModel())Two runs, two instructions
Run twice with a different Customer. The stand-in EchoModel returns the system prompt it was given, so the computed instructions are what you see printed.
Runner.run_sync(agent, "hi", context=Customer("Ada", "gold"))
Runner.run_sync(agent, "hi", context=Customer("Sam", "free"))
# EchoModel returns system_instructions, so the prompt is visibleBuilding the prompt from the customer
The whole program. Because EchoModel echoes the system prompt, each line of output is the exact instructions that run computed.
from dataclasses import dataclass
from agents import Agent, Runner, RunContextWrapper, set_tracing_disabled
from agents.models.interface import Model
from agents.items import ModelResponse
from agents.usage import Usage
from openai.types.responses import ResponseOutputMessage, ResponseOutputText
set_tracing_disabled(True)
@dataclass
class Customer:
name: str
tier: str
def build_instructions(ctx: RunContextWrapper[Customer], agent: Agent) -> str:
c = ctx.context
return f"You are helping {c.name}, a {c.tier} customer. Greet them by name."
def _message(text):
return ResponseOutputMessage(
id="msg", role="assistant", type="message", status="completed",
content=[ResponseOutputText(text=text, type="output_text", annotations=[])],
)
class EchoModel(Model):
async def get_response(self, system_instructions, input, model_settings, tools,
output_schema, handoffs, tracing, **k):
return ModelResponse(output=[_message(system_instructions or "")],
usage=Usage(), response_id=None)
async def stream_response(self, *a, **k):
raise NotImplementedError
agent = Agent(name="Greeter", instructions=build_instructions, model=EchoModel())
print(Runner.run_sync(agent, "hi", context=Customer("Ada", "gold")).final_output)
print(Runner.run_sync(agent, "hi", context=Customer("Sam", "free")).final_output)Why each prompt is different
- build_instructions ran twice: once per run, each time with that run's context.
- The name and tier came from ctx.context: Ada/gold in the first run, Sam/free in the second.
- EchoModel made it visible: it returns the system prompt, so the output is the prompt the SDK built.
String instructions vs a function
| A string | A function | |
|---|---|---|
| Computed | once, at definition | every run |
| Can read context | no | yes, through ctx.context |
| Signature | not applicable | func(ctx, agent) -> str |
| Use when | the prompt is the same for all | the prompt depends on the caller |
When to compute the prompt
- Personalising: greet the caller by name or note their plan.
- Time or state: put today's date or an open ticket count into the prompt.
ctx and agent and return a string. Returning None or taking one argument makes the run fail when it tries to build the prompt.Related
- Previous: Passing data with RunContextWrapper
- Next: Handoffs: passing control to another agent
- Reference: Agents: dynamic instructions
- Add
", and never share prices"to the returned string and see it echoed. - Change the second run's tier to
"gold"and compare the two lines. - Make
build_instructionstake onlyctxand read the error.
This is what real progress feels like.