A field guide to wiring AI agents as graphs. The shapes that keep coming up, the ones people oversell, runnable code for each, and the token and latency bills nobody prints on the box.
Most of what got written about "graph engineering" this month is one paragraph rephrased four hundred times: loops are dead, graphs are the future, here's a 14-step roadmap. Almost none of it ships a graph you can run, and none of it tells you what fanning out to eight agents does to your bill or your p95. This repo is the part that got skipped.
There's no framework here. The runner in runner/graph.py is about forty lines of standard-library Python built on graphlib. Every pattern is a small shape on top of it. Replace the stub in runner/agent.py with your own model call and the examples spend real tokens; leave it and they run offline, for free, so you can read the flow before you pay for it.
Strip the branding and a graph is three things:
- nodes — a unit of work. Usually one model call with one job. Sometimes a plain function (parse, dedupe, write to disk).
- edges — "this node needs that node's output first." Nothing more. An edge is a dependency, not a message bus.
- state — a dict every node reads from and writes to. A node returns a value; the runner files it under the node's name; downstream nodes read it.
The scheduler runs each node the moment its dependencies are done, in parallel when several are ready at once. That is the whole model. A loop is a graph with one node and a back-edge — not a lesser thing than a graph, a special case of one. Keep that in mind every time someone tells you loops are obsolete.
If you want the longer version with the vocabulary sorted out, read primer.md first.
Each file is self-contained: a diagram, when to reach for it, when not to, runnable code, the cost shape, and the ways it breaks.
| pattern | reach for it when |
|---|---|
| chain | the work is genuinely sequential and you want each step small enough to debug |
| router | one classifier up front can send the request down a cheaper or more specialised path |
| parallel fan-out | several independent takes on the same input, gathered at the end |
| orchestrator–worker | you can't list the subtasks in advance; a planner decides them at runtime |
| evaluator–optimizer | quality matters more than latency and a second agent can judge the first |
| reflection | the model can improve its own draft against a rubric before anyone sees it |
| map–reduce | the input is bigger than a context window and splits cleanly |
| human-in-the-loop | a step is expensive, irreversible, or someone has to sign off |
The first five are the ones you'll actually use. The rest are for when a specific problem demands them.
This is what makes the guide worth linking to instead of the roadmaps.
- costs.md — the arithmetic. Fan-out multiplies tokens, adds a barrier, and turns one retry into N. Read this before you draw anything wide.
- when-not-to-use-a-graph.md — most tasks want a single call or a loop. Graphs earn their complexity only under real parallelism or hard branching. The skeptics circling the hype are mostly right.
- failure-modes.md — stampedes, stuck loops, context rot, the aggregator that quietly drops half its inputs. All of them are avoidable once you've seen them once.
python runner/example_review.py
Reviews a diff with three specialists in parallel, reconciles their findings, then has a separate verifier drop the ones that don't hold. Runs offline on the stub. To use a real model:
pip install anthropic
AGENT_BACKEND=anthropic ANTHROPIC_API_KEY=sk-... python runner/example_review.py
Everything is Python 3.9+ and standard library except the optional model call.
Built on prior art, not invented here. The pattern names mostly come from Anthropic's Building Effective Agents; the execution model is the same dataflow idea LangGraph and, further back, Google's Pregel formalise. Full list with what each one is good for in references.md.
MIT. Take it apart.