Skip to content

Volume ITechnology

12.10.6 — Workflows, Planning and Routing ​

Once the refund agent worked, Cartwise's finance team asked for one too. Suppliers email invoices, often as PDFs; someone reads each one, checks it against the purchase order, and enters it into the accounts system. The first version was built as an agent with tools to read the email, look up purchase orders, and post to the ledger. It worked, but each invoice took 8 to 15 steps, cost about 6 cents, took 40 seconds, and in roughly one case in a hundred the agent skipped the check against the purchase order because the invoice "looked fine".

Then someone pointed out that every invoice goes through the same steps in the same order: read the invoice, check its arithmetic, find the purchase order, compare them, post or flag. Nothing about the path depends on what is found except a yes-or-no at each check. The team rebuilt it as a workflow: one model call to read the invoice into a structured form, and plain code for everything else. It now costs a fraction of a cent, takes 3 seconds, and cannot skip the purchase-order check, because the check is a line of code rather than a decision the model makes.

That is the central idea of this page, and the first rule of building with models: if you can write down the steps, write them down. The arithmetic of 12.10.1 explains why. Every step the model decides is a step that can go wrong, while a step fixed in code goes right every time. This page covers the workflow patterns that solve most problems, the planning techniques that give agents structure when they are genuinely needed, and the automation tools, n8n and Zapier, that build workflows without writing code.

1. Five workflow patterns ​

Anthropic's 2024 guide Building Effective Agents described five patterns that keep recurring in production systems. They are worth knowing by name, because nearly every well-built AI feature is one of them or a combination.

① chain with gatesmodelgatecodegatefails → person② routingclassifyrefund agentdelivery flowperson③ parallelinputcheck Acheck Bcheck Ccombine④ orchestrator and workersorchestratorplans subtasksworkerworkerworkercount andkind decidedat run time⑤ evaluator and optimizerwrite draftdraftevaluateproblems foundpasses → done (or limit → editor)
The five patterns. In ①–③ your code fixes the structure completely. In ④ the model decides what the subtasks are, inside a structure your code fixes. In ⑤ a fixed loop repeats until a check passes. None of them is an open-ended agent.

Chain with gates ​

A chain runs fixed steps in order, each step's output feeding the next. A gate is a check between steps, done in code wherever possible, that stops the chain when something is wrong. The invoice workflow is this pattern:

python
from pydantic import BaseModel

class Line(BaseModel):
    description: str
    quantity: int
    unit_price_rupees: int

class Invoice(BaseModel):
    supplier: str
    invoice_number: str
    po_number: str | None
    total_rupees: int
    lines: list[Line]

def process_invoice(email) -> str:
    invoice = client.messages.parse(
        model="claude-haiku-5-5", max_tokens=2000,
        messages=[{"role": "user", "content": [*email.attachments_as_blocks(),
                   {"type": "text", "text": "Extract this supplier invoice."}]}],
        output_format=Invoice).parsed_output                                      # (1)

    if sum(l.quantity * l.unit_price_rupees for l in invoice.lines) != invoice.total_rupees:
        return flag(email, "Line items do not add up to the invoice total.")          # (2)
    po = purchase_orders.get(invoice.po_number) if invoice.po_number else None
    if po is None or po.supplier != invoice.supplier:
        return flag(email, "No matching purchase order for this supplier.")           # (3)
    if abs(po.total_rupees - invoice.total_rupees) > 100:
        return flag(email, "Invoice total differs from PO by more than ₹100.")

    ledger.post(invoice, po, idempotency_key=f"{invoice.supplier}:{invoice.invoice_number}")  # (4)
    return "posted"

Line (1) is the only model call: reading a messy PDF into a clean structure is exactly what models are good at and code is bad at. It uses the small, cheap model, because extraction into a fixed schema does not need deep reasoning, and the structured output of 12.8.3 guarantees the shape. Line (2) is a gate in code: if the line items do not add up to the total, either the invoice is wrong or the extraction misread it, and either way a person should look. This one check catches most extraction errors, because a misread digit almost never leaves the arithmetic consistent. Line (3) is the purchase-order check the agent sometimes skipped; as code, it runs every time. Line (4) posts the invoice with an idempotency key built from the supplier and invoice number, so a supplier who emails the same invoice twice is not paid twice.

The model contributes the one thing only it can do. Every rule is code. That division, models for reading and writing, code for deciding and checking, is what makes workflows reliable.

Routing ​

A router classifies an input and sends it to the path built for that kind of input. "Ask Cartwise" receives every kind of message; a router sends refund requests to the refund agent, delivery questions to a simple workflow that looks up tracking, account problems to an account workflow, and anything unclear to a person.

python
from typing import Literal

class Route(BaseModel):
    category: Literal["refund", "delivery", "account", "other"]
    confident: bool

HANDLERS = {"refund": refund_agent.start, "delivery": delivery_workflow,
            "account": account_workflow, "other": human_queue}

def route(customer_id: str, message: str):
    r = client.messages.parse(
        model="claude-haiku-5-5", max_tokens=100, system=ROUTER_PROMPT,
        messages=[{"role": "user", "content": message}], output_format=Route).parsed_output
    if not r.confident:
        return human_queue(customer_id, message)                                       # (1)
    return HANDLERS[r.category](customer_id, message)                                  # (2)

Line (1) sends uncertain cases to a person rather than guessing, so the router's mistakes cost a little staff time instead of a wrong answer. Line (2) hands the message to its handler. Routing pays twice. Each path can be specialised, with its own prompt and tools, so it does its one job better than a general assistant. And each path can use a different model: delivery questions are answered by the cheapest model, while only refunds reach the expensive agent. Sending easy cases to cheap handlers and hard cases to expensive ones is the cascade idea from 12.3.5, and 12.10.10 works out what it saves. The router itself can be a model call, as here, or a classical classifier trained on labelled tickets, which is faster and cheaper once enough labels exist.

Parallel calls ​

When several pieces of work do not depend on each other, run them at the same time. There are two reasons to do it.

Splitting divides work into independent parts. Before an agent's reply goes to a customer, Cartwise checks it three ways at once: does it contradict policy, does it promise anything not yet approved, is the tone right? Each check is its own focused call, and running them together takes as long as the slowest one rather than the sum of all three:

python
import asyncio

async def review_reply(draft: str, case_notes: str) -> list[str]:
    results = await asyncio.gather(
        check(POLICY_CHECK, draft, case_notes),
        check(PROMISE_CHECK, draft, case_notes),
        check(TONE_CHECK, draft))                                     # (1) all three at once
    return [problem for r in results for problem in r.problems]      # (2) empty list means send it

Line (1) starts the three calls together using an asynchronous client, so three one-second calls take about one second in total. Line (2) gathers every problem found. Three narrow checks are more reliable than one call asked to check everything, because each prompt can focus on one question.

Voting asks the same question more than once, with different prompts or models, and acts on the agreement. Cartwise flags suspicious product reviews this way: three differently worded prompts each judge whether a review looks fake, and a review is hidden only when at least two agree. Voting trades cost for reliability, so it belongs on decisions where a mistake is expensive.

Orchestrator and workers ​

Sometimes the subtasks cannot be known in advance. When Cartwise changed its return window for electronics from 10 days to 14, someone had to find and update every help page, policy document and email template that mentioned it, and nobody knew in advance how many there were or which ones. In the orchestrator-workers pattern, an orchestrating model reads the task, searches, and decides the subtasks ("update these 23 pages"); worker calls carry out each subtask, often in parallel; and the orchestrator combines the results into one set of proposed changes for review.

This pattern sits on the boundary between workflows and agents. The model decides what the subtasks are, which is agent-like, but the structure of plan, fan out, gather and review is fixed in code. It suits tasks that are wide, with many similar pieces, rather than deep, with one long chain of dependent decisions.

Evaluator and optimizer ​

Some outputs can be improved by criticism. Cartwise writes product descriptions with a model, and they must follow brand rules: no superlatives the company cannot prove, no medical claims, under 80 words. In the evaluator-optimizer loop, one call writes a draft, another checks it against the rules and lists specific problems, and the first revises, until the check passes or a limit is reached:

python
def write_description(product) -> str:
    draft = generate(product)
    for _ in range(3):                                               # (1) bounded
        review = evaluate(draft, BRAND_RULES)                        # (2) structured: passes, problems
        if review.passes:
            return draft
        draft = revise(draft, review.problems)                       # (3) fix only what was found
    return send_to_editor(product, draft, review.problems)           # (4)

Line (1) bounds the loop, because a writer and critic that disagree could otherwise argue forever. Line (2) returns a structured verdict with a list of specific problems ("'best earphones in India' is an unprovable superlative"), because specific criticism produces specific fixes. Line (3) revises against those problems only. Line (4) hands the case to a human editor if three rounds were not enough. Where a rule can be checked in code, such as the word count, check it in code; the evaluating model is for rules that need judgement.

The pattern works when two conditions hold: the criticism is genuinely useful, and checking is easier than writing. Both are true for "does this break a brand rule?". It is less useful when the evaluator is no better at judging than the generator was at writing.

2. Planning inside agents ​

When an agent is genuinely needed, structure can still be added inside it. The most common form is a plan: before acting, the agent writes out the steps it intends to take, and then works through them.

Plans help in three ways. Writing them makes the model think about the whole task before diving into the first step, which reduces wandering. A written plan is something a person can review before anything happens: "here is what I will do; approve?". And a plan kept in the run's state survives context management (12.10.5) and keeps a long task on course after the details of early steps have been cleared away.

There are two common styles. In plan-then-execute, one call produces a full plan and then each step is carried out, often by simpler, cheaper calls; this works when the plan is unlikely to change. In plan-and-revise, the agent keeps a short task list as part of its state, through tools like update_plan(items), and revises it as it learns. Coding agents work this way: their visible to-do lists ("read the checkout code ✓, find where the form is submitted ✓, fix the field name, verify in the browser") are plans the agent updates as it goes.

Plans have a cost: a plan written before the agent has looked at anything is often wrong, and an agent that sticks rigidly to a wrong plan does worse than one that adapts. So the plan should be cheap to change, and the harness should ask for one only on tasks long enough to benefit, typically more than a handful of steps.

3. n8n and Zapier: workflows without code ​

Many workflows do not need a programmer at all. Zapier and n8n are workflow automation tools. You build a workflow by connecting boxes in a visual editor. Each box is one step, such as "when a new email arrives in this inbox", "send this text to a model", "add a row to this spreadsheet" or "post a message to this chat channel". They are worth understanding both as tools you may use and as systems you may be asked to design.

Zapier, founded in 2011, is a hosted service. A workflow is called a Zap: one trigger (the event that starts it) followed by one or more actions. It connects to thousands of applications, and you never run a server. n8n, started in 2019 by Jan Oberhauser in Berlin, does the same job but can also be run on your own servers. Its name is a shortening of "nodemation" (node automation): "n", then the eight letters in between, then "n". Its workflows can branch, loop and run code, and both tools now include AI steps, from a single model call to a full agent step with tools.

How they work underneath ​

The design is the same in both, and it is a good example of the systems design of Volume I.

triggerswebhookapp calls uspollerasks every few min,skips seen idsscheduleevery night 2 a.m.queueexecution jobsworkersstep 1: read emailstep 2: model callstep 3: add rowretry, then error flowconnectorsemail, sheets, chat…credentialsencrypted storedatabaseworkflows, execution history: every input, output and error
The parts every workflow automation system has. Triggers turn events into jobs; a queue holds them; workers run each step through a connector using stored credentials; every execution is recorded. n8n's "queue mode" is exactly this, with Redis as the queue and a database for the records.

Triggers start an execution in one of three ways. A webhook is a URL the other application calls the moment something happens, which is instant. A poller asks the application every few minutes "anything new?". Zapier's polling interval depends on the plan, from every 15 minutes on the free plan to every minute on the highest. To avoid running twice for the same email, the poller remembers the IDs of items it has already seen and starts a run only for new IDs; this is deduplication. A schedule starts a run at fixed times, like the scheduled jobs of any server.

The queue holds executions waiting to run. It decouples receiving events from running workflows, so a burst of 5,000 emails does not overwhelm anything: they wait their turn. This is the message queue of Chapter 10.8.1, and n8n's documentation describes its scaled-out queue mode in exactly these terms: a main process handles the editor, schedules and incoming events and puts execution IDs on a queue in Redis, and any number of worker processes take jobs off the queue and run them.

Connectors are the reusable integrations: "send a message to this chat channel", "add a spreadsheet row". Each knows one application's API. They use credentials that the user connected once, typically by signing in through that application's OAuth page, and that the system stores encrypted. The connector library is the real product; it is why these tools are useful.

Execution history records every run: the data each step received and produced, and any error. When something goes wrong at 2 a.m., this is how you find out what happened. Failed steps are retried, because the APIs being called fail temporarily, and after repeated failures the execution goes to an error workflow, which might alert someone in a chat channel.

When to use them, and when not ​

These tools are excellent for internal automation glue: "when a supplier emails an invoice, extract it with a model and post it to a spreadsheet for review", "every morning, summarise yesterday's one-star reviews into the team channel". They let people who are not programmers build and change workflows, and they come with logging and retries built in.

They are a poor fit when the workflow is part of your product's core: when it needs careful code review, unusual performance, complex logic, or the same tests and deployment controls as the rest of the codebase. Cartwise's invoice workflow started in n8n, and moved into the main codebase when finance asked for it to handle 20,000 invoices a month with strict audit requirements. Starting in a tool and moving to code when the workflow becomes important is a perfectly good path.

4. Choosing the design ​

SituationDesign
Steps fixed, one hard reading stepchain, model reads, code decides
Different kinds of inputrouter to specialised paths
Independent checks or partsparallel calls
High-stakes judgementvoting
Many similar subtasks, unknown in numberorchestrator and workers
Output must meet checkable rulesevaluator and optimizer
Path depends on what is foundagent, with a plan for long tasks
Internal glue between appsautomation tool, then code if it grows

Real systems combine these. "Ask Cartwise" is a router whose refund path is an agent, whose replies go through parallel checks before sending. The skill is not choosing one pattern for everything; it is giving each part of the problem the least freedom that solves it.

What the interviewer will push on ​

"When would you use a workflow instead of an agent?" Whenever the steps can be written down in advance, which is most of the time. The tell of experience is a concrete reason: fixed steps make every check certain to run, cost and latency predictable, and failures easy to find, while an agent decides each step and can skip one.

"Name some workflow patterns." Chain with gates, routing, parallel calls (splitting and voting), orchestrator-workers, evaluator-optimizer. Better still, give a real example of each and say where your code makes the decisions.

"How would you design a system like Zapier?" Triggers (webhooks, pollers with deduplication by item ID, schedules) create execution jobs on a queue; workers run steps through connectors using encrypted stored credentials; every execution's inputs, outputs and errors are recorded; retries with backoff, then an error workflow. Mention rate limits per connected account and idempotency for steps that might be retried.

"How do you stop an evaluator-optimizer loop running forever?" A fixed round limit and a hand-off to a person, structured specific critiques, and code checks for anything code can check.

"Do plans help agents?" On long tasks, yes: they reduce wandering, can be reviewed before execution, and survive context clearing. They hurt if followed rigidly when wrong, so keep them revisable.

Something to volunteer: the invoice numbers. The same task as an agent took 8 to 15 steps, 6 cents and 40 seconds and occasionally skipped a check; as a workflow it took one model call and 3 seconds and cannot skip the check. Interviewers remember a candidate who has seen an agent replaced by something simpler and better.

Recall

  • If you can write the steps down, write them down: a workflow fixes the steps in code, so checks always run and cost is predictable. Models for reading and writing; code for deciding and checking.
  • Five patterns:
    1. A chain of steps, with checks between them.
    2. Routing to specialised paths, and to cheaper models.
    3. Parallel calls, either for separate parts (splitting) or to compare answers (voting).
    4. An orchestrator with workers, for subtasks decided while it runs.
    5. An evaluator and optimizer loop, with a limit on rounds.
  • Agents can still be structured with plans: plan-then-execute for stable tasks, a revisable plan in state for long ones, as coding agents' to-do lists are.
  • Zapier (hosted, 2011) and n8n (self-hostable, 2019) build workflows visually. Underneath: triggers (webhook, poller with deduplication, schedule), a queue, workers, connectors with encrypted credentials, execution history, retries and error workflows.
  • Give each part of a problem the least freedom that solves it, and combine patterns freely.

Self-test: Why does the line-total check in the invoice workflow catch most extraction errors? What makes the electronics-window update an orchestrator-workers task rather than a chain? A Zapier poller sees the same email twice in a row; what stops a second run?

Next: 12.10.7 Multi-Agent Systems — the orchestrator-workers pattern already uses several model calls; the next page asks when a task genuinely needs several agents working together, what it costs, and the two situations where it clearly pays.