Build Bounded OpenAI Tool and Codex Agent Loops
Connect OpenAI models to built-in and custom tools through narrow schemas, matched call results, enforced authority, explicit budgets, approvals, sandbox boundaries, and verified side effects.
By the end
You will be able to
- Distinguish provider-hosted built-in tools from application-executed function tools.
- Define strict, narrow function schemas and match calls to results with call identifiers.
- Enforce identity, authorization, approval, idempotency, postconditions, and recovery outside the model.
- Bound Codex and other agent loops with sandbox, approval, budget, cancellation, and terminal-state controls.
Tools have execution owners
The Responses API can expose built-in tools operated through OpenAI and custom function tools executed by your application. A custom function call is a structured proposal: your code validates, authorizes, runs, and returns the result. The model does not secretly execute your business operation.
Identify the execution owner for every tool. Ownership determines credentials, network access, data handling, billing, failure semantics, audit evidence, and who verifies a side effect. Review current provider behavior before enabling a hosted tool with external access.
Define narrow, strict functions
Give each function one legible purpose, a precise name and description, constrained properties, required fields, and no unrecognized properties. Prefer a small operation-specific function over a generic shell, HTTP, SQL, or arbitrary-command interface.
Strict schema conformance reduces malformed arguments but does not establish user intent or authority. Treat arguments as untrusted, resolve the acting identity server-side, authorize the exact resource and operation, apply business rules, and reject unknown scope before execution.
{
"type": "function",
"name": "set_training_status",
"description": "Set one learner module to an allowed status after authorization.",
"strict": true,
"parameters": {
"type": "object",
"properties": {
"learnerId": { "type": "string" },
"moduleId": { "type": "string" },
"status": { "type": "string", "enum": ["not-started", "in-progress", "completed"] },
"operationId": { "type": "string" }
},
"required": ["learnerId", "moduleId", "status", "operationId"],
"additionalProperties": false
}
}Match calls, results, and loop state
Inspect response.output for function_call items, parse their arguments as untrusted data, and preserve each call_id. Return a function_call_output with the matching call_id so the next response can associate evidence with the correct request. Handle multiple calls without depending on array position.
Drive the loop through explicit states: model response, validation, authorization, approval, execution, result return, postcondition verification, and terminal outcome. Bound model turns, tool calls, elapsed time, tokens, cost, consecutive failures, and parallel work; include cancellation and human escalation.
Enforce side-effect safety outside the model
Separate reads, reversible writes, communications, privileged changes, and destructive operations. Require fresh approval where policy demands it, pass the authenticated principal independently of model arguments, use operation identifiers or idempotency keys, and log the authorization decision.
A handler returning success is not proof that the desired external postcondition holds. Verify the system of record. If a timeout occurs after a write may have been accepted, mark the outcome unknown and reconcile before retrying, compensating, rolling back, or telling the user it completed.
Bound Codex and agent autonomy
For Codex, treat sandbox mode and approval policy as enforceable execution boundaries. Start with the least filesystem, network, credential, and command authority that can complete the task. Review the proposed command and target before expanding access, and never treat a model's confidence as approval.
Use a deterministic workflow when steps and branches are known. Use a bounded agent when observations and tool results make the path impractical to enumerate. Add autonomy only when evaluations justify it, with explicit objectives, allowed tools and scope, budgets, checkpoints, rollback, audit, and terminal states such as completed, needs approval, refused, failed safely, budget exceeded, cancelled, and outcome unknown.
Practice activity
Implement and attack a bounded OpenAI tool loop
- Define one read function and one reversible write function with strict schemas, authenticated identity, exact resource authorization, operation identifiers, documented result contracts, and no arbitrary execution fields.
- Implement or diagram a Responses API loop that matches every function_call to function_call_output by call_id and reaches explicit completion, approval, refusal, failure, cancellation, budget, or unknown-outcome states.
- Test malformed JSON, excess arguments, unauthorized scope, duplicate operation identifier, rejected approval, mismatched call_id, tool timeout after a possible write, malicious tool output, and maximum-turn exhaustion.
- Compare a deterministic workflow with the bounded agent and defend the release choice using quality, risk, latency, cost, recovery, sandbox, and approval evidence.
What to produce
- Two secret-free function contracts plus loop, identity, authorization, approval, idempotency, sandbox, budget, postcondition, and reconciliation implementation or design evidence.
- A passing adversarial report and a workflow-versus-agent decision linked to measurable release requirements and explicit residual risks.
Reflect before continuing
Which control still blocks an unauthorized write if the model chooses the correct tool with convincing but attacker-supplied arguments?
Evidence
Sources and verification
- Function callingOpenAI · verified 2026-07-25
- Safety best practicesOpenAI · verified 2026-07-25
- Model guidanceOpenAI · verified 2026-07-25
- Agent approvals and securityOpenAI · verified 2026-07-25
Knowledge check
Make it stick.
Choose the strongest answer for each question. Your attempts become part of your device-local transcript.