Skip to content
HARBAN AI LABS

Explainer

What counts as an AI agent? A working definition for engineers

A definition you can check against code: a model, a set of tools, a loop, state carried between steps and an explicit stop condition — plus how to tell when a fixed workflow is the better engineering choice.
Author
HARBAN AI LABS
Published
Reading time
6 min read

Why a definition matters

"Agent" is used for everything from a chatbot with a long system prompt to a system that works unattended for hours. That vagueness is expensive: teams argue past each other in design reviews, buy frameworks they do not need, and evaluate the wrong thing.

For engineering purposes, a useful definition is one you can check against code. Which components exist? Who decides what happens next — your program, or the model?

A working definition

Definition. An AI agent is a system in which a model repeatedly chooses the next action from a set of tools, observes the result, and continues — carrying state between steps — until a stop condition is met.

That sentence has five parts, and each is something you can point to in a codebase:

  1. Model. The component that decides the next step. It reads the current state and returns either an action or a final answer.
  2. Tools. Typed actions the model can request: search, run code, query a database, call an API, edit a file. Each has a schema for its arguments and returns an observation.
  3. Loop. After each observation, control returns to the model. Crucially, the model chooses the path, not a fixed sequence in your code.
  4. State. Whatever carries information from one step to the next: the transcript, a scratchpad, external memory, and — often forgotten — the state of the environment the tools changed.
  5. Stop condition. The model signalling it is done, backed by hard limits your code enforces: a step cap, a budget, a timeout, a human approval gate.

Remove a part and you have something else. No tools: a chat model. Tools but no loop: a single function call. A loop whose path your code fixes in advance: a workflow.

This lines up with a distinction Anthropic draws in its engineering guidance: workflows orchestrate models and tools through code paths defined in advance, while in agents the model directs its own process and tool use (Anthropic, December 2024 (external site)).

An early formulation: ReAct

A clear early statement of this loop is ReAct (Yao et al., 2022), which prompted a language model to produce reasoning traces and task-specific actions in an interleaved way, with the actions querying external sources — in the paper, a Wikipedia API — and feeding the results back in. The authors evaluated it on question answering (HotpotQA), fact verification (FEVER) and two interactive decision-making environments (ALFWorld and WebShop) (arXiv:2210.03629 (external site)).

The think → act → observe cycle it describes is a good mental model for the loop in the definition above, whatever library you use. Our paper breakdown of ReAct covers the method and its results in more detail.

The smallest loop that qualifies

Here is the whole idea in plain Python. call_model is whatever client you use; it returns text plus zero or more requested tool calls. The message format is illustrative — real provider APIs need their own shapes and tool-call IDs.

from dataclasses import dataclass, field
from typing import Any, Callable


@dataclass
class ToolCall:
    name: str
    arguments: dict[str, Any]


@dataclass
class ModelTurn:
    text: str
    tool_calls: list[ToolCall] = field(default_factory=list)


@dataclass
class AgentResult:
    status: str  # "done", or "step_limit" when the hard cap stopped the run
    text: str  # the final answer, or the model's last message before the cap
    transcript: list[dict[str, Any]]  # everything that happened, for the caller


def run_agent(
    task: str,
    call_model: Callable[[list[dict[str, Any]]], ModelTurn],
    tools: dict[str, Callable[..., str]],
    max_steps: int = 10,
) -> AgentResult:
    messages: list[dict[str, Any]] = [{"role": "user", "content": task}]  # state
    last_text = ""
    for _ in range(max_steps):  # loop, with a hard limit
        turn = call_model(messages)  # the model chooses the next step
        last_text = turn.text
        messages.append({"role": "assistant", "content": turn.text})
        if not turn.tool_calls:  # no action requested: the model is done
            return AgentResult("done", turn.text, messages)
        for call in turn.tool_calls:  # act, then record the observation
            tool = tools.get(call.name)
            try:
                if tool is None:
                    raise KeyError(f"unknown tool {call.name!r}")
                result = tool(**call.arguments)
            except Exception as exc:  # errors are observations too
                result = f"error: {exc}"
            messages.append({"role": "tool", "name": call.name, "content": result})
    # Hard stop: say so, and hand back what was done so far.
    return AgentResult("step_limit", last_text, messages)

Every part of the definition is visible: the model call, the tool table, the for loop, the messages list as state, and two stop conditions — a natural one (no tool calls) and a hard one (max_steps). Two details are worth copying into real systems. Tool errors are returned to the model as observations instead of crashing the loop, so it can recover. And hitting the step cap is an explicit, distinguishable outcome — status="step_limit", with the transcript so far — rather than a silent empty answer or an exception that throws the work away.

State is where agents get hard

The loop is easy. State is where most production problems live.

  • The transcript grows. Every observation is appended, so long runs fill the context window and cost more per step. Worse, models do not use long contexts evenly: one study found performance often highest when the relevant information is at the beginning or end of the input, and degraded when it sits in the middle (Liu et al., 2023 (external site)). Summarise or prune old observations deliberately, and keep the task and constraints where the model will attend to them.
  • The environment has state too. Files written, rows updated and messages sent are not rolled back when the loop fails. Make tools idempotent where you can, offer dry-run modes, and run anything destructive in a sandbox.
  • Long tasks need durable state. If a run can outlive a process, persist the transcript and a step counter so it can resume instead of starting over.

Stop conditions are a design decision

"The model decides when it is done" is necessary but not sufficient. Decide, in code:

  • Natural stop. No tool call, or a dedicated finish tool whose arguments are the structured result.
  • Hard limits. Maximum steps, a token or cost budget, and a wall-clock timeout. Anthropic's guidance notes that stopping conditions such as a maximum number of iterations are a common way to keep control (Anthropic (external site)).
  • Guard stops. The same tool called with the same arguments several times in a row, or a run of consecutive tool errors, usually means the agent is stuck.
  • Human gates. Before irreversible actions — sending, deleting, paying, deploying — pause for approval.

Also decide what a stopped run returns. "Stopped at the step limit; here is what was done so far" is far more useful to a caller than an exception with no context.

When a workflow is the better choice

In a workflow, your code fixes the sequence and the model fills in individual steps: classify, extract, draft, check. It is often the better engineering choice. Prefer a workflow when:

  • the steps are known in advance and rarely change;
  • you need every run to follow an auditable path;
  • latency and cost must be predictable;
  • you want validation at specific checkpoints (after extraction, before sending);
  • you cannot yet evaluate open-ended behaviour.

Reach for an agent when the path genuinely depends on what is discovered along the way — debugging, open-ended research, navigating an unfamiliar system — and the environment gives useful feedback, such as test results or error messages, that the model can act on.

Many good systems are hybrids: a fixed workflow with one bounded agentic step inside it, with its own tools, budget and stop conditions. Anthropic's guidance recommends finding the simplest solution possible and adding complexity only when it is needed (Anthropic (external site)); that is sound advice regardless of vendor.

Evaluating an agent

Agents need evaluation designed for loops, not single responses:

  • Check outcomes, not transcripts. Define tasks with a verifiable end state: tests pass, the right record changed, the right answer returned.
  • Run each task several times. τ-bench introduced pass^k, the chance that all k independent trials of a task succeed, averaged across tasks (Yao et al., 2024 (external site)). For anything user-facing, reliability matters more than the best of several tries.
  • Record the cost of success. Steps, tokens, wall-clock time and tool errors per run belong in the results table alongside the success rate.
  • Read trajectories. Aggregate numbers tell you that runs fail; reading a sample of transcripts tells you why.

When you compare published agent results, remember that the score belongs to the model and its harness — see How to read an AI benchmark table.

A quick test for "is this an agent?"

  • Does a model choose between tools at run time?
  • Does control return to the model after each observation?
  • Is state carried between steps?
  • Is there a stop condition beyond "the model said so"?

Four yeses: an agent. Fewer: something else — and possibly something simpler, cheaper and easier to test, which is not a bad thing.

Written by HARBAN AI LABS. External facts link to their primary sources; the practical advice is our own judgement. If something is wrong or out of date, report it below — corrections are logged on this page.

Corrections

  1. The sample loop now returns a result with status "step_limit" and the transcript so far when it hits the step cap, instead of raising an exception without context — matching the advice later in the article.

Cite this page

HARBAN AI LABS (2026). “What counts as an AI agent? A working definition for engineers”. HARBAN AI LABS. https://harban-ai-labs.vercel.app/insights/what-counts-as-an-ai-agent

@misc{harban2026whatcountsasanaiagent,
  author = {HARBAN AI LABS},
  title = {What counts as an AI agent? A working definition for engineers},
  year = {2026},
  publisher = {HARBAN AI LABS},
  url = {https://harban-ai-labs.vercel.app/insights/what-counts-as-an-ai-agent}
}

Report an issue with this page

Spotted a wrong number, a broken source or something out of date in “What counts as an AI agent? A working definition for engineers”? Tell us what and, if you can, link the correct source.

Required. At least 10 characters.

Model registry/models
Benchmarks/benchmarks
Paper breakdowns/research
AI tools directory/tools
Work with us/work-with-us