Docs Frameworks
OpenAI Agents SDK
Status: verified. The adapter ships in
kiff-guard, passes the
conformance suite, and was checked against the real openai-agents
v0.17.4 SDK plus a live model call — confirming the guardrail is
accepted and reject_content genuinely skips the tool.
Shape: inverted-control. The SDK runs the tool after the guardrail
returns a verdict, so the adapter uses Guard.observe() /
Guard.decide_only(), not a run callback. See
how the guard works.
Install
pip install "git+https://github.com/kiffhq/kiff-guard.git#subdirectory=packages/python/kiff-guard"
pip install openai-agents # the framework you're guarding
(The openai extra maps to the openai-agents package.)
Observe — audit with zero config
Attach the guard as a tool input guardrail on a function_tool:
from agents import Agent, function_tool
from kiff_guard import Guard
from kiff_guard.adapters.openai_agents import kiff_tool_input_guardrail
guard = Guard(mode="observe") # zero-config audit; no KIFF account
kiff_gd = kiff_tool_input_guardrail(guard)
@function_tool(tool_input_guardrails=[kiff_gd])
def refund_order(order_id: str, amount_cents: int) -> str:
...
agent = Agent(name="support", tools=[refund_order])
In observe mode the guardrail records + learns and always allows the
tool to run. Read the trail from guard.receipts.
Activate
from kiff_guard import export_yaml
print(export_yaml("my-domain", guard.catalog))
Review and activate in the dashboard.
Enforce — govern at runtime
Build the guard with a client and mode="enforce"; the
@function_tool wiring is unchanged:
from kiff_guard import Guard, HTTPClient, ToolMap
client = HTTPClient(api_key="kiff_live_...", tool_map=ToolMap().bind(...))
guard = Guard(client=client, tenant="<tenant>", agent="support", mode="enforce")
kiff_gd = kiff_tool_input_guardrail(guard)
On a withheld decision the guardrail returns
ToolGuardrailFunctionOutput.reject_content(reason) — the SDK skips the
tool and hands the reason to the model instead of the tool result.
Enforce fails closed on a guard error by default.
The seam (verified, v0.17.4)
@tool_input_guardrail runs before the tool executes. The callback gets
data.context.tool_name, .tool_arguments (raw JSON string — the
adapter parses it to a dict), and .tool_call_id. It returns
allow() to run or reject_content(message) to skip.
Not
needs_approval.needs_approvalis the heavyweight human pause: it pauses the run, surfacesinterruptions, and resumes viaRunState. KIFF’s gate is a machine decision, so it belongs in the synchronous tool input guardrail. A withheldapproval_requiredcan still be bridged into the SDK’s nativeneeds_approvalflow on top of the gate.Boundary: tool input guardrails apply to
function_tools only. Hosted/built-in tools (web search, code interpreter, computer, shell, apply-patch) and handoffs bypass this pipeline; governing those needs a different seam.
Links
- Adapter code:
kiff_guard/adapters/openai_agents.py - Live check:
live_openai_check.py - Seam research: openai-agents.md
- SDK guardrails: openai.github.io/openai-agents-python