In the previous lesson, you built a tool execution pattern where you made two separate API calls to complete a calculation task. This manual approach helped you understand context flow, but it is not practical for production systems where tasks might require many steps. In this lesson, you will build an agentic loop that automates this process, implementing Factor 8 (Own your control flow) by giving your code full control over execution, and Factor 9 (Compact Errors into Context Window) by feeding structured failure information back to the model. By the end, you will have an agent that handles multi-step tasks like "What is 15 + 27? Then multiply the result by 3" without any manual intervention.
Before building the loop, you need to establish control variables that manage execution boundaries and track progress. These variables implement Factor 8 by putting your code in charge of when the loop continues and when it stops.
These control variables work together to manage your agent's execution lifecycle:
- The
max_stepsvariable acts as a safety boundary that prevents runaway execution, which is critical for production systems where you need predictable resource usage and cost control. - The
stepcounter tracks how many iterations have occurred, helping with debugging and observability. - The
doneflag serves as your completion signal that gets set toTruewhen the model callsfinal_answer. - The
final_answervariable stores the result once it is provided.
The max_steps boundary is particularly important because it ensures your agent cannot consume unlimited API calls or compute resources. The combination of done and step gives you both logical completion (when the task finishes) and physical completion (when you hit resource limits), allowing your system to handle both successful completions and edge cases gracefully.
The core agent loop uses a while condition that combines your completion flag and safety boundary to determine when execution should continue.
The condition not done and step < max_steps ensures that the loop stops either when the agent signals completion or when you hit your safety limit. At the start of each iteration, you increment the step counter and print a separator to make the execution flow visible during development.
Inside the loop, you make the same API call you learned in previous lessons, passing the current context, which contains the complete conversation history.
The model sees the full history at each iteration and decides what to do next based on what has already happened. The tool_choice="required" parameter ensures the model must call a tool rather than responding conversationally, maintaining the structured agent behavior you established in earlier lessons.
You iterate through the response.output items looking for function calls and extract the necessary information.
When you identify a function_call within the item.type, you extract its function_name and parse its item.arguments from JSON into a Python dictionary using json.loads. Printing the function call provides visibility during development so you can see what the model decided to do at each step.
Before executing the function, you add the function call to your context so the model can see its own decisions in future iterations.
Recording the function call maintains the conversation history and ensures the model has complete information on the next iteration. The call_id is particularly important because it links this call to its eventual result, which you will add to the context after execution.
You use a match statement wrapped in a try block to execute the corresponding Python function based on the function_name.
The match statement dispatches to the correct Python function using **args to unpack the parsed arguments as keyword parameters. The final_answer case is special because it signals completion by setting done = True and storing the result, rather than performing a calculation. For calculation functions like add and multiply, you execute them and format their results as JSON using json.dumps. The default case _ handles unexpected tool names by creating an error message, preventing crashes if the model requests a non-existent tool.
The except clause catches any error that occurs during tool execution and converts it into structured feedback that the model can understand.
Instead of letting errors crash your program, you convert them into a JSON object with an "error" field. This structured format gives the model consistent, parseable information about what went wrong. When this error output is added to the context, the model sees the failure in its next iteration and can potentially retry with different arguments or call a different tool. This error feedback mechanism implements Factor 9 by turning failures into recoverable situations rather than fatal crashes.
After executing the function (successfully or with an error), you print the result and add it back to the context.
Adding the function_call_output to context completes the execution cycle by giving the model feedback about what happened when its tool was executed. The call_id links this output to the original function call, maintaining the conversation structure. This feedback enables the model to make informed decisions about what to do next based on the complete history of actions and results.
When the model calls final_answer, you need to exit the item processing loop immediately since the agent's work is complete.
This check prevents any additional processing in the current iteration after handling final_answer. The loop will naturally terminate on the next while condition check since done is now True.
Now that you have built each piece of the loop individually, it is helpful to see the complete structure in one place. The following code shows the entire while loop including all the sections you built: the API call, function call processing, context recording, tool execution with error handling, result recording, and the completion break.
This complete loop structure automatically handles multi-step tasks without any manual intervention between steps. The context flows naturally from one iteration to the next, with each function call and result being recorded so the model can see the complete history at every step.
When you run the loop with the task "What is 15 + 27? Then multiply the result by 3", you will see the following output showing each step's function call and result.
Each step shows the function being called with its arguments, followed by the result that gets added to the context. The model sees all previous steps when making its next decision, which is why step 2 correctly uses 42 (the result from step 1) as an input to multiply. The execution continues until the model calls final_answer in step 3, setting the done flag that will cause the loop to terminate on its next condition check.
After the loop completes, you add two final checks that provide completion information and detect abnormal termination scenarios.
The first if statement detects whether you exited the loop due to hitting max_steps rather than the model signaling completion with final_answer. This is important for identifying cases where the agent could not complete the task within expected boundaries. The unconditional print statement displays the step count regardless of how the loop terminated. The final if final_answer check only displays the answer if one was actually provided, preventing misleading output when max_steps was reached without completion.
For the successful completion shown in the previous section, these post-loop checks produce the following summary output:
If the loop had terminated due to max_steps instead, you would see the "Reached maximum steps" message before the completion summary, and the "Final answer" line would not appear since final_answer would still be None.
You have now implemented a complete agentic loop that automates multi-step task execution through explicit control flow management. By defining loop control variables and using try/except blocks for error handling, you implemented Factor 8 to ensure your code owns the execution boundaries and Factor 9 to turn failures into structured feedback. The pattern you have learned generalizes beyond mathematical calculations to any multi-step agentic task: maintain explicit context, loop until completion or boundaries are reached, execute tools with error handling, and feed all results back to the model.
