Strip away the marketing and an agent is a small, unglamorous thing: a loop. A model is given a goal and a set of tools, it decides which tool to call, it reads the result, and it decides again — over and over — until it judges the goal met. That's the whole idea. Everything impressive an agent does and everything alarming it does both come from the same property: the control flow is decided by the model at runtime, not by you at design time.
That property is genuinely useful for a specific class of problem, and genuinely dangerous everywhere else. The engineering of a production agent is almost entirely about constraining that loop — deciding what it may touch, how far it may go, and who checks its work before anything irreversible happens. The interesting part isn't giving the model autonomy. It's the leash.
When you don't need one
The most valuable thing I can say about agents is that most tasks people reach for one to solve don't need it. If you know the steps in advance — fetch this, transform it, call that, return the result — then write those steps. A fixed pipeline with a model call or two inside it is cheaper, faster, fully testable, and it never wanders off. Handing a deterministic task to an agent is paying for a loop's unpredictability to accomplish something a straight line would have done better.
If you can draw the flowchart, you don't have an agent problem — you have a pipeline, and a pipeline is the safer thing to build.
An agent earns its complexity only when the steps genuinely can't be known ahead of time: when the next action depends on what the last one returned, the number of steps varies by input, and the branching is too wide to enumerate. Investigative and triage tasks fit this — "figure out why this invoice doesn't match any purchase order" can go a dozen different ways depending on what's actually wrong. That's the shape worth an agent. Nearly everything else is a pipeline wearing a costume.
Tools are the attack surface
An agent is exactly as powerful, and as dangerous, as the tools you give it. A tool that reads data can, at worst, read the wrong data. A tool that writes, sends, pays, or deletes can do real and irreversible harm if the loop calls it on a bad decision — and the loop will occasionally make a bad decision, because it's a model. So the first design question is never "what can this agent do" but "what's the worst thing it can do," and the answer has to be survivable.
The rule I hold to: read-only tools are cheap to hand out, write tools are rationed and guarded. Give the agent broad freedom to look, and narrow, gated permission to act. A write tool doesn't execute on the agent's say-so; it stages an intent that either clears an automated policy check or waits for a human, depending on stakes.
// Reads: free. Writes: staged behind a guard, never fired blind. const tools = [ readOnly("fetch_invoice", fetchInvoice), readOnly("match_po", matchPurchaseOrder), readOnly("search_ledger", searchLedger), guarded ("post_to_ledger", postToLedger, { requiresApproval: (args) => args.amount > AUTO_LIMIT, // big ones → human idempotencyKey: (args) => args.invoiceId, // never double-post }), ];
The leash: budgets and bounds
A loop whose exit condition is "the model thinks it's done" needs a hard stop the model doesn't control, because the failure mode of an agent isn't a crash — it's a model that loops, re-calling tools, spending money and time, convinced it's making progress when it's stuck. Left unbounded, that's a runaway bill and a hung request.
So every agent runs under explicit budgets enforced outside the loop: a maximum number of steps, a ceiling on total tokens or dollars, and a wall-clock timeout. Hit any of them and the agent stops and escalates rather than grinding on. These bounds are not a safety afterthought; they're load-bearing, and they connect directly to the per-request economics — an unbounded agent is the fastest way to blow the cost model you so carefully built.
A verifier that must sign off
The agent deciding it succeeded is not the same as it succeeding. A model is an unreliable judge of its own work — it will report a job well done on an output that's subtly wrong. So a production agent's result isn't trusted on the agent's word; it passes through a separate verification step before it counts as done.
Sometimes that verifier is deterministic code — does the invoice total actually equal the sum of line items, does the referenced purchase order exist. Sometimes it's a second model call, prompted narrowly to check one thing and to be skeptical. Either way it's independent of the agent that produced the result, and a failed check sends the work back into the loop or escalates it to a human. The agent proposes; something else disposes.
Never let the same process that did the work be the one that certifies it. Generation and verification must be able to disagree.
Every step, on the record
When a fixed pipeline breaks, the stack trace tells you where. When an agent produces a wrong outcome, there's no stack trace — there's a sequence of decisions, and without a record of them the failure is unrepeatable and undebuggable. So the single most important operational feature of an agent is a complete, structured trace: every tool call, its arguments, its result, the model's stated reasoning for choosing it, and the budget consumed at each step.
That trace is what turns "the agent did something weird last Tuesday" into a specific, fixable defect. It's also the raw material for evaluation — every real run becomes a case you can replay, grade, and fold into the suite that guards the next change, exactly the way I think about evaluation generally. An agent you can't fully replay after the fact is one you can't safely run in production, no matter how well it demos.
Boring agents ship
The agents that make it to production and stay there are unexciting by design. They have a handful of carefully chosen tools, most of them read-only. They run under hard budgets they can't override. They stage risky actions behind idempotent, escalating guards. Their output has to clear an independent verifier. And every step they take is logged well enough to replay. What's left is a system that does something genuinely dynamic while remaining bounded, observable, and cheap enough to run at scale.
The hype says agents will autonomously run your business. The engineering says an agent is a loop you have to keep on a short, well-instrumented leash — and that within those bounds it does a specific, valuable thing nothing else does as well. If you've got a task too branchy for a pipeline and you want it done without handing over the keys, that's the kind of build I like.
Want to compare notes on agent design?
Get in touch