An agent is a control loop around a model that can call tools. The model proposes an action, your code executes it, the result returns as context, and the cycle repeats until a completion condition is met. The difficult engineering is in the termination rules and the error handling, not the model.
"Agent" carries a lot of implication it has not earned. It suggests autonomy, initiative, something with intent. The implementation is considerably more ordinary, and the ordinariness is good news: it means the behaviour is yours to control.
The loop
Send the model a task and a list of tools it may use. The model replies either with an answer or with a request to call a tool. If it asked for a tool, your code runs it and appends the result to the conversation. Then you call the model again.
That is the entire architecture. What varies between frameworks is ergonomics around it — how tools are declared, how state is stored, how errors surface — not the shape.
Your code is always in the middle
The model never executes anything. It produces a structured request naming a tool and arguments; execution is yours. Every authorisation check, every rate limit, every "are you sure" belongs in that gap, because it is the only place where the decision is actually made.
Stopping is the hard part
Getting a loop started is an afternoon. Getting it to stop correctly is the work. An agent with no budget will happily spend your money exploring, and a model that has lost the thread will call the same tool repeatedly with slight variations.
Give every run three ceilings — steps, tokens and wall-clock time — and treat hitting one as a reportable outcome rather than a crash. Detect repeated identical calls and break. An agent that gives up clearly is more useful than one that persists expensively.
Errors are context, not exceptions
When a tool fails, the instinct is to raise. Usually the better move is to return the failure to the model as a result: "search returned no rows", "that file does not exist", "the API rejected the date format".
Models recover from this well. Given a clear error they adjust arguments and try something sensible, which is exactly the behaviour you wanted from a loop. Reserve real exceptions for conditions the model cannot possibly fix.
Make the run legible
Log every step: what was proposed, what ran, what came back, what it cost. Without that, debugging is guesswork, because the interesting failures happen three steps in and depend on the accumulated context.
The teams who find agents tractable are almost always the ones who can replay a run and see exactly where it went sideways.
Frequently asked questions
Does the agent run my code by itself?
No. The model emits a request naming a tool and its arguments. Your code decides whether to run it, runs it, and returns the result. That gap is where every permission check, rate limit and confirmation belongs.
How do I stop it looping forever?
Set explicit budgets — maximum steps, maximum tokens, wall-clock timeout — and detect repetition. An agent calling the same tool with the same arguments twice is stuck, and the right response is to break out and report, not to try again.