JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

Anatomy of an Agent: The Reasoning–Acting Loop

What separates an agent from a chatbot is a loop: think, act on the world through a tool, observe what came back, repeat. We build that loop from the ground up.

From a text predictor to an actor

A large language model in isolation does exactly one thing: given a prefix of tokens, it predicts the next one. That is a remarkable amount of latent capability, but it is passive — the model cannot look anything up, cannot run a calculation it has not memorized, and cannot change the world. An agent is what you get when you wrap that predictor in a loop that lets it take actions and read back the consequences. The model stops being an oracle that answers in one shot and becomes a controller that works toward a goal over many steps.

In isolation the model does only this: extend a token prefix one step at a time.

Diagram of autoregressive generation: each predicted token is appended and fed back to predict the next.

The conceptual leap is small but consequential. Vol I introduced tool use as a capability bolted onto a model. Here we treat it as the organizing principle of a system. Everything in this track — search over reasoning, planning, memory, code execution, multi-agent teams — is an elaboration of one humble idea: interleave generation with feedback from outside the model.

The reasoning–acting loop

The canonical pattern is the reasoning–acting loop, popularized as the ReAct pattern. At each step the model emits a free-text Thought (its private chain-of-thought), then a structured Action (a tool call), the environment returns an Observation, and the loop repeats until the model emits a final answer instead of an action. Reasoning and acting are not separate phases — they are braided, so each new observation immediately reshapes the next thought.

The reasoning–acting loop: a Thought drives an Action, whose Observation feeds the next step.

An LLM agent loop cycling through reason, act, and observe stages.

state = [system_prompt, user_goal]
for step in range(max_steps):
    out = model.generate(state)          # "Thought: ...  Action: search(q)"
    if out.is_final_answer:
        return out.answer
    obs = tools[out.action.name](**out.action.args)
    state += [out, format_observation(obs)]
return give_up()
The whole agent in eight lines: generate, branch on whether it is a final answer, otherwise execute the tool and append the observation.

Function calling: actions with a schema

If actions are just free text, parsing them is a nightmare. Function-calling agents fix this by giving the model a set of typed tool schemas — name, description, and a JSON-shaped argument signature — and asking it to emit a structured call that conforms to one of them. The model is, in effect, doing constrained generation against a grammar of legal API calls. Modern serving stacks enforce this with guided decoding so the output is always parseable JSON, never a half-formed call.

This is where tool use becomes robust enough to build on. A good schema does more than name the function: its descriptions are prompts in disguise, teaching the model when the tool is appropriate. Write them as carefully as you would write the system prompt, because to the model they are part of the same context.

  1. Define each tool as `{name, description, parameters}` where `parameters` is a JSON schema with types and which fields are required.
  2. Pass the tool list with the prompt; the model returns either a tool call or a final message.
  3. Validate the returned arguments against the schema before executing — reject and re-ask on a mismatch.
  4. Run the tool, serialize the result compactly, and feed it back as the next observation.

Where do tools come from?

Two complementary answers matter at the research level. The first is learning to call tools at all: Toolformer showed a model can teach itself, in a self-supervised way, where to insert API calls — by sampling candidate calls in its own training text, executing them, and keeping only the insertions that reduce the loss on the following tokens. No human-labeled tool demonstrations required; the usefulness of a call is its own supervision signal.

The second answer is standardizing the wiring. Every agent re-implementing its own glue to every data source is the integration nightmare of the 2000s all over again. The Model Context Protocol (MCP) is an open protocol that standardizes how a model-side client discovers and calls server-exposed tools, resources, and prompts. The win is composability: write a tool server once, and any MCP-speaking agent can use it without bespoke code. Function calling decides how a single action is expressed; MCP decides how the catalog of actions is delivered to the agent.

Failure modes and the control loop

An agent loop is a program with a stochastic instruction pointer, so it fails in ways ordinary code does not. It will invent tools that do not exist, loop forever re-issuing the same failing call, or declare victory prematurely. The control loop around the model — not the model itself — is where you make the system dependable: cap the step count, detect repeated identical actions, surface tool errors as observations so the model can recover, and require a final-answer schema so 'done' is an explicit, parseable event.

(\tau_t, a_t)\sim p_\theta(\cdot \mid h_t),\qquad h_{t+1}=h_t \,\Vert\,(\tau_t, a_t, o_t)

Formally the loop is a recursion: a thought and action are sampled from the history, which then grows by the observed triple.