this free lesson is part of
AI Engineering
$49 once, by PayPal
See the AI Engineering courseSave your progress and get the AI Engineering cheat sheet PDF
This idea carries a full system design question on its own. Each walks through the full answer.
you just read a free lesson from
AI Engineering
$49 once, by PayPal
See the AI Engineering courseReading for an interview? One lesson is not enough for it.
Each question has why it is asked, a full answer, what the interviewer listens for, the follow-ups, the common mistakes and a diagram (180 diagrams in all). A PDF you keep, junior to staff level.
India: UPI, cards or netbanking through Razorpay. Elsewhere: PayPal. Download at once from My packs.
An AI agent is a language model put in a loop. That is the whole idea, and it is worth taking literally, because almost every hard problem in production agents comes from the loop and not from the model.
The model looks at a goal and the history so far, and it decides one action. An orchestrator, ordinary code you write, runs that action as a tool, takes the result, and hands it back to the model. The model looks again and decides the next action. This repeats until the model declares the goal met, or until a budget stops it. The model never runs anything itself. It only proposes; the orchestrator executes and owns the loop, the budget, and the stop condition.

That division is the most important thing to see before anything else. Because the orchestrator owns execution, an agent is not a black box you can only hope behaves. It is an engineering artifact with seams you can reach into: you decide how many steps it may take, which tools it may call, what happens when a tool errors, and whether a proposed action is even allowed to run. Every reliability lever in this lesson lives at one of those seams.
Notice too that the model is stochastic. Ask it the same thing twice and you may get two different actions. That is fine for a chatbot, where a human reads the answer and moves on. It is a much bigger deal in a loop, where the output of one step becomes the input to the next, and a single wrong action can send the whole run somewhere useless. The first hard problem is the one that surprises almost everyone the first time they ship an agent: a system that was magical in the demo becomes unusable on real work, for a reason that has nothing to do with how smart the model is.
This lesson uses a small set of words again and again. Here they are in plain English, before we need them.
A tool is a function the agent is allowed to ask for. It can be a database query, a web search, a call to a payment API, or sending an email. The model does not run the tool. It writes a short request that says which tool it wants and with which inputs, called the arguments.
The orchestrator is the normal program around the model. It reads the model's request, checks it, runs the tool, and puts the result back into the conversation. It also counts steps and decides when to stop.
A step is one full turn of the loop: the model proposes one action, the orchestrator runs it, and the result comes back. A task that needs twenty actions is a twenty step task.
A side effect is a change in the outside world that stays after the call ends. Reading a row has no side effect. Charging a card, sending an email or inserting a row does.
A transient error is a failure that goes away if you wait and try again, for example a server that was busy for one second. A permanent error will fail the same way every time, for example a request with a missing field.
An operation is when doing it twice has the same result as doing it once. Reading is idempotent. Charging a card is not, unless you make it so.
A budget is a hard limit the orchestrator sets: the most steps, tokens (the small pieces of text a model reads and writes) or seconds a run may use before it must stop.
The number of steps a task takes is the strongest predictor of whether an agent finishes it correctly, and it is the one thing a demo quietly controls for you.
Count the steps in a typical demo: two, maybe three, on a clean chosen example. Count the steps in real work: ten, twenty, fifty, against messy inputs and tools that sometimes fail. Same agent, same model, same prompt. The only thing that changed is how many actions had to go right in a row.

The demo is not dishonest on purpose. The presenter picks a task they already know works, feeds it clean input, and the tools happen to respond on the first try. That is a three-step happy path, and three steps of a good model almost always succeed. The pilot then hands the same agent a real ticket, with an ambiguous request, a missing field, a stale record, and a tool that rate-limits under load. Now it is twenty steps, and twenty steps is a different universe.
You cannot prompt your way out of this, because it is not a wording problem. Rewriting the system prompt does not change the number of actions the task requires, and the number of actions is what is killing you. That single variable, length, is doing almost all the work in whether the agent succeeds, and almost nobody instruments it. The next slide is the arithmetic that makes length so brutal.
Because the steps are a chain, and a chain multiplies.
Think about what it takes for a whole task to succeed: every step in it has to succeed. If the steps are roughly independent, the probability that all of them succeed is the product of their individual success rates, not the average. Multiplication punishes length in a way intuition does not expect, because we are used to reasoning about averages, and averages hide the collapse.

Put numbers on it. Suppose each step succeeds 95 percent of the time, which sounds excellent. A five step task succeeds about 77 percent of the time. A twenty step task succeeds about 36 percent of the time. A fifty step task succeeds under 8 percent of the time. The per-step number never changed. Only the length did, and the curve falls off a cliff.
The same table also shows the other exit. Getting each step from 95 percent to 99 percent lifts the twenty step task from 36 percent to 82 percent, and getting to 99.9 percent lifts it to 98 percent. So there are exactly two ways down the curve: fewer steps, or a higher per-step rate. Hold that, because it is the whole strategy of the lesson.

Read the heatmap as a control panel. Where you land is decided far more by the column you sit in, the length, than most teams believe. A near-perfect 99.9 percent step over 50 steps still beats a merely-great 95 percent step over just 10 steps. Run the arithmetic yourself and watch the collapse.
The lesson in that table is blunt. A per-step reliability that feels great is not good enough once a task is long, and the fastest way to a reliable agent is usually to make the task shorter, not the model smarter.
I want you to see these numbers appear on paper, not only trust a chart. You need nothing more than repeated multiplication, and a trick to save work: square the number you already have.
Start with one step at 0.95. For two steps in a row, both must succeed, so multiply: 0.95 × 0.95 = 0.9025. For four steps, square that: 0.9025 × 0.9025 is about 0.8145. For five steps, multiply by one more 0.95: 0.8145 × 0.95 is about 0.7738, which is the 77 percent from the chart.
Now keep squaring. Ten steps is five steps twice: 0.7738 × 0.7738 is about 0.5987, so a ten step task succeeds about 60 percent of the time. Twenty steps is ten steps twice: 0.5987 × 0.5987 is about 0.3585, the 36 percent you saw. Notice how the loss grows. From five to ten steps you lose about 77 - 60 = 17 points. From ten to twenty you lose about 60 - 36 = 24 more.
Do the same for 0.99. Twenty steps gives 0.99 multiplied by itself twenty times, about 0.818, so 82 percent. And for 0.999 over twenty steps you get about 0.980, so 98 percent.
Here is a quick way to estimate this in your head. When the failure rate per step is small, call it f, the whole task fails about n × f of the time for n steps, as long as n × f stays well under 1. For 0.999 over twenty steps, f is 0.001, and 20 × 0.001 = 0.02, so about 2 percent fail, which matches the 98 percent. This shortcut stops working when n × f gets large. For 0.95 over twenty steps it would say 20 × 0.05 = 1.0, a 100 percent failure rate, which is clearly wrong; the true answer is 100 - 36 = 64 percent failure. So use it only for small failure rates.
One honest note. All of this assumes the steps fail independently, which means one step failing does not change the chance that the next one fails. It also assumes every step has the same success rate. Both are simplifying assumptions, and this is general knowledge, not something I measured in this lesson. In a real agent the rate is often not constant: the history grows with every step, and a late step has more to keep track of than an early one, which is the context drift from the failure table below. So treat the formula as a way to reason about length, not as a precise forecast for your agent. To get a real number you have to measure your own agent on your own tasks.
Before you can raise per-step reliability, you have to know what "a step failed" even means, because the 95 percent is an average over a pile of very different failures, and they do not all get fixed the same way.

Sort them by root cause. A transient tool error, a 429 or a 503 or a socket timeout, is not the model's fault at all; the world simply blinked, and the same call would have worked a moment later. That failure belongs to retries. The other four are the model: it emitted invalid JSON, or it picked the wrong tool, or it invented an order number that never existed, or it forgot a constraint stated fifteen steps ago because the transcript outgrew what it can track. Those belong to validation, tighter tool schemas, verification, and shortening the task, in that rough order.
This sorting matters because the wrong fix is worse than no fix. Retrying a permanent error, a 400 or a schema violation, wastes budget and can double a side effect, and it will never succeed no matter how many times you try. Adding a verification pass to a step that only ever fails transiently spends tokens catching a failure that a cheap retry would have erased. Notice what is not on the list: "use a smarter model." A bigger model nudges the four model failure modes down a little, but it is the most expensive lever you have, and it rarely touches the transient failures at all. Match the fix to the root, and most of your per-step reliability comes from cheap, boring engineering.
Zoom all the way into a single turn of the loop, because a step is not one call. It is a little pipeline, and inside it there are two specific places you can intervene to push that step toward the 99.9 percent that survives a long chain.

The first intervention is validation. When the model proposes an action, the orchestrator checks it against the tool's schema before anything runs. If the arguments are malformed or a required field is missing, the action is rejected right there and handed back to the model to try again. A caught malformed action never becomes a corrupted database row or a bad API write; it is a non-event. This is the cheapest reliability you will ever buy, because you already have the schema.
The second intervention is recovery. When the tool returns a transient 503, the orchestrator does not surface it to the model and it does not abort the run. It retries, after a short backoff, under the same key, and the second attempt succeeds. From the task's point of view the blink never happened. It never entered the success math at all.
The point of tracing one step this closely is that "raise per-step reliability" stops being a vague instruction and becomes two concrete pieces of orchestrator code: a validation gate on the way in, and a bounded retry on the way out. Neither one is model work. Both are the ordinary plumbing of a reliable system, and both live at seams the orchestrator already owns.
Retries are the lever for transient failures, but a naive retry is itself a bug in two ways, and you have to close both before a retry is safe.
The first way is retrying the wrong thing. Put a classifier between the failure and the response, so a transient error goes down the retry branch and a permanent error goes down the fail-fast branch. Retrying a 429 or a timeout is a free save; retrying a 400 or a 404 burns budget on something that can never succeed. And there must always be a third branch, the hard stop, so a step that keeps failing does not loop forever: cap the retries, cap the total wall-clock, and when the cap is hit, halt, checkpoint the state, and raise for a human or a fallback.

The second way a retry becomes a bug is more subtle, and it is the reason retries and are always taught together. A retry replays a side effect. If the first attempt actually succeeded and only the response was lost on the way back, a naive retry runs the effect a second time. For a read, that is harmless. For a charge, an email, or a row insert, you just did the thing twice.

The fix is a contract. Derive one stable key per logical action, an order id or a hash of the arguments, send it on every attempt, and let the tool or a dedup store collapse repeats so the effect runs at most once. Many payment and queue APIs offer this for exactly this reason: Stripe accepts an idempotency key header on its requests, and Amazon SQS FIFO queues accept a message deduplication id. Reads are naturally idempotent, so retry them freely; it is the writes where a key is the difference between a save and a double-charge. See how a bounded retry over an idempotent step changes the arithmetic.
The retry playground is short, but each line carries an assumption. Let me walk through it, then work the first row by hand.
The first two numbers set up the world. p_ok = 0.90 says a step works on its first try 90 percent of the time. transient_share = 0.70 says that when it fails, 70 percent of those failures are the transient kind that a retry can fix. The other 100 - 70 = 30 percent are permanent, and no retry will help them. These two numbers are an example I chose for teaching, not a measurement from a real system.
The function effective(retries) answers one question: if I allow this many extra attempts, how often does the step finally succeed? It starts from p = p_ok, the case with no retries. Then, once for each retry allowed, it updates p with one line. Read that line as a sentence: the step succeeds if the first try works (p_ok), or if the first try fails (1 - p_ok), the failure is transient (transient_share), and the retried step then succeeds (p, the answer with one fewer retry). Using the previous p is what lets a retry itself be retried.
Now by hand, for one retry. The chance of reaching the retry is 0.10 × 0.70 = 0.07. The retry works with the old p, 0.90, so it adds 0.07 × 0.90 = 0.063. The total is 0.90 + 0.063 = 0.963, the 96.3 percent in the output. Over twenty steps, 0.90 multiplied twenty times is about 0.12, and 0.963 multiplied twenty times is about 0.47. That is the jump from about 12 percent to about 47 percent.
There is no single fix. There is a stack of four levers, and a real system uses all of them. The first two change the arithmetic itself; the last two catch what the arithmetic still lets through.
The move with the biggest effect is to shorten the task, because the step count is the exponent. Decompose one long goal into smaller bounded ones with a verified checkpoint between them, and a failure is caught and retried at the boundary instead of sinking the whole run.

That figure is the crux of decomposition, and it is worth being precise about why it works, because "0.95 to the tenth twice" is literally the same number as "0.95 to the twentieth once." The win is not the split by itself. The win is the verified, recoverable boundary you insert at the seam: the checkpoint confirms stage one is correct before stage two starts, and a failed stage retries in isolation instead of propagating a quiet error into the next ten steps. That is what turns 36 percent into roughly 70 percent without touching the model at all.

Read the stack top to bottom, because that is the spending order. Shortening the task and hardening each step change the math for free. Verification and recovery cost tokens and , so aim them at the steps that are risky or irreversible rather than firing them on every step. And a bigger model sits below all four, because it is the priciest lever and the one that moves the number least per dollar. Reach for it last, once the cheap arithmetic is exhausted, not first.
These are the mistakes I see most often when a team ships its first agent. Each one follows from something in this lesson.
Judging the agent on the demo task. A three step demo at 95 percent per step succeeds about 86 percent of the time (0.95 × 0.95 × 0.95 is about 0.857). It tells you almost nothing about a twenty step task. Test on tasks with the length your users will really bring.
Measuring only the final answer. If you only log whether the whole task passed, you cannot tell which step broke, so you cannot choose a lever. Log every step: the action proposed, whether it passed validation, the tool result, and the error class if it failed.
Retrying everything. A retry on a 400 or a 404 will fail again every time. It burns the budget and can hide the real bug. Classify first, then retry only the transient errors.
Retrying a write without a key. This is how customers get charged twice. If a tool has a side effect, it needs an key before it gets a retry.
No budget. An agent with no step limit, no token limit and no time limit can loop for a very long time on a task it cannot finish, and you pay for every step. Always set all three, and decide what happens when one runs out.
Splitting a task without a check between the parts. As the decomposition slide showed, ten steps twice is the same number as twenty steps once. The split only helps when you verify the first part before you start the second, and when a failed part can be run again on its own.
Reaching for a bigger model first. It is the most expensive lever and it does not fix transient tool errors at all. Try the cheap levers first and measure what changed.
After all these warnings, a fair question is whether you should build an agent at all. Sometimes yes, often no.
An agent is a good fit when the steps needed truly depend on what the earlier steps found. For example, a support task where the next lookup depends on what the first lookup returned, or a research task where you cannot list the searches in advance. In these cases a fixed program cannot know the path ahead of time, and a model that decides the next step is useful.
An agent is a poor fit when you already know the steps. If every run is "fetch the order, check the refund rule, write the reply", write those three steps as ordinary code and call the model only for the part that needs language, such as writing the reply. That fixed pipeline, often called a workflow, has fewer places where the model can choose wrong, and it is easier to test.
It is also a poor fit when one wrong action is very costly and cannot be undone, and there is no human check. Moving money, deleting data or sending messages to customers should either stay out of the agent's hands or go through a verification step and, where the risk is high, an approval by a person.
A simple rule I use: start with the most fixed design that can do the job. Add model decisions only at the points where the path truly cannot be known in advance. Every decision you give the model is another step that can fail, and you now know how those failures multiply.
Underneath all four levers is a single change in how you think about the thing you are building, and once it clicks, the levers stop feeling like a checklist and start feeling obvious.
The mindset that fails treats an agent like a function that returns the right answer. You call it, you trust the output, you move on. The mindset that ships treats it like an unreliable distributed system, because that is exactly what it is: a sequence of calls over a network to a component that is stochastic and sometimes wrong.

You already know how to build reliable systems out of unreliable parts, because that is most of backend engineering. You use retries, verification, bounded scope, idempotency, and recovery. An agent needs exactly the same discipline, and each lesson that follows in this chapter takes one of those tools and shows how it applies to the loop: the contract that makes schemas hard to misuse, the durable-execution runtime that makes long runs recoverable, the evaluation harness that measures per-step reliability so you can improve it.
It is also why, later, you will see the advice to constrain an agent rather than maximize its autonomy. A shorter, narrower agent is not a weaker product. It is a more reliable one, and on a long task, reliability is what makes it shippable at all. The team that ships is not the one with the smartest model. It is the one that treated the loop like the distributed system it is.
5 questions - Score 80% to pass
Each step of an agent succeeds 95 percent of the time. Roughly how often does a 20-step task succeed end to end?
Why does a short demo make an agent look far more reliable than it is?
Which lever most directly attacks the compounding problem?
Why must a retried step be idempotent before you retry it?
What is the mindset the lesson recommends for building agents?
after “The Compounding, Worked by Hand”
Want to be able to explain this without notes?
In the AI Engineering course, the next step is Agent Reliability Math: pass@k, pass^k, and Why 95 Percent Fails.
You get it for life once you join, with every new lesson included.
Now look at the rows for two and three retries. They barely move. Two retries give 0.90 + 0.07 × 0.963, about 0.967, and three give 0.90 + 0.07 × 0.967, about 0.968. There is a ceiling, and you can find it with algebra. If retries never ran out, p would stop changing, so p = 0.90 + 0.07 × p. Solve it: p × (1 - 0.07) = 0.90, so p = 0.90 ÷ 0.93, about 0.968. The permanent failures set that ceiling, because every attempt, including every retry, can hit one. This is the most useful thing the code teaches: retries fix transient failures quickly, and after one or two retries more of them buy almost nothing. The rest of the reliability has to come from the other levers.