Jump to content

Connect Leeroopedia MCP: Equip your AI agents to search best practices, build plans, verify code, diagnose failures, and look up hyperparameter defaults.

Principle:Openai Openai agents python Tool Execution Loop

From Leeroopedia


Template:Openai Openai agents python Sidebar

Overview

The Tool Execution Loop is the core runtime cycle in the OpenAI Agents Python SDK that governs how an agent processes tool calls generated by the LLM. When the model produces one or more tool calls instead of a final text response, the runner executes those tools, collects the results, appends them to the conversation, and calls the model again. This loop continues until the model produces a final output, a handoff occurs, or the maximum number of turns is exceeded.

Property Value
Category Runtime Execution
Source (tool execution) src/agents/run_internal/turn_resolution.py (lines 481-640, execute_tools_and_side_effects)
Source (single turn) src/agents/run_internal/run_loop.py (lines 1312-1399, run_single_turn)
Related Implementation Execute Tools and Side Effects

Description

The tool execution loop is the mechanism that transforms a stateless LLM (which can only produce text or structured outputs) into an agent capable of taking actions. Without this loop, the model would produce a tool call and halt. The loop ensures that tool calls are fulfilled and their results are fed back to the model, enabling multi-step reasoning and action chains.

Theoretical Basis

The Turn-Based Execution Model

The agent runtime operates on a turn-based model. Each turn consists of:

  1. Input assembly: The framework gathers the conversation history (original input, prior tool results, system prompt) and sends it to the model.
  2. Model inference: The LLM produces a response that may contain text output, tool calls, handoff requests, or a combination.
  3. Response processing: The framework parses the model response into a ProcessedResponse containing categorized items (tool calls, handoffs, message output, etc.).
  4. Tool execution and side effects: If tool calls are present, the framework executes them and collects results.
  5. Loop decision: Based on the results, the framework determines the next step.

Next Step Resolution

After processing tools and side effects, the framework resolves one of four possible next steps:

  • NextStepRunAgain: Tool calls were executed and results are available. The model should be called again with the updated conversation to continue reasoning.
  • NextStepFinalOutput: The model produced a final text or structured output. The run terminates successfully.
  • NextStepHandoff: The model requested a handoff to a different agent. The run transfers control.
  • NextStepInterruption: A tool requires human approval before execution can continue. The run pauses and must be resumed explicitly.

Parallel Tool Execution

When the model generates multiple tool calls in a single response, the framework can execute them concurrently. The _execute_tool_plan internal function dispatches function tools, computer tools, shell tools, and other tool types in parallel using asyncio concurrency. This reduces latency when multiple independent tools are called simultaneously.

The Tool Use Behavior Setting

The tool_use_behavior configuration on an agent controls what happens after tool calls complete:

  • Default behavior: After tool results are collected, the model is called again to produce a follow-up response. This is the standard loop continuation.
  • "stop_on_first_tool": The run terminates immediately after the first tool call, using the tool result as the final output.
  • Custom function: A callable that inspects tool results and decides whether to produce a final output or continue the loop.

Max Turns Safety Limit

To prevent infinite loops (where the model keeps calling tools without producing a final answer), the runner enforces a max_turns limit. Each model invocation counts as one turn. When the limit is exceeded, the run terminates with a MaxTurnsExceeded exception. This is especially important for autonomous agents that might otherwise loop indefinitely.

Error Recovery Within the Loop

When a function tool raises an exception, the default behavior is to convert the error into a text message and send it back to the model as a tool result. This allows the model to:

  • Recognize the error.
  • Adjust its approach (e.g., correct malformed arguments).
  • Retry the tool call or choose an alternative strategy.

This resilience is a deliberate design choice: the loop continues rather than crashing, giving the LLM an opportunity to self-correct.

Integration of Approvals and Guardrails

The tool execution loop integrates with the approval and guardrail systems:

  • Approval gating: Before executing a tool marked with needs_approval=True, the loop checks whether approval has been granted. If not, it produces a NextStepInterruption that pauses the run.
  • Input guardrails: Validated before tool execution. If a guardrail fails, the tool is not invoked.
  • Output guardrails: Validated after tool execution. If a guardrail fails, the tool result may be modified or rejected.

Conversation State Accumulation

Each iteration of the loop appends new items to the conversation:

  • Tool call items: Represent the model's tool invocation (function name, arguments, call ID).
  • Tool result items: Represent the tool's return value (text, image, or file content).
  • Message items: Text output from the model between or after tool calls.

These items form a growing conversation history that the model sees on subsequent turns, providing full context of what has happened.

Usage

The tool execution loop operates automatically inside Runner.run(). The developer does not invoke it directly, but can influence its behavior:

from agents import Agent, Runner, function_tool

@function_tool
def calculate(expression: str) -> str:
    """Evaluate a math expression."""
    return str(eval(expression))

@function_tool
def get_exchange_rate(currency: str) -> str:
    """Get the exchange rate for a currency to USD."""
    rates = {"EUR": 1.08, "GBP": 1.27, "JPY": 0.0067}
    return str(rates.get(currency, "unknown"))

agent = Agent(
    name="finance_calculator",
    instructions="Use tools to solve financial calculations.",
    tools=[calculate, get_exchange_rate],
)

# The tool loop happens automatically inside Runner.run:
# Turn 1: Model calls get_exchange_rate("EUR") -> "1.08"
# Turn 2: Model calls calculate("100 * 1.08") -> "108.0"
# Turn 3: Model produces final text: "100 EUR is 108.0 USD"
result = await Runner.run(agent, "Convert 100 EUR to USD")
print(result.final_output)

Controlling Maximum Turns

from agents import Runner, RunConfig

# Limit the agent to at most 5 model invocations
result = await Runner.run(
    agent,
    "Solve this complex multi-step problem...",
    run_config=RunConfig(max_turns=5),
)

Related Pages

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment