OpenAI Agents SDK: stop a tool from running twice on retry or resume
How to make function tools in the OpenAI Agents SDK once-only across retries, resumed runs and approvals, with a runnable example.
Where repeats come from
The OpenAI Agents SDK can pause a run for human approval (needs_approval=True), store the run as
JSON with state.to_json(), and resume it later with RunState.from_json(...) and
Runner.run(agent, state). Its documentation also asks you to "only deserialize snapshots from
trusted storage". If your process stops after a tool ran but before you stored the new state, resuming from the
older state can run the tool again. Retries after timeouts have the same effect.
Once-only function tools
The Call4Agents adapter wraps a function tool. The SDK's decorator goes on top. The context argument is left out
of the key (it changes on every call), and the run id comes from your context or from c4a.run(...):
from agents import Agent, Runner, RunContextWrapper, function_tool
import call4agents as c4a
from call4agents.adapters import openai_agents as c4a_oa
c4a.init()
@function_tool
@c4a_oa.effect # once per session, tool and exact arguments
def send_email(ctx: RunContextWrapper[dict], to: str, body: str) -> str:
"""Send an email."""
return mailer.send(to=to, body=body)
agent = Agent(name="mailer", tools=[send_email])
await Runner.run(agent, "email bob", context={"session_id": "session-1"})
A runnable version with a fake model (no API key) prints {'emails_sent': 1}; the same code without
the kit sends 2:
pip install call4agents openai-agents
python -m call4agents.examples.openai_agents_tool
For approvals, @c4a_oa.effect(approval=True) with c4a.approve(...) binds the "yes" to
the exact arguments and lets it be used once.