Chapter 7 · an agent is a while loop with a dispatch dict
"Which items under $30 are in stock, and what would one of each cost?"
No single call answers that. Discover first, then price, then filter, then add.
DISPATCH = {"list_products": list_products,
"get_product": get_product}
for _ in range(10): # the safety rail
response = client.responses.create(
model="gpt-5.4-nano",
input=input_list, tools=TOOLS)
calls = [i for i in response.output
if i.type == "function_call"]
if not calls:
break # no calls = the answer
input_list += response.output
for call in calls:
result = DISPATCH[call.name](
**json.loads(call.arguments))
input_list.append({...call_id, output...})for _ in range(10):
response = client.models.generate_content(
model="gemini-3.1-flash-lite",
contents=contents, config=CONFIG)
if not response.function_calls:
break
contents.append(response.candidates[0].content)
# one function_response part per call, batched
Models batch several calls per turn — handle a list.
return {"error": f"no product called {name!r}"}
The model reads it, fixes the name, retries. Raise instead, and a recoverable mistake becomes a 500.
range(10), not while True.
A confused model will request the same tool forever. The cap is the difference between a bad answer and an outage.
Each one is a docker compose up web app.
The home-automation reply appends a Python-computed before/after diff. Models misreport what they did; the state doesn't.
An agent is a while loop with a dispatch dict. Production-worthy means: bounded, errors-as-data, side effects verified from computed state.