Five patterns cover almost everything teams are building right now: chaining, routing, parallelization, orchestrator-workers, evaluator-optimizer. Tonight: what each one costs, which failure mode each one buys you, and the honest answer that most systems sold as agents should have been one of these.
The word "agent" got attached to anything with a model in it, and teams bought non-determinism they never needed. The real question is not how smart the model is. It's who decides what happens next: your code, or the model.
A workflow is a recipe you wrote. An agent is a cook you hired. Both can produce dinner. Only one of them can decide, at 2am, to reorganise your kitchen. Hire the cook when the recipe genuinely can't be written, not before.
In a workflow, your code owns the path and the model fills in steps. In an agent, the model owns the path and your code executes what it asks for.
Path is fixed at write time. Testable per node, bounded by construction, cost is knowable before you ship.
Path is decided at run time. Every axis is unbounded until you bound it, and the worst case is "it never stops".
flowchart LR
wIn["Input"]:::in --> wS1["Step 1"]:::step
wS1 --> wG{"Check"}:::q
wG --> wS2["Step 2"]:::step
wS2 --> wOut["Output"]:::out
classDef in fill:#DCEBFE,stroke:#0D1B33,color:#0D1B33;
classDef step fill:#ffffff,stroke:#0D1B33,color:#0D1B33;
classDef q fill:#FEF3C7,stroke:#0D1B33,color:#0D1B33;
classDef out fill:#D6F5E3,stroke:#0D1B33,color:#0D1B33;
flowchart LR
aIn["Goal"]:::in --> aM["Model decides"]:::model
aM -->|act| aT["Run a tool"]:::step
aT -->|observe| aM
aM -->|done| aOut["Output"]:::out
classDef in fill:#DCEBFE,stroke:#0D1B33,color:#0D1B33;
classDef step fill:#ffffff,stroke:#0D1B33,color:#0D1B33;
classDef model fill:#EDE9FE,stroke:#0D1B33,color:#0D1B33;
classDef out fill:#D6F5E3,stroke:#0D1B33,color:#0D1B33;
Break the task into ordered steps and put a check between them. Validate the outline before you spend tokens writing the essay.
The task has clean, checkable sub-steps and each step's output is the next step's input.
The steps are actually independent, parallelise instead, or the shape isn't known up front, orchestrate instead.
flowchart LR
chIn["Brief"]:::in --> chA["Draft outline"]:::model
chA --> chG{"Valid?"}:::q
chG -->|no| chX["Reject early"]:::bad
chG -->|yes| chB["Write from outline"]:::model
chB --> chC["Tighten and proof"]:::model
chC --> chOut["Final"]:::out
classDef in fill:#DCEBFE,stroke:#0D1B33,color:#0D1B33;
classDef q fill:#FEF3C7,stroke:#0D1B33,color:#0D1B33;
classDef model fill:#ffffff,stroke:#0D1B33,color:#0D1B33;
classDef bad fill:#FEE4E2,stroke:#0D1B33,color:#0D1B33;
classDef out fill:#D6F5E3,stroke:#0D1B33,color:#0D1B33;
A small, fast model reads the request first and sends it down the right-sized path. Cheap model for the easy ones, frontier model for the hard ones, a dedicated flow for refunds.
Inputs fall into distinct kinds that each want different handling or a different model tier. The easiest cost win you get.
The classifier is wrong. A misroute sends a hard case down the cheap path and nobody notices. Measure routing accuracy.
flowchart LR
rtIn["Request"]:::in --> rtR{"Classify"}:::q
rtR -->|faq| rtS["Small model"]:::cheap
rtR -->|refund| rtF["Dedicated flow"]:::step
rtR -->|hard| rtL["Frontier model"]:::model
rtS --> rtOut["Response"]:::out
rtF --> rtOut
rtL --> rtOut
classDef in fill:#DCEBFE,stroke:#0D1B33,color:#0D1B33;
classDef q fill:#FEF3C7,stroke:#0D1B33,color:#0D1B33;
classDef cheap fill:#D6F5E3,stroke:#0D1B33,color:#0D1B33;
classDef step fill:#ffffff,stroke:#0D1B33,color:#0D1B33;
classDef model fill:#EDE9FE,stroke:#0D1B33,color:#0D1B33;
classDef out fill:#FEE4E2,stroke:#0D1B33,color:#0D1B33;
Two flavours. Sectioning splits one job into independent parts. Voting runs the same job several times and takes the consensus. Faster, and often better.
Subtasks don't depend on each other, or independent votes raise reliability on a hard judgement call.
Branches secretly depend on one another, or the merge is naive and quietly averages away real disagreement.
flowchart LR
paIn["Document"]:::in --> pa1["Summarise"]:::model
paIn --> pa2["Extract risks"]:::model
paIn --> pa3["List next actions"]:::model
pa1 --> paM["Merge or vote"]:::win
pa2 --> paM
pa3 --> paM
paM --> paOut["Result"]:::out
classDef in fill:#DCEBFE,stroke:#0D1B33,color:#0D1B33;
classDef model fill:#ffffff,stroke:#0D1B33,color:#0D1B33;
classDef win fill:#FEF3C7,stroke:#0D1B33,color:#0D1B33;
classDef out fill:#D6F5E3,stroke:#0D1B33,color:#0D1B33;
A planner splits work it cannot know in advance, spawns a worker per subtask, then synthesizes. The branches are decided while it runs. This is the first real taste of agency.
The number and shape of subtasks depend on the input: multi-file edits, research over an unknown set of sources.
The plan runs away with no cap on subtasks, or the synthesis step drowns in worker output and loses the thread.
flowchart LR
orIn["Task"]:::in --> orP["Planner"]:::model
orP --> orW1["Worker 1"]:::step
orP --> orW2["Worker 2"]:::step
orP --> orW3["Worker n, capped"]:::step
orW1 --> orS["Synthesize"]:::win
orW2 --> orS
orW3 --> orS
orS --> orOut["Result"]:::out
classDef in fill:#DCEBFE,stroke:#0D1B33,color:#0D1B33;
classDef model fill:#EDE9FE,stroke:#0D1B33,color:#0D1B33;
classDef step fill:#ffffff,stroke:#0D1B33,color:#0D1B33;
classDef win fill:#FEF3C7,stroke:#0D1B33,color:#0D1B33;
classDef out fill:#D6F5E3,stroke:#0D1B33,color:#0D1B33;
Generate, score against a rubric, revise, loop until it passes. Quality that improves without a human in every turn. Always cap the loop, or a $5 task quietly becomes a $50 one.
There's a clear rubric and revision reliably improves the draft: translation, code that must pass tests, structured extraction.
The evaluator can't tell better from worse, so it thrashes, or the loop has no cap and burns budget on cosmetic edits.
flowchart LR
evIn["Task"]:::in --> evG["Generate draft"]:::model
evG --> evE{"Score vs rubric"}:::q
evE -->|fail, under cap| evR["Revise"]:::model
evR --> evE
evE -->|pass or cap hit| evOut["Ship"]:::out
classDef in fill:#DCEBFE,stroke:#0D1B33,color:#0D1B33;
classDef model fill:#ffffff,stroke:#0D1B33,color:#0D1B33;
classDef q fill:#FEF3C7,stroke:#0D1B33,color:#0D1B33;
classDef out fill:#D6F5E3,stroke:#0D1B33,color:#0D1B33;
# orchestrator: the planner decides the branches, you decide the ceiling
plan = orchestrator(task) # -> list of subtasks
assert len(plan) <= MAX_SUBTASKS # 1. bound the fan-out
results = gather(*[worker(s) for s in plan])
answer = synthesise(task, results)
# evaluator-optimizer: generate, critique, revise, ALWAYS capped
draft = generate(task)
for _ in range(MAX_ROUNDS): # 2. bound the loop
v = evaluate(draft, rubric)
if v.passed: break
draft = revise(draft, v.feedback)
return draft
flowchart TB
cIn["Request"]:::in --> cR{"Router"}:::q
cR -->|simple| cS["Single call"]:::step
cR -->|structured| cC["Chain with gates"]:::step
cR -->|research| cO["Orchestrator"]:::model
cO --> cP["Parallel workers"]:::step
cS --> cE{"Evaluator"}:::q
cC --> cE
cP --> cE
cE -->|fail| cC
cE -->|pass| cOut["Response"]:::out
classDef in fill:#DCEBFE,stroke:#0D1B33,color:#0D1B33;
classDef q fill:#FEF3C7,stroke:#0D1B33,color:#0D1B33;
classDef step fill:#ffffff,stroke:#0D1B33,color:#0D1B33;
classDef model fill:#EDE9FE,stroke:#0D1B33,color:#0D1B33;
classDef out fill:#D6F5E3,stroke:#0D1B33,color:#0D1B33;
Most features stop at the first gate, and that is a good outcome, not a failure of ambition.
Can you write the steps in code ahead of time? If yes, a workflow wins.
Must it act, observe the result, and decide the next move on the fly? If a fixed chain would do, you don't need an agent.
Is the value worth the tokens, latency, and blast radius, and can you afford the guardrails and evals it demands?
flowchart TB
gStart["A task to build"]:::in --> g1{"Steps known in advance?"}:::q
g1 -->|yes| gWF["Use a workflow"]:::wf
g1 -->|"no, open-ended"| g2{"Must it act, observe, decide?"}:::q
g2 -->|no| gWF
g2 -->|yes| g3{"Worth cost, latency, risk?"}:::q
g3 -->|"not yet"| gWF
g3 -->|yes| gAG["Agent, with guardrails and evals"]:::ag
classDef in fill:#DCEBFE,stroke:#0D1B33,color:#0D1B33;
classDef q fill:#FEF3C7,stroke:#0D1B33,color:#0D1B33;
classDef wf fill:#D6F5E3,stroke:#0D1B33,color:#0D1B33;
classDef ag fill:#EDE9FE,stroke:#0D1B33,color:#0D1B33;
| Axis | Guardrail |
|---|---|
| Iterations | Hard max-steps, and break on no-progress. |
| Budget | Token and dollar ceiling per request. Kill the run on breach. |
| Tools | Allow-list, least privilege, no raw shell. |
| Irreversible acts | Human approval, or a dry-run first. |
| Time | Per-tool and per-request timeouts. |
| Input and output | Structured tool schemas. Validate every argument before it runs. |
| Failure | A typed fallback path. Never a raw stack trace to the user. |
| Shape | Model calls | Rel. tokens | Rel. p95 latency | Blast radius |
|---|---|---|---|---|
| Single call | 1 | 1× | 1× | tiny |
| 3-step chain | 3 | ~3× | ~3× | small, bounded |
| Router plus flow | 2 | ~1–2× | ~1.5× | small |
| Parallel, 4 branches | 4 | ~4× | ~1× concurrent | medium cost |
| Agent, ~10 turns | 10–30+ | 10–30× | 10×+ | large, unbounded |
| If the task is… | Reach for | Because |
|---|---|---|
| Ordered steps, each checkable | Chaining | Gate between steps, fail early and cheap. |
| Different inputs, different handling | Routing | Right-size the model per request. |
| Independent subtasks, known up front | Parallelization | Latency down, quality up via voting. |
| Subtasks unknowable until run time | Orchestrator-workers | The planner decides the branches. |
| Quality needs iteration to a bar | Evaluator-optimizer | Critique and revise, capped. |
| Open-ended, must loop, worth the risk | Agent | Only after all three gates clear. |
Five patterns cover almost everything: chaining, routing, parallelization, orchestrator-workers, evaluator-optimizer. Reach left on the ladder. Gate between steps with the cheapest check that catches the failure. Cap every loop, price the worst case, and instrument it before you scale. Say those out loud in a review and you're designing, not guessing.
Production-Ready Systems with LLMs and Agents: An Intensive for Engineers. Six weeks, twelve live sessions, graded projects where you ship real systems, not notebooks. Week 1 is exactly this: all five patterns, implemented, gated, and priced.