references/agents.md
# Agents and provider wiring
Read [model docs](https://openai.github.io/openai-agents-python/models/) and the
installed package source. Omitting `model` uses SDK defaults, which can change
(0.22.3 fallback: `gpt-5.6-luna`, not a model recommendation);
`OPENAI_DEFAULT_MODEL` is an SDK setting, while a
custom app variable needs explicit wiring. Tuned default ModelSettings apply
in 0.22.3 only to supported `gpt-5` names; other IDs, including `gpt-6.1-sol`, get a
plain `ModelSettings()` and the API's default reasoning effort, so set
`reasoning` explicitly. The installed `agents/models/default_models.py` shows
the current rules.
Use the [current API model catalog](https://developers.openai.com/api/docs/models)
and [model-selection guidance](https://developers.openai.com/api/docs/guides/model-selection)
for a new choice; check account/provider access and the chosen model's API page.
Configure only settings supported by the selected provider/model. Reasoning,
sampling and tool support differ by endpoint. Do not infer capabilities or a
model family from an Azure deployment's user-defined name.
Azure can use native OpenAI model classes with an `AsyncAzureOpenAI` client:
per agent via `OpenAIResponsesModel` or
`OpenAIChatCompletionsModel(model=<deployment>, openai_client=client)`, or
process-wide via `set_default_openai_client(client, use_for_tracing=False)`
plus `set_default_openai_api("chat_completions")` when the deployment lacks
Responses. Choose Responses/Chat Completions from deployed capabilities and the
current Azure API contract. GPT-6 Astra and GPT-6.1 Sol require Responses for
tool calling; GPT-6 Sol and Luna allow function calling on Chat
Completions only with reasoning effort `none`; an agent needing tools and
reasoning must use Responses (`LitellmModel` always uses Chat Completions).
See the [GPT-6 API guide](https://developers.openai.com/api/docs/guides/latest-model)
and the Azure deployment's capabilities before choosing an adapter.
Read deployment configuration or documented Azure management APIs to identify
the deployment; do not rely on an old generic listing endpoint.
LiteLLM/Any-LLM are beta adapters recommended when built-in integration points
are insufficient. They have independent model maps, imports and package extras.
A rejected parameter may be adapter capability metadata rather than the
provider: LiteLLM raises `UnsupportedParamsError ... ['reasoning_effort']` for
an Azure deployment name its model map does not recognize. After confirming the
deployment supports it, pass
`ModelSettings(reasoning=..., extra_args={"allowed_openai_params": ["reasoning_effort"]})`;
`litellm.drop_params=True` silently discards the setting.
Tracing is separate from model authentication. An Azure/LiteLLM key does not
authorize OpenAI trace uploads. Configure trace destination or disable uploads
according to the application's policy.
See [SDK models](https://openai.github.io/openai-agents-python/models/),
[LiteLLM integration](https://docs.litellm.ai/docs/tutorials/openai_agents_sdk)
and the configured provider's current docs.
references/guardrails.md
# Guardrail coverage
Agent input guardrails run for the first agent; output guardrails check the
final-output agent. They do not surround every intermediate handoff or tool.
Input guardrails default to parallel execution. The agent can already consume
tokens or execute tools before a tripwire cancels it. Use
`run_in_parallel=False` when the check must complete before the run starts.
Output guardrails happen after execution and cannot undo side effects or
already-streamed output.
Tool input/output guardrails use the FunctionTool pipeline. Hosted/built-in
execution tools and handoff calls have different coverage; local MCP tools can
use server-configured guardrails in supported releases. Verify the installed
coverage before assuming a generic policy applies. With approval, tool input
guardrails run after approval, just before execution.
`RunConfig(tool_execution=ToolExecutionConfig(pre_approval_tool_input_guardrails=True))`
also runs them before the approval interruption, where a rejection replaces the
approval prompt; an approved call then runs them again, so each guardrail runs
twice and its side effects must tolerate that (verified in 0.22.3).
Output guardrails combined with Responses server-managed history
(`conversation_id`, `previous_response_id` or `auto_previous_response_id`)
raise `UserError`: rejected output cannot be removed from that history.
A returned tripwire and a guardrail exception have different failure and
session-persistence semantics. Read the current
[guardrail documentation](https://openai.github.io/openai-agents-python/guardrails/)
for those details rather than writing a broad catch that hides the distinction.
Keep actual access control in application/tool code. Test a rejected input,
tool denial, output tripwire and relevant streaming behavior.
references/handoffs.md
# Delegation and history
A handoff changes the active agent, which continues the conversation. An
agent-as-tool returns a result to the manager. Either can be model-selected;
handoff versus tool is a control-flow decision, not a deterministic-routing
guarantee.
Use the installed handoff API for declared input, hooks and filtering. Preserve
the current `HandoffInputData` fields when changing history (for example with a
dataclass replacement) rather than reconstructing the structure field by field.
Filter only what the specialist should not receive. Keep tool-call/result and
provider protocol context valid. Use built-in filters when they meet the
contract and inspect their effect on the actual conversation. The recipient
gets full conversation history by default; handoff `input_type` supplies
model-written metadata to `on_handoff`, not replacement conversation input.
`remove_all_tools` removes tool items but not text already copied into messages;
`nest_handoff_history` summarizes history and is not a redaction boundary.
Local run context is separate from model-visible history. Authorization,
guardrail coverage and tool permissions need checking in the receiving path too.
Read [handoff docs](https://openai.github.io/openai-agents-python/handoffs/)
and [orchestration docs](https://openai.github.io/openai-agents-python/multi_agent/)
for compatible implementations.
references/patterns.md
# Orchestration, run limits and tracing
Use ordinary application control flow for fixed steps and independent work.
Add managers, workers or judge loops when the task benefits from model-selected
routing or measured quality improvement. A judge needs bounded retries and
criteria grounded in the evidence it receives.
Set the run's `max_turns` for the product budget and handle
`MaxTurnsExceeded` (default limit: 10). A turn limit does not impose a
wall-clock timeout or make tool side effects transactional. Durable execution needs the chosen runtime's
recovery contract.
Tracing is enabled by default and can include sensitive generation/tool data.
Set `RunConfig(trace_include_sensitive_data=False)` to omit model/tool payloads
from traces; metadata and custom spans need their own data policy.
Group related work with a trace when useful; use custom spans for app work.
OpenAI uploads require suitable OpenAI credentials independently of the model
provider. Disable with `set_tracing_disabled` or the documented environment
setting (`OPENAI_AGENTS_DISABLE_TRACING=1`), or configure a different processor.
`add_trace_processor` retains the existing OpenAI exporter;
`set_trace_processors` replaces processors. A missing key skips exports with a
warning in 0.22.3; an unsuitable supplied key can fail authentication.
Read [orchestration](https://openai.github.io/openai-agents-python/multi_agent/),
[running agents](https://openai.github.io/openai-agents-python/running_agents/)
and [tracing](https://openai.github.io/openai-agents-python/tracing/).
references/sandbox.md
# Sandbox agents
Use `SandboxAgent` when the agent needs a persistent workspace, filesystem/shell
tools or lazily loaded skills. Ordinary API/function-tool work need not pay for
a workspace runtime.
The sandbox hosts execution; the agent loop remains in the application process.
The docs labelled this API beta through 0.22.x and removed the label before
0.23. Read the current
[sandbox quickstart](https://openai.github.io/openai-agents-python/sandbox_agents/)
and installed source before choosing clients, capabilities, manifests or mounts.
A local Unix client, Docker client and remote environment do not establish the
same isolation/permission boundary. `UnixLocalSandboxClient` adds no OS
confinement on Linux; its macOS `sandbox-exec` filesystem restrictions do not
isolate the network (verified in 0.22.3). Use a suitably isolated backend for
untrusted commands. Configure allowed paths, commands, network access and
credentials for the task; do not copy demo defaults blindly.
Use documented session/snapshot state to resume the workspace. Define who owns
cleanup and how mounts or files persist. Skills are instruction bundles;
loading them does not authorize tools or grant isolation.
Verify the actual client on the target host, including cancellation, cleanup,
resume and access limits. Do not show an incomplete constructor sketch as a
working example. `agents.testing.scripted_sandbox_session()` supports offline
workflow tests without starting a sandbox (verified in 0.22.3).
references/sessions.md
# History ownership and sessions
Choose one conversation-history owner: application-managed input, an SDK session
or provider-managed Responses conversation state. Combining owners can duplicate
history or replay already-executed operations. In 0.22.3, `session=` combined
with `conversation_id`, `previous_response_id` or `auto_previous_response_id`
raises `UserError`. Application-managed history uses `result.to_input_list()`;
Responses-managed history sends only the new turn with the conversation ID
or `result.last_response_id`.
Use the installed session implementations for the actual storage/runtime.
SQLite fits local/single-instance storage; a supported SQL/Redis store may fit
distributed persistence. Session IDs need server-side ownership checks and
appropriate tenant scoping. A database row is not automatically durable workflow
state.
Persist replay-valid SDK input/items, including required tool-call/result and
reasoning context. Compaction, branching and encryption wrappers have their own
semantics; read the relevant adapter before introducing them.
`EncryptedSession` defaults to `ttl=600` seconds (verified in 0.22.3); expired
items are skipped when read. Configure retention intentionally. Encryption at
rest does not encrypt provider requests or trace uploads.
Define deletion/retention across application history, hosted conversation state,
logs and backups when those are in scope.
Read [sessions](https://openai.github.io/openai-agents-python/sessions/)
and [running agents](https://openai.github.io/openai-agents-python/running_agents/)
for compatible history/resume examples.
references/streaming.md
# Streaming contracts
`Runner.run_streamed` returns a streamed result. Consume `stream_events()`
through completion to establish final output, usage and terminal failure.
Raw provider deltas, run items and agent changes are distinct event shapes:
`raw_response_event` (`event.data`), `run_item_stream_event` (`event.item`)
and `agent_updated_stream_event` (`event.new_agent`). The handoff-completed
event name is spelled `handoff_occured`.
`result.cancel()` cancels immediately; `result.cancel(mode="after_turn")`
finishes the current turn. Approval pauses end with `result.interruptions`;
resume the approved/rejected state with `Runner.run_streamed(original_agent, state)`.
Choose the UI protocol explicitly. A stream already opened with a successful
HTTP status can still emit a terminal error. Handle cancellation and safe public
errors; keep provider/DB diagnostics on the server.
Input guardrails can run in parallel, and output guardrails run after agent
completion. Already-sent deltas or completed side effects cannot be withdrawn.
If the product must withhold unvalidated content, buffer it or use the matching
blocking/tool boundary rather than claiming output guardrails prevent display.
Read [streaming docs](https://openai.github.io/openai-agents-python/streaming/)
and [guardrails](https://openai.github.io/openai-agents-python/guardrails/) for
event and persistence behavior in the installed version. Verify tool events,
partial output, tripwires and disconnects alongside successful text.
references/structured-output.md
# Structured output
Use `output_type` with a supported Pydantic/dataclass shape or
`AgentOutputSchema` when the task needs a schema. Confirm strict-schema
compatibility in the installed SDK and provider.
`AgentOutputSchema(T, strict_json_schema=False)` disables strict schema mode;
the SDK still validates returned JSON with its Pydantic TypeAdapter. A schema
with unsupported `additionalProperties` (for example a bare `dict` field) can
raise `UserError` in strict mode; prefer an explicit compatible type.
A structurally valid object can still contain incorrect claims or unauthorized
IDs. Validate those against the app's data and policy.
Configure ModelSettings from the actual model/endpoint capabilities. A supported
field in the SDK does not mean every provider accepts it; avoid universal
temperature/reasoning assertions and copied settings tables.
Read [agents](https://openai.github.io/openai-agents-python/agents/),
[model settings](https://openai.github.io/openai-agents-python/ref/model_settings/)
and the installed output-schema types.
references/tools.md
# Tool execution boundaries and approvals
Use typed function tools for application operations; keep tenant/role checks,
input validation and idempotency in the executor. Model input/context is not
authorization. Tool errors returned to the model or UI need the app's safe
formatting policy. By default `@function_tool` catches exceptions.
Through 0.22.x the exception text reached the model; from 0.23.0 the model gets
a fixed generic message. Return an error string or pass `failure_error_function`
for model-visible detail; `None` re-raises. `timeout=` bounds a call; it does not make side effects
transactional. `from agents.decorators import tool` is an alias in current
releases (verified in 0.22.3).
Hosted tools run under provider contracts; local tools execute in the app's
runtime or sandbox. Confirm installed tool types and endpoint support in
[tools docs](https://openai.github.io/openai-agents-python/tools/). Do not assume
function-tool guardrails cover hosted shell, computer, MCP or patch tools.
Tool execution limits and cancellation apply to real side effects too.
Ordinary function-call dict/Pydantic results, including a structured
`as_tool()` final output, default to `str(value)` in 0.22.3. Declared output
schemas serialize JSON; recognized `ToolOutputText`/image/file content retains
its structured format. Programmatic calls have separate JSON serialization.
Return JSON text or use `custom_output_extractor` when JSON formatting matters.
`tool_use_behavior="stop_on_first_tool"` or
`StopAtTools(stop_at_tool_names=[...])` returns a selected tool output as the
final result; `Agent.reset_tool_choice` defaults to `True` so a forced tool
choice does not force calls forever.
For model-written programmatic tool calling (`ProgrammaticToolCallingTool`, a
hosted Responses tool), verify the selected model and installed SDK
constraints, allowed callers and tool exposure before enabling it. A general
function-tool list is not automatically callable from model-generated code.
Use SDK human-in-the-loop interruption/resume state when the task needs approval.
Tie the decision to an authorized server-issued run/tool call and recheck
policy before executing it; do not trust client-edited run state.
`result.interruptions` contains approval items; use `state = result.to_state()`,
`state.approve(item)` / `state.reject(item)`, then resume with
`await Runner.run(original_agent, state)`. Restore serialized state with
`await RunState.from_string(original_agent, text)`; that method is async and
the serialized state includes run context. Pass `context_override=` to replace
that saved context with trusted server context; approval decisions and usage
still come from the string (verified in 0.22.3).
SKILL.md
---
name: openai-agents-sdk
description: OpenAI Agents SDK (Python) development. Use when building AI agents, multi-agent handoffs, function tools, guardrails, sessions, streaming, or tracing with the `openai-agents` / `agents` Python package — including Azure OpenAI through its native client or LiteLLM. Triggers on imports from `agents`, uses of `Runner.run_sync`/`Runner.run_streamed`, `@function_tool`, `AgentOutputSchema`, `SQLiteSession`, or questions about the openai-agents-python SDK. Python only — not the TypeScript `@openai/agents` SDK.
---
# OpenAI Agents SDK for Python
Use this skill for `openai-agents` / `agents`, not the TypeScript SDK.
Resolve the installed package and its provider integrations before implementing
an API. Current [SDK documentation](https://openai.github.io/openai-agents-python/)
and a matching source tag take precedence over static examples.
Choose a model from the current [OpenAI model catalog](https://developers.openai.com/api/docs/models)
and [model-selection guide](https://developers.openai.com/api/docs/guides/model-selection).
Check the selected model's tools, reasoning settings and provider availability;
an SDK fallback or a demo ID is not a permanent recommendation.
The wheel ships no docs. Offline, read the installed source: the version from
`importlib.metadata.version("openai-agents")`, public exports in
`agents/__init__.py`, docstrings and types in the package directory
(`python -c "import agents; print(agents.__file__)"`). The documentation
source is `docs/` at repository tag `v<version>`.
## Core shape
`Runner.run_sync` raises inside a running event loop (async handlers and
notebooks); use `await Runner.run(...)`. `Runner.run_streamed(...)` itself
is not awaited; consume its `stream_events()` async iterator to completion.
Checked with openai-agents 0.22.3; confirm names in the installed source for
another version:
```python
import asyncio
from pydantic import BaseModel
from agents import Agent, ModelSettings, Runner, SQLiteSession, function_tool
from openai.types.shared import Reasoning
class OrderAnswer(BaseModel):
order_id: str
status: str
reply: str
@function_tool
def get_order_status(order_id: str) -> str:
"""Return an order's shipping status.
Args:
order_id: Order ID given by the customer.
"""
return f"{order_id}: shipped" # authorize and read the app's data here
support = Agent(
name="Support",
instructions="Answer order questions. Use get_order_status for order data.",
model="gpt-6.1-sol", # example API ID; use the project's configured model
model_settings=ModelSettings(reasoning=Reasoning(effort="medium")),
tools=[get_order_status],
output_type=OrderAnswer,
)
# A manager keeps control with tools=[order_tool]; handoffs=[support] transfers it.
order_tool = support.as_tool("order_support", "Answer an order question.")
async def main() -> None:
session = SQLiteSession("user-123", "conversations.db") # ID owned by the signed-in user
result = await Runner.run(support, "Where is order A-17?", session=session)
answer: OrderAnswer = result.final_output # validated OrderAnswer instance
print(answer.status, answer.reply)
asyncio.run(main())
```
The example uses Responses (the SDK's default API). [GPT-6.1 Sol](https://developers.openai.com/api/docs/models/gpt-6.1-sol)
requires Responses for tool calling and does not support `none` or `minimal`
reasoning effort.
## Integration decisions
- Keep the project's provider, model and storage unless the task requires a
change. Verify model IDs/capabilities in provider configuration or live docs.
SDK model/settings defaults may change; configure product-critical choices
explicitly.
- Use native provider/client support when it fits. LiteLLM/Any-LLM are optional
integrations with their own compatibility and settings behavior.
- Choose handoffs when a specialist takes over, or `agent.as_tool()` when the
manager should continue after delegated work. A fixed pipeline does not need
extra agents merely to implement ordinary control flow.
- Authorize side effects in tools. Agent instructions, output schemas and
guardrails do not replace authorization or idempotency.
- Decide history ownership, approval/resume and tracing data policy before
exposing a multi-turn agent to untrusted clients.
## Read for the feature
- [Agents/providers](references/agents.md): model defaults, Azure and adapters.
- [Tools](references/tools.md): local/hosted execution, delegation and approval/resume.
- [Structured output](references/structured-output.md): schema and capability constraints.
- [Streaming](references/streaming.md): event types, failures and guardrails.
- [Handoffs](references/handoffs.md): control transfer and filtering.
- [Guardrails](references/guardrails.md): execution timing and coverage.
- [Sessions](references/sessions.md): history ownership and persistence.
- [Orchestration/tracing](references/patterns.md): run limits and observability.
- [Sandbox](references/sandbox.md): workspace execution and resume state.
Use the OpenAI Developer Docs MCP (`https://developers.openai.com/mcp`) if
available for current OpenAI API/provider behavior, or its plain-text index
`https://developers.openai.com/api/docs/llms.txt`; use the Python SDK's own
reference for SDK signatures. Read selected
[official examples](https://github.com/openai/openai-agents-python/tree/main/examples)
from a compatible tag, not a copied catalog of demos.
## Verification
Verify changed tools, multi-turn history, approval/denial and failure recovery.
Use installed `agents.testing` (verified in 0.22.3) for offline orchestration:
`ScriptedModel`, `ModelStep`, `assistant_message`, `function_call` and
`model.assert_complete()`, with `RunConfig(tracing_disabled=True)`; assigning
`agent.model = ScriptedModel([...])` runs the core shape offline. Run the
project's checks; report missing provider access separately from verified SDK
behavior.