In my final put up, . Instrument Calling is the mechanism that enables an AI mannequin to resolve which operate must be used and with what arguments, as an alternative of simply producing textual content as output. By the tip of that put up, we had a setup that might resolve to get_current_weather or convert_currency, or do each without delay by calling them in parallel, or neither of them, and simply generate textual content. In different phrases, the mannequin decides what it must do subsequent, we (the remainder of the code) execute that call, move again the outcome to the mannequin, and the mannequin finally gives an knowledgeable reply to the consumer in textual content format.
A extra superior model of this loop doesn’t cease after only one spherical of mannequin deciding – code executing – passing again the outcome – mannequin answering. As a substitute of producing a response on the finish, the mannequin can use the results of one instrument name to resolve whether or not, and which, instrument to name subsequent. As already talked about on the finish of the Instrument Calling put up, it is a ReAct loop (Motive + Act), and is precisely what lets brokers deal with duties that may’t be solved in a single name.
However what would such a job be? Within the earlier put up’s parallel calling instance, we requested What is the climate in Athens and the way a lot is 100 USD in EUR?, that are two separate issues requiring the usage of two separate instruments to acquire a response, however are additionally unbiased from each other. In different phrases, we are able to reply these two questions independently, concurrently, with no need any info from the primary query to be able to reply to the second.
However what if we ask one thing like I guess my good friend 100 EUR that it will rain in Athens right this moment. If I received, what number of USD is that? Right here, the mannequin received’t be capable to resolve if it must name convert_currency till it first calls get_current_weather and finds out whether or not it really rained. Merely put, the reply to the second query relies upon completely on the result of the primary. That is exactly the type of dependency that parallel instrument calling can’t resolve in a single spherical, and precisely what a ReAct loop is constructed for.
So, let’s have a look!
🍨 DataCream is a publication about AI, information, and tech. If you’re fascinated with these subjects, subscribe right here!
However what precisely is a ReAct loop?
A ReAct loop is simply three steps repeated in sequence:
- Motive
- Act
- Observe

At the start of the loop, the mannequin causes about what info it already is aware of and what further info is lacking to be able to present an accurate response to the consumer’s question. It then acts by calling an acceptable instrument with the aim of acquiring this lacking info. Lastly, as soon as the respective instrument name is executed and its result’s handed again to the mannequin, the mannequin observes the outcome (provides the instrument’s outcome into its context). Then, it loops again to reasoning once more, besides this time with this new commentary sitting in its context. This loop is repeated till the mannequin evaluates that the obtainable info is sufficient for answering the consumer’s question, and at this level, it stops calling instruments and simply responds with textual content.
However isn’t this like the identical because the instrument calls we already know? Sort of, however not precisely. The half that makes this completely different from what we lined within the Instrument Calling put up is the loop itself. In a single instrument name, the mannequin asks for one thing, will get it, and that’s the tip of the transaction so far as that decision is anxious. Within the ReAct loop, the dialog stays open, as every new commentary turns into new context for the following reasoning step, and the mannequin can change its plan based mostly on what it simply realized.
Identical Instruments, New Trick
To make this concrete, let’s return to the guess instance from the intro and assume by means of what the mannequin really must do to be able to present us a dependable reply. The query is: I guess my good friend 100 EUR that it will rain in Athens right this moment. If I received, what number of USD is that? Discover the conditional assertion in the course of it: if I received. Whether or not the mannequin must convert any forex in any respect is dependent upon what the climate name returns. If it rained, the mannequin must name convert_currency with 100 EUR as an enter parameter and provides again the transformed winnings. If it didn’t rain, the guess is misplaced, convert_currency is irrelevant, and the mannequin ought to simply straight return the respective textual content, with out making a second name.
To place it in another way, the mannequin genuinely can not plan its full sequence of instrument calls upfront. It has to examine the climate first, observe the outcome, purpose about what that outcome implies for the guess situation, and solely then resolve whether or not a second instrument name is required. In contrast to the parallel instrument calling that labored effectively for answering What is the climate in Athens and the way a lot is 100 USD in EUR?, this query requires a loop.
The great factor a few ReAct loop is that it doesn’t want new instruments. We will nonetheless use the identical capabilities, simply in a distinct method. So we’re going to be utilizing get_current_weather and convert_currency precisely as we constructed them final time utilizing Open-Meteo for climate and Frankfurter for forex conversion (each nonetheless requiring no API key):
import requests
import json
from openai import OpenAI
consumer = OpenAI(api_key="your_api_key")
def get_current_weather(metropolis: str, unit: str = "celsius") -> dict:
# Step 1: geocode town title to coordinates
geo = requests.get(
"https://geocoding-api.open-meteo.com/v1/search",
params={"title": metropolis, "depend": 1}
).json()
lat = geo["results"][0]["latitude"]
lon = geo["results"][0]["longitude"]
# Step 2: fetch present climate
climate = requests.get(
"https://api.open-meteo.com/v1/forecast",
params={
"latitude": lat,
"longitude": lon,
"present": "temperature_2m,precipitation",
"temperature_unit": unit
}
).json()
return {
"metropolis": metropolis,
"temperature": climate["current"]["temperature_2m"],
"precipitation_mm": climate["current"]["precipitation"],
"unit": unit
}
def convert_currency(quantity: float, from_currency: str, to_currency: str) -> dict:
response = requests.get(
f"https://api.frankfurter.dev/v2/fee/{from_currency}/{to_currency}"
).json()
fee = response["rate"]
transformed = spherical(quantity * fee, 2)
return {
"quantity": quantity,
"from_currency": from_currency,
"to_currency": to_currency,
"converted_amount": transformed,
"fee": fee
}
Discover one small addition in comparison with final time: get_current_weather now additionally returns precipitation_mm, since that’s the sector the mannequin wants to be able to consider the guess situation. Every little thing else is identical. The instruments schema can also be unchanged from our earlier put up:
instruments = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "Get the current weather for a given city, including temperature and precipitation",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "The name of the city"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
},
{
"sort": "operate",
"operate": {
"title": "convert_currency",
"description": "Convert an quantity from one forex to a different",
"parameters": {
"sort": "object",
"properties": {
"quantity": {"sort": "quantity", "description": "The quantity to transform"},
"from_currency": {"sort": "string", "description": "The supply forex code, e.g. EUR"},
"to_currency": {"sort": "string", "description": "The goal forex code, e.g. USD"}
},
"required": ["amount", "from_currency", "to_currency"]
}
}
}
]
We additionally must outline a lookup dictionary that our code will use to dispatch the mannequin’s instrument option to the precise Python operate:
available_functions = {
"get_current_weather": get_current_weather,
"convert_currency": convert_currency
}
This lets us go from a instrument title the mannequin provides us again, as a string, to the precise Python operate we run. We’ll want that mapping in a second, since this time we don’t know prematurely what number of instrument calls we’re going to should resolve, and even whether or not there might be multiple.
Watching the loop assume
Right here’s the half that’s really new. As a substitute of creating one request and studying off the instrument name, we wrap the entire alternate in a loop. On every move, we ship the mannequin the complete dialog up to now, examine whether or not it requested for a instrument, run that instrument in that case, append the outcome, and go round once more. We solely cease when the mannequin responds with plain textual content and no instrument calls left to make.
messages = [
{
"role": "user",
"content": "I bet my friend 100 EUR that it would rain in Athens today. If I won, how many USD is that?"
}
]
max_iterations = 5
for i in vary(max_iterations):
print(f"--- Step {i + 1}: Motive ---")
response = consumer.chat.completions.create(
mannequin="gpt-4o-mini",
messages=messages,
instruments=instruments
)
message = response.selections[0].message
messages.append(message)
# If there is not any instrument name, the mannequin is able to reply
if not message.tool_calls:
print("Ultimate reply:")
print(message.content material)
break
# In any other case, act on each instrument name the mannequin requested
for tool_call in message.tool_calls:
function_name = tool_call.operate.title
function_args = json.masses(tool_call.operate.arguments)
print(f"--- Step {i + 1}: Act ({function_name}) ---")
print(f"Calling {function_name} with {function_args}")
function_response = available_functions[function_name](**function_args)
print(f"--- Step {i + 1}: Observe ---")
print(function_response)
# Feed the commentary again in so the following Motive step can use it
messages.append({
"position": "instrument",
"tool_call_id": tool_call.id,
"content material": json.dumps(function_response)
})
Additionally, discover the max_iterations cap stopping a mannequin that decides it wants “only one extra piece of knowledge” from looping indefinitely. That is of explicit significance as a result of we’re paying for each name to the mannequin inside every of these loops.
In the end, the ensuing commentary of the loop is appended as a position: "instrument" message tied to the particular tool_call_id. This enables the mannequin to match every outcome again to the decision that produced it.
And now that we’ve got arrange every thing, we are able to lastly see the ReAct loop in motion.
So, our guess query can play out two methods relying on what the climate really is. Let’s take a look at each.
1. If it rained in Athens, our code would print within the terminal one thing like the next:
--- Step 1: Motive ---
--- Step 1: Act (get_current_weather) ---
Calling get_current_weather with {'metropolis': 'Athens'}
--- Step 1: Observe ---
{'metropolis': 'Athens', 'temperature': 17.4, 'precipitation_mm': 3.2, 'unit': 'celsius'}
--- Step 2: Motive ---
--- Step 2: Act (convert_currency) ---
Calling convert_currency with {'quantity': 100, 'from_currency': 'EUR', 'to_currency': 'USD'}
--- Step 2: Observe ---
{'quantity': 100, 'from_currency': 'EUR', 'to_currency': 'USD', 'converted_amount': 108.5, 'fee': 1.085}
--- Step 3: Motive ---
Ultimate reply:
It did rain in Athens right this moment (3.2mm of precipitation), so that you received the guess!
Your 100 EUR comes out to 108.50 USD at right this moment's alternate fee.
2. And if it didn’t rain in Athens, we’d get the next printout:
--- Step 1: Motive ---
--- Step 1: Act (get_current_weather) ---
Calling get_current_weather with {'metropolis': 'Athens'}
--- Step 1: Observe ---
{'metropolis': 'Athens', 'temperature': 34.1, 'precipitation_mm': 0.0, 'unit': 'celsius'}
--- Step 2: Motive ---
Ultimate reply:
Sadly, it didn't rain in Athens right this moment, so it appears to be like such as you misplaced the guess.
No forex conversion wanted!
Take a look at what occurred within the second state of affairs: the loop ran precisely as soon as. The mannequin noticed that precipitation_mm was 0.0, reasoned that the guess situation wasn’t met, and stopped with out ever calling convert_currency. No person informed it to skip the second instrument name, however it slightly determined that by itself, based mostly purely on what it noticed within the first run of the loop.
That is the main differentiation (no less than for this straightforward state of affairs) between parallel instrument calling and the ReAct loop. In parallel instrument calling, we wouldn’t be capable to exit early from the whole course of, and never carry out the decision convert_currency. As a substitute, in a parallel setup, each instruments would have been known as upfront, and the mannequin would compose the ultimate response afterward. That is of explicit significance as a result of keep in mind! we do pay for each name to the mannequin. Thus, with the ability to architecturally slim down the AI mannequin calls to what we’d like, with out performing pointless further calls, could be very substantial.
On my thoughts
So, when does a ReAct loop really beat parallel instrument calling?
The reply is: each time the variety of instrument calls, or the arguments to these calls, can solely be decided after seeing an earlier outcome.
In our guess instance, the mannequin can’t resolve whether or not to name convert_currency in any respect till get_current_weather tells whether or not it rained. No quantity of upfront reasoning resolves that, as a result of the knowledge merely doesn’t exist but inside the mannequin’s world. We’ve got to step outdoors of the mannequin’s world, decide up exterior info from the climate API, and add it to the mannequin’s context. Quite the opposite, parallel instrument calling assumes the mannequin already is aware of what it wants earlier than it initiates any instrument calls. A ReAct loop doesn’t require that assumption: it lets the mannequin uncover what it wants because it goes.
Particularly, a ReAct loop wins over parallel instrument calling the next circumstances:
- When one result’s a situation for whether or not one other name is required in any respect, as within the guess instance.
- When the arguments to a later name rely on the worth returned by an earlier one. For instance, if the mannequin first needed to lookup which forex a metropolis makes use of earlier than it may name
convert_currencywith the fitting code. - When an earlier outcome comes again unexpectedly, for instance, the consumer might present a metropolis title that doesn’t geocode, or an API returns an error, and the mannequin must adapt its plan slightly than simply report again no matter it obtained.
Nonetheless, in an easy case the place all of the wanted instruments and their arguments are apparent from the consumer’s message alone, parallel instrument calling is definitely the higher selection, since on this method we get fewer round-trips, much less latency, and the identical outcome.
To me, essentially the most fascinating a part of transferring from parallel instrument calling to the ReAct loop is how little code it really took 😅: a for loop, an if assertion, and a dictionary lookup. Nonetheless, that small quantity of code is doing wonders. This ReAct loop, in a single kind or one other, is the precise mechanism behind most of what individuals imply by an “agent”.
✨ Thanks for studying! ✨
Should you made it this far, you may discover pialgorithms helpful — a platform we’ve been constructing that helps groups securely handle organizational information in a single place.
Beloved this put up? Be a part of me on 💌Substack and 💼LinkedIn
All photos by the writer, besides talked about in any other case

