Workflow Design Patterns
10 minute read
A surprising amount of what is marketed as “Agentic AI” is, structurally, a workflow: a sequence of steps fixed in code, in which a language model does some of the work but never decides what happens next. That is not a criticism. Workflows are predictable, testable and cheap, and most production systems should be mostly workflow.
These notes introduce the one distinction that organises this chapter and the next, who decides the next step, and then catalogue six workflow patterns. For each we give a concrete example, when to use it, and the failure it invites. We finish by locating the patterns inside the course’s two reference agents.
By the end of these notes you should be able to:
- tell a workflow from an agent by asking who chooses the next step;
- recognise and draw the six workflow patterns, and choose between them;
- name the characteristic failure of each pattern and the guard against it;
- read a real agent’s graph and identify which parts are workflow and which are agentic.
In every agentic system, something decides what happens after each step. There are only two candidates.
In a workflow, code decides. The path is written down before the run starts: you could print the graph in advance. A model may do work inside a step (summarise this, classify that), but it never chooses which step comes next. In an agent, the model decides. The path emerges at run time from the model’s choices, so you cannot know in advance how many steps a run will take or in what order. That is the subject of Agentic Design Patterns.
| Workflow | Agent | |
|---|---|---|
| Who picks the next step | Code | The model |
| When the path is known | Before the run | Only at run time |
| Properties | Predictable, testable, cheap | Flexible, open-ended, harder to certify |
The choice between them follows directly from the three questions of the previous notes. Low cardinality (the situations can be enumerated) points to a workflow. High cardinality with a bounded blast radius is where it pays to let the model choose.
The simplest and most common pattern: each step’s output is the next step’s input, in a fixed order.
graph TD
A[Extract text] --> B[Translate] --> C[Summarise] --> D[Format brief]
classDef c1 fill:#dcfce7,stroke:#15803d,stroke-width:2px,color:#14532d
classDef c2 fill:#dbeafe,stroke:#1d4ed8,stroke-width:2px,color:#1e3a8a
classDef c3 fill:#ffedd5,stroke:#c2410c,stroke-width:2px,color:#7c2d12
classDef c4 fill:#f3e8ff,stroke:#7e22ce,stroke-width:2px,color:#581c87
classDef c5 fill:#fee2e2,stroke:#b91c1c,stroke-width:2px,color:#7f1d1d
classDef c6 fill:#ccfbf1,stroke:#0f766e,stroke-width:2px,color:#134e4a
class A c1
class B c2
class C c3
class D c4When to use it. When a task splits naturally into fixed stages, each simpler than the whole. Smaller steps are easier for a model to do well, and each link can be unit-tested and replayed on its own.
What goes wrong. Latency adds up across steps, and an early mistake flows downstream: a bad extraction produces a confident summary of nothing. The guard is a cheap validation between steps. If extraction returned an empty page, stop there instead of paying for two model calls to translate and summarise it.
Code inspects the input and picks exactly one branch from a closed set. This pattern is also called routing.
graph LR
T[Classify ticket] --> B[Billing]
T --> H[Technical]
T --> G["General (fallback)"]
B --> R[Reply]
H --> R
G --> RWhen to use it. When inputs fall into known classes that need genuinely different handling: different prompts, different tools, even different models (a small, cheap model for easy questions and a large one for hard ones).
What goes wrong. Every misroute is a silent failure, so the misroute rate becomes your error budget. Always keep a fallback branch, and watch its volume: when it grows, your classes need updating. Note that a classifier may well be a model, but it is still a workflow: the classifier produces a label, and code maps the label to a branch.
A fixed set of independent branches runs at the same time, and their results are joined.
graph LR
C[Contract] --> L[Legal check]
C --> F[Finance check]
C --> S[Security check]
L --> M[Merge report]
F --> M
S --> MWhen to use it. When the branches do not depend on each other’s output and latency matters. Running them together cuts the latency to that of the slowest branch rather than the sum.
What goes wrong. The interesting design lives in the join. Does the report wait for all three branches, or the first? If the security check times out, do you publish a partial report with a flag, or fail the whole thing? These questions must be answered explicitly. Parallel calls also hit provider rate limits sooner (Resilience).
The same step over N items, where N is known only at run time, followed by one aggregation.
graph LR
S[Split into N] --> R1[Summarise review 1]
S --> R2[Summarise review 2]
S --> RN[Summarise review N]
R1 --> C[Combine]
R2 --> C
RN --> CThis looks like fan-out, with one important difference: the number of branches is not on the diagram. It comes from the data at run time.
What goes wrong. Two things. First, bound N and the concurrency, or one large input becomes one large bill. Second, test the reduce step hardest: it is where contradictions between items get silently averaged away.
Repeat a step until a check passes, at most N times, with the cap set in code.
graph LR
G[Generate code] --> T{Run tests}
T -->|"tests fail · at most 3 attempts"| G
T -->|pass| D[Done]
T -->|cap reached| H[Hand over to a person]Why this is still a workflow. The stopping rule is code: “tests pass, or three attempts”. The model writes the code inside the step, but it does not decide whether to go round again. In the next chapter, it does.
What goes wrong. A loop without a cap is a runaway bill. Good loops also detect no progress: the same failure twice in a row is a reason to stop early. We formalise this as loop control.
The run pauses, saves its state, and resumes when a person decides. This is level 5 of the automation scale, expressed as a workflow shape.
graph LR
D[Draft email] --> M{Manager}
M -->|approve| S[Send]
M -->|reject with a note| R[Revise]
R --> MWhen to use it. Whenever an approval or an answer is needed before an action that matters.
What goes wrong. The pause must be durable: the approval may arrive in five minutes or five days, after a deployment or a crash, and the run must resume exactly where it stopped. That requires checkpointing the run’s state (state management). And keep what reaches people small: if everything goes to a person, nobody really reviews anything (the ironies of automation).
Patterns are easiest to understand in a real system. Here is where each one appears in the course’s two reference agents, and why it is there.
| Pattern | Where it appears | Why there |
|---|---|---|
| Sequential | The CSA’s front door; the RA’s scope → contract → run | Order is safety: personal data is redacted before any hosted screen sees the text |
| Branching | The CSA routes a plain status question to a database read; the RA routes /research vs a plain question | Cheap answers skip the model entirely |
| Fan-out / fan-in | The RA’s research batches; deliberately not the CSA’s screens | Speed in the RA; in the CSA, the privacy order wins over speed |
| Map-reduce | The RA’s sub-questions, then synthesis | N depends on the question, and is capped |
| Fixed-N loop | The CSA’s tool loop (≤ 5); the RA’s research rounds | Cost and runaway protection |
| Human-in-the-loop | CSA refunds over $50; the RA’s contract approval | Level 5, checkpointed |
The support agent’s graph. Compiled with every feature on, the CSA has fifteen nodes, and only one of them lets the model choose the next step:
graph TB
subgraph FD ["Front door: a sequential pipeline"]
direction LR
A[authenticate] --> CO[choose order] --> P1[PII filter] --> P2[injection screen]
P2 --> P3[moderation] --> P4[deterministic checks] --> CI[classify intent]
end
subgraph Core ["The model-driven loop"]
direction LR
AC[assemble context] --> PF[prefetch] --> PR[propose]
PR -->|tool call| EX[execute]
EX -->|"loop ≤ 5"| PR
end
subgraph Close ["Finish"]
direction LR
FN[finalize] --> AW[await approval] --> E((END))
RI[read intent] --> E
end
FD --> Core
Core --> CloseRead it as four workflow patterns. The screens from PII filtering to context assembly form a sequential pipeline. Classify intent branches a plain read away with no model call. Propose ⇄ execute is a loop capped at 5 (whether to go round again is the model’s choice, but the cap is code). And await approval is a human-in-the-loop step, reached only when a refund was escalated.
The research agent’s graphs. The RA has three graphs, and the router between them is code: a plain question goes to a direct-answer graph; a /research request goes to a scoping interview, which ends when you approve a frozen contract; the run graph then researches autonomously and returns a report.
graph LR
M[message] -->|plain question| AG["answer graph"]
M -->|/research| SG["scoping graph · an interview"]
SG -->|"you approve · contract frozen"| RG["run graph · autonomous"]
RG --> V[report with a verdict]Inside the run graph, a model fills the steps but code decides where the run goes next: map-reduce over sub-questions, capped research and gap-filling loops, and a branch after an independent audit (gap fill, revise, or finalize). A scope is a conversation; a run is a job.
- A workflow is a system in which code picks the next step; models may still work inside steps. An agent lets the model pick.
- Six workflow shapes cover most needs: pipeline, branching, fan-out, map-reduce, capped loop, human-in-the-loop. Each has a characteristic failure and guard.
- The support agent is mostly workflow: one model-driven loop in fifteen nodes. The research agent branches, maps and stops by code; the model fills the steps.
Before you call it an agent, draw the graph. If code picks every next step, it’s a workflow, and that’s usually what you want.
- Anthropic, Building Effective Agents (2024): a catalogue of workflow patterns and when to prefer them over agents.
- LangGraph, Documentation: the graph runtime used for the reference agents, including branching, fan-out and checkpoints.
- LangGraph, Persistence: how durable pauses for human-in-the-loop steps work.