Lightning Lesson · 30 min · live

Most things shipped as agents
should have been workflows.

Five ways to wire a model into a system. Tonight we implement all five on the same task and measure every one on screen, so the choice stops being a matter of taste. The dearest costs six times the cheapest, and nobody picks that on purpose.

Ehsan Gazar
Staff Software Engineer · 16 years in production · 500+ mentees
Where we're going · three moves, 30 minutes

One argument, in three moves.

0–101 · Name the fiveand measure all of them on one task, so the numbers compare
10–202 · Find where the money goesthe moment a system stops having a forecastable bill
20–303 · The one questionthat decides between all five, and it isn't a capability question
Everything runs live and offline. The repo needs no API key and makes no network call, so you can clone it on the train home.
By the end of tonight

You'll be able to…

1Name all five patterns and say what each one buys. Chaining, routing, parallelisation, orchestrator-workers and evaluator-optimiser, with the fit for each, so you can name a shape in a design review and have the room follow.
2Measure the trade instead of arguing about it. Latency and token cost per pattern, with the one measurement mistake that hides the whole benefit of concurrency.
3Answer "do we need an agent here" in one question. And know why the honest answer is usually no, even when the capability answer is yes.
The recognition beat

Everything is called an agent now.
Almost nothing needs to be one.

Ask a team what they're building and you'll hear "an agent". Ask which steps it takes and most of them can list the steps, in order, on a whiteboard, before any request has arrived.

What people say

"It's an agent." Autonomy, tools, a loop, a plan. The word does a lot of work in a room where nobody wants to sound behind.

What they've built

Three prompts in a fixed order with a retry. Which is fine. It's a workflow, it's cheaper, it can be load tested, and calling it an agent costs them the ability to reason about it.

This isn't pedantry about words. The two things have different bills, different failure modes and different on-call stories.
The spine · say this one out loud twice tonight

A workflow is a system you can draw
before the request arrives.

An agent is one you cannot. That single distinction predicts everything downstream: whether you can forecast the bill, whether you can cap the latency, whether a load test means anything, and whether you can write the runbook before the incident.

Everything else in this talk is a consequence of that sentence.
The five · and the fit for each

Five shapes.
They are not interchangeable.

The names are the common vocabulary now, and using the common names matters more than the names being perfect. The point is being able to say "that's an orchestrator-workers shape" and have the room know what you mean.

flowchart TB
  A["Chaining
step N+1 needs step N's answer"] --> B["Routing
inputs are different kinds of thing"] B --> C["Parallelisation
pieces don't need to see each other"] C --> D["Orchestrator-workers
the model picks the subtasks"] D --> E["Evaluator-optimiser
you can write the bar down"] style A fill:#EDE9FE,stroke:#0D1B33,stroke-width:2px style B fill:#EDE9FE,stroke:#0D1B33,stroke-width:2px style C fill:#EDE9FE,stroke:#0D1B33,stroke-width:2px style D fill:#FEF3C7,stroke:#0D1B33,stroke-width:2px style E fill:#DCFCE7,stroke:#0D1B33,stroke-width:2px
chaining
routing
parallelisation
orchestrator-workers
evaluator-optimiser
Live · the control run

Start with the thing
everything else has to beat.

One support ticket. One call. One prompt. 120 tokens, 169 milliseconds. This is the row every other pattern is competing against, and a surprising number of production systems lose to it on every axis at once.

npm run patterns
The task is deliberately boring, because all six patterns can do it. If the task suited only one, the table would just be measuring the task.
The measurement · same ticket, all six

Same job. Six times the cost.

pattern
calls
tokens
wall
cost
single call
1
120
169ms
$0.00003
routing (bug)
2
242
415ms
$0.00006
evaluator-optimiser
2
323
539ms
$0.00010
chaining
3
388
643ms
$0.00011
parallelisation
4
613
697ms
$0.00019
orchestrator-workers
6
710
1.00s
$0.00025
Nobody chooses to spend six times more for the same answer. It's what you get by default when the shape is picked before the requirement.
The break · where the obvious answer is wrong

Parallelisation was slower.

Look at that table again. Parallelisation ran three branches at once and still took 697ms against chaining's 643ms, for 58% more tokens. The pattern whose entire job is to be faster lost to the pattern whose latency is additive by definition.

What we expected

Three calls at once beats three calls in a row. Obviously.

What actually happened

Three branches, then a merge call that has to wait for all of them. At N=3 you're dominated by the one call you still have to make afterwards.

This is real and it is the most useful thing in the talk. Now watch what happens when N stops being 3.
Live · the same code, two widths

The benefit is a function of N.

npm run race
3 branches
2.26x
faster, for the same tokens
10 branches
7.65x
faster, for the same tokens
Identical prompts both ways. The only difference in the source is await in a loop versus Promise.all.
The number that matters and nobody logs

If your metrics sum call durations,
concurrency is invisible.

The meter in this repo reports two different things, and the difference between them is the pattern.

serial time

The sum of every call's duration. This is the bill. It does not change when you parallelise, and it should not.

wall time

First call starts to last call ends. This is the wait. It is the only thing concurrency moves.

Most tracing defaults give you the sum. Which means the one benefit you paid for is the one number you cannot see.
The turn · where the bill stops being knowable

The line isn't the word "agent".
It's who writes the list.

Parallelisation and orchestrator-workers look identical on a diagram. In parallelisation you wrote the branches before the request arrived. In orchestrator-workers the model writes them, per request.

That is a real capability, and it costs you the ability to forecast anything.

flowchart LR
  R["request"] --> P{"who decides
the subtasks?"} P -->|"you, at design time"| W["workflow
forecastable bill
latency you can cap
load testable"] P -->|"the model, at run time"| A["agent-shaped
bill is unknown
latency is unknown
load test means little"] style W fill:#DCFCE7,stroke:#0D1B33,stroke-width:2px style A fill:#FEE4E2,stroke:#0D1B33,stroke-width:2px style P fill:#FEF3C7,stroke:#0D1B33,stroke-width:2px
The defect this pattern usually ships with

An uncapped fan-out is
a $2 request waiting to happen.

export const MAX_WORKERS = 4;

export function planToSubtasks(plan: string): string[] {
  const tasks = parse(plan);
  return tasks.slice(0, MAX_WORKERS);   // <- the whole slide
}

Without that line the number of workers is whatever the model felt like. A $0.002 request becomes a $2 one on an input nobody tested, and it will be a real customer's input, on a Friday.

The cap has a cost of its own, and it must be said out loud: it turns an unbounded system into a bounded one that fails above the cap. Decide what happens to those inputs before you ship.
The honest contrast · where my own advice runs out

If you can't write the rubric down,
you don't have evaluator-optimiser.

The critique loop is the best pattern on this list when it fits: translation against a style guide, code against tests, copy against a brand voice. It fits when a model can apply a bar more reliably than it can hit it first time.

When the bar isn't written down, what you have is a loop that costs money and terminates on a token budget. The stopping condition is the design, not a detail.

The default that saves you

isAcceptable returns true when it can't tell. Erring toward stopping is the safe direction.

Why that direction

The cost of shipping one mediocre draft is bounded. The cost of a loop that won't terminate is not.

Live · the takeaway command

The first question that answers "no"
stops you.

npm run choose

Five real-shaped systems through one ordered set of questions. Cheapest shape first, and every pattern has to earn its place by naming what it buys. An agent is last, and it is gated on cost, not on capability.

Because the capability question nearly always answers yes. That is exactly why everything becomes an agent.
The slide to screenshot

Same system. Same capability.
Different answer.

Fix a failing build in an unfamiliar repo

Steps unknowable. Needs to act. Unbounded cost accepted.

an agent

The same fix, on a workload finance has capped

Steps unknowable. Needs to act. Unbounded cost not accepted.

orchestrator-workers, with a hard cap

Identical engineering problem. The thing that flipped the answer was money, and money was never discussed in the design review.

Yours, tonight

Public, MIT, no key, no network.

npm install
npm run patterns   # all five, measured, one ticket
npm run race       # serial vs concurrent, at two widths
npm run choose     # the rule

Zero runtime dependencies, 20 tests, typecheck clean. If you only run one, run race, because it's the one you just watched and it's the one you can point a colleague at.

The caveat is in the README's first section rather than buried: the wiring is real, the model is simulated. Don't quote the absolute numbers as a benchmark. The comparison is what transfers.
Your turn · answer in the chat

Name a system your team calls an agent.
Could you draw it before the request arrives?

Not "does it use tools". Not "does it have a loop". Just: could you write its steps on a whiteboard this afternoon, without knowing what the user is going to ask?

If yes, it's a workflow, and it's costing you agent money.
Recap · five lines

What to take away.

1A workflow is a system you can draw before the request arrives. An agent is one you cannot. Draw it first.
2On the same task, the dearest pattern cost 6x the cheapest. The single call is a real contender, not a strawman.
3Concurrency buys latency, never tokens. 2.26x at three branches, 7.65x at ten, identical cost in both.
4If your metrics sum call durations you cannot see that benefit at all. Log wall time separately.
5Cap every fan-out, and set a max round count on every critique loop. Unbounded is a bill, not a feature.
Two things, in this order

Clone it. Then tell me
the one you can't draw.

The repo is public and MIT. Run race first and read the README's caveat section before the code.

Then reply and tell me about a system on your team that genuinely cannot be drawn in advance. I read every reply, and those are the interesting ones, because the honest agent case is much rarer than the word suggests.