LLM Agents

Imagine asking a system to "book me the cheapest flight to Tokyo next month under $900, then email me the itinerary." A plain language model can only produce text describing what it would do. An LLM agent actually does it. It searches flights, reads the results, and reasons about which ones satisfy the constraint. It calls a booking API, checks for errors, and drafts the email. This happens over many steps, using real tools, adjusting its plan as new information arrives. This is the shift from language models as text predictors to language models as decision-making systems embedded in a loop with the world. This shift matters because most valuable tasks are not single-turn question answering. They are multi-step, involve uncertainty, and require interacting with external systems (databases, browsers, code interpreters, other software). They also demand recovery from partial failure. A model that only completes text cannot do any of this on its own. It needs a scaffold that turns its outputs into actions, and feeds back the consequences of those actions. This scaffold must let it iterate until a goal is reached or a budget is exhausted. That scaffold — the agent loop — is the subject of this article. The big picture is that an LLM agent is not a new kind of neural network. It is an architecture around a language model: a control loop, a set of tools, a memory mechanism, and a stopping criterion. All of these coordinate a policy that happens to be implemented by next-token prediction. The intelligence is borrowed from the underlying model — including its capacity for Chain-of-Thought Reasoning and its ability to invoke external functions via Tool Use & Function Calling. But the agency (the loop, the planning discipline, the error recovery) is a systems-design problem layered on top. This article gives you the canonical agent loop (ReAct-style interleaving of reasoning and acting), and a precise pseudocode implementation. It includes a worked multi-step trace with a real tool-failure and recovery. It also gives the complexity and correctness argument for why bounded loops with verifiable actions can succeed reliably. Finally, it covers the common failure modes — hallucinated tool calls, infinite loops, compounding errors — that every agent builder must guard against.

Key takeaways
  • An LLM agent interleaves reasoning, acting, and observing in a loop until a goal is reached or a budget is exhausted.
  • The core pattern (ReAct) alternates a "Thought" (reasoning trace) with an "Action" (tool call) and an "Observation" (tool result).
  • Agents need explicit state: a scratchpad/history, working memory, and sometimes long-term memory retrieved via embeddings.
  • Correctness is not guaranteed by architecture alone — it depends on tool reliability, verifiable observations, and bounded iteration.
  • Failure modes are systemic: hallucinated actions, error compounding across steps, and runaway loops without a stopping condition.
  • Agents power real systems — coding assistants, browser automation, customer-support bots, and multi-agent research pipelines.
The core insight

An LLM agent turns a language model into a policy inside a perceive-think-act loop, where each action's real-world observation becomes the next input, so multi-step goals are achieved through iterated, grounded reasoning rather than a single forward pass.

The idea

An LLM agent is a system in which a large language model repeatedly produces an action, receives an observation from executing that action in an environment, and updates its internal reasoning state, continuing until it emits a final answer or exhausts a budget. Formally, we can view this as a partially observable sequential decision process: at each timestep tt the agent holds a context ctc_t (the running transcript of thoughts, actions, and observations), selects an action at∼πθ(⋅∣ct)a_t \sim \pi_\theta(\cdot \mid c_t) where πθ\pi_\theta is the LLM's next-token distribution constrained to a structured action format, receives an observation ot=Env(at)o_t = \mathrm{Env}(a_t), and forms ct+1=ct∥at∥otc_{t+1} = c_t \Vert a_t \Vert o_t. This is a Markov decision process over an unbounded context, with the crucial twist that the "policy" is a pretrained language model steered by prompting rather than a network trained end-to-end by reinforcement learning (though RL fine-tuning of agents is an active and increasingly common variant). The canonical instantiation is ReAct (Reason + Act): at each step the model is prompted to produce a Thought (free-form chain-of-thought reasoning about what to do next), an Action (a structured tool call, e.g. `search("flights NYC to Tokyo")`), and then the harness supplies an Observation (the tool's return value) before the next Thought. Interleaving reasoning with acting lets the model use intermediate results to correct its plan — a search may return nothing useful, prompting a reformulated query — something a pure chain-of-thought trace without tool grounding cannot do, because it never touches ground truth. Three components distinguish an agent from a plain prompted model. First, tools: external functions (search, calculator, code execution, database queries, browser actions) that the model invokes via structured Tool Use & Function Calling syntax, extending its effective knowledge and capability beyond its parameters. Second, memory: short-term memory is simply the growing transcript in context; long-term memory may be an external store (a vector database) that the agent reads from and writes to across sessions, since context windows are finite. Third, a control loop and stopping criterion: a program that parses the model's output, dispatches the action, appends the observation, and decides when to terminate — via an explicit "Final Answer" token, a step budget, a success check, or a supervisor's veto.

How it works

The agent loop is a straightforward program wrapped around repeated LLM calls, but its correctness depends on careful handling of parsing, error propagation, and termination. Below is a complete, language-agnostic implementation of the ReAct-style loop, including the parts most toy descriptions skip: malformed-action recovery, tool-error handling, and a hard step budget to guarantee termination. ``` function RUN_AGENT(goal, tools, model, max_steps, max_tokens_budget): history = [] # list of (thought, action, observation) tuples step = 0 tokens_used = 0 prompt_prefix = build_system_prompt(goal, tools) while step < max_steps and tokens_used < max_tokens_budget: step += 1

# 1. Assemble context: system prompt + goal + full transcript so far context = prompt_prefix + render(history)

# 2. Query the model for the next reasoning + action raw_output, n_tokens = model.generate(context) tokens_used += n_tokens

# 3. Parse structured output: expect "Thought: ... \n Action: name(args) \n" parsed = parse_action(raw_output) if parsed is PARSE_FAILURE:

# Model produced malformed action syntax — do not crash, correct it observation = "ERROR: could not parse action. Use format Action: tool(args)." history.append((raw_output, None, observation)) continue thought, action_name, action_args = parsed

# 4. Check for termination action if action_name == "FINISH": return SUCCESS(final_answer = action_args, trace = history)

# 5. Validate the tool exists if action_name not in tools: observation = f"ERROR: unknown tool '{action_name}'. Available: {tool_names(tools)}" history.append((thought, action_name, observation)) continue

# 6. Execute the tool, catching runtime failures try: observation = tools[action_name].execute(action_args) except ToolExecutionError as e: observation = f"ERROR: tool failed with: {e}"

# 7. Append this step to the transcript (the agent's growing state) history.append((thought, action_name, observation))

# Budget exhausted without an explicit finish — fail gracefully return TIMEOUT(partial_trace = history) ``` ``` function parse_action(raw_output):

# Extract "Thought:" and "Action:" segments with a strict grammar;

# tolerate whitespace variance but reject anything that doesn't match. match = regex_match(raw_output, THOUGHT_ACTION_GRAMMAR) if match is None: return PARSE_FAILURE return (match.thought, match.action_name, match.action_args) ``` Three design decisions in this pseudocode are load-bearing, not stylistic.

(a) Malformed output never crashes the loop. Because the model's output is free text constrained only by prompting, it will occasionally violate the expected grammar. The loop treats a parse failure as just another observation — an error message fed back in — rather than an exception that halts the program. This mirrors how a compiler reports a syntax error rather than segfaulting.

(b) Every branch converges to `history.append(...)` or a return. This guarantees the loop body always makes forward progress (either by appending a new transcript entry, which will change the next prompt and therefore the next model output, or by returning). Without this invariant, an unhandled branch could spin the model on an identical context, wasting the entire step budget on a repeated request.

(c) Two independent budgets — `max_steps` and `max_tokens_budget` — enforce termination. An LLM agent has no built-in halting proof; nothing in the mathematics of a transformer bounds how long a badly-prompted agent will call tools. Termination is therefore engineered, not derived, and both a step count and a token count are checked because a single verbose step could otherwise exhaust context length before exhausting the step counter.

Figure
The agent loop: reasoning, acting, and observing interleaved over time
LLM π(action | context) Context Thought₁ Action₁ Obs₁ Thought₂ Action₂ Obs₂ … Tool search / code / API Environment web, DB, filesystem Final Answer stop condition met action a_t dispatch observation o_t call result FINISH(x)

Complexity & correctness

Per-step cost. Each iteration issues one LLM call whose cost scales with the length of the context, which grows by roughly one thought+action+observation chunk per step. If a chunk contributes on average kk tokens and the transformer's forward pass is O(n2)O(n^2) in sequence length nn (for the standard dense self-attention; sub-quadratic and cached-KV implementations reduce the marginal per-token cost but not the asymptotic footprint of very long transcripts), then after tt steps the context has length roughly n0+tkn_0 + tk, and the tt-th step costs O((n0+tk)2)O((n_0 + tk)^2) with a naive re-encoding, or O((n0+tk)⋅k)O((n_0+tk) \cdot k) per step if key-value caching lets the model only encode the new suffix and reuse cached attention keys/values from prior steps — which is standard practice.

Total cost over TT steps. Summing the incremental (KV-cached) per-step cost gives

∑t=1TO((n0+tk)⋅k)=O(Tn0k+T2k2),\sum_{t=1}^{T} O\big((n_0 + tk)\cdot k\big) = O\big(T n_0 k + T^2 k^2\big),

which is quadratic in the number of steps TT — the same asymptotic shape as processing one long sequence of length TkTk, because the transcript is effectively one growing sequence. This is the fundamental reason agent frameworks impose step budgets and summarization/compaction of old history: without bounding TT or periodically compressing `history` into a shorter summary, cost grows quadratically and eventually the context exceeds the model's window, hard-capping feasible episode length regardless of budget.

Space. The context held in memory is O(n0+Tk)O(n_0 + Tk) tokens; if a long-term memory store is used, retrieval adds O(log⁡m)O(\log m) per lookup for an indexed store of mm memories (e.g. approximate nearest-neighbor search over embeddings), amortizing the cost of unbounded history by only pulling in the relevant slice at each step.

Correctness — what can and cannot be guaranteed. There is no proof that an LLM agent reaches a correct final answer, because the "policy" πθ\pi_\theta is not verified against a formal specification — it is a learned distribution over plausible next actions. What can be argued rigorously is a conditional termination guarantee and a conditional progress argument: 1. Termination is guaranteed by construction, not by the model. Because the loop enforces `step < max_steps` and `tokens_used < max_tokens_budget` as loop invariants checked every iteration, and every code path inside the loop body either returns or executes exactly one iteration of `step += 1`, the loop halts within max⁡_steps\max\_steps iterations. This is a simple structural invariant, analogous to bounding recursion depth to prevent stack overflow — it is a property of the harness, not of the language model's reasoning quality. 2. Progress toward the goal is only as reliable as the observations are informative and truthful. If tool outputs are grounded (e.g., a search API genuinely returns relevant documents, a calculator genuinely computes the requested arithmetic), each observation reduces the agent's uncertainty about the world state, analogous to how new evidence updates a Bayesian belief — the agent's next Thought conditions on strictly more grounded information than the last. This is why ReAct-style grounding empirically outperforms pure chain-of-thought on knowledge-intensive tasks: chain-of-thought's intermediate "facts" are unverified model outputs that can drift from truth over many steps (a compounding hallucination problem), while tool observations are anchored to an external, checkable source. 3. Compounding error is the dual risk. If step tt's observation is wrong or misparsed and the agent does not detect this, that error becomes an unchallenged premise in the context for all subsequent steps — an analogue of error propagation in iterative numerical methods without a correction step. Because nothing in the transformer architecture "notices" that Thought5_5 contradicts Observation2_2 unless the model happens to attend to and reconcile it, correctness in practice depends heavily on scaffold-level safeguards: verifiers, self-consistency checks, or a human/automated critic step that can re-open an earlier decision rather than blindly proceeding. The honest summary: the engineering guarantees (bounded steps, no crashes on malformed output, monotone progress through the transcript) are provable from the pseudocode's structure. The semantic guarantee (the agent actually accomplishes the goal) is empirical and probabilistic, resting on the quality of the underlying model, the fidelity of the tools, and the presence of verification steps — not on any theorem.

Worked example

Goal: "Find the population of the capital of the country whose flag is the Vietnamese flag's mirror image color-wise, then tell me if that population exceeds 10 million." (Deliberately convoluted, to force genuine multi-step tool use and a recoverable error.)

StepThought (abbreviated)ActionObservation
1"I need to know Vietnam's flag colors first."`search("Vietnam flag colors")`"Red field with a yellow star."
2"Mirror image color-wise likely means red↔yellow swapped roughly, i.e. yellow field with red symbol — that resembles Japan's flag conceptually, but let me search directly instead of guessing."`search("flag yellow field red symbol country")`"ERROR: query too vague, 0 relevant results."
3"Search failed; reformulate more specifically using known vexillology terms."`search("flag color inverse of Vietnam flag")`"Discussions suggest no exact canonical 'inverse' flag; closest common answer given in trivia contexts is Japan (red circle, white field, treated loosely as color-swap of red-dominant Vietnam flag)."
4"Proceeding with Japan as the intended answer; need capital of Japan."`search("capital of Japan")`"Tokyo."
5"Need Tokyo's population."`search("Tokyo population 2023")`"Approximately 14 million (city proper), ~37 million (metro area)."
6"14 million > 10 million, so answer is yes, using the city-proper figure conservatively."`FINISH`—

Final answer: "Under the interpretation that the color-inverted flag points to Japan, the capital is Tokyo, with a city population of roughly 14 million — yes, this exceeds 10 million." This trace exercises exactly the properties argued above. Step 2 is a tool failure (`ERROR: 0 relevant results`) that the loop does not crash on — it becomes an observation, and the agent's Step-3 Thought explicitly reacts to it by reformulating the query, which is the grounded-correction behavior that distinguishes ReAct from a single unverified chain-of-thought guess. Step 3's observation is itself soft/uncertain ("closest common answer... treated loosely") and the agent's Step 4 Thought explicitly flags the assumption rather than silently asserting it as fact — good agent behavior surfaces its own uncertainty in the transcript so a downstream reader (or a supervising verifier) can audit the chain. Note also that the transcript length grew by roughly one (Thought, Action, Observation) triple per row, illustrating the linear growth in nn that underlies the quadratic total-cost bound derived above.

Common pitfalls & traps

PitfallWhy it happensConsequenceMitigation
Hallucinated tool callModel emits an action for a tool that does not exist or with malformed argumentsLoop wastes a step, or worse, silently no-opsStrict grammar validation + explicit `ERROR: unknown tool` observation fed back
Unbounded loopNo step/token budget, or the model repeats the same failing actionRunaway cost, never terminatesHard `max_steps`/`max_tokens_budget`, plus loop-detection on repeated (action, args) pairs
Compounding hallucinationAn early wrong "fact" is never re-verified, later steps build on itConfidently wrong final answerPeriodic self-consistency checks; require citation of the observation that supports each claim
Context overflowFull transcript kept verbatim exceeds context windowTruncation drops early crucial context, or hard failureSummarize/compact old steps; retrieve from external memory instead of keeping everything in-context
Silent tool failure treated as successTool returns an empty/degenerate result that parses without an explicit errorAgent proceeds on missing informationTools should return explicit status codes, not just empty strings, on failure
Over-trusting single-pass plansAgent commits to a rigid multi-step plan upfront and never revises itBrittle to any single wrong intermediate resultInterleave planning and acting (ReAct-style) rather than plan-then-execute blindly

Pioneers & history

Origins · 1 minYuchen Zhu et al. (ReAct authors) and the broader agentic-LLM research community and how this was discovered
YZ
Yuchen Zhu et al. (ReAct authors) and the broader agentic-LLM research community
2022–present · Princeton/Google Research and the wave of tool-augmented LLM work that followed GPT-3-era prompting

The ReAct paper (Yao, Zhao, Yu, Du, Shafran, Narasimhan, Cao — Princeton and Google Research, 2022) formalized the interleaving of chain-of-thought reasoning with discrete tool actions and demonstrated it reduced hallucination and improved task success versus reasoning-only or acting-only baselines, on knowledge-intensive QA and interactive decision-making benchmarks. This built directly on earlier chain-of-thought prompting work and the emerging practice of exposing structured function-calling interfaces to LLMs, and it catalyzed a rapid proliferation of agent frameworks (AutoGPT-style autonomous loops, tool-use libraries, and multi-agent orchestration systems) through 2023–2024.

History
1950
— Turing proposes the imitation game, framing machine intelligence as behavioral, goal-directed interaction rather than static computation.
2017
— the Transformer architecture (Vaswani et al.) supplies the sequence model that later becomes the reasoning engine inside agent loops.
2022
— ReAct formalizes interleaved reasoning-and-acting, and chain-of-thought prompting (Wei et al.) shows explicit reasoning traces improve multi-step task performance.
2023
— Tool-use and function-calling interfaces (plugins, structured JSON tool schemas) become standard, and autonomous-loop frameworks (e.g. AutoGPT-style systems) popularize long-horizon LLM agents.
2024
–present — RL-fine-tuned and multi-agent orchestration systems mature, alongside growing emphasis on agent evaluation, sandboxing, and safety oversight for autonomous tool use.

Where this matters

LLM agents underlie coding assistants that read a repository, run tests, interpret failures, and iteratively patch code — a loop structurally identical to the ReAct pattern, with a test suite serving as the grounding "environment." Browser-automation agents navigate web pages, click elements, and extract information to complete tasks like form-filling or price comparison, treating the DOM as the tool interface. Customer-support and enterprise-workflow agents query internal databases, CRM systems, and ticketing APIs, using the same perceive-think-act loop with domain-specific tools substituted in. Scientific and data-analysis agents call code interpreters and statistical libraries, verifying their own computations against executed code rather than trusting the model's mental arithmetic. Multi-agent systems compose several such loops — a planner agent, a critic agent, an executor agent — communicating through shared transcripts or message-passing, extending the single-loop architecture described here into a small distributed system. Because these agents take real actions with real side effects (sending emails, executing trades, modifying files), the reliability arguments in this article — bounded loops, grounded observations, explicit error handling — are precisely the concerns that motivate the discipline of Safety & Red-Teaming for deployed autonomous systems.

Ask the tutor: How would you redesign the agent loop to support a supervising "critic" model that can veto or revise actions before they are executed?

Is an LLM agent just chain-of-thought prompting with extra steps?

No — chain-of-thought is reasoning entirely inside one model call, with no way to verify intermediate claims against the real world. An LLM agent's Observations are grounded: they come from executing real tool calls (search, code, APIs), so incorrect intermediate beliefs can be caught and corrected using external evidence rather than the model's own unverified generation. This grounding is exactly what the ReAct pattern adds on top of plain chain-of-thought, and it is why agents tend to outperform reasoning-only prompting on tasks requiring up-to-date or verifiable facts.

Does an LLM agent require a specially trained or fine-tuned model?

Not necessarily — many agents work by prompting an off-the-shelf instruction-tuned model to follow the Thought/Action/Observation format, using few-shot examples in the system prompt. However, fine-tuning (including reinforcement learning on agent trajectories) can substantially improve reliability, especially for producing well-formed tool calls and recovering gracefully from errors, because prompting alone gives no guarantee the model will consistently follow the required format under distribution shift.

How is agent "memory" different from the context window?

The context window (transcript of Thoughts/Actions/Observations) is short-term memory, bounded by the model's maximum sequence length. Long-term memory is an external store — typically a vector database indexed by embeddings — that the agent can write summaries or facts to and later retrieve relevant entries from via nearest-neighbor search, letting the agent operate across sessions or tasks whose full history would never fit in-context. Retrieval-augmented memory trades exhaustive recall for scalability, at the cost of possibly missing a relevant memory that the similarity search ranks too low.

Can an LLM agent get stuck in an infinite loop, and how is that prevented?

Yes — nothing in the transformer's mathematics prevents the model from repeatedly choosing the same failing action, especially if error messages are uninformative or the model's confidence in a wrong sub-goal doesn't decrease. Prevention is a harness-level responsibility: hard caps on step count and token budget guarantee termination structurally, and additional loop-detection logic (flagging repeated identical actions) can trigger an early intervention, such as forcing a re-plan or escalating to a human.

Why do agents need explicit error handling for tools instead of assuming tools always succeed?

Real tools fail constantly in practice — networks time out, APIs rate-limit, search queries return nothing, parsers choke on malformed input. If a tool failure is silently treated as a normal (empty or degenerate) result rather than surfaced as an explicit error, the agent has no signal to reformulate its approach, and it will often proceed confidently on missing or wrong information. Explicit, structured error observations (as in the pseudocode's `try/except` around tool execution) give the model the chance to notice failure and adapt, which is precisely the self-correcting behavior that distinguishes robust agents from brittle ones.

Is agent behavior deterministic given the same goal and tools?

No, in general — the underlying LLM samples from a probability distribution over tokens (even at low temperature, ties and near-ties can break differently across runs, and any external tool like a live search API may itself return different results at different times). This non-determinism is one reason agent evaluation typically reports success rates over many trials rather than a single pass/fail, and why safety-critical agent deployments often add deterministic guardrails (whitelisted actions, confirmation steps) around the inherently stochastic core.

How does an LLM agent relate to reinforcement learning?

The base loop described here (prompted ReAct-style acting) requires no RL — it is pure prompting plus a control harness. However, agent behavior can also be improved by treating the sequence of (context, action) pairs as trajectories and applying reinforcement learning (e.g., using task success or human/automated feedback as reward) to fine-tune the model's action-selection policy directly, analogous to RLHF for text generation. This blurs the line between "prompted agent" and "trained policy," but the control-loop architecture — perceive, think, act, observe — remains the same regardless of how the underlying policy was produced.

Before this

Where this leads