
IMPLEMENTATION
Multi-Agent Patterns as Workflow Loops
Two-agent loops, group chat, manager-worker and hierarchies all compile to the same graph. What decides whether one runs safely is the termination logic, and the framework defaults are looser than you think.
TL;DR
Every multi-agent pattern people draw as a distinct diagram, two agents ping-ponging, a group chat, a manager and its workers, a reporting hierarchy, compiles down to the same graph: agent nodes, a conditional node deciding what runs next, and an edge that can point backward. The diagram is not where these systems fail. The termination logic is.
The frameworks agree, and their defaults are looser than most people assume. LangGraph raised its default
recursion limit to 1,000 supersteps in version 1.0.6. Google's ADK says plainly that a LoopAgent "does not
inherently decide when to stop looping". CrewAI caps an agent at 20 iterations. Only one of those is a number
you would choose deliberately.
The largest study of this we know of, the MAST taxonomy built from 1,600-plus annotated traces across seven frameworks, sorts 14 failure modes into three buckets, and one whole bucket is "task verification". That is the conditional node.
We are not claiming multi-agent designs don't work. We are claiming that a conditional node deciding "keep going" is a model making a judgment call, as capable of being wrong, or never firing, as any other model output, and that almost nobody instruments it.
What to do today: give every loop a hard budget that routes to an explicit failed state, count reruns per worker rather than per round, and make the stop condition a structured field rather than free text a model has to phrase correctly.
Overview
The design patterns for multi-agent systems, manager-worker, peer collaboration, debate, get covered as concepts in a companion post on agent design patterns: what each buys you and when it's the right call. This one is about what happens after that decision, when you have to wire the pattern up as something that runs, loops, and eventually stops.
Every one of these patterns, no matter how different the diagram looks, reduces to three ingredients: agent nodes that produce output, a conditional node that inspects that output and decides what happens next, and an edge from the conditional back to an earlier node, which is what makes the thing a loop rather than a straight line. A two-agent back-and-forth, a group chat, and a five-layer reporting hierarchy are the same graph with different node counts and different rules about who talks to whom.
Agent → Conditional("continue?") → back to Agent, or → End
That sameness is useful, because it means the interesting engineering problem isn't the topology. It's the conditional node: what it's checking, how it decides, and what stops it from deciding "keep going" forever. The rest of this post is about that node, the budgets the major frameworks put around it, and what the published failure data says goes wrong.
The shape of the graph is the easy part. The conditional node that decides whether to loop back or stop is the hard part, and it's the piece that determines whether the system is safe to run unattended.
The Two-Agent Loop
The simplest version: one agent produces something, a second agent or a human responds, and a conditional checks whether the exchange is done. An assistant looping with a human until the user says they're satisfied is one version. Two agents ping-ponging, one drafting and one reviewing, until the reviewer signs off, is another.
This is the pattern worth building with the least hesitation, because the stopping condition is usually concrete: the human says "done", or the reviewer's output carries a pass/fail field you can check without asking a model to interpret free text.
The failure mode worth naming anyway is self-review dressed up as peer review. Agent B critiquing Agent A's output from the same context Agent A had isn't an independent check. It's the same blind spot with an extra model call attached. The research on intrinsic self-correction is fairly direct about the consequence: Huang et al. report that "LLMs struggle to self-correct their responses without external feedback, and at times, their performance even degrades after self-correction". If the second agent in the loop can't see anything the first one didn't have, the loop converges on agreement rather than correctness, and each extra round costs a full model call to move nothing.
Make the reviewer's advantage explicit and concrete. Give it a tool the drafter doesn't have, a document the drafter didn't get, a schema validator, a test run, a lookup against the system of record. External feedback is the variable the self-correction literature keeps landing on, and a tool call is the cheapest way to manufacture some.
Group Chat
Group chat patterns add an aggregator that broadcasts the conversation to several agents each round, collects responses, and asks a conditional whether the group is "done". It looks like the natural generalization of the two-agent loop. In practice it's the pattern we'd think hardest about before shipping.
Every extra agent in the room is another voice the conditional has to make sense of, and "is the group done" is a much fuzzier judgment than "did the reviewer approve this". Four agents producing a paragraph each, for several rounds, produce a transcript that is expensive to run and genuinely hard for anything to summarize into a clean stop signal. The rounds keep going not because more rounds help, but because nothing in the design forces a crisp answer to "helping compared to what".
The cost side is measurable and worse than it feels. Anthropic's write-up of its multi-agent research system reports that "agents typically use about 4× more tokens than chat interactions, and multi-agent systems use about 15× more tokens than chats", and that in one evaluation "token usage by itself explains 80% of the variance" in performance. Read together, those two facts say the spend is real and largely undirected: more tokens is the mechanism, so a loop that runs three rounds too long is not a rounding error.
If a task seems to want group chat, ask first whether it's manager-worker with the manager label missing. Most tasks that feel like they need several agents talking freely decompose into independent pieces once you look for the seams, and independent pieces don't need to be in the same room.
Manager-Worker Delegation
A manager dispatches subtasks to workers, collects results, and a conditional decides whether the overall task needs another round. This is the shape we'd default to, because when the subtasks are genuinely independent the manager's stopping condition is "have all dispatched subtasks returned, and did they succeed", which is a checkable fact rather than a judgment call.
manager -> [workerA, workerB] -> managerDecider
managerDecider: all results present and valid? -> end
managerDecider: a worker failed or returned incomplete? -> re-dispatch that worker onlyThe detail worth getting right is re-dispatching only the worker that failed, not the whole round. A manager that reruns every worker because one came back wrong turns a targeted retry into a full do-over, tripling cost for a problem that lived in one branch. Track success per worker, not per round, and the conditional's job gets much easier: it checks a small set of pass/fail flags instead of re-reading everything and deciding from scratch.
The pattern's real weakness is on the other side of the fan-out. Anthropic's post is candid that with synchronous execution "the lead agent can't steer subagents, subagents can't coordinate, and the entire system can be blocked while waiting for a single subagent". A per-worker timeout is therefore not optional politeness. It is the only thing standing between one stuck worker and a manager that never reaches its conditional at all.
Hierarchical Delegation
Nest manager-worker one or more levels deep, a director delegating to team leads who delegate to workers, and each layer gets its own loop and its own conditional deciding whether to send work back down. On paper this is manager-worker recursively applied. In practice it's the shape most likely to burn through a step budget without anyone noticing, because a failure at the bottom can trigger a rerun that ripples upward before anyone catches that the total work being redone has multiplied.
Take a worker whose output a team lead rejects. The worker retries, burning its own budget. If the team lead's output then also gets rejected by the director, the team lead reruns its entire delegation, not just its own summarization step, and the worker's retries start over inside that new round. Three layers each allowed five retries is not fifteen worker runs in the worst case. It's 125, because the layers compound multiplicatively.
Figure 1 — Why nested retries are not additive. Each layer's budget multiplies the layer below it, so one global counter hides the real worst case.
We'd only build a hierarchy past two levels when the structure it mirrors is genuinely that deep, and even then give every layer its own hard-capped budget rather than one global counter, so a bad worker can exhaust its own retries without silently eating the director's.
What the Frameworks Actually Cap
Before writing your own counter, find out what the framework already counts, because the defaults vary by almost two orders of magnitude and two of them are effectively "no limit".
| Framework | Knob | Default | What happens at the limit |
|---|---|---|---|
| LangGraph | recursion_limit, counted in supersteps | "Starting in version 1.0.6, the default recursion limit is set to 1000 steps" | Raises GraphRecursionError. Nodes running in parallel share one superstep; sequential nodes each cost one |
| OpenAI Agents SDK | max_turns on Runner.run | No default in the docs; max_turns=None "disables this turn limit" | Raises MaxTurnsExceeded. A turn is one LLM call, and the loop only ends on "text output with the desired type, and there are no tool calls" |
| Google ADK | max_iterations on LoopAgent | Optional. The docs warn the agent "does not inherently decide when to stop looping" | Loop terminates after that many iterations, or earlier when a sub-agent sets actions.escalate = True |
| CrewAI | max_iter per agent, plus max_retry_limit | max_iter 20, max_retry_limit 2 | At max_iter the agent "must provide its best answer", which is a stop, not a failure |
Table 1 — Loop budgets across four frameworks, read from each project's own documentation.
Three things in that table are worth arguing with.
A default of 1,000 supersteps is not a safety limit, it's a crash prevention limit. LangGraph's own
troubleshooting guidance says the error is "often due to an infinite loop" while acknowledging that "complex
graphs may hit the default limit naturally", which is exactly the ambiguity a loud small limit resolves and a
quiet large one doesn't. Pick a number that reflects how many rounds your task should need, set it per graph,
and read the counter while you run: LangGraph exposes the current step in
config["metadata"]["langgraph_step"] and a RemainingSteps managed value "that tracks how many steps remain
before hitting the recursion limit", which is what lets a node degrade gracefully instead of crashing.
CrewAI's behaviour at the limit is the one that will bite quietly. Hitting max_iter produces a best-effort
answer, not an exception, so a truncated result flows downstream looking exactly like a finished one. If you
use it, record the iteration count alongside the output and treat a run that hit the cap as suspect.
And a turn is not a round. The OpenAI SDK counts one LLM call per turn, so a single "round" of a two-agent exchange where each side makes a tool call and then speaks can consume four turns. Budgets set in rounds and enforced in turns are how a loop dies three quarters of the way through the work it was given.
What the Failure Data Says
The useful published work here is MAST, the Multi-Agent System Failure Taxonomy. Cemri et al. built it from "rigorous analysis of 150 traces, guided closely by expert human annotators and validated by high inter-annotator agreement (kappa = 0.88)", releasing a dataset of "1600+ annotated traces collected across 7 popular MAS frameworks". The taxonomy lands on "14 unique modes, clustered into 3 categories: (i) system design issues, (ii) inter-agent misalignment, and (iii) task verification".
Notice what a third of that taxonomy is about. Task verification is the conditional node: deciding whether the work is actually done and correct. It is not a subtopic of multi-agent failure, it is one of the three top-level categories, alongside the design of the system itself. The paper's own framing of the motivation is worth keeping in mind too, that performance gains for multi-agent systems "on popular benchmarks are often minimal".
The reliability numbers from τ-bench say why loops make this worse rather than better. Yao et al. introduced
pass^k, success on all k attempts rather than any one, and found that "even state-of-the-art function
calling agents (like gpt-4o) succeed on <50% of the tasks, and are quite inconsistent (pass^8 <25% in
retail)". A loop is a pass^k machine. Every iteration is another independent chance for the conditional to
misjudge, and an agent that is right half the time on a single pass is not right half the time across eight.
Anthropic's engineering account of running one of these systems in production adds the operational half: "minor system failures can be catastrophic for agents", because "one step failing can cause agents to explore entirely different trajectories, leading to unpredictable outcomes". The same post reports its multi-agent configuration outperforming single-agent Claude Opus 4 "by 90.2% on our internal research eval", which is the honest version of the trade. The pattern buys real capability and it buys it with compounding fragility.
Termination Is the Whole Problem
Figure 2 — Every pattern above is this state machine underneath. The Failed exit, triggered by a counter rather than a judgment call, is what makes "continue" a safe answer instead of an open-ended one.
Two things fix most of what goes wrong here, and neither is "make the decider smarter".
A hard, per-loop counter that fails closed. Every conditional needs a rerun limit, and hitting it needs to route to an explicit failed state, not a silent stop that looks like success downstream. A loop that quietly stops at its limit and returns whatever it had fails the same way every time and never tells anyone.
A termination signal that doesn't depend on free-text interpretation. "Does the response say DONE" is a worse check than "does the response carry a structured field with an explicit status", because the first depends on a model reliably producing an exact string under pressure and the second is something you can validate without asking a model anything. Where the task allows it, push the stop condition into something checkable: a schema field, a count matching an expected total, a test that passes.
The A2A protocol is worth borrowing from here even if you never speak it on the wire, because its task state
enum is what a serious answer looks like. It separates terminal states, where processing has concluded, from
interrupted ones, where it has paused awaiting the client. Completed, failed, canceled and rejected are
terminal. input-required and auth-required are interruptions. Two of those, rejection and
authentication, are conditions most homegrown loops express as a retry, which is why they show up in
production as a spin rather than a stop.
A conditional node that can only say "continue" or "stop" is missing a state. Give it at least a third: "stop, and flag this for a human", for cases where the loop is out of budget but the task genuinely isn't resolved. Silently returning a partial answer as final is worse than admitting the loop didn't finish.
Telling Loop Failures Apart
Four things go wrong in these loops and they present similarly enough that teams fix the wrong one. The distinguishing signal is usually in the counters, not in the transcript.
| Symptom | Likely cause | How you tell |
|---|---|---|
| Run ends at a suspiciously round number of steps | A budget hit, swallowed as a normal stop | Compare the step count against your cap. CrewAI's max_iter and any silent stop look identical to completion in the output |
| Reviewer approves everything | No independent signal. The reviewer shares the drafter's context | Check whether the reviewer has a tool or document the drafter lacked. If not, the approval carries no information |
| Cost per run doubles with no change in output quality | Retries re-running a whole round rather than one branch | Log dispatches per worker per round. A round with two dispatches of the same worker is the tell |
| Loop never ends and no error fires | No terminal state for "cannot proceed", or a limit set high enough to look like no limit | Look for a wait on input expressed as a retry, and check whether the framework default is 1,000 supersteps or None |
Table 2 — Four loop failures and the counter that separates each from its neighbours.
The instrumentation that resolves all four is small: reruns per node, dispatches per worker per round, the framework's own limit recorded next to the run, and which exit the run took. None of it is in the model output, so you have to record it yourself.
Frequently Asked Questions
What is the default recursion limit in LangGraph? As of version 1.0.6, 1,000 supersteps, raised from the
much lower earlier default. It counts supersteps rather than node executions, so parallel nodes share one and
sequential nodes each cost one. Exceeding it raises GraphRecursionError.
How do I stop an agent loop from running forever? Cap it in the framework, route the cap to a failed state
rather than a silent stop, and make the success condition structured. In the OpenAI Agents SDK that means
setting max_turns and catching MaxTurnsExceeded. In ADK it means setting max_iterations and having a
sub-agent set actions.escalate on real completion.
How many agents should a multi-agent system have? Fewer than the diagram wants. Each additional agent makes the "are we done" judgment harder and multiplies token cost. Anthropic measured multi-agent systems at roughly 15 times the tokens of a chat interaction.
Is group chat or manager-worker better? Manager-worker, unless the task genuinely resists decomposition. Its stopping condition is a set of pass/fail flags rather than an interpretation of a transcript.
Why does my agent loop stop before finishing the task? Usually because a budget expressed in rounds is being enforced in LLM calls. One conversational round can be several turns once tool calls are counted. Check the framework's unit before raising the number.
Do multi-agent systems actually outperform single agents? Sometimes, and the published picture is mixed. Anthropic reports a 90.2 percent improvement over single-agent Opus 4 on an internal research eval, while the MAST authors open by noting that multi-agent performance gains "on popular benchmarks are often minimal".
Can one agent review another agent's work? Only if the reviewer has something the author didn't. Intrinsic self-correction without external feedback is documented to be unreliable and sometimes actively harmful, and two agents sharing one context are closer to self-correction than to review.
Conclusion
The diagrams are the least interesting part of building one of these. Agent nodes, a conditional, an edge that loops back, and every pattern from a two-agent exchange to a five-layer hierarchy is those three pieces rearranged. What separates a multi-agent system that runs safely from one that quietly burns budget is the part nobody draws: the counter that fails closed, and a stop condition that doesn't rely on a model reporting on itself.
Build the two-agent loop and manager-worker delegation first, because their stopping conditions are nearly free. Reach for group chat and deep hierarchies only when the task can't be flattened, and when you do, give every layer its own budget rather than trusting one global counter. Then go and read what your framework's default limit actually is, because on current defaults it is either 1,000 or nothing at all.
Topology is a design choice. Termination is a safety requirement. Get the second one wrong and it doesn't matter how well the first was chosen.
Sources
All URLs read 2026-09-29.
- Why Do Multi-Agent LLM Systems Fail? (arXiv:2503.13657) — the MAST taxonomy: 14 failure modes in three categories, 150 traces analysed at kappa = 0.88, 1,600-plus annotated traces across seven frameworks.
- τ-bench: A Benchmark for Tool-Agent-User Interaction in Real-World Domains (arXiv:2406.12045) — the
pass^kmetric, and gpt-4o at under 50 percent single-pass success withpass^8under 25 percent in retail. - Large Language Models Cannot Self-Correct Reasoning Yet (arXiv:2310.01798) — intrinsic self-correction without external feedback, and performance degrading after self-correction.
- How we built our multi-agent research system, Anthropic — the 4× and 15× token multiples, token usage explaining 80 percent of variance, the 90.2 percent internal eval result, and the synchronous-execution coordination limits.
- LangGraph GRAPH_RECURSION_LIMIT troubleshooting — what the error means and how to raise the limit per invocation.
- LangGraph graph API reference — the 1,000-superstep default from version 1.0.6 and how parallel nodes share a superstep.
- Running agents, OpenAI Agents SDK —
max_turns,MaxTurnsExceeded, disabling the limit withNone, and the final-output rule that ends the loop. - LoopAgent, Google Agent Development Kit —
max_iterations,actions.escalate, and the warning that the loop agent does not decide on its own when to stop. - Agents, CrewAI documentation —
max_iterdefault 20,max_retry_limitdefault 2, and the best-answer behaviour at the iteration cap. - A2A protocol specification — the task state enum, and the separation of terminal states from interrupted ones such as
input-requiredandauth-required. - OWASP Top 10 for Agentic Applications — ASI08 Cascading Failures and ASI10 Rogue Agents, the security framing of loops that will not stop.