ap.Abhishek Patel

Search

Search notes, essays, projects, the library and the AI course. Use arrow keys to choose and Enter to open.

Lesson 6 of 10 30 min

Workflow patterns: chain, route, parallelise, orchestrate, evaluate

Recognise and build the five workflow patterns from Anthropic's Building effective agents, and compose them instead of reaching for one giant agent.

Words in this lessonAugmented LLM · Prompt chaining · Routing · Parallelisation · Orchestrator-workers · Evaluator-optimizer
Augmented LLM
A single model call given helpers such as retrieval, tools or memory: the basic building block of every workflow and agent. Like a single kitchen station with its own tools: the stations are combined in different ways to run the kitchen.
Prompt chaining
A workflow that breaks a task into fixed steps, where each model call works on the previous call’s output. Like an assembly line where the cake is baked, then iced, then boxed, with an inspector between stations.
Routing
A workflow that classifies an input first, then sends it to a specialised handler. Like the front desk of a hospital sending each patient to the right department.
Parallelisation
A workflow that runs several independent model calls at the same time and then combines their results. Like three cooks preparing the starter, main and dessert at once instead of one cook doing them in turn.
Orchestrator-workers
A pattern where one model call plans the sub-tasks for each input, hands them to workers, and combines the results. Like a head chef reading an unusual catering order and deciding which stations need to be involved.
Evaluator-optimizer
A loop where one model call produces work, another critiques it against clear criteria, and the first revises. Like a writer and an editor passing a draft back and forth until it is ready to print.
On this page
  1. Why one big agent fell over
  2. The building block: one augmented model
  3. Following one hamper through five patterns
  4. Opening the hood: the five patterns
  5. 1. Prompt chaining
  6. 2. Routing
  7. 3. Parallelisation
  8. 4. Orchestrator-workers
  9. 5. Evaluator-optimizer
  10. And then: agents
  11. Building the patterns in code
  12. Routing
  13. Prompt chaining with a gate
  14. Parallelisation by sectioning
  15. Orchestrator-workers with a typed plan
  16. Evaluator-optimizer
  17. Where it breaks
  18. Try it yourself
  19. Common mistakes
  20. Recap
  21. Go deeper

Every November, Crumb & Co. launches its Christmas hampers. This year Meera wanted twelve: a plum-cake classic, a nut-free family box, an eggless office hamper, and nine more. Each needed a list of items, a check that those items would be in stock at all six stores through December, a price that kept the margin above 35%, and a description for the website in English and Marathi. Last year, four people spent two weeks on it. The hampers went live on 12 December, too late for most corporate orders.

“Can a pile of agents do it?” Meera had asked.

Arjun did not build a pile. He built one very big agent. It had nine tools (recipe search, stock lookup, cost lookup, margin calculator, copywriter, translator, and three more) and a 2,000-word system prompt describing every rule. He typed “Plan all twelve Christmas hampers” and pressed enter.

It ran for six minutes and forty-seven steps, and cost about $3, roughly ₹260. The first five hampers were good. By hamper nine it had forgotten the 35% margin rule. Hamper eleven, the “nut-free family box”, had a description that mentioned “crunchy walnut brownies”. Two hampers used a gift tin that Wakad had sold out of. And when Arjun tried to find where things went wrong, he was scrolling through forty-seven steps of a conversation that was by then longer than a short novel.

Important

The idea in one sentence: most “agent” problems are better solved by composing a few simple, well-understood workflow patterns (prompt chaining, routing, parallelisation, orchestrator-workers and evaluator-optimizer), each of which puts one model call in a small, testable box, and you only hand the whole task to an open-ended agent when no fixed shape fits.

Why one big agent fell over

Priya read the log for ten minutes, then asked one question. “How does the Crumb kitchen make two hundred hampers a day in December?”

“Stations,” Arjun said. “One person bakes, one checks stock, one packs, one does labels. And the supervisor checks every tenth box.”

“Does one cook do everything for one hamper, from flour to ribbon?”

“No. It would take forever, and they’d forget things.”

“Your agent is that one cook. Every hamper, every rule, every tool, in one long conversation. Remember what happens to the conversation as it grows?”

He remembered the ticket rail from Your first agent. “Every step re-reads everything. By step forty, it’s re-reading a novel. And the margin rule from the system prompt is buried under thousands of words of stock lookups.”

“So it isn’t stupid. It’s overloaded. Models pay less attention to instructions when they’re surrounded by huge amounts of other text. And you can’t test it, because there’s no ‘step’ you can point at. There’s just one enormous conversation.”

She drew a row of boxes on the whiteboard. “Anthropic’s engineers wrote an essay, Building effective agents, after working with dozens of teams. Their main finding is uncomfortable for people who love agents: the most successful systems mostly weren’t using complex frameworks or giant autonomous agents. They used simple, composable patterns. There are five of them, built from one block. Learn the five, and you’ll design most systems in an afternoon.”

The building block: one augmented model

Every pattern is built from the same brick: an augmented LLM, meaning a single model call with access to helpers such as retrieval (searching your documents), tools (functions it can ask your code to run) and memory (facts carried over from before). You’ve built this brick several times already: the support agent’s tool calls, the allergy search, the database tool.

The patterns are different ways of wiring bricks together. In each, your code decides the wiring. That’s what makes them workflows rather than agents, in the sense from the first lesson: your code holds the steering wheel.

Following one hamper through five patterns

Priya and Arjun rebuilt the hamper planner as a composition of patterns, then traced one hamper through it end to end. The input was a single line from Meera’s brief:

"Nut-free family box, budget ₹2,500 retail, for families with a nut allergy."

Pattern 1: routing. A small, fast call classifies the brief into one of four hamper types, each with its own specialised instructions:

{"type": "allergy-safe", "reason": "explicitly nut-free, for allergy families"}

The router costs about 400 tokens: a fraction of a paisa. Allergy-safe hampers go to a path with stricter rules (only items from certified nut-free suppliers, mandatory allergen check). Office hampers go to a path focused on bulk pricing.

Pattern 2: orchestrator-workers. An orchestrator call reads the brief and decides what sub-tasks this particular hamper needs. It returns a plan as structured data:

{"items": ["Nut-free brownie box (sealed)", "Eggless plum cake 500g", "Kids' gingerbread kit", "Hot chocolate tin"],
 "subtasks": ["check_stock", "check_allergens", "price_and_margin"]}

The orchestrator chose four items and three sub-tasks. For a different brief it might choose different sub-tasks; that flexibility is the point of this pattern.

Pattern 3: parallelisation. The three sub-tasks don’t depend on each other, so they run at the same time, each as its own small call with the right tools:

Worker Result Time
check_stock All four items stocked at 6/6 stores through 31 Dec 4.1 s
check_allergens Eggless plum cake: “may contain nuts” (Aundh line) 3.8 s
price_and_margin Cost ₹1,510; at ₹2,500 retail, margin 39.6% 2.9 s

Run one after another, these take 10.8 seconds. In parallel, 4.1 seconds: the slowest one. And notice the allergen worker caught something: the plum cake is unsafe for this hamper. (You met that exact problem in Agentic RAG.) The orchestrator swaps it for the certified nut-free fruit loaf and re-runs only the allergen check. Pass.

Pattern 4: prompt chaining. Now the website copy, as a fixed chain of three steps with a check in the middle:

  1. Draft the English description from the final item list.
  2. Gate (plain Python, no model): does the draft mention any word on the nut list (walnut, almond, cashew, pistachio, hazelnut, praline, marzipan)? The first draft said “rich, nutty hot chocolate”. The gate fails it, and the draft step runs again with the failure noted.
  3. Translate the approved English into Marathi.

Pattern 5: evaluator-optimizer. A separate evaluator call scores the English copy against Meera’s checklist (warm tone, under 80 words, mentions the allergy promise, no invented claims) and returns specific feedback. Round 1: “82 words; claims ‘award-winning’ with no source.” The writer revises. Round 2: “Pass.” Two rounds, then stop.

The final hamper took 14 calls, about 19,000 input tokens and 3,500 output tokens: roughly $0.15, or ₹13, in about 25 seconds. All twelve hampers: about ₹160 and five minutes. That’s less than the single big agent, and every one of the 14 steps can be logged, inspected and tested on its own. When the copy for hamper seven came out flat, Arjun knew exactly which step to look at.

Note

Pause and think. Step 2 of the chain (the nut-word gate) is plain Python, not a model call. Why use code there, when the evaluator in pattern 5 is a model?

Show the answer

Because the nut check has a precise, mechanical answer: either a forbidden word appears or it doesn’t. Code checks that instantly, for free, and never misses. The evaluator’s job (“is this warm? does it invent claims?”) needs judgement, which code can’t do. A good rule: if you can write the check as an if statement, write it as an if statement. Use a model only for the checks that genuinely need understanding. (A word list can’t catch every possible mention, which is one reason allergy hampers also get a human review before launch.)

Opening the hood: the five patterns

Here they are properly, each with its shape, when to use it, and what it costs you. The names come from Anthropic’s essay, and you’ll see them across the industry.

1. Prompt chaining

A fixed sequence of calls, where each step’s output is the next step’s input, with optional checks (gates) in between.

flowchart LR
  In["Input"] --> C1["Call 1: draft"] --> G{"Gate: passes?"}
  G -->|"yes"| C2["Call 2: translate"] --> Out["Output"]
  G -->|"no"| C1

Use it when the task splits cleanly into fixed steps, and each step is easier than the whole. You gain accuracy, because each call does one simple thing, and debuggability. You pay in latency, because steps run one after another.

2. Routing

Classify the input, then send it to a specialised handler.

flowchart LR
  In["Input"] --> R["Router call"]
  R -->|"allergy-safe"| A["Strict allergy path"]
  R -->|"office"| B["Bulk pricing path"]
  R -->|"other"| C["General path"]

Use it when inputs fall into distinct categories that deserve different handling: different prompts, different tools, even different models (a cheap, fast model for easy cases, a strong one for hard cases). The support inbox router from What is an AI agent? is exactly this. You pay when the router misclassifies, so always have a sensible default path.

3. Parallelisation

Run independent calls at the same time, then combine. It comes in two flavours. Sectioning splits a task into independent parts (stock, allergens, pricing). Voting runs the same task several times and compares answers (three independent checks for “is this description safe for nut allergies?”; flag it if any says no).

flowchart LR
  In["Plan"] --> W1["Stock check"]
  In --> W2["Allergen check"]
  In --> W3["Pricing"]
  W1 --> M["Combine"]
  W2 --> M
  W3 --> M

Use it when sub-tasks don’t depend on each other (sectioning) or when you want more confidence than one call gives (voting). You gain speed and, with voting, reliability. You pay for every parallel call, and you can hit your provider’s rate limits.

4. Orchestrator-workers

A central model call (the orchestrator) breaks the task down, decides which sub-tasks are needed, hands them to workers, and combines the results. Unlike parallelisation, the sub-tasks are not known in advance: the orchestrator decides them per input.

flowchart TD
  In["Brief"] --> O["Orchestrator: plan sub-tasks"]
  O --> W1["Worker A"]
  O --> W2["Worker B"]
  O --> W3["Worker ..."]
  W1 --> S["Orchestrator: combine"]
  W2 --> S
  W3 --> S

Use it when you can’t predict the sub-tasks: a hamper with custom engraving needs a supplier check; one with alcohol needs a licence check. You pay in predictability: the plan varies run to run. This is the pattern closest to a true agent, and the bridge to Multi-agent systems in the next lesson.

5. Evaluator-optimizer

One call generates; another evaluates against clear criteria and gives feedback; the generator revises. Loop until it passes or you hit a round limit.

flowchart LR
  G["Generator: write"] --> E["Evaluator: score and critique"]
  E -->|"needs work + feedback"| G
  E -->|"pass"| Out["Output"]

Use it when you have clear evaluation criteria and revision measurably helps, as with copy, translations or code that must pass tests. You pay for every round, and an evaluator that is too lenient (or too harsh) makes the loop useless.

And then: agents

When none of these fixed shapes fits, because the steps can’t be predicted and the task is open-ended, you hand the steering wheel to the model: the agent loop you built in Your first agent. Notice the order. Anthropic’s advice, and Priya’s, is to try the simplest pattern that works and climb only when you must.

Note

Pause and think. Meera asks for a weekly report: “For each of our six stores, summarise last week’s customer complaints and suggest one fix.” Which pattern, or combination, fits best?

Show the answer

Parallelisation by sectioning: six independent summaries, one per store, run at the same time, then a final call that combines them into one report. You know the sub-tasks in advance (one per store), so you don’t need an orchestrator. If the summaries must follow a strict format, add a chain step that checks the format. An agent would be overkill: there is nothing open-ended about it.

Building the patterns in code

Every pattern below uses one small helper, so the patterns themselves stay readable.

# patterns.py
import anthropic
from dotenv import load_dotenv

load_dotenv()
client = anthropic.Anthropic()

def ask(prompt: str, system: str = "", effort: str = "low") -> str:
    """One augmented-LLM brick: a single call that returns text."""
    response = client.messages.create(
        model="claude-opus-5-5",
        max_tokens=2000,
        system=system,
        output_config={"effort": effort},   # 'low' keeps simple steps fast and cheap
        messages=[{"role": "user", "content": prompt}],
    )
    return next(b.text for b in response.content if b.type == "text")

effort controls how much the model thinks before answering. Simple steps (classify, translate) run well at "low"; harder ones (planning, judging) deserve "medium" or "high". Each pattern lets you choose per step, which is one more advantage over a single giant agent.

Routing

ROUTES = {"allergy-safe", "office", "family", "premium"}

def route(brief: str) -> str:
    label = ask(f"Classify this hamper brief. Reply with exactly one word from "
                f"{sorted(ROUTES)}.\n\nBrief: {brief}").strip().lower()
    return label if label in ROUTES else "family"   # safe default if the model goes off-script

The last line matters. Never trust a classifier’s output blindly: check it against the allowed set and fall back to a sensible default.

Prompt chaining with a gate

NUT_WORDS = ["walnut", "almond", "cashew", "pistachio", "hazelnut", "praline", "marzipan", "nutty"]

def write_copy(items: list[str], route_name: str, max_tries: int = 3) -> tuple[str, str]:
    feedback = ""
    for _ in range(max_tries):
        draft = ask(f"Write a warm website description (under 80 words) for a Christmas hamper "
                    f"containing: {items}. {feedback}")
        hits = [w for w in NUT_WORDS if w in draft.lower()]
        if route_name != "allergy-safe" or not hits:      # the gate, in plain Python
            marathi = ask(f"Translate into natural Marathi:\n\n{draft}")
            return draft, marathi
        feedback = f"Your previous draft used the words {hits}. This hamper is nut-free: avoid them."
    raise ValueError("Copy failed the nut gate three times: send to a human.")

Parallelisation by sectioning

from concurrent.futures import ThreadPoolExecutor

def check_all(items: list[str]) -> dict[str, str]:
    checks = {
        "stock":     f"Using the stock report below, say whether each item is available at all 6 stores "
                     f"through 31 Dec.\nItems: {items}\nReport: {STOCK_REPORT}",
        "allergens": f"Using the allergen table below, flag any item that contains or may contain nuts.\n"
                     f"Items: {items}\nTable: {ALLERGEN_TABLE}",
        "pricing":   f"Using the cost list below, total the cost and compute the margin at ₹2,500 retail.\n"
                     f"Items: {items}\nCosts: {COST_LIST}",
    }
    with ThreadPoolExecutor(max_workers=3) as pool:
        futures = {name: pool.submit(ask, prompt) for name, prompt in checks.items()}
        return {name: f.result() for name, f in futures.items()}

STOCK_REPORT, ALLERGEN_TABLE and COST_LIST stand in for data you’d fetch from Crumb’s systems. Three threads, three calls, total time equal to the slowest. (For the margin itself, in production you’d compute it in Python from the cost list and only ask the model to explain it: remember the smolagents lesson.)

Orchestrator-workers with a typed plan

The orchestrator returns structured data, so your code can loop over the sub-tasks safely. messages.parse checks the model’s output against a Pydantic model (a Python class that describes and validates the shape of data), which you met in Module 06.

from pydantic import BaseModel

class Plan(BaseModel):
    items: list[str]
    subtasks: list[str]          # names of workers to run

WORKERS = {"check_stock": ..., "check_allergens": ..., "price_and_margin": ..., "check_licence": ...}

def plan_hamper(brief: str) -> Plan:
    response = client.messages.parse(
        model="claude-opus-5-5", max_tokens=2000,
        messages=[{"role": "user", "content":
                   f"Plan a Christmas hamper for this brief. Choose 3-6 items and the sub-tasks "
                   f"needed from {list(WORKERS)}.\n\nBrief: {brief}"}],
        output_format=Plan,
    )
    plan = response.parsed_output
    plan.subtasks = [s for s in plan.subtasks if s in WORKERS]   # ignore invented workers
    return plan

Your code then runs each named worker (in parallel, if they’re independent) and makes one final call to combine the results.

Evaluator-optimizer

def polish(copy: str, max_rounds: int = 3) -> str:
    for round_number in range(1, max_rounds + 1):
        verdict = ask(
            "You are a strict editor for Crumb & Co. Check this hamper description against: "
            "(1) warm tone, (2) under 80 words, (3) mentions the allergy promise if relevant, "
            "(4) no claims that can't be verified, like 'award-winning'. "
            "Reply 'PASS' or 'FAIL: <specific fixes>'.\n\n" + copy,
            effort="medium",
        )
        print(f"round {round_number}: {verdict[:60]}")
        if verdict.strip().upper().startswith("PASS"):
            return copy
        copy = ask(f"Revise this description. Apply exactly these fixes: {verdict}\n\n{copy}")
    return copy   # best effort after max_rounds; flag for human review in real use

Run all five on one brief and you get the trace from earlier: a route, a plan, three parallel checks, a gated chain and a short polishing loop.

Where it breaks

The patterns made the hamper planner reliable, but not perfect. What the team learned in the first week:

  • The evaluator was too kind. Using the same model to write and to judge, the evaluator passed 95% of first drafts, including some Meera disliked. The fix: give the evaluator a sharper checklist with examples of failing copy, and have Meera label twenty drafts so they could measure whether the evaluator agreed with her. (This is “LLM-as-judge alignment”, coming in Module 23.)
  • Polishing never ends if you let it. One description bounced between “too formal” and “too casual” for five rounds. The round limit is not optional.
  • Parallel calls hit rate limits. Running all twelve hampers at once meant 36 simultaneous calls, and the API started returning 429 (too many requests) errors. The fix: limit the thread pool and retry with a pause. Module 19 covers retries properly.
  • The router misfiled a hamper. “Diabetic-friendly Christmas box” was classified as family, not a special diet. The safe default meant nothing broke, but the brief needed a new route. Routers need maintenance as your categories evolve.
  • The orchestrator invented workers. Once it asked for check_customs, which didn’t exist. The filter line in plan_hamper silently dropped it; logging dropped names would have surfaced the gap sooner.

None of these needed the forty-seven-step conversation to diagnose. Each one lived in a single, named step.

Try it yourself

Build the routing and evaluator-optimizer patterns for the support inbox.

  1. Write route_email(text) -> str returning one of delivery, allergy, refund, other, with a fallback to other.
  2. Write draft_reply(text, route) with a different system prompt per route.
  3. Wrap the reply in polish(), with an evaluator that checks: apologises if something went wrong, makes no promises about refunds, under 100 words.

Test with: “My Diwali hamper came with a crushed box and I want my money back.”

Show the expected result

The router should return refund. The first draft may promise a refund (“We’ll refund you right away!”). The evaluator should FAIL it for promising a refund, the revision should say a teammate will review the request, and round 2 should PASS. If the evaluator passes the first draft, make its checklist more explicit, for example: “FAIL if the reply says ‘refund’ together with ‘will’, ‘right away’ or ‘processed’.” Sharper criteria make better evaluators.

Common mistakes

  • Starting with an agent when a pattern fits. Ask, in order: can a single call do it? A chain? A router? Parallel calls? Only then reach for orchestrators and agents.
  • Using a model where an if would do. Word lists, totals, formats and margins are code’s job. Models judge; code checks.
  • No limits on loops. Every evaluator-optimizer loop and every retry needs a maximum.
  • Trusting structured output blindly. Validate router labels and orchestrator plans against what actually exists.
  • Mixing patterns into a tangle. Compose them deliberately: route first, then plan, then parallel checks, then chain, then polish. Draw it before you code it.

Recap

  • Most successful LLM systems are compositions of simple patterns, not giant autonomous agents. Each pattern puts a model call in a small, testable box.
  • Prompt chaining: fixed steps with gates. Routing: classify, then specialise. Parallelisation: independent calls at once, by sectioning or voting.
  • Orchestrator-workers: a model plans sub-tasks that can’t be known in advance. Evaluator-optimizer: generate, critique, revise, with a round limit.
  • In all five, your code decides the wiring. Use agents only when no fixed shape fits.
  • Each step can have its own prompt, tools, effort level and test, which is why the hamper planner went from ₹260 and unexplainable to ₹160 and debuggable.

On Friday, Arjun showed Meera the twelve hampers, each with a trace she could click through. “Before, one giant AI did everything in one long conversation and got sloppy by hamper nine. Now it’s like the kitchen: one step sorts the brief, one plans, three checks run side by side, one writes, one edits. If something’s wrong, I can see which station did it.”

“Five minutes for twelve hampers,” Meera said. “We’ll launch on the first of December this year.” Then, after a moment: “You said the planning step is ‘closest to an agent’. What happens when the tasks are too big for one plan? Next year I want a whole Christmas campaign: hampers, store displays, social posts, supplier orders.”

Priya looked at Arjun. “Then you need a team. Several agents, each a specialist, coordinating. That’s where things get powerful, and where they get expensive.”

Go deeper

Check your understanding

3 quick questions
1. Stock, allergen and pricing checks for a hamper do not depend on each other. Which pattern makes them faster?
2. How does orchestrator-workers differ from parallelisation?
3. In the copy chain, why is the "no nut words" check written in plain Python rather than as a model call?