Skip to content
AI Engineering: Building Production LLM Applications

Agents

Last verified against its sources on 23 September 2026

An agent is what you get when you take the tool-calling loop from module 2 and hand the model control over when to stop calling tools and start answering. This module covers when that trade — more autonomy for more cost and less predictability — is actually worth making, how a long-running agent keeps its context coherent instead of drowning in its own history, and the guardrails and multi-agent patterns that keep an agent's autonomy from becoming a liability.

Agent Loops and Planning

  • Distinguish a workflow (predefined code paths) from an agent (the model directs its own process), and decide which one a task actually needs.
  • Build an agent loop with a clear termination condition, and explain why "ground truth" from the environment matters at each step.

Anthropic draws a specific line between two things people often lump together as "agentic." A workflow is a system where LLMs and tools are orchestrated through code paths you wrote in advance — you decide the order of steps, even if an LLM call happens at each one. An agent is a system where the LLM itself dynamically directs its own process and tool usage, deciding what to do next based on what it's learned so far, with you controlling the goal and the guardrails rather than every branch.

That distinction matters because agents aren't automatically the better choice. Anthropic's own guidance is to find the simplest solution possible and add complexity only when it's actually needed — often that means a single, well-optimized LLM call with retrieval and good examples, or a workflow with a fixed sequence of steps, rather than a full agent loop at all.

The agent loop: plan, act, observe, repeat — until something says stop.

An agent, stripped down, is a loop: the model plans what to do next, calls a tool, and receives feedback from the environment — a tool's return value, an error message, the result of running code — before deciding what to do after that. Anthropic's own description of this loop stresses that gaining "ground truth" from the environment at each step is what lets the agent actually assess its progress, rather than just asserting that it's making progress.

That loop needs an exit. Most of the time the task simply finishes — the model decides it's done and stops calling tools. But because a model can misjudge its own progress, it's common practice to add an explicit stopping condition on top of that, most often a maximum number of iterations, so a confused loop can't run indefinitely. Human checkpoints are the other common brake: pausing for a person's input at a natural decision point, or whenever the agent hits something it can't resolve on its own.

python

def run_agent(user_task, tools, max_iterations=10):
    messages = [{"role": "user", "content": user_task}]

    for step in range(max_iterations):
        response = call_model(messages, tools=tools)
        messages.append(response.message)

        if not response.tool_calls:
            return response.message  # model decided it's done

        for call in response.tool_calls:
            result = execute_tool(call)
            messages.append(tool_result_message(call, result))

    return "Stopped: reached max_iterations without finishing."
The loop from module 2's tool calling, with an explicit stopping condition added.

Whether to reach for an agent at all comes down to predictability. Anthropic's guidance: agents fit open-ended problems where you can't predict the number of steps needed and can't hardcode a fixed path — a coding agent that has to figure out which files to touch is a good example, since that's genuinely different for every task. Workflows fit everything with a knowable shape, because a fixed path is easier to test, debug, and trust.

The trade-off runs in both directions. An agent's autonomy means higher cost (more model calls per task) and higher latency, and it opens the door to compounding errors — a wrong turn early in a long loop can steer everything that follows. Anthropic's own recommendation is to test extensively in sandboxed environments and pair autonomy with the kind of guardrails covered later in this module, rather than granting an agent unrestricted reach on the strength of a few successful runs.

You're automating a task with a fixed, known sequence: fetch a record, validate three fields, send one email. Should you build this as an agent?Answer it yourself first, then open this.

No — this is exactly the kind of fixed, predictable sequence Anthropic's own guidance points toward a workflow for. An agent adds cost, latency, and unpredictability for a task that doesn't need the model to decide its own next step.

Memory and State Across Turns

  • Explain why growing context degrades an agent's performance, and apply compaction to keep a long-running agent coherent.
  • Choose between compaction, structured note-taking, and sub-agent architectures for a given long-horizon task.

Studies on needle-in-a-haystack benchmarks surfaced a pattern Anthropic calls context rot: as the number of tokens in a model's context window grows, its ability to accurately recall information from that context decreases. Every model shows this to some degree — some more gently than others — which means context can't be treated as a free resource you simply keep adding to. It behaves more like a limited attention budget: every new token you put in front of the model draws down that budget by some amount.

The underlying reason is architectural. A transformer lets every token attend to every other token, which creates a relationship for every pair of tokens in the context — a number that grows much faster than the context itself. As a conversation or an agent's history grows, that budget gets stretched thinner across more pairs, and precision on any one piece of information degrades. This is exactly why a long-running agent needs a deliberate strategy for what stays in context and what doesn't, rather than just letting history accumulate.

Anthropic describes three techniques for keeping a long-running agent coherent once a task outgrows a single context window. Compaction takes a conversation nearing its context limit, has the model summarize it, and reinitiates a new context window built from that summary. In Claude Code, this means passing the message history to the model to compress: architectural decisions, unresolved bugs, and implementation details are preserved, while redundant tool outputs and exploratory dead ends are dropped, and the agent continues with the compressed summary plus its five most recently accessed files.

Getting this right takes iteration. Anthropic's own guidance is to first tune a compaction prompt for recall — make sure it captures everything that later turns out to matter — and only then trim for precision by cutting what turned out to be superfluous. Tool result clearing (dropping old tool outputs from history once a fresher result has superseded them) is described as one of the lightest-touch, safest forms of this.

python

def maybe_compact(messages, model, threshold_tokens=150_000):
    if count_tokens(messages) < threshold_tokens:
        return messages

    summary = model.summarize(
        messages,
        instructions="Preserve decisions, unresolved issues, and key details.",
    )
    return [{"role": "user", "content": summary}] + messages[-5:]
Compaction summarizes older history and reinitiates a smaller context, rather than growing forever.

Two other techniques handle the same problem differently. Structured note-taking — Anthropic also calls this agentic memory — has the agent periodically write notes to storage outside the context window, then read them back in later. Anthropic's own beta-released memory tool implements this as a file-based system an agent can consult across sessions; a simpler version of the same idea is an agent maintaining its own running NOTES.md or to-do list. In one striking example, an agent playing a video game over thousands of steps kept precise tallies — "for the last 1,234 steps I've been training my Pokémon in Route 1" — entirely through self-directed notes, with no prompting about how to structure its own memory.

Sub-agent architectures solve it by not putting everything in one context window at all. A lead agent delegates a focused piece of work to a sub-agent, which might explore extensively — tens of thousands of tokens of searching and reading — but returns only a condensed summary, often in the 1,000–2,000 token range, back to the lead agent. The detailed exploration stays isolated in the sub-agent; only the distillation crosses back.

A coding agent has been working for hours across many tool calls and is approaching its context limit, but the task isn't done. Which of the three techniques — compaction, note-taking, or sub-agents — keeps the conversation going as one continuous thread, rather than splitting work across separate contexts?Answer it yourself first, then open this.

Compaction. It summarizes the existing history and reinitiates a smaller context built from that summary, so the same ongoing thread continues — as opposed to note-taking (writing outside context and reading it back later) or sub-agents (isolating detailed work in a separate context entirely).

Guardrails and Multi-Agent Patterns

  • Apply input, output, and tool guardrails to constrain what an agent can do, and add a human-approval step for sensitive actions.
  • Choose a multi-agent pattern — routing, parallelization, or orchestrator-workers — appropriate to a task's structure.

A guardrail is an automatic check; human review is a deliberate pause for a person to decide. The two work together to define when a run continues, pauses, or stops. OpenAI's Agents SDK names three kinds of guardrails by where they sit: input guardrails validate a request before the expensive or side-effecting part of a run starts, and — importantly — only run for the first agent in a chain; output guardrails validate or redact the final result before it leaves the system, and only run for whichever agent actually produces that final output; tool guardrails check arguments or results around one specific tool call, wherever that call happens in the chain.

Each of these can raise what the SDK calls a tripwire: when a guardrail's check fails, it raises an exception and halts execution immediately, rather than letting a bad input or output continue quietly through the rest of the run.

python

from agents import Agent, GuardrailFunctionOutput, Runner, input_guardrail

@input_guardrail
async def scope_guardrail(ctx, agent, user_input):
    check = await Runner.run(scope_checker_agent, user_input, context=ctx.context)
    return GuardrailFunctionOutput(
        output_info=check.final_output,
        tripwire_triggered=check.final_output.out_of_scope,
    )

support_agent = Agent(
    name="Support agent",
    instructions="Help customers with support questions.",
    input_guardrails=[scope_guardrail],
)
An input guardrail runs a lightweight check before the main agent ever starts.

Guardrails handle checks a program can run automatically; some actions need a person instead. The Agents SDK's approval pattern marks a specific tool as needing review — cancelling an order, deleting a record — and when the model wants to call it, the run pauses rather than executing: it returns a list of pending interruptions alongside a resumable state. Your application approves or rejects each one, then resumes the exact same run from that state, rather than starting a fresh turn.

One boundary is easy to miss: agent-level guardrails don't cover everything. Input guardrails only run for the first agent in a chain, and output guardrails only for the one that produces the final answer — so in a manager-style, multi-agent workflow, a tool call buried in the middle of the chain isn't covered by either. The documented fix is to put validation next to the specific tool that creates the side effect, rather than assuming an agent-level guardrail reaches every step.

The multi-agent patterns from Anthropic's own guidance map cleanly onto specific task shapes. Routing classifies an input and sends it to a specialized follow-up — different prompts (or different models) for a refund request versus a technical question — which avoids the performance hit of one prompt trying to handle every case. Parallelization runs multiple LLM calls at once and combines their outputs programmatically, in two variations: sectioning, where independent pieces of a task run in parallel, and voting, where the same task runs multiple times for a more confident combined answer — useful for something like flagging a piece of code for vulnerabilities from several independent passes.

Orchestrator-workers is the pattern for tasks whose subtasks genuinely can't be predicted in advance: a central LLM breaks the task down and delegates pieces to worker LLMs, then synthesizes what comes back. Anthropic's own coding agents use exactly this pattern to handle GitHub issues that touch an unpredictable number of files.

A support agent's output guardrail checks its final reply for policy violations. Deep in the same conversation, a different agent in the chain calls a `refund_customer` tool directly. Does the output guardrail catch that tool call?Answer it yourself first, then open this.

No — an output guardrail only runs on the final output of whichever agent produces it, not on tool calls made by other agents earlier in the chain. That tool call needs its own tool guardrail (or a human-approval step) attached directly to it.

Sources