Agents

An agent is a model running in a loop, picking the next tool to call until a task is done. The rest of this chapter is what that means, where it earns its keep, and where it quietly costs you.
The agent loop
In tool use the model called one function and you sent the result back. But some tasks take several steps, and you do not know the steps ahead of time. "Find the cheapest flight and book it" might mean searching, comparing, checking a calendar, then booking, and the model has to work out that sequence as it goes.
So instead of handling a single tool call, you put the whole thing in a loop. This repeating cycle is the agent loop, one turn repeated until the task is done:
- Send the conversation and the available tools to the model.
- The model either calls a tool or gives a final answer.
- If it called a tool, run it, add the result to the conversation, and go back to step 1.
- If it gave a final answer, stop.
Each turn the model sees everything that happened so far, including previous tool results, and decides the next move. That is the whole difference from tool use: you keep looping until the model is satisfied it has finished.
The real shift is who is steering. In every chapter so far, your code decided the control flow: you chose when to call the model, when to run a tool, what came next. An agent hands that decision to the model. The loop keeps asking "what should happen next?" and the model's prediction is the answer each time. You trade control for flexibility, and that trade is the source of both reach and risk.
Underneath, nothing new is happening. It is the same prediction on a growing context, the tool-use round-trip on repeat. The model does not draw up a plan and follow it. Each turn it predicts the single best next step from what it can see right now, then the loop runs again.
When that works, it looks like reasoning toward a goal. When it does not, the agent loops on itself or wanders down a dead end, because no part of the system is holding the overall plan.
A minimal agent
Here is the loop in code. It reuses the tool definitions and implementations from tool use and wraps them in a loop that runs until the model stops asking for tools.
import json
def run_agent(user_text, tools, tool_impls, max_steps=6):
messages = [{"role": "user", "content": user_text}]
step = 0
while step < max_steps:
response = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
message = response.choices[0].message
messages.append(message)
calls = message.tool_calls
if not calls:
return message.content # no tool wanted: this is the final answer
# run every requested tool and feed the results back
for call in calls:
args = json.loads(call.function.arguments)
result = tool_impls[call.function.name](args)
messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})
step += 1
return "Stopped: the agent reached its step limit." # safety netRead it top to bottom: call the model, push its message onto the conversation, and if it asked for tools, run them all and append the results, then loop again. When the model returns text instead of a tool call, that is the final answer and you return it.
The max_steps value sets a step limit, the safety net that keeps the loop from running away, which the next section is about. The shape here matches one provider's SDK; the field names differ across providers, but the loop is the same everywhere.
Stopping and guardrails
A loop that lets a model decide when to stop can fail to stop. It might call tools forever, repeat itself, or chase a dead end. Because every step is a model call billed by tokens, a runaway agent burns money and time fast.
So guardrails, the limits you put around the loop, are part of the design, not a polish step you add at the end:
- A step limit. Cap the number of loops, the way
max_stepsdoes. When it is hit, stop and return. - Validate tool inputs. The model chooses the arguments, so check them before running anything with real consequences.
- Limit what tools can do. Give the agent the least power it needs. A tool that reads is safer than one that deletes.
- Watch the cost. Several model calls per task multiply quickly, so track tokens and set a budget.
The thread tying these together: the model is improvising the path, so you put the limits in place that it will not. Assume the agent will do the wrong thing at least once, and make that survivable.
When a workflow beats an agent
Agents are exciting, which makes it tempting to reach for one when you do not need it. The plain rule: if you already know the steps, do not use an agent. Write them out as plain code, a workflow, where each step runs in a fixed order.
A workflow is more reliable, cheaper, and far easier to debug, because you control the path instead of hoping the model finds it. Save agents for open-ended tasks where the steps depend on what is discovered along the way.
Many features that look like they need an agent are better as a structured output call or a RAG lookup wired together by hand. Reach for an agent only when you cannot write the steps down in advance.
In practice
The run_agent function above is the whole thing. Give it a question, a set of tools, and their implementations, and it loops until done or until it hits the step limit. A small task shows the loop earning its keep:
answer = run_agent(
"What's the weather in Oslo and should I bring an umbrella?",
tools, # includes get_weather from the tool-use chapter
tool_impls, # {"get_weather": ...}
)
# The agent calls get_weather, reads "cloudy", then answers in one final turn:
# "It's cloudy in Oslo at 18 degrees, so an umbrella is a sensible idea."One tool, one loop, one answer. The same structure scales to several tools and several steps without changing shape. Once you can build things this capable, the next question is how you know they actually work, which is what evals is about.
run_agent loop is the whole pattern: give it a question, tools, and their implementations, and it loops until the model stops asking for tools or hits the step limit. One tool and one loop scale to many without changing shape. The next question is how you know it actually works, which is evals. 
