A useful agent rarely completes a task in one tool call - it needs to break a goal into steps, execute them in order, and adjust based on what it learns along the way.
A simple multi-step example
Consider the goal "book the cheapest flight from city A to city B next Friday." That requires: search available flights, compare prices, and then book the selected one - three separate tool calls, where each one depends on the result of the last.
The ReAct pattern: reason, then act
A common and effective pattern is having the model explicitly reason about what to do next before each action, not just jump straight to calling a tool:
# Conceptual trace of a ReAct-style loop:
# Thought: I need to find available flights first.
# Action: search_flights(from="A", to="B", date="next Friday")
# Observation: [list of 5 flights with prices]
# Thought: The cheapest is Flight 203. I should book that one.
# Action: book_flight(flight_id="203")
# Observation: Booking confirmed.
# Thought: Task complete.
Making the model write out its reasoning before acting measurably improves reliability - it is much easier to catch a flawed plan in the "Thought" step than after a wrong action has already happened.
Knowing when to stop
An agent needs a clear way to recognize the task is actually done, not just keep looping - usually a maximum step count as a safety net, combined with the model itself concluding the goal is achieved based on the observations so far.
Why this is harder than it looks
Real plans go wrong: a flight might sell out between searching and booking, a tool might return an unexpected format, a step might need to be retried. The next lesson covers exactly this - handling failure inside a multi-step plan without the whole task silently breaking.