Chapter 3 of 5
Workflow Patterns: Chaining, Routing, Parallelization
The three workflow patterns that cover most real tasks — prompt chaining, routing, and parallelization (sectioning and voting).
Workflow Patterns: Chaining, Routing, Parallelization
These three patterns, from Anthropic's Building Effective Agents, cover the majority of real tasks. Each trades a little latency for a lot of reliability.
1. Prompt chaining
Decompose a task into a sequence of steps, where each LLM call processes the output of the previous one. Add programmatic checks ("gates") on intermediate steps to catch problems early.
[LLM 1] → gate → [LLM 2] → gate → [LLM 3] → output
When to use it: the task decomposes cleanly into fixed subtasks. You're trading latency for accuracy — each call becomes easier.
Examples:
- Generate marketing copy, then translate it.
- Write a document outline, check it meets criteria, then write the document from the outline.
outline = llm("Write an outline for: " + task)
if not meets_criteria(outline):
return "Outline rejected at gate"
document = llm("Write the document from this outline: " + outline)
2. Routing
Classify an input and direct it to a specialized follow-up. This separates concerns and lets you build more specialized prompts.
┌→ [specialist A]
[input] → [classifier] ┼→ [specialist B]
└→ [specialist C]
When to use it: there are distinct categories that are better handled separately, and classification can be done accurately.
Examples:
- Route customer-service queries (refunds, tech support, general) into different prompts and tools.
- Route easy questions to a cheaper model (Haiku) and hard ones to a stronger model (Sonnet) — optimizing cost per query.
Routing is the cheapest cost optimization
Before adding any other complexity, routing lets you send 80% of traffic to a cheap, fast model and reserve the expensive one for the 20% that needs it. It's a workflow that pays for itself.
3. Parallelization
Run LLM calls simultaneously and aggregate their outputs programmatically. Two variations:
- Sectioning — break a task into independent subtasks run in parallel.
- Voting — run the same task multiple times to get diverse outputs.
Sectioning: Voting:
┌→ [LLM A] ─┐ ┌→ [LLM 1] ─┐
[input] ┼→ [LLM B] ─┼→ aggregate [input] ┼→ [LLM 2] ─┼→ vote
└→ [LLM C] ─┘ └→ [LLM 3] ─┘
When to use it: when subtasks can run in parallel for speed, or when multiple perspectives raise confidence.
Examples:
- Sectioning: one model instance processes a user query while another screens it for policy violations — performs better than one model doing both.
- Voting: review code for vulnerabilities with several different prompts; flag if any find a problem.
import concurrent.futures
# Sectioning: review different aspects in parallel
with concurrent.futures.ThreadPoolExecutor() as ex:
security = ex.submit(llm, "Review for security issues: " + code)
style = ex.submit(llm, "Review for style: " + code)
correctness = ex.submit(llm, "Review for correctness: " + code)
review = aggregate(security.result(), style.result(), correctness.result())
Parallelization multiplies token cost
Sectioning with three calls costs roughly 3× the tokens of one call. Voting with five identical calls costs 5×. Parallelization buys speed or confidence — never use it "just because"; use it when the added cost buys something you can measure.
The meta-pattern
Notice what these three share: the path is written down. Chaining is a fixed sequence. Routing is a classifier plus branches. Parallelization is a fan-out plus aggregation. None of them asks the model to decide what to do next — that decision lives in your code.
That's what makes them workflows, not agents. The next chapter covers the one workflow that starts to blur the line — orchestrator-workers — where the model does decide, but within bounds you control.
Next: Orchestrator-Workers — dynamic decomposition, the bridge between fixed workflows and full agents.