This chapter defines an agent as a loop around goals, state, tools, context, decisions, and stop conditions.

Section
Start Here
Type
Concept
Level
Beginner
Read
8 min
Effort
10-20 min read
StudentArchitect

What Is An Agent?

An agent is not magic. It is a loop with access to a model, some state, a few decisions, and sometimes tools. The loop is the part that matters; everything else is packaging.

A framework may call it a graph, a crew, a swarm, a harness, an assistant, or a runtime. Strip away the names and you usually find the same shape underneath: observe, decide, act, observe again, and eventually stop.

This chapter owns the mechanical definition of an agent. It does not define a framework, product interface, memory system, or production runtime. Those layers surround the loop, but they do not replace it.

Read this before the pattern catalog if you want the plain model first: an agent is a controlled loop, not a personality, a chat window, or a product category. After this, Architecture Before Autonomy explains when that loop is worth using.

Agent loop architecture

The Minimal Agent

The smallest useful agent needs a goal or input, instructions, context, access to a model, a decision step, state, a rule for what happens next, and a stop condition. Tools are optional, but they are usually what makes the agent worth building.

In plain terms, that is:

input
  -> build context
  -> call model
  -> parse decision
  -> answer, call a tool, ask for help, or stop
  -> update state
  -> repeat if needed

Most of agent engineering is making each of those arrows explicit and safe. The diagram is easy. The discipline is in deciding what the model is allowed to do at each step, and what code keeps for itself.

The smallest useful loop can be sketched like this:

type Decision =
  | { kind: 'answer'; text: string }
  | { kind: 'tool'; name: 'lookup_order'; input: { orderId: string } }
  | { kind: 'ask_human'; question: string }
  | { kind: 'stop'; reason: string };

interface AgentState {
  goal: string;
  steps: number;
  observations: unknown[];
  maxSteps: number;
}

function validateDecision(decision: Decision): Decision {
  if (decision.kind !== 'tool') return decision;
  if (!decision.input.orderId.trim()) {
    return { kind: 'stop', reason: 'invalid_tool_input' };
  }
  return decision;
}

async function runAgent(state: AgentState): Promise<Decision> {
  while (state.steps < state.maxSteps) {
    const context = buildWorkingSet(state);
    const proposal = await callModelForDecision(context);
    const decision = validateDecision(proposal);

    if (decision.kind === 'answer' || decision.kind === 'ask_human') {
      return decision;
    }

    if (decision.kind === 'tool') {
      const result = await callApprovedTool(decision.name, decision.input, {
        idempotencyKey: `step:${state.steps}`,
      });
      state.observations.push(result);
      state.steps += 1;
      continue;
    }

    return decision;
  }

  return { kind: 'stop', reason: 'step_budget_exhausted' };
}

The important part is not the syntax. The model proposes a decision, software validates it before execution, state records the observation, and the loop stops for an explicit reason. The full runnable version appears in Agent Loop.

A Model Call Is Not Yet An Agent

A model call takes input and returns text or structured output. For summarization, extraction, classification, or rewriting, that is often all you need.

An agent adds control flow around the call. It can decide whether more work is needed, whether to use a tool, whether to ask a human, whether to retry, or whether to stop. The difference is not intelligence. It is runtime shape.

System What It Can Do Main Risk
Model call Produce one response from provided context. No action or recovery.
Prompt chain Move through known steps. Brittle gates and unnecessary latency.
Workflow with LLM steps Let code own the path while models handle judgment. Overfitting to a fixed process.
Agent loop Choose the next step from observations. Loops, tool misuse, and hidden state drift.

So be careful with the word. Not every model-backed feature is an agent. Reserve the term for systems that can make at least some runtime decisions about what to do next.

A Quick Classification Test

When a team says “agent”, classify the system before debating the framework.

Example Better Name Why
A button that rewrites a paragraph once. Model call There is no loop, tool use, or runtime decision.
A three-step support reply generator: classify, retrieve policy, draft response. Prompt chain or workflow The path is known before execution.
A refund flow where code checks policy, the model summarizes evidence, and approval gates the payment. Workflow with LLM steps Code owns the path and side effects.
A research assistant that searches, reads results, decides whether more retrieval is needed, and stops when evidence is sufficient. Agent loop The next step depends on observations from the run.
A delivery system with planner, dispatcher, driver-communication worker, and escalation policy. Multi-agent system or workflow with agents Work is split across roles with separate context and authority.

The classification is not about prestige. A model call is not worse than an agent. A workflow is not less modern than a loop. The right name tells you what must be tested, traced, secured, and operated.

Running Example: Refund Support

The support refund assistant used across this book starts as a workflow, not an agent. The known path is clear: classify the request, load the order, retrieve the current refund policy, summarize evidence, validate the recommendation, and request approval when money can move. Code should own that path.

The agentic part appears only when the workflow hits bounded uncertainty. Maybe the order data conflicts with the delivery record. Maybe the customer claims a policy exception. Maybe the evidence is missing and the system must decide whether to search another source, ask a clarifying question, or stop. That narrow investigation can justify a loop.

Part of the refund system Better shape Owner
Intake, account lookup, and policy version selection Deterministic workflow Application code
Evidence summary and recommendation draft Model call or structured-output step Model proposes, code validates
Missing or conflicting evidence investigation Agent loop with max steps Loop runtime
Refund threshold, exception policy, and approval requirement Policy gate Software
Payment execution Tool call after approval Tool gateway

This is the discipline behind the word “agent.” Do not ask whether the refund assistant sounds autonomous. Ask which runtime decision needs autonomy, which side effects need authority, and which stop reason the system will record.

Decision Trees Come Before Autonomy

Many useful agents are mostly decision trees with model calls inside them. That is not a weakness, and it is often the right design:

if request is out of scope:
  refuse or redirect
else if account data is missing:
  ask a question
else if policy evidence is missing:
  retrieve documents
else if action is high risk:
  request approval
else:
  call the model to draft the recommendation

This is still an agentic system as long as the model is making bounded judgments inside the flow. What keeps it safe is that code owns the policy, the state, and the high-risk transitions. Add autonomy where the decision tree gets too large to maintain, or where the next step genuinely depends on something the run discovers along the way. Adding it anywhere else just buys you risk you did not need.

Tools Make The Loop Consequential

Without tools, an agent can only produce text. With tools, it can search, calculate, retrieve, write files, browse, call APIs, run code, send messages, update tickets, or change customer data. That is where the system becomes useful, and it is the same place it becomes dangerous.

Never treat a tool call as “the model did it.” The model proposed the call; software executed it. That boundary is where most of your safety lives, so it has to be real: tool names are known ahead of time, arguments are typed, permissions are checked before execution, timeouts exist, results are recorded, side effects are auditable, and risky actions wait for approval. The more powerful the tool, the less trust you should leave sitting in the prompt.

State Makes The Loop Coherent

An agent needs state because the loop needs a memory of its own run. At a minimum, state should be able to answer what the active goal is, what context was used, what has already happened, which tools were called, what evidence was found, what errors occurred, how much budget remains, and why the run stopped.

Conversation history does not cover this. History is evidence of what was said; it is not a model of what the system knows and has done. Real state is what lets the system resume after an interruption, avoid repeating work, explain a decision after the fact, and turn a production incident into an eval case.

Stop Conditions Are Part Of The Agent

A loop without a stop condition is not an agent. It is a cost generator. Common stop conditions include the success criteria being met, a required field being missing, a non-recoverable tool failure, a policy block, a pending human approval, a max-iteration limit, an exhausted cost or latency budget, or a user cancelling the run.

Whatever ends the run, the stop reason should be explicit. “Done” is not enough. A system worth operating records whether the agent completed, refused, escalated, failed, timed out, or stopped because a budget ran out, because every one of those needs a different response from the people watching it.

What Frameworks Add

Frameworks do not remove the loop. They package it, and they save real work: graph execution, routing, tool registries, model adapters, memory stores, skills, subagents, durable checkpoints, streaming, human-in-the-loop interrupts, tracing, and deployment hooks. None of that is wasted effort, and rebuilding these primitives by hand is rarely a good use of a team’s time.

But the framework does not change the question you have to answer: what does the loop observe, what can it decide, what can it do, and when does it stop? If your team cannot answer that without first naming a framework, it does not understand its own agent yet.

What Makes An Agent Good

A good agent is not the one with the most autonomy. It is the one whose autonomy is useful and bounded. In practice that means clear goals, narrow tools, explicit state, typed outputs, visible decisions, bounded loops, evals that actually catch regressions, safe failure modes, and human escalation for the risky cases. Bad agents hide all of this inside a prompt and hope the model behaves.

The design rule follows from everything above: if you cannot draw the loop, the tools, the state, and the stop condition, you do not understand the agent yet, and you are not ready to give it autonomy.

Before calling something an agent, write down four things:

  1. The runtime decision it is allowed to make.
  2. The state it reads and writes outside the model context.
  3. The tools or side effects it can request.
  4. The exact stop reasons the system records.

If any of those are missing, you probably have a model-backed feature, chain, or workflow that needs clearer boundaries before it needs more autonomy.

The next question is not how to add more autonomy. It is whether the task needs a loop at all. Continue with Architecture Before Autonomy, then return to Agent Loop when runtime decisions are justified.