OpenAI Agents SDKopenai-agents 0.22 · Python 3.10+
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
25 small wins to finish your pathNext lesson

Stopping to ask a human

Some things should not happen because a model decided they should. A refund is one of them.

Mark the tool and the SDK stops the run before it is called, instead of after.

python
@function_tool(needs_approval=True)
def refund(amount: int) -> str:
    """Refund an amount in rupees to the customer."""
    return f"Refunded {amount} rupees."
A run in two halves
The run starts. The model reads the message and asks for the refund tool.Step 1 of 4

The run comes back early

python
result = await Runner.run(agent, "Please refund 500 rupees")

print("interruptions:", len(result.interruptions))
print("waiting on:   ", result.interruptions[0].raw_item.name)

No exception, and nothing blocked. You get a normal result with an interruptions list on it, which is what lets a web server hand the decision to a human and go serve somebody else.

Saying yes

python
state = result.to_state()
state.approve(result.interruptions[0])
done = await Runner.run(agent, state)

to_state is the run, frozen. Approve the interruption on it, hand the state back to Runner.run in place of a question, and the run picks up where it stopped.

Example
result = await Runner.run(agent, "Please refund 500 rupees")

print("interruptions:", len(result.interruptions))
print("waiting on:   ", result.interruptions[0].raw_item.name)

state = result.to_state()
state.approve(result.interruptions[0])
done = await Runner.run(agent, state)

print("after approval:", done.final_output)

There is a reject as well. Rejecting tells the model the tool was refused, and the model carries on and explains that to the customer, which is usually what you want rather than an error.

What to mark

Anything that spends money, deletes something, or is visible outside your system. Refunds, emails, cancellations, posting anywhere.

A rule of thumb
Pausing is cheap and being wrong is not. Start by marking everything one way, and unmark the ones that turn out to be noise. That is a much easier conversation than the other direction.
Try it yourself
  • Call state.reject(...) instead and read what the agent says.
  • Add a second approval tool and see two interruptions at once.
  • Print result.interruptions[0].raw_item.arguments and show the amount to the approver.

Slow is fine. Stopping is the only problem.