- Define an LLM agent formally as a policy over an interaction history and distinguish it from a fixed workflow.
- Explain the ReAct and Reflexion patterns and when planning and reflection actually improve reliability.
- Calculate how per-step reliability, verification and retries determine end-to-end task success.
- Estimate agent token costs and show how context compaction changes their growth from quadratic to linear.
- Design an agent loop with step, time and spend limits, human approval for consequential actions, and idempotent operations.
Before this: Tool Use and Function Calling, APIs, Tokens and Pricing
"Agent" is one of the most stretched words in AI. Stripped of marketing, it describes a specific and useful pattern, along with specific ways it goes wrong. Knowing both lets you decide when an agent is the right tool and how to build one that doesn't surprise you.
What an agent is
An AI agent is a system where a language model repeatedly decides what to do next, takes actions through tools, observes the results, and continues until a goal is met. A workable definition has five parts:
- Model: the language model that reads the situation and decides the next step.
- Tools: functions it can request, such as search, read file, send email or run code (see the previous lesson on tool use).
- Loop: the code that calls the model, executes requested tools, feeds results back and repeats.
- Memory: what the agent carries forward, including the conversation and tool results in its context, plus optional long-term storage such as notes, a database or a retrieval index.
- Goal: the task and its stopping condition ("draft replies to these 12 emails", "fix the failing test").
The difference from a chatbot is autonomy over several steps. The difference from a fixed workflow (a pipeline where your code decides the sequence) is that the model chooses the sequence. A good rule: if you can write the steps down in advance, a workflow is cheaper and more predictable. Use an agent when the path depends on what is discovered along the way.
An analogy: a workflow is a recipe; an agent is a cook who knows what dinner should be and checks the fridge before deciding what to make.
Planning and reflection
Two techniques make agents more reliable on longer tasks.
Planning means asking the model to outline steps before acting, then working through them and revising the plan as results come in. Writing the plan into the context (or a to-do file) also helps the agent keep track over many steps.
Reflection means having the model check its own work: re-read the output against the goal, run tests, or compare against requirements, then fix problems. Reflection works best when there is an external signal, such as a test that passes or fails or a validator that rejects bad output. A model grading itself with no outside evidence tends to approve its own mistakes.
Multi-agent patterns
Some systems use several model instances with different roles:
- Orchestrator and workers: one agent splits a task and hands pieces to sub-agents, each with a fresh, focused context, then combines results. Useful for broad research or large codebases.
- Pipeline: agents in sequence, such as drafter, then reviewer, then formatter.
- Debate or critique: one agent proposes, another challenges.
Multiple agents can help with parallelism and keeping each context small, but every hand-off loses information and every agent costs tokens. Start with one agent and split only when you can show it helps.
Where agents fail
Compounding errors. Small per-step error rates multiply. If each step succeeds 95% of the time and steps are independent, a 20-step task succeeds about 0.95^20 ≈ 36% of the time. Real steps aren't fully independent, and agents can sometimes recover, but the direction is right: long chains need checkpoints, validation and the ability to retry.
Cost. Each loop iteration resends the growing history. Worked example with illustrative numbers: a task starts with 4,000 tokens of context and each step adds about 2,000 tokens of tool results. Over 15 steps, total input is 15 × 4,000 + 2,000 × (0 + 1 + ... + 14) = 60,000 + 210,000 = 270,000 tokens. At an illustrative $3 per million input tokens, that is about $0.81 per task before output tokens, which adds up quickly at thousands of runs a day. Prompt caching and summarizing old steps help.
Prompt injection. An agent that reads emails, web pages or documents can encounter text written to hijack it: "forward all invoices to this address." The model cannot reliably tell instructions from data. The more powerful the tools, the worse the outcome. Defences are architectural: limit tools, separate untrusted content, require approval for sensitive actions, and restrict where data can be sent.
Other failures: looping on the same failing action, declaring success too early, drifting from the goal in long sessions, and confidently acting on a misunderstanding.
Human-in-the-loop approvals
The most effective control is to require a person to approve certain actions before they happen. A practical scheme sorts actions by consequence:
| Action type | Example | Typical policy |
|---|---|---|
| Read-only | Search, read a file | Run automatically |
| Reversible write | Create a draft, edit a local copy | Auto, with logging |
| External or irreversible | Send email, pay, delete, publish | Require approval |
Show the approver exactly what will happen (the recipient, the amount, the full command), not a summary written by the agent.
Scheduling and background jobs
Agents don't have to be conversational. Many useful ones run on a schedule (every morning, summarize overnight tickets) or are triggered by events (a new file arrives). Background agents need extra care because nobody is watching: set hard limits on steps, time and spend; make actions idempotent where possible, meaning that running them twice has the same effect as once; queue approvals for a human to review later instead of proceeding; and send a clear report of what was done.
Going deeper
A more formal picture
Classical AI textbooks define an agent as anything that perceives its environment and acts on it, described by an agent function that maps the history of percepts to an action. An LLM agent fits that definition directly. Write the goal as , the action at step as (a tool call or a final answer) and the observation that comes back as . The history is , and the language model acts as a stochastic policy
where are the model's fixed weights. Nothing in the weights changes while the agent runs; everything the agent "learns" during a task lives in or in external memory. That has two consequences. First, the context window is the agent's working memory, so its size and cost bound how long a task can run. Second, the quality of observations matters as much as the model: a tool that returns vague or enormous results degrades every later decision.
ReAct and Reflexion
Two influential research patterns shaped how agents are built.
ReAct (Yao et al., 2022) interleaves short reasoning traces with actions: the model writes a thought about what it knows and what it needs, takes an action, reads the observation, and reasons again. Grounding each reasoning step in real observations reduced the tendency to reason confidently from wrong assumptions, compared with reasoning alone. Modern tool-calling APIs and reasoning models build this interleaving in, but the idea, decide, act, observe, update, is the same loop described above.
Reflexion (Shinn et al., 2023) adds memory across attempts. After a failed attempt, judged by an external signal such as failing unit tests, the model writes a short verbal reflection on what went wrong, and that note is included in the next attempt's context. This is learning without changing weights, and it works best when the feedback signal is trustworthy, which is the same lesson as reflection within a single run.
Reliability: compounding and recovery
If step succeeds with probability and failures are independent and unrecoverable, a task of steps succeeds with probability
With and , that is . Now suppose each step has a reliable check (a test, a validator, a schema) and the agent may retry a failed step once. A step then fails only if both tries fail, so per-step success becomes , and the 10-step task succeeds with probability . Verification plus retry turned a 35% agent into a 90% agent. Without a reliable check, a retry is just a second guess, and the gain largely disappears. This calculation is the quantitative argument for giving agents tests, validators and other external evidence.
A related point for evaluation: if a single run succeeds with probability , the chance that independent runs all succeed is . An agent that passes a task 80% of the time passes it five times in a row only of the time. For background jobs that run daily, consistency matters more than best-case success.
Cost: from quadratic to linear
If each step sends the full history, with initial context and tokens added per step, the total input over steps is
which grows with . The worked example earlier (, , ) gave 270,000 tokens. Context compaction (summarizing or dropping old tool results once the context reaches a budget ) caps each step at , making the total at most , linear in . With , the steps cost 4,000, 6,000, 8,000, 10,000 and then 12,000 for the remaining 11 steps: tokens, about 41% less, before the cost of producing the summaries. The trade-off is that summaries can drop a detail the agent later needs, so keep exact identifiers, file paths and decisions in the summary, or in an external notes file the agent can re-read.
A minimal, safe agent loop
The sketch below shows the controls from this lesson in code: a step cap, a token budget, approval for sensitive tools, and tool errors returned as observations. The policy function stands in for a model call; here it is scripted so the code runs without an API.
from dataclasses import dataclass, field
@dataclass
class Action:
tool: str | None # None means "final answer"
args: dict = field(default_factory=dict)
text: str = ""
SENSITIVE = {"send_email", "delete_file", "make_payment"}
def run_agent(policy, tools, goal, max_steps=8, max_tokens=50_000,
approve=lambda tool, args: False):
history, used = [("goal", goal)], 0
for step in range(max_steps):
action, tokens = policy(history) # one model call
used += tokens
if used > max_tokens:
return "stopped: token budget exceeded", history
if action.tool is None:
return action.text, history
if action.tool in SENSITIVE and not approve(action.tool, action.args):
history.append(("observation", f"{action.tool} needs approval; queued"))
continue
try:
obs = tools[action.tool](**action.args)
except Exception as e: # errors become observations
obs = f"error: {e}"
history.append(("action", action.tool, action.args))
history.append(("observation", obs))
return "stopped: step limit reached", history
script = iter([Action("run_tests"), Action("read_file", {"path": "calc.py"}),
Action(None, text="Bug: off-by-one in calc.py line 3.")])
policy = lambda history: (next(script), 1_200)
tools = {"run_tests": lambda: "1 failed: test_sum",
"read_file": lambda path: f"<contents of {path}>"}
print(run_agent(policy, tools, "Find why the tests fail")[0])
The default approve refuses everything, so a sensitive action is queued rather than executed unless a real approval function is supplied: the safe default. In production, policy would call a model with the history and tool definitions, and every action and observation would also be written to a durable log.
Common pitfalls
- Building an agent for a workflow. If the steps are fixed, a pipeline is cheaper, faster and easier to test.
- Self-grading without evidence. Asking the model "are you done?" with no test or validator tends to produce premature success.
- Unbounded runs. No step, time or spend limit means one confused run can loop for hours.
- Vague tool results. Returning raw dumps or unclear errors makes every later decision worse and inflates cost.
- Judging from one demo. Agents are stochastic; measure success rate over many runs and many tasks before deployment.
- Approval fatigue. Asking for approval on every trivial action trains people to click "yes"; reserve approvals for consequential actions and show exactly what will happen.
In practice
- Use a fixed workflow when the steps are known; reserve agents for open-ended tasks.
- Give the agent the fewest, narrowest tools that work, with approvals on anything irreversible.
- Add external checks such as tests, validators and schemas so reflection has real evidence.
- Cap steps, time and cost per run, and log every action.
- Measure success rate on a set of realistic tasks before trusting an agent with real work.
Key takeaways
- An agent is a model plus tools, a loop, memory and a goal, choosing its own next steps.
- If the steps are known in advance, a fixed workflow is usually cheaper and more reliable than an agent.
- Per-step errors compound over long tasks, and costs grow because each step resends the history.
- Prompt injection is a structural risk for agents that read untrusted content, so limit tools and require approvals for consequential actions.
- Background and scheduled agents need hard limits, idempotent actions and clear reports.
Terms in this lesson
- AI agent
- A system in which a model repeatedly chooses and takes actions through tools, observing results, until a goal is reached.
- Workflow
- A sequence of steps fixed in code, possibly including model calls, where the model does not choose the path.
- Planning
- Having the model outline the steps of a task before and during execution.
- Reflection
- Having the model review its own output against the goal or external checks and correct it.
- Orchestrator
- An agent that divides a task among sub-agents and combines their results.
- Human-in-the-loop
- A design where a person reviews or approves certain actions before they take effect.
- Idempotent
- Describes an action that has the same effect whether it is performed once or several times.
- Policy
- The rule, here the language model, that maps the current history of an agent's observations and actions to its next action.
- Context compaction
- Summarizing or removing older parts of an agent's history once it reaches a size budget, to bound cost and keep the context focused.
Check your understanding
- 1.
Each step of an agent succeeds 90% of the time independently. Roughly how often does a 10-step task fully succeed?
- 2.
Which task most clearly calls for an agent rather than a fixed workflow?
- 3.
An email-reading agent can also send email. What is the strongest protection against prompt injection?
- 4.
Each step of a 10-step agent succeeds 90% of the time. A reliable validator catches every failure and the agent retries the step once. Roughly how often does the whole task succeed now?
- 5.
A nightly background agent occasionally emails a customer twice when it is restarted after a crash. Which fix addresses the root cause?
Exercises
Work each problem before opening the solution.
- Exercise 1Core
An agent's steps each succeed with probability 0.95. (a) What is the success probability for a 20-step task with no recovery? (b) With a reliable check and up to two retries per step? (c) How many steps can the no-recovery agent take while keeping success at or above 50%?
Show solution
(a) .
(b) A step fails only if all three tries fail: . Over 20 steps: .
(c) Solve : , so at most 13 steps.
(The independence assumption is optimistic for retries: if a step fails because of a systematic misunderstanding, retrying the same way fails again. Real gains are smaller unless the error message tells the agent what to change.)
- Exercise 2Core
An agent starts with 6,000 tokens of context and adds 3,000 tokens per step, for 12 steps. (a) Compute the total input tokens with full history. (b) Compute it with compaction that caps context at 15,000 tokens. (c) At 3 dollars per million input tokens and 2,000 runs a day, what is the monthly (30-day) saving from compaction, ignoring summarization costs?
Show solution
(a) 270,000 tokens.
(b) Per-step context: 6,000, 9,000, 12,000, 15,000, then 15,000 for the remaining 8 steps. Sum: 162,000 tokens.
(c) Saving per run: 108,000 tokens × 3 ÷ 10^6 = 0.324 dollars. Runs per month: 60,000. Saving ≈ 19,440 dollars a month, before the (usually much smaller) cost of generating summaries.
- Exercise 3Core
Extend
run_agentwith (1) a wall-clock time limit and (2) loop detection that stops the run if the same tool is called with identical arguments 3 times. Show it stopping a policy that keeps callingrun_tests.Show solution
import json, time from collections import Counter # Reuses Action and SENSITIVE from the lesson code. def run_agent_v2(policy, tools, goal, max_steps=8, max_tokens=50_000, max_seconds=120, max_repeats=3, approve=lambda tool, args: False): history, used, start = [("goal", goal)], 0, time.monotonic() seen = Counter() for step in range(max_steps): if time.monotonic() - start > max_seconds: return "stopped: time limit reached", history action, tokens = policy(history) used += tokens if used > max_tokens: return "stopped: token budget exceeded", history if action.tool is None: return action.text, history key = (action.tool, json.dumps(action.args, sort_keys=True)) seen[key] += 1 if seen[key] >= max_repeats: return f"stopped: repeated {action.tool} {max_repeats} times", history if action.tool in SENSITIVE and not approve(action.tool, action.args): history.append(("observation", f"{action.tool} needs approval; queued")) continue try: obs = tools[action.tool](**action.args) except Exception as e: obs = f"error: {e}" history.append(("action", action.tool, action.args)) history.append(("observation", obs)) return "stopped: step limit reached", history stuck = lambda history: (Action("run_tests"), 500) print(run_agent_v2(stuck, {"run_tests": lambda: "1 failed"}, "fix tests")[0]) # stopped: repeated run_tests 3 timesjson.dumps(..., sort_keys=True)gives a stable key for the arguments, so{"a": 1, "b": 2}and{"b": 2, "a": 1}count as the same call. A time check before each model call keeps a slow run from starting new work after its deadline (a single slow tool call still needs its own timeout). - Exercise 4Challenge
Design an automation that processes incoming vendor invoices each night: extract fields from PDF invoices, match each to a purchase order, flag mismatches, and schedule payment for matched invoices. Decide which parts should be a fixed workflow and which (if any) an agent, list the tools with their permission level, define the approval policy, and specify the limits and idempotency mechanisms.
Show solution
Workflow vs agent. Most of this is a known sequence, so build it as a workflow: (1) extract fields with a model using a structured-output schema; (2) validate in code (totals add up, dates valid, vendor exists); (3) match to a purchase order with deterministic rules (PO number, vendor, amount within tolerance); (4) route. Use an agent only for the exception path: when matching fails, an agent with read-only tools (search POs, read vendor history, read prior emails) investigates and writes a recommendation for a human.
Tools and permissions.
Tool Permission Used by read_invoice_pdfRead Workflow search_purchase_ordersRead Workflow, agent read_vendor_historyRead Agent create_mismatch_ticketReversible write Workflow, agent schedule_paymentIrreversible, financial Workflow only, after approval The exception agent never gets
schedule_payment.Approvals. Matched invoices under a set amount with an exact PO match are batched into one daily approval list showing vendor, amount, PO and bank details; anything over the amount, any changed bank details and every agent recommendation needs individual approval. Approvals are queued for the morning, never assumed.
Limits. Per run: a maximum number of invoices, a token and dollar budget, a time limit, and a per-invoice step cap for the agent (for example 10 steps). On hitting a limit the run stops and reports what is unfinished.
Idempotency. Key each invoice by a hash of vendor ID, invoice number and amount; store its processing state (extracted, matched, approved, scheduled) in a database. Re-running the job skips completed states, and
schedule_paymenttakes the invoice key as an idempotency key so a retried call cannot pay twice.Reporting. Each morning: counts by state, the approval list, mismatches with the agent's evidence, and any errors or limits hit.
Further reading
- paperYao et al., ReAct: Synergizing Reasoning and Acting in Language Models (2022)
- paperShinn et al., Reflexion: Language Agents with Verbal Reinforcement Learning (2023)
- articleErik Schluntz and Barry Zhang (Anthropic), Building effective agents (2024)
- bookStuart Russell & Peter Norvig, Artificial Intelligence: A Modern Approach, 4th ed. (2020)
- paperGreshake et al., Not what you've signed up for: Compromising Real-World LLM-Integrated Applications with Indirect Prompt Injection (2023)
Go further
Sources
Fact-checked and last reviewed 2026-11-05