The mystique around agents evaporates the moment you write one. At its heart it is a loop: ask the model what to do, do the safe part of it, feed back the result, repeat until done or until a human says stop.
The whole thing, roughly
Here is a stripped-down version of the pipeline-triage agent we run internally. It reads a failing render log, proposes a fix, and — crucially — never applies anything destructive without confirmation.
def agent(task, tools, model, max_steps=8):
history = [system_prompt(tools), user(task)]
for step in range(max_steps):
reply = model.complete(history)
if reply.done:
return reply.answer
call = reply.tool_call
if call.destructive and not human_ok(call):
history.append(tool_result(call, "denied by human"))
continue
result = tools[call.name](**call.args)
history.append(tool_result(call, result))
return "stopped: step budget reached"
Notice what is doing the work. It is not a clever model. It is the guardrails: a step budget, a destructive-action gate, and a log the human can read. The intelligence is in the constraints.
An agent is only as trustworthy as the smallest action it can take without asking.
What broke, and what I learned
- Give tools narrow, boring interfaces. The model behaves when the surface area is small.
- Log every step in human language. You will need it at 2 a.m.
- Design for the model being wrong. Then it being wrong is a Tuesday, not an incident.
Start with one tool and a loop this weekend. You will understand more about this moment in our industry than a hundred think-pieces will teach you.