Understanding How GPT-5 Uses Tools

Introduction & Overview

Welcome! In this lesson, you will learn how GPT-5 responds when it decides to use your tools. In the previous lesson, you learned how to create tool schemas that describe your methods to GPT-5. Now, you will discover how to include these tools in your Responses API requests and, more importantly, how to interpret GPT-5's responses when it seeks to use them.

In this lesson, you will learn how to configure your requests to enable tool use, understand the different types of items GPT-5 returns, and extract the specific information needed to execute the tools GPT-5 requests. By the end, you will be able to recognize when GPT-5 wants to use a tool and gather all the details necessary to facilitate that process.

Setting Up the OpenAI Client and Message Format

Before we send anything to GPT-5, we need to set up the OpenAI client and build our messages in the format the Responses API expects. We start by requiring the openai gem and initializing the client:

require "openai"

# Initialize the OpenAI client
client = OpenAI::Client.new

The Responses API uses a slightly more structured message format than you might be used to. Each message has a role (like "user", "assistant", or "developer") and a content array containing typed content blocks. The most common block type for inputs is "input_text". To keep our code clean, we'll define a small helper to build text messages:

def text_message(role, text)
  {
    role: role,
    content: [
      { type: "input_text", text: text }
    ]
  }
end

This helper produces a properly-shaped message object that we can use throughout our conversation. Using input_text for inputs is the convention used by the OpenAI Responses API.

Using the Developer Prompt to Guide Tool Use

In the Responses API, instructions to the model are typically provided through a special "developer" message rather than a separate system parameter. The developer role is conceptually similar to the system role you may have used before, but in the Responses API it's passed inline as the first item in the input array.

# Developer prompt with explicit instructions to use tools
developer_prompt = (
  "You are a helpful math assistant. " \
  "Always use the available tools to perform calculations accurately."
)

This guidance helps GPT-5 understand your preferences for when and how to use tools, but it is not strictly required for tool functionality. GPT-5 can still recognize and use your tools based solely on their availability in the request — although a clear developer prompt typically results in more consistent and reliable tool use.

Providing Tools to GPT-5

The tools parameter on client.responses.create is the essential component that makes your methods available to GPT-5. This parameter accepts the array of function schemas you created in the previous lesson:

require "json"

# Load your tool schemas
tool_schemas = JSON.parse(File.read("schemas.json"))

# Create a message requesting a calculation
messages = [
  text_message("user", "Calculate 15 + 27")
]

# Send the request with tools enabled
response = client.responses.create(
  model: "gpt-5",
  input: [
    text_message("developer", developer_prompt),
    *messages
  ],
  tools: tool_schemas,   # This makes your tools available to GPT-5
  store: false
)

A few important details about this request:

  • The input parameter is an array that contains both the developer prompt and any user messages, in order. We use the splat operator *messages to expand our messages array into the input array.
  • The tools parameter accepts the array of function schemas — GPT-5 reads them to understand which functions are available and how to call them.
  • The store: false parameter tells OpenAI not to persist the response on its servers. This is useful when you want to manage conversation state on your own side, which is exactly what we'll be doing.

GPT-5 recognizes the available tools from the tools parameter alone. While developer prompt guidance can be helpful for encouraging consistent tool usage patterns, GPT-5 can see and use your tools even without explicit instructions in the developer prompt.

Understanding GPT-5's Response Structure

When GPT-5 decides to use a tool, the response is delivered in a structured form through response.output, which is an array of items. Each item has a type that tells you what kind of content it represents. Let's examine a complete response by converting it to a Hash and formatting it as JSON:

# Print the complete response structure
puts JSON.pretty_generate(response.to_h)

A typical tool-use response from GPT-5 includes many metadata fields, but the essential structure looks like this:

{
  "id": "resp_mjG7PSlRqMwSS3b4lKLY41i2...",
  "object": "response",
  "model": "gpt-5",
  "output": [
    {
      "id": "rs_00bf837ce81ef8a2016a175e1a68a081...",
      "type": "reasoning",
      "summary": []
    },
    {
      "id": "fc_00bf837ce81ef8a2016a175e1bf23881...",
      "type": "function_call",
      "name": "sum_numbers",
      "arguments": "{\"a\":15,\"b\":27}",
      "call_id": "call_8ABIpz28VVaxgjg1Z4uhNxVf",
      "status": "completed"
    }
  ],
  "usage": {
    "input_tokens": 117,
    "output_tokens": 163,
    "total_tokens": 280
  }
}

The key things to notice are:

  • output is an array of items, not a single text response. The items might include reasoning, message, and function_call items in any order.
  • reasoning items represent GPT-5's internal thinking process. They generally don't have visible text content but are part of the model's response. In the example above, you'll see a high number of output_tokens (163) compared to the final tool call; most of those were used for this "hidden" reasoning.
  • function_call items signal that GPT-5 wants to use a tool. Each has a name, an arguments JSON string, and a unique call_id.
  • message items would contain output_text blocks with text that GPT-5 wants to show to the user.

Iterating Through Output Items

Because output is an array of mixed item types, you need to iterate through it and dispatch on each item's type. Here's a clean pattern for doing that:

# Process each output item
response.output.each_with_index do |content_item, i|
  puts "\nContent Item #{i + 1}:"
  puts "Type: #{content_item.type}"

  case content_item.type.to_s
  when "message"
    # Extract text from all output_text content blocks in the message
    text = content_item.content
      .select { |block| block.type.to_s == "output_text" }
      .map(&:text)
      .join
    puts "Content: #{text}"
  when "function_call"
    puts "Tool Name: #{content_item.name}"
    puts "Tool Input: #{content_item.arguments}"
    puts "Tool Call ID: #{content_item.call_id}"
  end
end

Running this code will show you the structure of each content item:

Content Item 1:
Type: reasoning

Content Item 2:
Type: function_call
Tool Name: sum_numbers
Tool Input: {"a":15,"b":27}
Tool Call ID: call_8ABIpz28VVaxgjg1Z4uhNxVf

Notice that we use .to_s on content_item.type in our case statement. The OpenAI Ruby SDK returns API enum fields (like type) as symbol-like objects rather than plain strings. Converting them to strings with .to_s ensures a safe, robust comparison against our string literals.

Each function_call item contains three essential pieces of information that your system needs to execute the requested method:

  • name: The function name that matches your tool schema (e.g., "sum_numbers").
  • arguments: A JSON-encoded string of the arguments the model wants to pass. You will need to parse this with JSON.parse before calling your Ruby method.
  • call_id: A unique identifier for this specific call, which you will need when sending the result back to GPT-5.

The fact that arguments is a JSON string (rather than a parsed object) is an important detail — GPT-5 always returns arguments as a JSON-encoded string, and you'll parse it on your end.

Extracting Plain Text Quickly

When GPT-5 provides a direct text answer (without tool use), iterating through items to extract text can feel verbose. The OpenAI Ruby SDK provides a convenient helper called output_text that gathers all text from message items in the response:

# Quick way to get any direct text response from GPT-5
puts response.output_text

This shortcut is handy for non-tool responses or for getting the model's final answer after tool execution. We'll use it frequently in the next units.

Summary and Next Steps

You now understand how GPT-5 communicates its tool use intentions through the structured output array of the Responses API. When GPT-5 decides to use tools, it produces function_call items with everything you need to execute them on your side: the function name, a JSON-encoded arguments string, and a unique call_id.

In the upcoming practice exercises, you will work with these response structures hands-on, learning to parse function_call items and preparing for the next step: executing the requested tools and sending the results back to GPT-5 to complete the conversation flow.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal