Repository Public
1# Guardrails and human review23> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.45Use guardrails for automatic checks and human review for approval decisions. Together, they define when a run should continue, pause, or stop.67- **Guardrails** validate input, output, or tool behavior automatically.8- **Human review** pauses the run so a person or policy can approve or reject a sensitive action.910## Choose the right control1112| Use case | Start with |13| --------------------------------------------------------------------------------------------- | --------------------------- |14| Block disallowed user requests before the main model runs | Input guardrails |15| Validate or redact the final output before it leaves the system | Output guardrails |16| Check arguments or results around a function tool call | Tool guardrails |17| Pause before side effects like cancellations, edits, shell commands, or sensitive MCP actions | Human-in-the-loop approvals |1819## Add a blocking guardrail2021Use input guardrails when you want a fast validation step to run before the expensive or side-effecting part of the workflow starts.2223Block a request with an input guardrail2425```javascript26import { Agent, InputGuardrailTripwireTriggered, run } from "@openai/agents";27import { z } from "zod";2829const guardrailAgent = new Agent({30 name: "Homework check",31 instructions: "Detect whether the user is asking for math homework help.",32 outputType: z.object({33 isMathHomework: z.boolean(),34 reasoning: z.string(),35 }),36});3738const agent = new Agent({39 name: "Customer support",40 instructions: "Help customers with support questions.",41 inputGuardrails: [42 {43 name: "Math homework guardrail",44 runInParallel: false,45 async execute({ input, context }) {46 const result = await run(guardrailAgent, input, { context });47 return {48 outputInfo: result.finalOutput,49 tripwireTriggered: result.finalOutput?.isMathHomework === true,50 };51 },52 },53 ],54});5556try {57 await run(agent, "Can you solve 2x + 3 = 11 for me?");58} catch (error) {59 if (error instanceof InputGuardrailTripwireTriggered) {60 console.log("Guardrail blocked the request.");61 }62}63```6465```python66import asyncio6768from pydantic import BaseModel6970from agents import (71 Agent,72 GuardrailFunctionOutput,73 InputGuardrailTripwireTriggered,74 RunContextWrapper,75 Runner,76 TResponseInputItem,77 input_guardrail,78)798081class MathHomeworkOutput(BaseModel):82 is_math_homework: bool83 reasoning: str848586guardrail_agent = Agent(87 name="Homework check",88 instructions="Detect whether the user is asking for math homework help.",89 output_type=MathHomeworkOutput,90)919293@input_guardrail94async def math_guardrail(95 ctx: RunContextWrapper[None],96 agent: Agent,97 input: str | list[TResponseInputItem],98) -> GuardrailFunctionOutput:99 result = await Runner.run(guardrail_agent, input, context=ctx.context)100 return GuardrailFunctionOutput(101 output_info=result.final_output,102 tripwire_triggered=result.final_output.is_math_homework,103 )104105106agent = Agent(107 name="Customer support",108 instructions="Help customers with support questions.",109 input_guardrails=[math_guardrail],110)111112113async def main() -> None:114 try:115 await Runner.run(agent, "Can you solve 2x + 3 = 11 for me?")116 except InputGuardrailTripwireTriggered:117 print("Guardrail blocked the request.")118119120if __name__ == "__main__":121 asyncio.run(main())122```123124125Use blocking execution when the cost or risk of starting the main agent is too high. Use parallel guardrails when lower latency matters more than avoiding speculative work.126127## Pause for human review128129Approvals are the human-in-the-loop path for tool calls. The model can still decide that an action is needed, but the run pauses until you approve or reject it.130131Pause for approval before a sensitive action132133```javascript134import { Agent, run, tool } from "@openai/agents";135import { z } from "zod";136137const cancelOrder = tool({138 name: "cancel_order",139 description: "Cancel a customer order.",140 parameters: z.object({ orderId: z.number() }),141 needsApproval: true,142 async execute({ orderId }) {143 return `Cancelled order ${orderId}`;144 },145});146147const agent = new Agent({148 name: "Support agent",149 instructions: "Handle support requests and ask for approval when needed.",150 tools: [cancelOrder],151});152153let result = await run(agent, "Cancel order 123.");154155if (result.interruptions?.length) {156 const state = result.state;157 for (const interruption of result.interruptions) {158 state.approve(interruption);159 }160 result = await run(agent, state);161}162163console.log(result.finalOutput);164```165166```python167import asyncio168169from agents import Agent, Runner, function_tool170171172@function_tool(needs_approval=True)173async def cancel_order(order_id: int) -> str:174 return f"Cancelled order {order_id}"175176177agent = Agent(178 name="Support agent",179 instructions="Handle support requests and ask for approval when needed.",180 tools=[cancel_order],181)182183184async def main() -> None:185 result = await Runner.run(agent, "Cancel order 123.")186187 if result.interruptions:188 state = result.to_state()189 for interruption in result.interruptions:190 state.approve(interruption)191 result = await Runner.run(agent, state)192193 print(result.final_output)194195196if __name__ == "__main__":197 asyncio.run(main())198```199200201This same interruption pattern applies even when the approving tool lives deeper in the workflow, such as after a handoff or inside a nested `agent.asTool()` in TypeScript or `agent.as_tool()` in Python call.202203## Approval lifecycle204205When a tool call needs review, the SDK follows the same pattern every time:2062071. The run records an approval interruption instead of executing the tool.2082. The result returns `interruptions` plus a resumable `state`.2093. Your application approves or rejects the pending items.2104. You resume the same run from `state` instead of starting a new user turn.211212If the review might take time, serialize `state`, store it, and resume later. That's still the same run.213214## Workflow boundaries matter215216Agent-level guardrails don't run everywhere:217218- Input guardrails run only for the first agent in the chain.219- Output guardrails run only for the agent that produces the final output.220- Tool guardrails run on the function tools they're attached to.221222If you need checks around every custom tool call in a manager-style workflow, don't rely only on agent-level input or output guardrails. Put validation next to the tool that creates the side effect.223224## Review cybersecurity actions before execution225226For authorized cybersecurity workflows, evaluate each sensitive tool call227before it executes. Use tool guardrails and approval interruptions to enforce228the written engagement scope at the boundary where side effects occur:2292301. Check the proposed target, action, tool arguments, calling identity, and231 engagement window against the approved scope.2322. Give a separate policy component or reviewer the exact proposed action and233 only the context needed to evaluate it.2343. Deny out-of-scope hosts, credential theft, persistence, data exfiltration,235 destructive changes, production access, and attempts to bypass policy.2364. Pause ambiguous or high-risk actions for explicit human approval before the237 tool runs.2385. Enforce independent filesystem, network, identity, and project boundaries,239 record decisions and execution outcomes, and fail closed if review times out240 or becomes unavailable.241242Responses API and Agents SDK applications don't automatically inherit243[Codex Auto-review](https://developers.openai.com/codex/sandboxing/auto-review). Add review and enforcement244to your own harness. The245[open-source Codex reviewer policy](https://github.com/openai/codex/blob/main/codex-rs/core/src/guardian/policy.md)246illustrates one approach. Review [Models and Trusted Access](https://developers.openai.com/codex/cyber-safety)247for approved model access and [Recommended configuration](https://developers.openai.com/codex/cyber-safety/recommended-configuration)248for safe engagement setup.249250## Streaming and delayed review use the same state model251252Streaming doesn't create a separate approval system. If a streamed run pauses, wait for it to settle, inspect `interruptions`, resolve the approvals, and resume from the same `state`. If the review happens later, store the serialized state and continue the same run when the decision arrives.253254## Next steps255256Once the control boundaries are clear, continue with the guide that covers the runtime or tool surface around them.257258259260 [Running agents261262263264 See how interruptions and resumptions fit into the runtime loop.](https://developers.openai.com/api/docs/guides/agents/running-agents)265 [Results and state266267268269 Learn which result surfaces paused runs return to your application.](https://developers.openai.com/api/docs/guides/agents/results)270 [Using tools271272273274 Decide which tool surfaces need validation or approval before side effects275 happen.](https://developers.openai.com/api/docs/guides/tools#usage-in-the-agents-sdk)