BG
FR
All articles
#IA#Agents#Architecture

An agent is a language model that decides its own next action. That's powerful, and it's exactly why it needs boundaries. What I've learned from wiring agents into real systems.

Boris Gautier5 November 2026 3 min read

We call it "agentic AI" as soon as a model stops merely answering and starts choosing an action: query a database, call an API, write a file, then look at the result and decide what comes next. In a demo, it's magic. In production, it's a program whose control flow is partly written by a probabilistic model. It has to be treated as such.

Start by asking whether you need an agent

Many "agentic" needs are really workflows: a sequence of steps known in advance, where the model classifies, extracts or drafts. A workflow is faster, cheaper, testable and predictable. An agent earns its place when the path truly depends on what is discovered along the way: diagnosing an incident, exploring a code base, answering a question that takes several chained searches.

The loop, without the magic

Under the hood, every agent is the same loop: send the model the conversation and the list of tools; it answers either with final text or with a tool call request; run the tool, send back the result, and repeat. Everything that matters is what you put around that loop.

agent/run.ts
export async function runAgent(task: string, tools: Tool[], opts = { maxSteps: 12, maxCostUsd: 0.5 }) {
  const messages: Message[] = [{ role: "user", content: task }];
  let cost = 0;

  for (let step = 0; step < opts.maxSteps; step++) {
    const reply = await model.complete({ messages, tools: tools.map((t) => t.schema) });
    cost += reply.usage.costUsd;
    trace.step({ step, reply }); // every decision is logged and replayable

    if (reply.type === "final") return reply.text;
    if (cost > opts.maxCostUsd) throw new BudgetExceeded(cost);

    const tool = tools.find((t) => t.name === reply.call.name);
    const args = tool?.input.safeParse(reply.call.args); // validate, never trust
    if (!tool || !args?.success) {
      messages.push(toolError(reply.call, "Unknown tool or invalid arguments"));
      continue;
    }
    if (tool.irreversible) await requireHumanApproval(tool.name, args.data);

    messages.push(toolResult(reply.call, await tool.run(args.data)));
  }
  throw new StepLimitReached(opts.maxSteps);
}

Narrow, typed, idempotent tools

An agent is first and foremost as good as its tools. A "run SQL" tool gives the model every power and every chance to get it wrong. A "find a customer's orders by phone number" tool does one thing, with validated parameters and a compact result. The model chooses better when the menu is short and each dish is clearly described.

  • A strict schema for every tool, checked on the server: the model proposes, the code decides.
  • Short, structured results: return 5 useful rows rather than 5,000 that drown the context.
  • Idempotent tools: if the agent replays a call, nothing must happen twice.
  • Readable errors sent back to the model, so it corrects course instead of going in circles.

Guardrails that are not optional

An agent without limits always ends up finding the infinite loop or the surprise bill. I always set a maximum number of steps, a cost and time budget, and human approval for any irreversible action: sending an e-mail, paying, deleting, publishing. The agent's permissions are those of the user who started it, never more.

Measure before you believe

An agent that works on three examples has proved nothing. I build an evaluation set of about fifty real tasks with their expected outcome, and replay it on every prompt, tool or model change. I track success rate, average number of steps and cost per task. Each run's traces explain the failures: in the vast majority of cases, the problem is a poorly described tool, not the model.

In short

  • Workflow first; agent when the path is truly unpredictable.
  • Few tools: narrow, typed, idempotent.
  • Step and budget limits, human approval for anything irreversible.
  • Full traces and an evaluation set replayed on every change.

Read next