0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
27 small wins to finish your pathNext lesson →

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.

python
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.

python
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.

python
# 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.

python
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 visible

Building 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.

Example
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 stringA function
Computedonce, at definitionevery run
Can read contextnoyes, through ctx.context
Signaturenot applicablefunc(ctx, agent) -> str
Use whenthe prompt is the same for allthe 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.
Watch out. The function must take both ctx and agent and return a string. Returning None or taking one argument makes the run fail when it tries to build the prompt.
Try it yourself
  • 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_instructions take only ctx and read the error.

This is what real progress feels like.