Building an Autonomous GPT-5 Agent
Introduction & Overview
Throughout this course, you have mastered the fundamentals of tool integration with GPT-5: creating tool schemas, understanding GPT-5's function call responses, and executing single tool calls. However, the approach you've learned so far has a significant limitation — it handles only one tool-execution cycle per conversation. While this works perfectly for simple tasks, many real-world problems require multiple sequential steps, and often the number and nature of these steps cannot be determined in advance.
In this lesson, we'll work together to transform GPT-5 from a single-turn tool user into an autonomous agent capable of iterative problem-solving. We'll build an Agent class that can call tools, analyze results, decide what to do next, and continue this process until complex multi-step tasks are completed. This represents a fundamental shift from reactive tool usage to proactive, intelligent problem-solving that mirrors how humans approach complex challenges.
The Action-Feedback Loop Concept
Before we start coding, let's understand how autonomous agents operate through action-feedback loops in which each tool execution provides information that influences the next decision. This iterative process mirrors human problem-solving: we take an action, observe the result, decide what to do next, and repeat until we reach our goal. The action-feedback loop consists of four key phases that repeat until task completion:
- Decision Phase:
GPT-5analyzes the current situation and determines the next action, which may include calling one or more tools. - Action Phase: Our agent executes the requested tool(s) based on
GPT-5's instructions. - Feedback Phase: The results from the tool execution(s) are captured and appended to the conversation as
function_call_outputitems. - Evaluation Phase:
GPT-5reviews the new information, decides whether the task is complete, or if additional steps are needed, and the loop continues.
This loop structure enables complex problem-solving because each iteration builds upon previous results. For example, when solving a quadratic equation, GPT-5 might first calculate the discriminant, then use that result to determine if real solutions exist, then calculate the square root of the discriminant, and finally compute the two solutions. The key insight is that GPT-5 doesn't need to plan all steps in advance — it can adapt its approach based on intermediate results, just like a human mathematician working through a problem.
Now let's start building our agent class to make this iterative process possible.
Building Our Agent Class Foundation
Let's begin by creating the foundation of our autonomous agent. We need to establish the core structure that will manage extended conversations, tool execution, and decision-making loops. We'll start with the class definition and constructor:
Our agent's foundation relies on key design decisions that enable autonomous behavior while maintaining flexibility for different use cases:
-
BASE_DEVELOPER_PROMPT: Explicitly tellsGPT-5that it can make multiple tool calls and that users won't see the intermediate steps — only the final result. We combine this with a customsystem_promptto allow for domain-specific instructions while maintaining the autonomous behavior. -
Naming note: the constructor accepts a parameter called
system_prompt(a familiar term), but internally we store the combined instructions in@developer_promptbecause the Responses API expects them as adeveloperrole message. -
Constructor parameters provide flexibility for different scenarios while ensuring safe defaults:
name:: A clear identifier for the agent, useful for debugging and managing multiple agents.system_prompt:: Domain-specific instructions appended to the base prompt.model:: Defaults to"gpt-5".tools:andtool_schemas::- Default to
nilto avoid shared mutable defaults. - Are duplicated via
.dupso each agent gets its own independent registry and schema list.
- Default to
max_turns:: Prevents infinite loops by limiting iterative steps.reasoning_effort:: GPT-5 is a reasoning model."low"is fast and inexpensive — a good default — while"medium"or"high"will spend more tokens thinking, which can help on complex tasks.
This architecture separates concerns cleanly while preparing us to implement the core functionality that will make our agent truly autonomous.
Adding Helper Methods for State Management
As our agent works through complex problems, we need to manage conversation state properly. Let's add two essential helper methods that will support our main loop:
These helper methods are essential for clean separation between the complex orchestration logic we're about to write and the details of message construction:
-
text_message: Builds a properly-shaped Responses API message with a role and a singleinput_textcontent block. -
build_request_args: Centralizes how we construct API requests, ensuring consistent parameters across all agent interactions. Note how we conditionally include tool schemas usingunless @tool_schemas.empty?— this prevents API errors when we create agents without tools while still supporting full tool integration when needed.
Implementing Tool Execution
Now let's add the method that handles individual tool executions. This method needs to be robust because tool failures shouldn't break our entire autonomous process:
This method handles the individual tool executions occurring within our larger iterative loop:
- Extracts function call information: Gets the
tool_name, parses theargumentsJSON string into a hash, and captures thecall_id. - Debug tracking: Prints which tool is being called with what arguments — invaluable for debugging and understanding how our agent thinks.
- Executes with comprehensive error handling:
- Uses a
begin...rescueblock to handle errors gracefully. rescue KeyErrorcatches cases where the tool doesn't exist in our registry (whenfetchfails).rescue => ecatches any other execution failures.- Transforms string keys from the API into Ruby symbols via
transform_keys(&:to_sym)so they work with our keyword-argument methods.
- Uses a
- Returns structured outputs: Builds a
function_call_outputhash matching thecall_idwith the result JSON-encoded as a string.
Building the Core Loop - Part 1: Stateless Design
Now we're ready to implement the heart of our autonomous agent: the run method. Let's start by understanding how our agent handles conversation state:
The input_messages.map(&:dup) call ensures our agent remains stateless. Each time you call agent.run, you provide the full context through input_messages, and the agent processes only that specific conversation without any memory of previous interactions.
By creating a shallow copy of each item hash instead of modifying them directly, we preserve the original conversation and allow the same agent instance to handle multiple independent conversations.
Building the Core Loop - Part 2: Setting Up the Iteration
Now let's add the basic loop structure that will enable our agent's iterative problem-solving:
We're starting with a controlled loop that will continue until GPT-5 provides a final answer or we reach our maximum turn limit. Each iteration represents one complete action-feedback cycle: GPT-5 produces a response, and we filter out any function calls.
Notice that we use the double-splat operator ** to expand our build_request_args hash into keyword arguments for the create method, following Ruby's idiomatic approach to method calls.
Building the Core Loop - Part 3: Handling Function Calls
Now let's add the logic for handling function calls within our loop:
When GPT-5 decides to use tools, we handle the execution through a systematic process:
- Append the function call items: We push each
function_callproduced by GPT-5 ontomessagesso the next API call has a complete view of the conversation. - Execute all requested tools: We map over each function call through our
call_toolhelper, which returns a properly structuredfunction_call_output. - Append the outputs: Using
messages.concat(function_outputs), we add every output to the conversation. GPT-5 will see them on the next turn.
Each tool result influences GPT-5's subsequent reasoning, allowing it to build upon what it just learned and make more informed decisions in the next iteration.
Building the Core Loop - Part 4: Managing Flow Control
Finally, let's complete our loop with the logic for handling final responses and error conditions:
When GPT-5 reaches a final answer (no function calls in the output), we:
- Capture the assistant's reply: We use
response.output_textto grab GPT-5's text response and add it tomessagesas anassistantmessage. - Return complete state: We return both the full conversation history (
messages) and the final text (response.output_text) as an array. This stateless design lets the caller decide how to use the result. - Safety net for runaway loops: The
raise "Max turns reached"outside the loop prevents infinite iterations if something goes wrong.
Complete Run Method
Here's how our complete run method looks when put together:
Testing Our Autonomous Agent
Now let's put our agent to work! We'll create a math-focused autonomous agent and see how it handles a complex quadratic equation. We will provide a richer toolbox with subtract_numbers, divide_numbers, power, and square_root in addition to our sum_numbers and multiply_numbers. Each function uses keyword arguments to match the inputs GPT-5 provides.
For example, square_root looks like this:
And its corresponding schema defines a as the only required parameter.
Now let's set up the agent and ask it to solve a quadratic equation:
When we run this code, our agent demonstrates sophisticated autonomous reasoning:
Our agent systematically applied the quadratic formula by calculating b² ((-7)²), computing ac and then 4ac, finding the discriminant, taking the square root (√25), and finally calculating both solutions by dividing (7 − 5) and (7 + 5) by 4. Each tool call built upon previous results, demonstrating true autonomous reasoning across multiple iterations of the loop.
Summary & Practice Preparation
Together, we've successfully built an autonomous agent capable of complex, multi-step problem-solving. Our Agent class encapsulates conversation management, tool execution, and iterative decision-making in a reusable structure that can tackle problems requiring many sequential operations.
The architecture we created enables GPT-5 to operate as a true autonomous agent: it can assess situations, make decisions, execute tools, learn from results, and continue iterating until complex tasks are completed. This represents a fundamental advancement from simple tool usage to intelligent, adaptive problem-solving.
In the upcoming practice exercises, you'll implement your own autonomous agents, experiment with different developer_prompt and tool combinations, and tackle increasingly complex multi-step problems.
