Module 4 of 7 · 62 min

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.

OpenAI

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.
01

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.

02

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.

Narrow Responses API function tool
json
{
  "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
  }
}
03

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.

04

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.

05

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

  1. 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.
  2. 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.
  3. 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.
  4. 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

Knowledge check

Make it stick.

Pass at 80%

Choose the strongest answer for each question. Your attempts become part of your device-local transcript.

01Who executes a custom function tool requested by an OpenAI model?
02What does strict schema conformance prove?
03How should the application associate a function result with its request?
04A write times out after the target may have accepted it. What is the correct next state?
05What should govern Codex command execution?