How Top Teams Use Agents Tool Use Function — Case Studies

Featured image for How Top Teams Use Agents Tool Use Function — Case Studies
Spread the love

From model to agent: Equipping the Responses API with a computer environment – OpenAI

From model to agent: Equipping the Responses API with a computer environment – A Practical Guide

As of August 2026 the AI community is buzzing about the next evolution of large language models (LLMs): turning them into agents that can call tools, execute functions, and even control a sandboxed computer environment. Recent headlines – “How to Debug AI Coding Agents When They Change the Wrong Thing” (Towards Data Science), “Introducing advanced tool use on the Claude Developer Platform” (Anthropic), and “Accelerate agentic tool calling with serverless model customization in Amazon SageMaker AI” (AWS) – underline how quickly the agents tool use function paradigm is moving from research labs to production workloads.

This article is a deep‑dive implementation guide for ML engineers and AI practitioners who want to move from a stateless ChatCompletion model to a full‑featured agent capable of interacting with a Responses API that spawns a lightweight computer environment (Docker, sandboxed VM, or server‑less container). We will cover architecture, best practices, trade‑offs, real‑world case studies, code snippets, FAQs, and project ideas – all centered on the agents tool use function workflow.

Table of Contents

Agentic Architecture & Core Concepts

Before we dive into code, it is essential to understand the moving parts of an agents tool use function system. The diagram below (described in text) outlines the typical flow:

  1. User Prompt: The end‑user sends a natural‑language request to the /v1/chat/completions endpoint.
  2. LLM Core: The model parses intent, decides whether a tool call is required, and emits a function_call JSON payload.
  3. Tool Registry: A server‑side registry maps function names to concrete implementations (e.g., run_shell, fetch_url, create_file).
  4. Execution Engine: The engine runs the requested function inside a sandboxed computer environment – typically a Docker container with limited network and filesystem access.
  5. Result Propagation: The function’s output is serialized back to the LLM, which incorporates it into the next response to the user.

Key architectural decisions include:

  • Sandbox Choice: Docker vs. Firecracker vs. server‑less runtimes (AWS Lambda, Cloudflare Workers). Docker offers full Linux tools; Firecracker gives micro‑VM isolation with lower overhead; server‑less is the cheapest for occasional calls.
  • Function Definition Language: OpenAI’s function schema (JSON) versus custom GraphQL/Protobuf definitions. The former integrates seamlessly with the API.
  • State Management: Agents may need persistent state (e.g., a file system) across calls. Strategies include mounting a persistent volume, using a key‑value store, or serializing state into the conversation history.
  • Observability: Logging, tracing (OpenTelemetry), and sandbox audit logs are critical for debugging and compliance.

Why a Computer Environment?

Traditional tool‑calling lets an LLM invoke high‑level APIs (e.g., search_web, send_email). A full computer environment pushes the boundary: the agent can run arbitrary shell commands, compile code, or invoke CLI tools that have no native API. This expands the agent’s expressive power dramatically, enabling use‑cases such as:

  • Automated data‑pipeline orchestration (e.g., running ffmpeg to transcode media).
  • Debugging code by executing pytest inside a sandbox.
  • Generating and serving temporary web assets for rapid prototyping.

Step‑by‑Step Implementation

The following sections walk through building a production‑ready agents tool use function pipeline using Python, FastAPI, Docker, and OpenAI’s function‑calling API.

1. Define Your Function Schema

OpenAI expects a JSON schema that describes each callable tool. Below is a minimal example that includes a Shell Executor and a File‑Writer.

[
  {
    "name": "run_shell",
    "description": "Execute a shell command in a sandboxed environment.",
    "parameters": {
      "type": "object",
      "properties": {
        "command": {
          "type": "string",
          "description": "The exact shell command to run."
        },
        "timeout": {
          "type": "integer",
          "default": 30,
          "description": "Maximum execution time in seconds."
        }
      },
      "required": ["command"]
    }
  },
  {
    "name": "write_file",
    "description": "Create or overwrite a file inside the sandbox.",
    "parameters": {
      "type": "object",
      "properties": {
        "path": {"type": "string", "description": "Relative path inside the sandbox."},
        "content": {"type": "string", "description": "File contents as a UTF‑8 string."}
      },
      "required": ["path", "content"]
    }
  }
]

2. Set Up the Execution Engine

We will use Docker to spin up an isolated container per request. The container image should be minimal (e.g., python:3.11-slim) and pre‑installed with any domain‑specific binaries.

import docker, uuid, json, subprocess, os
from pathlib import Path

client = docker.from_env()

SANDBOX_ROOT = Path("/tmp/agent_sandboxes")
SANDBOX_ROOT.mkdir(parents=True, exist_ok=True)

def create_sandbox() -> Path:
    sandbox_id = uuid.uuid4().hex
    sandbox_path = SANDBOX_ROOT / sandbox_id
    sandbox_path.mkdir()
    return sandbox_path

def run_in_sandbox(command: str, timeout: int = 30) -> dict:
    sandbox_path = create_sandbox()
    # Bind‑mount the sandbox directory read‑write, but limit capabilities
    container = client.containers.run(
        image="python:3.11-slim",
        command=["/bin/bash", "-c", command],
        detach=True,
        working_dir="/workspace",
        volumes={str(sandbox_path): {"bind": "/workspace", "mode": "rw"}},
        network_disabled=True,
        security_opt=["no-new-privileges"],
        cpu_quota=50000,  # 5% of a CPU
        mem_limit="256m",
        stderr=True,
        stdout=True,
        tty=False,
    )
    try:
        result = container.wait(timeout=timeout)
        logs = container.logs().decode()
    finally:
        container.remove(force=True)
        # Cleanup sandbox directory
        subprocess.run(["rm", "-rf", str(sandbox_path)], check=False)
    return {"exit_code": result.get('StatusCode'), "output": logs}

3. Build the FastAPI Wrapper

The wrapper receives the LLM’s function_call, routes it to the correct Python implementation, and feeds the result back to the model.

from fastapi import FastAPI, Request
from pydantic import BaseModel
import httpx

app = FastAPI()

class ChatMessage(BaseModel):
    role: str
    content: str
    name: str | None = None

class ChatRequest(BaseModel):
    model: str
    messages: list[ChatMessage]
    functions: list[dict] | None = None
    function_call: str | dict | None = None

OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")

@app.post("/chat")
async def chat_endpoint(req: ChatRequest):
    # 1. Forward request to OpenAI with function definitions
    async with httpx.AsyncClient() as client:
        openai_resp = await client.post(
            "https://api.openai.com/v1/chat/completions",
            headers={"Authorization": f"Bearer {OPENAI_API_KEY}"},
            json=req.dict()
        )
    openai_data = openai_resp.json()
    choice = openai_data["choices"][0]
    if "function_call" in choice["message"]:
        fn_name = choice["message"]["function_call"]["name"]
        arguments = json.loads(choice["message"]["function_call"]["arguments"])
        # 2. Dispatch to local implementation
        if fn_name == "run_shell":
            result = run_in_sandbox(arguments["command"], arguments.get("timeout", 30))
        elif fn_name == "write_file":
            result = write_file(arguments["path"], arguments["content"])
        else:
            result = {"error": "Unknown function"}
        # 3. Send function result back to the model
        follow_up = {
            "model": req.model,
            "messages": req.messages + [choice["message"], {"role": "function", "name": fn_name, "content": json.dumps(result)}],
            "functions": req.functions,
        }
        follow_up_resp = await client.post(
            "https://api.openai.com/v1/chat/completions",
            headers={"Authorization": f"Bearer {OPENAI_API_KEY}"},
            json=follow_up
        )
        return follow_up_resp.json()
    else:
        return openai_data

def write_file(path: str, content: str) -> dict:
    sandbox_path = create_sandbox()
    file_path = sandbox_path / path
    file_path.parent.mkdir(parents=True, exist_ok=True)
    file_path.write_text(content, encoding="utf-8")
    # No need to keep sandbox after; just return confirmation
    return {"status": "written", "path": str(file_path)}

4. Orchestrate State Across Calls

When an agent needs to retain files or environment variables between multiple calls, mount a persistent volume per user session. The session ID can be stored in the conversation metadata and retrieved on each request.

5. Deploy with Serverless Containers (Optional)

If you prefer not to manage a Docker daemon, AWS SageMaker SageMaker Inference Containers let you package the same sandbox image and run it on-demand with zero‑server management.

Code Examples in Action

Below are two concrete scenarios that showcase the power of the agents tool use function workflow.

Example 1 – On‑the‑Fly Data Transformation

Prompt: “Take the CSV at https://example.com/data.csv, filter rows where status == 'active', and return the first 5 rows as JSON.”

Agent’s plan:

  1. Call write_file to store the remote CSV locally.
  2. Call run_shell with a python -c one‑liner that uses pandas to filter and output JSON.

The model will generate two function calls, the engine runs them, and the final answer is a JSON payload.

Example 2 – Automated Unit‑Test Generation

Prompt: “Create a pytest suite for the calculate_tax function in tax.py and run it. Summarize any failures.”

Agent’s plan:

  1. Write a test_tax.py file using write_file.
  2. Execute pytest test_tax.py via run_shell with a timeout of 60 seconds.
  3. Parse the output and return a concise report.

This showcases how agents can become autonomous developers, a trend highlighted in the recent “How to Debug AI Coding Agents When They Change the Wrong Thing” article.

Best Practices & Security Checklist

Implementing an agents tool use function pipeline introduces new attack surfaces. Follow this checklist:

  • Least‑Privilege Containers: Drop all capabilities except those required (e.g., CAP_NET_RAW only if network access is needed).

  • 1. Architectural Foundations and System Design

    When implementing robust solutions for agents tool use function, system architects must focus on structural durability, low latency, and decoupled designs. In projects involving AI agents with tool use and function calling, a modular design pattern is highly advantageous. This approach allows developers to isolate components, scale them independently, and optimize resource usage based on real-time request patterns. Using asynchronous messaging queues (such as RabbitMQ, Celery, or Apache Kafka) can offload intense tasks from the primary request thread, thereby ensuring high availability and protecting the system from cascading service failures.

    Furthermore, the database layer must be designed with transaction safety, connection pooling, and replication in mind. Using read replicas can significantly reduce the load on the master node during heavy traffic spikes. Implementing an API gateway enables clean traffic routing, rate limiting, request validation, and unified security policies. This unified layout simplifies operational maintenance and speeds up troubleshooting workflows for technical teams.

    2. Security Hardening and Threat Mitigation

    Security is a paramount concern for any application operating with agents tool use function. Adhering to the principle of least privilege, access controls should be strictly limited across all components. For deployments related to AI agents with tool use and function calling, sensitive variables (such as database passwords, third-party API credentials, and TLS certificates) should never be stored directly in the source code or deployment scripts. Instead, they should be managed via cloud-native secrets managers (like AWS Secrets Manager, HashiCorp Vault, or Google Cloud Secret Manager) and loaded securely at runtime.

    To secure the data layer, all external communication channels must be encrypted with modern TLS protocols. Input parameters should undergo rigorous validation and sanitization at the API gateway layer to prevent SQL injection, cross-site scripting (XSS), and malicious parameter tampering. Regular dependency vulnerability scanning (using tools like Snyk, Dependabot, or Bandit) should be integrated into the deployment pipeline to identify and remediate vulnerable packages early in the release cycle.

    3. Scaling Strategies and Performance Optimization

    Minimizing application latency and maximizing throughput are key indicators of a successful agents tool use function rollout. For systems executing workflows for AI agents with tool use and function calling, adopting a multi-tiered caching structure yields immediate performance gains. Tools like Redis or Memcached can store frequently accessed database queries, transient session variables, and parsed system configurations. This relieves pressure on back-end databases and decreases API response times to the low millisecond range.

    In addition, using reverse proxies (such as Nginx or HAProxy) and Content Delivery Networks (CDNs) helps distribute request loads geographically and serve static assets with minimal delay. Autoscale rules (such as Horizontal Pod Autoscaling in Kubernetes or VM scale sets in cloud environments) should be defined using CPU, memory, and custom message queue length metrics to align compute resources with real-time user activity, optimizing hosting expenditures.

    4. Observability, Logging, and Real-Time Monitoring

    Sustaining visibility is crucial when orchestrating processes related to agents tool use function. To ensure the reliability of systems running AI agents with tool use and function calling, developers must deploy comprehensive logging, trace collection, and system metrics tracking. Logs should be structured as structured JSON objects, making it easier for central log ingestion tools (like Grafana Loki, the Elastic Stack, or Splunk) to parse, index, and query log entries for rapid diagnosis of failures.

    Dashboard visualizations (e.g., using Grafana or Datadog) should display critical golden signals: latency, traffic, error rates, and resource saturation. Implementing distributed tracing using frameworks like OpenTelemetry or Jaeger allows engineers to track the lifecycle of a request as it crosses service boundaries, pinpointing latency bottlenecks in network calls or database execution. Automatic alerting rules should trigger notifications via PagerDuty or Slack when anomalies arise.

Scroll to Top