Loops and graphs

How do you design a graph of AI agents?

Written as of · Published · By Robert Scales, with AI tools · 8 min read

Short answer

Start from the units of work and the checks each unit must pass, then add the nodes that connect them, which are a splitter that breaks the job into units, workers that run in parallel where units are independent, plain code steps, and gates that can fail a unit. Send a failed unit back alone with the evidence of what failed, turn repeated failures into lessons for the brief, and keep a person at the step where a mistake would do real damage.

A single AI loop makes one piece of work correct: produce it, check it, fix what failed, repeat. Real jobs usually contain many pieces. Thirty help articles to update after a price change, forty sources to read for a report, a dozen files to change in a codebase. A graph is the plan that organizes those pieces: which units of work exist, which step handles each one, what can run at the same time, where the checks sit, and where failed work goes next.

The word comes from mathematics, where a graph is a set of nodes (points) joined by edges (connections). In an agent system a node is a step that does something, either a model call or ordinary code, and an edge says which step runs next. Frameworks such as LangGraph build workflows this way explicitly, and Anthropic's guide to building agents describes the common shapes. You do not need a framework to design one, though. The design questions come first.

The four kinds of node

Most agent graphs are built from four roles.

RoleWhat it doesUsuallyExample
SplitterBreaks the job into units and writes a brief for eachA model, checked by codeLists the 30 articles that mention the old price
WorkerDoes one unitA modelRewrites one article
Code stepDoes something exact and repeatablePlain codeFetches a page, counts words, merges results
GateDecides whether a unit passesCode first, a model judge only where code cannot decideFails any article that still shows the old price

Anthropic calls the first two together the orchestrator-workers pattern: a central model breaks the task down, hands pieces to workers and combines what comes back. Its multi-agent research system works that way, with a lead agent planning the search and starting several subagents that search at the same time, each with its own context window.

Two points about the splitter. Its output is a list, so check it with code: does it cover every item it should, with no duplicates? And each unit's brief must stand on its own. Anthropic reports that vague instructions to its research subagents led them to duplicate each other's work or leave gaps; each one needed an objective, an output format, guidance on tools and sources, and clear limits.

Gates deserve the most care, because a graph is only as reliable as its checks. A gate is real only if it can fail a unit on its own with evidence the worker cannot argue with, such as a test that fails, a quote that is not in the source, or a file changed outside the plan. The companion article on what makes a check real covers this in detail.

Decide what runs in parallel

Units that do not depend on each other can run at the same time. Anthropic calls this sectioning, one of its two forms of parallel work (the other, voting, runs the same task several times and compares the results). In LangGraph, a node can fan out to the same worker once per item with Send, each copy getting its own input, and a reducer (a small function that says how results are combined) collects the outputs, for example by adding each one to a list. The OpenAI Agents SDK documentation shows the plain-Python version: run independent agents together with asyncio.gather, and keep the flow in your own code when you want speed, cost and behavior to be predictable.

Parallel work has two real costs, and the design must account for both.

Tokens. Anthropic reports that its multi-agent research setup used about fifteen times as many tokens as an ordinary chat. That pays off for broad research questions, much less for small jobs.

Consistency. Workers that cannot see each other's work make decisions independently, and those decisions can clash. Cognition, the company behind the Devin coding agent, argued in 2025 that this makes parallel subagents fragile for tasks like writing code, where every part depends on choices made in the others. Anthropic made a related point: tasks in which all agents need the same context, including most coding, are a poor fit for splitting across agents today.

The practical rule: split only along lines where units really are independent, and put any shared decision (the new price, the tone, the naming convention) in every unit's brief before the workers start. Then add a code step after the join that checks the units against each other, not only one by one.

Two ways back: correction and learning

A graph has two kinds of return path, and mixing them up is a common design error.

Correction edgeLearning edge
PurposeFix this unit nowStop the same failure in future units
CarriesThe unit, the evidence of what failed, the scope of the fixA short lesson
Goes toThe worker, for one more attemptThe brief or instructions every worker reads
Example"Paragraph 3 still says $10/month; change only that paragraph""Write prices as $12/month, never as 12 dollars"
LimitA fixed number of attempts, then a personReviewed before it becomes permanent

The correction edge is the short loop. It should be specific: what failed (the gate's output, copied in full) and how much of the unit may change. "Fix the price in paragraph 3" invites a small, checkable change; "try again" invites a rewrite that can break what already passed.

The learning edge is the longer loop. When several units fail the same way, the cause is usually the brief, not the workers. The Reflexion paper (Shinn and colleagues, 2023) showed one form of this: an agent turns feedback from a failed attempt into a short written reflection, keeps it in memory, and reads it before the next try. Anthropic's long-running coding agents use a plainer form, a progress file and a git history each new session reads before starting work. In a graph, the lesson goes into the shared brief, so units that have not started yet benefit too. Keep lessons short, and have a person review them before they become permanent instructions, because a wrong lesson spreads to every unit.

Return one failed unit, not the whole batch

When three units out of thirty fail a gate, send back those three. Rerunning all thirty wastes tokens, and worse, gives twenty-seven units that already passed a chance to come back different and fail. To make this possible, keep each unit's status, output, attempt count and last failure separately, rather than one shared blob of results.

Set a limit on attempts per unit (two or three is common) and route anything that hits it to a person with the full failure history. Add a limit for the whole run as well. LangGraph, for example, stops a run that exceeds its recursion limit, a cap on the number of steps, and raises an error. Anthropic notes that maximum-iteration limits are a common way to keep agents under control.

The control flow fits in a page of plain Python. In this sketch, split, work, the gates and lesson_from are functions you supply; each gate returns a list of failure messages, empty when the unit passes.

from concurrent.futures import ThreadPoolExecutor

MAX_ATTEMPTS = 3

def run_graph(job, split, work, gates, lesson_from):
    units = split(job)                      # splitter: list of {"id", "task"}
    lessons = []                            # learning edge: shared by future units
    results, attempts, evidence = {}, {}, {}
    pending = [u["id"] for u in units]
    by_id = {u["id"]: u for u in units}

    while pending:
        with ThreadPoolExecutor() as pool:  # workers run in parallel
            outs = pool.map(lambda uid: work(by_id[uid], lessons, evidence.get(uid)), pending)
            outputs = dict(zip(pending, outs))
        retry = []
        for uid, out in outputs.items():
            attempts[uid] = attempts.get(uid, 0) + 1
            failed = [msg for gate in gates for msg in gate(by_id[uid], out)]
            if not failed:
                results[uid] = out
            elif attempts[uid] < MAX_ATTEMPTS:
                evidence[uid] = failed      # correction edge: this unit only
                retry.append(uid)
            else:
                results[uid] = {"escalate": failed}
        new = lesson_from([evidence[u] for u in retry])
        if new and new not in lessons:
            lessons.append(new)
        pending = retry
    return results                          # next: one human review of the joined result

Only the units in retry go around again, each with its own evidence. Units that hit MAX_ATTEMPTS come out marked for escalation instead of looping forever.

A worked example: updating thirty help articles

A software company changes its basic plan from $10 to $12 a month. Its help center has hundreds of articles; some mention the price.

  1. Splitter (code, then model). A search finds every article containing "$10" or "10 dollars". A model reads each hit and keeps only those about the basic plan's price, writing a one-line brief per article. A code check confirms that every search hit was either kept or rejected with a reason. Result: 30 units.
  2. Workers (model, in parallel). Each worker gets one article, its brief, and the shared decisions: the new price, the date it applies, the wording for existing customers.
  3. Code gates. Each rewritten article must no longer contain the old price, must contain "$12/month" where the old price was, must differ from the original only in paragraphs that mentioned the price, and must keep every link working.
  4. Judge gate (model). A separate model checks one question per article, pass or fail: does the article still say anything that contradicts the new price, such as an annual total worked out from the old one?
  5. Correction edge. Say four articles fail: three still have an annual total of $120, one changed a paragraph it should not have touched. Each goes back alone, with the gate's message and the paragraph it may change.
  6. Learning edge. Three failures share a cause, the annual total. The lesson "also update annual totals: $144/year" is added to the brief, after a person glances at it, so any articles still in progress get it.
  7. Join and human gate. A code step assembles all thirty changes into one review page, with a diff per article, the gate results, and the four that needed correction. A person reviews that page once and approves the publish. Publishing is the only step that reaches customers, so it is the only step a person must approve.

The brief each worker receives can follow a fixed template:

Prompt
You are updating one help-center article.
Unit: {article_id} — {title}
Goal: {one-line brief from the splitter}
Shared decisions (apply exactly): {new price, date, wording}
Lessons from earlier units: {lessons, or "none yet"}
Scope: change only paragraphs that mention the price. Leave every other sentence as it is.
Checks your output will face: old price absent, "$12/month" present, no changes outside price paragraphs, links unchanged.
{If this is a retry: "Your last attempt failed these checks:" followed by the gate output, word for word.}
Return the full article text and nothing else.

Try it: Take a job you would hand to several agents and fill in four columns before writing any code: the units, the check each unit must pass, the evidence a failed check returns, and the one step a person must approve. If a column is hard to fill in, the graph is not ready to build.

When is a single agent loop better than a graph?

Often. Anthropic's advice is to start with the simplest design that works and add structure only when it clearly improves results, and the graph above is more structure than many jobs need. If the work is small, or if every part depends on decisions made in the other parts, one agent working through the task in order, with real checks along the way, is usually cheaper and more consistent. Cognition's argument against parallel subagents is strongest in exactly these cases: when context is split, decisions drift apart.

A graph earns its cost when the units are genuinely independent, when there are enough of them that parallel work saves real time, and when each unit has a check that can fail it on its own. Research across many sources fits that description; many coding tasks do not.

There is also a middle path. A single main agent can hand narrow, read-only questions to helpers (find every file that uses this function, summarize this document) while keeping all the decisions and edits in its own thread. That keeps one consistent line of decisions and still gets help with the work that splits cleanly. The multi-agent workflow guide on this site walks through the building blocks if you decide a graph is the right fit.

Sources

  1. Building effective agents — Anthropic, December 19, 2024
  2. How we built our multi-agent research system — Anthropic, June 13, 2025
  3. Graph API overview (LangGraph docs source) — LangChain, September 3, 2026
  4. Agent orchestration (OpenAI Agents SDK documentation) — OpenAI (GitHub), March 8, 2026
  5. Reflexion: Language Agents with Verbal Reinforcement Learning — arXiv (Shinn et al., Northeastern, MIT, Princeton), October 10, 2023
  6. Don't Build Multi-Agents — Cognition, June 12, 2025
  7. Effective harnesses for long-running agents — Anthropic, November 26, 2025

Sources for the articles linked in the text

Each linked article has its own page, with its sources and its date.

From the Guide to Artificial Intelligence, by Robert Scales, published by Holistic Hybrid Inc. Share and adapt it under CC BY-NC-SA 4.0: credit the guide, link to https://guidetoartificialintelligence.com/en/learn/design-graph-of-agents/, no commercial use without permission, and keep the same licence.

Get the free Guide to Artificial Intelligence

Sign up for the newsletter and receive the free guide: what changed in AI, one lesson, one prompt to try. Unsubscribe in one click.

How we handle your address

Back to top

Display

Your choices are saved in this browser only. No data is sent.

Text size
100%
Colours
Reading
More settings
Accessibility statement

Get the app

Add the guide to your home screen. It opens like an app, and the pages you have read stay available offline.

Read without a connection: download the articles

iPhone and iPad (Safari, Chrome, Edge)

  1. Tap the Share button (the square with an arrow). In Chrome and Edge, it is in the address bar or the menu.
  2. Choose “Add to Home Screen”.
  3. Tap “Add”.

Android (Chrome, Edge, Samsung Internet, Firefox)

  1. Open the browser menu (⋮ or ☰).
  2. Choose “Install app” or “Add to Home screen”.
  3. Confirm with “Install”.

Computer (Chrome, Edge)

  1. Click the install icon at the right end of the address bar, or open the browser menu (⋮ or …).
  2. In Chrome, choose “Cast, save and share”, then “Install page as app”. In Edge, choose “Apps”, then “Install this site as an app”.
  3. Click “Install”.

Mac (Safari)

  1. Open the File menu.
  2. Choose “Add to Dock”.
  3. Click “Add”.

Firefox on a computer can’t install a site as an app. Use Chrome, Edge or Safari instead.