Skip to content
NewFree 30-minute AI strategy call for new clients.Book yours
SYF Tech
Agentic AIEngineering

How an AI agent actually works: the loop, the tools, the guardrails

An AI agent is a language model in a loop that calls tools, reads the results and decides again. Here is how the loop, tools and guardrails fit together.

In short

An agent is an LLM in a loop with tools, a step limit and approval gates on anything risky. The model only proposes actions; your code runs them, decides when to stop and records every step so you can test it.

"AI agent" now covers everything from a chatbot to a system that edits your database. Underneath, the working ones share one small mechanism. Once you see it, the production questions get easier: what can it touch, when does it stop, and how do you know it did the right thing?

The loop

An agent is a language model running in a loop. It gets a goal, thinks about the next step, picks a tool, your code runs that tool, and the result goes back to the model. It repeats until it decides the goal is met or something stops it.

The ReAct paper (Yao et al., 2022) named the core idea: interleave reasoning with actions, so each thought is checked against real information instead of the model's guesses. Anthropic's guide puts it as getting "ground truth" from the environment at every step.

Animated diagram of the agent loop. A glowing token starts at a "Goal" box and moves into a ring of four stations, going clockwise: "Think", where the model reasons about the next step; "Pick tool"; "Act", where your code runs the tool; and "Observe", where the result is added to the conversation. Each station lights up as the token passes. The token goes around the ring twice while a counter labelled "step limit" moves from "step 1 of 8" to "step 2 of 8", then the token leaves "Think" for a green "Done" box.

Each lap is one model call plus its tool runs, so the lap count is a first measure of cost and latency.

Tools are a contract

The model never runs anything itself. You describe each tool with a name, a description and a JSON schema for its input. When the model wants one, it returns a structured call instead of prose. Your code runs it and sends back the result, tagged with the call's id.

The two big APIs use the same shape. With Claude, the response has stop_reason: "tool_use" and a tool_use block, and you reply with a tool_result. With OpenAI's Responses API, the output contains a function_call item with a call_id, and you reply with a function_call_output. Both offer strict: true so calls match your schema.

Animated diagram of one tool call. A "Model" panel on the left emits a structured call card, search_invoices with status "overdue", instead of prose. The card slides right into a "Your code" panel, where a tick shows the code runs it and checks the inputs. A result card listing two invoice ids slides back into the Model panel, and the model continues with the answer "Two invoices are overdue…". The tool name and ids are illustrative.

The model proposes; your code decides whether to run it.

Tool design deserves as much care as the prompt. Write descriptions a new colleague could follow, and shape the arguments so mistakes are hard to make. Anthropic calls this "poka-yoke" for tools.

Memory and state

The conversation is the state. On each lap you send the whole message list again: the goal, every call the model made and every result. Anything that must last longer than one run, such as notes or past decisions, lives in a store you own and reach through a tool.

When it stops

With Claude, the loop continues while the stop reason is tool_use and ends on anything else, normally a final answer. That is the model's view of "done". You need your own as well:

  • a maximum number of steps,
  • a time or token budget,
  • a clean failure when either runs out, so a person picks it up.
type ToolCall = { id: string; name: string; input: unknown };
type ModelTurn = { done: boolean; text: string; calls: ToolCall[] };
type Message = { role: "user" | "assistant" | "tool"; content: unknown };

declare function callModel(messages: Message[]): Promise<ModelTurn>;
declare function runTool(call: ToolCall): Promise<string>;
declare function needsApproval(call: ToolCall): boolean;
declare function requestApproval(call: ToolCall): Promise<boolean>;

const MAX_STEPS = 8;

export async function runAgent(goal: string): Promise<string> {
  const messages: Message[] = [{ role: "user", content: goal }];
  for (let step = 0; step < MAX_STEPS; step++) {
    const turn = await callModel(messages);
    messages.push({ role: "assistant", content: turn });
    if (turn.done) return turn.text;
    for (const call of turn.calls) {
      const approved = !needsApproval(call) || (await requestApproval(call));
      const result = approved ? await runTool(call) : "Rejected by a reviewer.";
      messages.push({ role: "tool", content: { id: call.id, result } });
    }
  }
  throw new Error(`Stopped after ${MAX_STEPS} steps without finishing.`);
}

This is provider-neutral: callModel wraps whichever API you use. In production the approval is often a saved, paused run that resumes later, not an await.

Guardrails and human approval

Split tools by what they can break. Reading data is usually safe to run freely. Sending, paying, deleting or changing records goes through a gate where a person approves the exact action.

Animated diagram of guardrails. Three proposed actions move from "agent proposes" on the left towards a vertical gate labelled "Human approves", then on to "runs" on the right. "Read bank feed" and "Match invoice" pass straight through and turn green. "Send reminder email" stops at the gate, which glows pink, with the label "paused: waiting for approval". When an "Approved" tick appears it passes through and turns green. The still version shows it paused at the gate.

Safe steps flow; risky ones wait for a person, and the agent can't route around the gate.

Our invoice reconciliation agent works this way. It can read the bank feeds and write only to reconciliation fields in the ERP. Unsure matches go to a review queue, it drafts reminder emails, and nothing is sent until someone approves it. Every match and approval is in an audit log.

Mostly a workflow, with small agents inside

That case is typical. The order of operations, permissions and retries belong to a plain workflow, and the agent gets one narrow job with a short tool list. We covered how to decide in Agent or workflow?

Testing and observing it

Log every step as a trace: each model call, tool call, input, result and approval. That trace is your debugger, audit trail and test data.

Then build evals. Anthropic's guide on agent evals suggests starting with 20 to 50 tasks drawn from real failures, and grading both the outcome and the transcript with a mix of code, model and human graders. Check that the agent reached the right end state without taking a shortcut on the way, and read the transcripts yourself.

In practice

  • Give the agent one goal and the fewest tools that can reach it.
  • Make read tools free and put every side effect behind approval.
  • Set a step and budget limit, and fail loudly when it's hit.
  • Trace every call before you ship, and turn each production failure into an eval case.

If you are planning an agent and want a second opinion on where the loop should end and the workflow begin, see our services.

Sources

Agentic AIEngineering
Share

SYF Tech

AI, automation and app development from Pakistan, for teams worldwide.

All insights

Have a project in mind?

Tell us what you're building. A senior engineer replies within 24 hours.

Start a project