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.
@function_tool(needs_approval=True)
def refund(amount: int) -> str:
"""Refund an amount in rupees to the customer."""
return f"Refunded {amount} rupees."The run comes back early
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
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.
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.
- 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.argumentsand show the amount to the approver.
Slow is fine. Stopping is the only problem.