In the previous lessons, you learned how to create tool schemas and understand GPT-5's responses when it wants to use tools. You can now recognize when GPT-5 requests tool execution by checking for type: "function_call" items in the output array and extracting the necessary details from function call items. However, knowing what GPT-5 wants to do is only half the story — you still need to actually execute those tools and complete the conversation cycle.
In this lesson, you'll learn how to bridge that gap by executing the functions GPT-5 requests, capturing their results, and sending those results back to GPT-5 in the proper format. By the end of this lesson, you'll have a complete tool execution pipeline that can handle GPT-5's tool requests from start to finish, maintaining proper conversation flow throughout the entire process.
Before we dive into the implementation, let's understand the complete workflow we'll be building in this lesson. Here's the step-by-step process that transforms GPT-5 from a simple chatbot into a capable agent:
- Set up the foundation - Create function mappings, tool schemas, and initial conversation messages
- Send the initial request - Make the first API call to GPT-5 using
responses.createwith the user's question and available tools - Detect tool use requests - Filter through the
outputarray to find items withtype: "function_call" - Extract tool information - Pull out the function name, arguments, and call ID from each function call item
- Execute the requested functions - Parse the JSON arguments and use our function mapping to call the actual TypeScript functions with GPT-5's parameters
- Collect and format tool results - Structure all function outputs in the
function_call_outputformat with matchingcall_idvalues - Send results back to GPT-5 - Make a second API call with the complete conversation, including both the original
function_callitems AND their outputs - Display the final response - Show GPT-5's natural language answer that incorporates the tool outputs
This complete cycle enables GPT-5 to seamlessly use tools as part of its reasoning process, transforming raw function outputs into conversational responses that directly answer user questions.
As covered in previous lessons, we need to map tool names to functions and prepare our tool schemas. Here's the complete setup that enables tool execution:
The tools object is the key component here — it enables dynamic function lookup when GPT-5 requests tool execution by name.
With everything in place, we make our first API call using the Responses API parameters:
This call provides GPT-5 with the user's question and available tools, enabling it to decide whether function execution is needed.
After receiving GPT-5's response, we need to examine the output array to determine if tool execution is needed. Unlike checking a single stop_reason field, we filter through the output items and look for function call requests.
The response.output array contains all output items from GPT-5, which can include text messages, function calls, and other content types. By using the .filter() method and checking each item's type property for "function_call", we identify all the tools GPT-5 wants to execute. The filter returns a new array containing only the function call items, which might be empty if GPT-5 doesn't need any tools, or contain multiple items if GPT-5 requests several function calls.
Once we've collected all function calls, we check if any were found and proceed accordingly:
When we find function call items, we extract the essential information from each one: the function name (which function to call), the (a unique identifier we'll need to match results back to requests), and the (provided as a JSON string). The call is essential because the arguments come as a JSON string, not a TypeScript object, so we must parse them before we can use them to call our functions.
With the function information extracted and arguments parsed, we can now execute the actual TypeScript functions using our mapping object. This is where the tool names from GPT-5's requests get translated into actual function calls.
The critical step here is parsing the arguments with JSON.parse(functionCall.arguments) before passing them to the function. Without this parsing step, we'd be trying to use a JSON string instead of an object, which would cause an error. Using a try-catch block provides comprehensive error handling that catches not only missing tools but also any runtime errors that might occur during function execution. For example, if a function receives invalid parameters (like trying to divide by zero), the exception will be caught and converted into an error message that can be sent back to GPT-5. This feedback mechanism allows the model to reason about the failure and decide on a next step, such as retrying with corrected parameters, using an alternative tool, or explaining the issue to the user, rather than simply failing.
After executing each tool, we must format both the function call and its result, then add them to the messages array. The Responses API requires both pieces in specific formats.
We reconstruct each function call item because the original functionCall objects are SDK model objects, not plain JSON objects. The Responses API requires a strictly shaped payload with only the allowed fields (type, name, arguments, call_id).
For function outputs, the call_id must exactly match the original function call's ID, and the output must be a JSON string. It's critical to provide an output for every function call — if you skip any call_id, your next API call will fail because OpenAI expects results for all requested functions.
Finally, we add both the function calls and their outputs to the messages array using the spread operator (...), maintaining the proper conversation structure that GPT-5 expects.
Once we've executed all requested functions and added both the function calls and their results to the messages array, we can send the updated conversation back to GPT-5 to get the final response that incorporates the tool results.
This second API call uses the same parameters as the first, but now the input array contains the complete conversation history, including both the original function call items and their corresponding outputs. GPT-5 can now provide a comprehensive answer that incorporates the tool execution results, transforming raw calculation outputs into natural, conversational responses.
The output_text property provides a convenient way to extract just the text content from GPT-5's response, filtering out any non-text items in the output array.
The complete execution flow produces the following output, showing both the tool execution and GPT-5's final response:
This output demonstrates the complete tool execution cycle: our code executes the requested function with the provided parameters, captures the numerical result, and then GPT-5 transforms that raw output into a natural, conversational response that directly answers the user's original question.
Not every user request will require tool usage. When GPT-5 can answer directly without needing to execute functions, the functionCalls array will be empty after we filter through the output array.
By checking if the functionCalls array contains any items using .length > 0, we can determine whether GPT-5 needs tools or can answer directly. When the array is empty (meaning no function call items were found in the output array), we simply display GPT-5's response using response.output_text, which extracts the text content from the output array. For example, if a user asks, "What is the capital of France?", GPT-5 would respond directly without requesting any function calls, since no mathematical calculations are needed. This ensures your application handles both tool-requiring and non-tool scenarios gracefully, maintaining proper conversation flow in all cases.
Throughout the tool execution cycle, maintaining proper conversation history enables GPT-5 to understand context and provide coherent responses. Let's examine how the complete conversation flow develops through each step of the process.
GPT-5's message structure is simpler than some other systems — each item in the messages array has a type field that indicates whether it's a regular message, a function_call, or a function_call_output. Regular user and assistant messages have role and content fields, function calls have name, arguments, and call_id fields, and function outputs have call_id and output fields.
This debugging output reveals the complete conversation structure that enables GPT-5's tool-using capabilities:
This conversation history shows the complete tool execution cycle: the user's initial request, the function call that GPT-5 requested with its arguments and unique ID, and the function output with the matching call ID. After this, GPT-5 would receive these messages and generate its final response incorporating the tool output. This structure demonstrates how proper message management creates seamless conversation flow that transforms GPT-5 into a capable agent.
You now understand the complete tool execution workflow that transforms GPT-5 from a text-only assistant into an agent capable of performing actions and calculations. The process involves filtering through the output array to find function calls, parsing JSON arguments before execution, collecting and formatting all results with matching call IDs, reconstructing original function call items, and maintaining conversation history throughout the entire cycle.
The key components work together to create a robust system: function mapping enables dynamic execution, JSON parsing ensures arguments are properly formatted, proper result collection ensures all tools are handled, correct message formatting with both function calls and outputs allows GPT-5 to understand results, and conversation history maintains context across the entire interaction. This foundation allows GPT-5 to act as an intelligent agent that can reason about when to use tools and incorporate their results into natural responses.
In the upcoming practice exercises, you'll implement this complete workflow yourself, working with different types of tools and handling various scenarios, including multiple tool uses and error handling. Happy coding!
