TechByteByByte

The Agent Loop

A full, working run of the agent loop from goal to resolution — plus what happens when a tool fails, a diagnosis is wrong, information is missing, or permission is denied.

#Agentic AI#AI Agents#Agent Loop#LLM

Think about solving a maze. You look around, choose a direction, move, and then look again. You cannot safely choose every turn before seeing what each move reveals. An agent loop works in the same way.

Goal → Understand → Choose → Act → Observe
  ↑                                  ↓
  └──── Continue, retry, or replan ──┘

                        Stop

What You Will Learn

  • What happens during every stage of an agent loop.
  • How a goal becomes a concrete tool call and a real observation.
  • How state changes after successful, failed, or incomplete actions.
  • When the agent should continue, retry, replan, ask for help, or stop.
  • How limits such as maximum steps prevent endless or expensive loops.

The Loop in a Real Product

Gemini’s custom function-calling flow shows one trip around the loop: the application sends tool descriptions, the model returns a structured function request, application code executes it, and the result is sent back to the model. The model can then answer or request another tool. (Google Gemini tool-calling documentation)

Notice the boundary: the model requests; the application validates and executes. Keeping that boundary visible is essential for understanding both reliability and security.

Every module so far has been building toward this one. You know the loop’s shape — goal, observe, reason, decide, act, observe result, repeat. You know the components that make it up — state, tools, feedback, reasoning, termination. What you haven’t seen yet is the loop running, start to finish, with all the mess a real task brings: an API that fails partway through, a first diagnosis that turns out to be wrong, information the agent can’t get.

This module is where we do that properly, because the loop’s shape is the easy 20% to understand. What separates a reliable agent from a fragile one is how it behaves in exactly these moments — and that’s the 80% this module is about.

The stages, named precisely

Let’s expand the loop from Module 1 into the form we’ll use for the rest of this module:

Goal

Understand

Plan

Choose action

Execute

Observe

Evaluate

Continue / Retry / Replan / Stop

Understand means taking the current situation — the goal, plus whatever’s accumulated in state so far — and forming a read on what it means. Plan, as covered in Module 3, is deciding roughly what needs to happen, which might be as light as “figure out the next single step” or more structured, depending on the task. Choose action is selecting a specific tool call (or deciding no tool call is needed and it’s time to respond).

Execute is your code running that call. Observe is capturing whatever comes back — success, failure, or something ambiguous in between. Evaluate is the step people skip when they build their first agent, and it’s the one that matters most: asking whether that observation moved the task closer to the goal, or whether it revealed something that changes the picture.

And then the loop has to decide, based on that evaluation, whether to continue normally, retry the same action, replan with a different strategy entirely, or stop — because the goal is met, or because continuing wouldn’t help.

That last line is doing the real work of this entire module. Let’s watch it happen for real.

A complete run, start to finish

Recall the setup: the goal is to find out why a customer’s payment situation is unresolved, and to take whatever action correctly resolves it. The tools available: get_customer(), get_payment_history(), check_payment_gateway(), check_account_status(), retry_payment(), create_support_ticket(), send_email().

Iteration 1. Understand: the ticket says a payment failed twice and no refund has arrived. Plan: start by confirming the account itself is fine before looking at the payment specifically. Choose action: get_customer(customer_id="C-4471"). Execute: the call runs. Observe: account active, in good standing, no flags. Evaluate: nothing alarming here — this doesn’t change the plan, it just clears one possibility. Decision: continue.

Iteration 2. Understand: account is fine, so the next question is what happened with the payment. Choose action: get_payment_history(customer_id="C-4471"). Execute: runs. Observe: two declined charges this week, both with reason code insufficient_funds. Evaluate: this is significant — it directly contradicts the customer’s own framing of the issue as a gateway problem. A shallow agent might stop here and simply tell the customer “your card was declined,” but a well-designed one recognizes that acting on an unverified assumption, in either direction, is risky. Decision: continue, but replan slightly — the next step should verify this specific mismatch rather than proceed straight to a resolution.

Iteration 3. Understand: need to rule out a gateway-side problem before concluding the failures are on the customer’s end. Choose action: check_payment_gateway(status_check=True). Execute: runs. Observe: zero reported outages in the relevant window. Evaluate: this confirms the earlier read — the declines are real, and they’re on the card, not the system. This is now enough grounded information to act. Decision: continue to a final action.

Iteration 4. Understand: the correct resolution here isn’t a refund — nothing was successfully charged — and a payment retry with the same card would just fail the same way again. The right action is informing the customer and prompting a card update. Choose action: send_email(template="card_update_needed", customer_id="C-4471"). Execute: runs. Observe: email sent successfully. Evaluate: the goal — determine the cause, take the correct action, inform the customer — has been satisfied. Decision: stop.

Four iterations, each one grounded in what the previous one revealed. Notice specifically what happened at the evaluate step in iteration 2 — that’s the entire point of this module. A weaker system would have treated “payment declined” as the end of the investigation. This one used it to redirect the plan, which is exactly the behavior that makes an agent worth the complexity of building one in the first place. Now let’s break each of the places this can go wrong.

When a tool call fails outright

Suppose, in iteration 3, check_payment_gateway() doesn’t return a clean result — it times out, or the API returns a 500 error. This is a different situation from “the gateway reported no outages,” and it’s critical that the agent’s loop treats it differently, because these two outcomes point toward opposite conclusions.

Observe: a tool execution error, not a business result. Evaluate: this observation says nothing at all about whether the gateway is healthy — it says the check itself didn’t succeed. A correctly built agent recognizes this distinction explicitly rather than silently treating “no result” as “no problem.” Decision: retry the same call, ideally after a brief pause, since this class of failure is often transient — a network blip, a momentary rate limit.

If the retry succeeds, the loop simply continues as normal. If it fails again, the agent has a meaningful decision to make: it cannot confirm or rule out a gateway issue, and proceeding to a conclusion anyway would mean guessing. The honest move here is not to fabricate a diagnosis — it’s to acknowledge the limitation directly, either by trying a different verification path if one exists, or by creating a support ticket with everything discovered so far and explaining exactly what couldn’t be confirmed. That’s a different, better outcome than an agent that presses forward with a conclusion it can’t support.

When the diagnosis itself is wrong

This is a subtler and more dangerous failure than a tool erroring out, because nothing in the trace necessarily looks broken. Every tool call succeeded. Every observation was real. And the agent still reached the wrong conclusion.

Imagine the payment history shows one declined charge and one that appears successful — but the “successful” charge was a duplicate authorization that will be automatically reversed within 24 hours, something the payment history tool doesn’t explicitly flag. An agent that doesn’t know to look for this pattern might reasonably, but incorrectly, conclude the customer’s payment went through and tell them so — only for that charge to disappear the next day, making the situation worse, not better.

This is exactly why the evaluate step in the loop matters so much, and why a single successful-looking pass through the loop isn’t proof of correctness. The real mitigations here are the ones you’ll study properly in Module 12: evaluating the agent against a set of tricky, realistic scenarios — including ones with exactly this kind of subtle trap — rather than trusting that “it worked on the cases I happened to try.”

A wrong diagnosis rarely announces itself in the trace the way a tool timeout does. It just looks like a confident, plausible answer that happens to be incorrect, which is precisely why you can’t catch this class of failure by reading traces alone — you need, deliberate evaluation against known-tricky cases.

When a retry doesn’t fix it

Say the agent decides, correctly, that a payment retry is the right action — perhaps the customer’s card was declined due to a temporary issue that’s since resolved. It calls retry_payment(). It fails again.

Evaluate: one retry failing isn’t automatically catastrophic — it could still be transient. But this is exactly the point in the loop where an unconstrained agent risks looping indefinitely: retry, fail, retry, fail, with the model’s own reasoning each time concluding “let’s try once more,” believing it’s making progress when it isn’t.

This is precisely the infinite-loop failure mode you’ll study in full in Module 10, and the fix has to be structural, not something left to the model’s own judgment: a hard cap on how many times the same action can be retried within one run, enforced by your code regardless of how confident the model’s reasoning sounds at each attempt.

Once that cap is hit, the correct behavior is a change of strategy, not a stop-and-fail. Maybe the agent checks the account status to see if there’s a deeper reason the retries are failing — perhaps the account itself is flagged, which would explain repeated declines regardless of the card. If that check reveals something new, replanning around it is the right move. If it doesn’t, escalating to a human with the full trace of what was attempted is the honest, correct stopping point — better than either an indefinite retry loop or a fabricated resolution.

When required information isn’t available

Sometimes the agent needs something that simply doesn’t exist anywhere it can reach. Maybe the customer never provided a specific order number, and the customer record doesn’t disambiguate between two similar recent transactions.

Evaluate: this isn’t a tool failure and it isn’t a wrong conclusion — it’s a gap the agent cannot resolve on its own. The correct response is recognizing this explicitly rather than picking one of the two transactions arbitrarily and proceeding as if it were certain. Good agent design includes an explicit path for this: asking the customer a clarifying question, or escalating with the ambiguity clearly stated, rather than forcing a decision the available information doesn’t support.

This connects directly back to the incorrect- assumptions failure mode you’ll see catalogued fully in Module 10 — the core discipline here is teaching an agent (through its instructions and its available actions) to distinguish “verified” from “assumed,” and to treat a gap as a reason to ask or escalate, not as a gap to paper over with a plausible guess.

When the agent hits a permission boundary

Suppose the situation calls for a refund above a threshold that requires human sign-off — say, this company’s policy requires manager approval for any refund over 500,andthisoneis500, and this one is 650.

Evaluate: the diagnosis is solid, the correct action is clear, and the agent still cannot simply take it. This is a fundamentally different kind of stop than any of the others in this module — it’s not a failure of reasoning or a limitation of available information. It’s a deliberate boundary, and the loop needs to recognize hitting one as its own distinct outcome: not “retry,” not “replan,” but “prepare the action and route it for approval.”

We’ll cover the full mechanics of approval gates properly in Module 9, but it’s worth planting here, inside the loop itself, because this is exactly where that boundary gets enforced — at the moment the agent has decided on an action and is about to execute it, not earlier and not later.

How the agent chooses among retry, replan, escalate, and stop

Step back from the specific scenarios and notice the pattern underneath all of them. At the evaluate step, the agent (and the system around it) is asking a small set of real questions: *Was this failure likely transient, or is it structural? * A network timeout suggests retry. A consistent decline reason suggests the underlying situation, not the mechanism, is the problem. *Does this observation change what I believe about the situation, or does it just confirm what I already suspected?

  • A confirming result means continue along the current plan. A contradicting one means replan. *Do I have what I need to reach a confident conclusion, or would proceeding mean guessing? * If it’s the latter, the honest move is asking for more information or escalating — not manufacturing confidence that isn’t there. *Is this action something I’m authorized to take on my own? * If not, the correct outcome isn’t failure — it’s routing for approval.

None of these are exotic judgments. They’re the same judgments a careful, competent human support agent makes constantly, mostly without consciously noticing they’re making them. The entire engineering challenge of building a reliable agent is making these judgments happen reliably, every time, rather than only when the underlying model happens to reason well on a given day — which is exactly why so much of the rest of this course is about the guardrails, evaluation, and hard limits that make these decisions dependable rather than merely hopeful.

Where this shows up in real, current systems

This exact challenge — completing a full loop reliably, not just producing a plausible-looking single response — is precisely why serious benchmarks for coding agents, like SWE-bench, evaluate whether an agent can take a real GitHub issue all the way through diagnosis, code changes, and a passing test suite, rather than just judging whether a single generated code snippet looks reasonable.

A coding agent that writes plausible-looking code for one step but can’t correctly evaluate whether its change fixed the issue — the same “evaluate” step this whole module has been centered on — will fail that benchmark even if every individual line of code it wrote was syntactically fine. The loop, run correctly end to end including its failure branches, is the actual product. A convincing single step along the way isn’t.

You can also see the exact stages from this module show up, by name, in real shipped systems, each with a different loop shape. AutoGPT — the project referenced back in Module 2 — runs a task-queue loop: it executes the highest-priority task, writes what it learns into a long-term memory store, and reprioritizes its remaining task list based on that.

Its evaluate step doesn’t just decide continue or stop — it can rewrite the plan itself, which is also exactly why an unconstrained version of it became the canonical cautionary tale for the infinite-loop failure mode this module covered.

Anthropic’s Computer Use, which powers Claude and Claude Code, literally names this mechanism “the agent loop” in its own documentation: Claude requests a screenshot, decides on an action, your code executes it, a new screenshot comes back, and the cycle repeats — with a hard step cap enforced in code, exactly the retry-limit discipline this module argued for.

OpenAI’s Computer-Using Agent, first shipped as Operator and now folded into “ChatGPT agent,” names its own stages explicitly as perception, reasoning, and action, cycling the same way, with mandatory human confirmation built in before certain actions execute. We’ll go back into each of these properly — what they get right in production, and what’s gone wrong — in Module 13.

Numbered Walkthrough: One Loop With Changing State

Suppose a school-library agent must find and reserve a science book for Maya.

  1. Starting state: student=Maya, subject=science, book=unknown, reserved=false, steps_used=0.
  2. Choose: the model requests search_books(subject="science").
  3. Act: application code validates the subject and runs the library search.
  4. Observe: the tool returns two book IDs: B17 is available and B42 is already borrowed.
  5. Update state: book=B17, steps_used=1. The goal is not complete because nothing is reserved yet.
  6. Choose again: the model requests reserve_book(student="Maya", book_id="B17").
  7. Observe again: the tool returns reservation_id=R903.
  8. Stop: state becomes reserved=true, steps_used=2; the agent can now report the verified reservation.

The model did not reserve a book by writing a sentence. Each real change happened only after application code accepted and executed a tool call.

Common Misconception

Incorrect idea: An agent should continue looping until it finds some answer.

Why it is incorrect: A safe loop stops when the goal is met, a step budget is exhausted, required information is unavailable, or a permission boundary requires a human. Continuing without a valid path wastes money and may increase harm.

Key Takeaways

  • The full loop — understand, plan, choose action, execute, observe, evaluate, continue/retry/replan/stop — has a specific step, evaluate, that most beginner implementations skip, and it’s the one that determines whether an agent behaves reliably or fragilely.
  • A single successful-looking observation doesn’t always mean “continue as planned.” In the worked example, discovering the customer’s stated cause didn’t match the payment data was itself the signal to change strategy, not just another data point to note.
  • A tool failing outright (timeout, error) and a tool returning a real but unfavorable result are different situations, and an agent’s loop needs to distinguish them explicitly — one usually warrants a retry, the other doesn’t.
  • A wrong diagnosis is often the most dangerous failure precisely because it doesn’t look broken in the trace — every tool call can succeed and the conclusion can still be incorrect, which is why deliberate evaluation against tricky, realistic scenarios matters more than reading a handful of successful traces.
  • Retry loops need a hard, code-enforced cap on how many times the same action can be attempted — relying on the model’s own confidence that “one more try” will work is exactly how infinite loops happen.
  • in reality missing information should trigger an explicit ask-or-escalate path, not a plausible guess dressed up as a conclusion. Hitting a permission boundary should trigger routing for approval, not failure and not silent override.
  • The evaluate-and-decide judgment at the heart of this loop mirrors what a careful human does instinctively — the engineering challenge is making that judgment happen dependably every single time, which is exactly the throughline for the rest of this course.

Think Like an AI Engineer

  • Walk back through the four-iteration run at the start of this module and identify exactly which iteration’s evaluate step would have changed the final outcome if it had been skipped. What would the agent have incorrectly concluded?

  • You’re building an agent that processes expense reports. It calls a currency-conversion API that occasionally returns a rate that’s technically valid but clearly stale (unchanged for several days during a period when that currency was volatile). Is this closer to a tool failure, a wrong diagnosis, or missing information, in the terms this module used? What would you build to catch it?

  • Design a retry policy for a tool call in your own domain. How many attempts is reasonable before the loop should stop retrying and change strategy instead? What would make you choose a smaller number versus a larger one for a specific tool?

  • Think about the permission-boundary scenario in this module. Where, precisely, in the loop — before choosing the action, after choosing it but before executing, or somewhere else — should that check happen? What would go wrong if you enforced it at the wrong point?

Module 5 goes deep on the piece that made every action in this module’s walkthrough possible in the first place: tools. We’ll cover exactly how a tool schema works, how arguments get validated before execution, what a well-designed versus poorly-designed tool description looks like, and what happens — mechanically — between the model deciding to call something and your code running it.

Author
TechByteByByte Editorial Team
Reviewed by
TechByteByByte Admin
Published
Last reviewed