PROMETHEUS OS
NATIVE AI OPERATING SYSTEM
[INIT] Prometheus OS loading...
[CORE] Neural interface initializing...
[AI] LLM router connecting...
[MEM] Memory graph loading...
[AGT] Agent chains on standby...
[SEC] Security protocols active...
[NET] Provider mesh established...
[UI] Workspace rendering...
[DONE] All systems nominal.
Welcome to PROMETHEUS OS.
READY · OFFLINE · SECURE · PRIVATE
🎨 THEME ENGINE
PROMETHEUS OS · 23 THEMES
Trial version: Dark + Light themes only.
Full theme access with PROMETHEUS OS license.
Dark theme active
Experience all 23 themes in PROMETHEUS OS

AI Agents Explained

Advanced 14 min· 4 exercises

What an AI agent actually is, how planning, reflection and multi-agent designs work, where agents fail, and how human approvals and scheduled runs keep them useful and safe.

By the end of this lesson you will be able to
  • 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 typeExampleTypical policy
Read-onlySearch, read a fileRun automatically
Reversible writeCreate a draft, edit a local copyAuto, with logging
External or irreversibleSend email, pay, delete, publishRequire 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 gg, the action at step tt as ata_t (a tool call or a final answer) and the observation that comes back as oto_t. The history is ht=(g,a1,o1,…,at−1,ot−1)h_t = (g, a_1, o_1, \dots, a_{t-1}, o_{t-1}), and the language model acts as a stochastic policy

at∼πθ(a∣ht),a_t \sim \pi_\theta(a \mid h_t),

where θ\theta are the model's fixed weights. Nothing in the weights changes while the agent runs; everything the agent "learns" during a task lives in hth_t 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 ii succeeds with probability pip_i and failures are independent and unrecoverable, a task of nn steps succeeds with probability

P(success)=∏i=1npi,which is pn when all steps are alike.P(\text{success}) = \prod_{i=1}^{n} p_i, \qquad \text{which is } p^n \text{ when all steps are alike.}

With p=0.9p = 0.9 and n=10n = 10, that is 0.910≈0.350.9^{10} \approx 0.35. 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 1−(1−p)2=0.991 - (1 - p)^2 = 0.99, and the 10-step task succeeds with probability 0.9910≈0.900.99^{10} \approx 0.90. 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 pp, the chance that kk independent runs all succeed is pkp^k. An agent that passes a task 80% of the time passes it five times in a row only 0.85≈33%0.8^5 \approx 33\% 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 C0C_0 and dd tokens added per step, the total input over NN steps is

Tin=NC0+d N(N−1)2,T_{\text{in}} = N C_0 + d \,\frac{N(N-1)}{2},

which grows with N2N^2. The worked example earlier (C0=4,000C_0 = 4{,}000, d=2,000d = 2{,}000, N=15N = 15) gave 270,000 tokens. Context compaction (summarizing or dropping old tool results once the context reaches a budget Cmax⁡C_{\max}) caps each step at Cmax⁡C_{\max}, making the total at most NCmax⁡N C_{\max}, linear in NN. With Cmax⁡=12,000C_{\max} = 12{,}000, the steps cost 4,000, 6,000, 8,000, 10,000 and then 12,000 for the remaining 11 steps: 28,000+132,000=160,00028{,}000 + 132{,}000 = 160{,}000 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. 1.

    Each step of an agent succeeds 90% of the time independently. Roughly how often does a 10-step task fully succeed?

  2. 2.

    Which task most clearly calls for an agent rather than a fixed workflow?

  3. 3.

    An email-reading agent can also send email. What is the strongest protection against prompt injection?

  4. 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. 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.

  1. 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) 0.9520≈0.3580.95^{20} \approx 0.358.

    (b) A step fails only if all three tries fail: 1−0.053=0.9998751 - 0.05^3 = 0.999875. Over 20 steps: 0.99987520≈0.99750.999875^{20} \approx 0.9975.

    (c) Solve 0.95n≥0.50.95^n \ge 0.5: n≤ln⁡0.5/ln⁡0.95≈0.6931/0.0513≈13.5n \le \ln 0.5 / \ln 0.95 \approx 0.6931 / 0.0513 \approx 13.5, 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.)

  2. 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) NC0+d N(N−1)/2=12×6,000+3,000×66=72,000+198,000=N C_0 + d\,N(N-1)/2 = 12 \times 6{,}000 + 3{,}000 \times 66 = 72{,}000 + 198{,}000 = 270,000 tokens.

    (b) Per-step context: 6,000, 9,000, 12,000, 15,000, then 15,000 for the remaining 8 steps. Sum: 42,000+120,000=42{,}000 + 120{,}000 = 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.

  3. Exercise 3Core

    Extend run_agent with (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 calling run_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 times
    

    json.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).

  4. 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.

    ToolPermissionUsed by
    read_invoice_pdfReadWorkflow
    search_purchase_ordersReadWorkflow, agent
    read_vendor_historyReadAgent
    create_mismatch_ticketReversible writeWorkflow, agent
    schedule_paymentIrreversible, financialWorkflow 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_payment takes 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

Go further

Sources

Fact-checked and last reviewed 2026-11-05

Stay in the loop
Prometheus OS™ · Native AI OS · Built for the future © 2025 - 2026