# OpenAI Agents SDK

> Run the OpenAI Agents SDK against sference: Responses API, tools, handoffs, guardrails, and background steps on the 24h window.

Source: https://sference.com/docs/agents/openai-agents-sdk

The [OpenAI Agents SDK](https://openai.github.io/openai-agents-python/) is built on the Responses API, which sference serves natively. Point the client at sference and agents, handoffs, and guardrails run on open models.

## Install

```bash
pip install -U openai-agents
```

## Point the SDK at sference

```python
import os
from agents import set_default_openai_client, set_default_openai_api
from openai import AsyncOpenAI

client = AsyncOpenAI(
    base_url="https://api.sference.com/v1",
    api_key=os.environ["SFERENCE_API_KEY"],
)

set_default_openai_client(client)
set_default_openai_api("responses")
```

Model names are catalog ids:

```python
from agents import Agent, Runner

agent = Agent(
    name="Assistant",
    model="zai-org/GLM-5.3",
    instructions="You are a concise assistant. Answer in one paragraph.",
)

result = await Runner.run(agent, "Explain prefix caching in one paragraph.")
print(result.final_output)
```

## Tools

The SDK's function-tool decorator produces the OpenAI tool schema, which sference accepts unchanged:

```python
from agents import function_tool

@function_tool
def search_docs(query: str) -> str:
    """Search the internal documentation."""
    return docs_index.search(query)

agent = Agent(
    name="Docs assistant",
    model="moonshotai/Kimi-K3",
    instructions="Answer using search_docs. Cite the document title.",
    tools=[search_docs],
)

result = await Runner.run(agent, "How do I rotate an API key?")
```

## Structured output

Use `output_type` with a Pydantic model — the SDK maps it to a JSON schema on the Responses API:

```python
from pydantic import BaseModel

class Triage(BaseModel):
    severity: str
    team: str
    summary: str

agent = Agent(
    name="Triage",
    model="zai-org/GLM-5.3",
    instructions="Classify the ticket.",
    output_type=Triage,
)

result = await Runner.run(agent, ticket_text)
triage: Triage = result.final_output
```

## Handoffs

```python
from agents import Agent, handoff

billing = Agent(name="Billing", model="zai-org/GLM-5.3-Flash", instructions="Handle billing questions.")
technical = Agent(name="Technical", model="zai-org/GLM-5.3", instructions="Handle technical questions.")

triage = Agent(
    name="Triage",
    model="zai-org/GLM-5.3-Flash",
    instructions="Route the user to the right specialist.",
    handoffs=[handoff(billing), handoff(technical)],
)

result = await Runner.run(triage, "My invoice is wrong.")
```

Use a cheap, fast model for the router and a stronger one for the specialist — the catalog spans `GLM-5.3-Flash` (0.20 / 0.60) to `Kimi-K3` (3.00 / 15.00), so routing decisions are the natural place to economize.

## Guardrails

```python
from agents import Agent, GuardrailFunctionOutput, input_guardrail, Runner

@input_guardrail
async def no_pii(ctx, agent, input):
    flagged = contains_pii(str(input))
    return GuardrailFunctionOutput(output_info={"pii": flagged}, tripwire_triggered=flagged)

agent = Agent(
    name="Support",
    model="zai-org/GLM-5.3",
    instructions="Answer support questions.",
    input_guardrails=[no_pii],
)
```

## Background steps on the 24h window

The SDK's `Runner` is synchronous from your code's perspective, but individual steps do not have to be. For long-horizon work — research passes, bulk classification, overnight evaluation — submit the step as a background response and collect it later:

```python
response = await client.responses.create(
    model="zai-org/GLM-5.3",
    input=research_question,
    background=True,
    metadata={"completion_window": "24h"},
)
print(response.id)   # collect later, or register a webhook
```

Background responses bill at **0.5 ×** the list rate. For steps where someone is waiting but which are not on the critical path, `service_tier: "flex"` gives **0.7 ×**. See [Pricing](https://sference.com/docs/pricing).

> **`store` and conversation state**
>
> sference answers every request from the content you send — there is no server-side thread to point at. Pass the full conversation in `input` on each turn (the SDK does this by default) and do not rely on `previous_response_id` or a stored `conversation`. See [API compatibility](https://sference.com/docs/compatibility).

## Tracing

The SDK's tracing exports to OpenAI's platform by default. To disable it or point it elsewhere:

```python
from agents import set_tracing_disabled
set_tracing_disabled(True)
```

## Cost control

```python
client = AsyncOpenAI(
    base_url="https://api.sference.com/v1",
    api_key=os.environ["SFERENCE_API_KEY"],
    default_headers={"X-Sference-Run": run_id},   # your own correlation header
)
```

- **`prompt_cache_key`** — set it to the run id so the system prompt and tool definitions bill at the cached rate on every step. See [Prompt caching](https://sference.com/docs/guides/prompt-caching).
- **`max_queue_wait_s`** — bound the wait and fail over. See [Max queue wait](https://sference.com/docs/guides/max-queue-wait).
- **Cheap routers, strong specialists** — handoffs and guardrails rarely need the largest model.

- [Agent SDKs](https://sference.com/docs/agents): Compare frameworks and per-step window choices.
- [API compatibility](https://sference.com/docs/compatibility): Which Responses fields sference honors, and which it ignores.
- [Webhooks](https://sference.com/docs/guides/webhooks): Get notified instead of polling background responses.
