Workflow patterns
Most reliable 'agentic' systems in production are workflows. These five patterns cover the vast majority of them.
Learning objectives
- Describe the five workflow patterns
- Choose the right pattern for a task
- Implement routing in Python
- Implement an evaluator–optimiser loop with a stopping condition
- Combine patterns thoughtfully
3.1 Five workflow patterns
Anthropic's Building effective agents describes five composable patterns that cover most production systems. Each is a predefined code path with LLM calls inside it.
| Pattern | How it works | Use when |
|---|---|---|
| Prompt chaining | A sequence of LLM calls, each processing the previous output, with optional checks ("gates") between | The task splits cleanly into fixed steps — e.g. outline → check → draft |
| Routing | Classify the input, then send it to a specialised prompt, tool or model | Distinct categories need different handling — e.g. refunds vs technical support |
| Parallelisation | Run calls simultaneously: sectioning (independent subtasks) or voting (several attempts, then aggregate) | Speed, or more confidence through multiple perspectives |
| Orchestrator–workers | A central LLM breaks the task down dynamically, delegates to worker LLMs, and synthesises results | You can't predict the subtasks in advance — e.g. changes across many files |
| Evaluator–optimiser | One LLM generates; another evaluates and gives feedback; repeat | Clear evaluation criteria and iterative refinement adds value — e.g. translation, search |
3.2 Routing in code
Python · a routing workflowdef call_llm(prompt): # thin wrapper around your provider's SDK
r = client.messages.create(model=MODEL, max_tokens=400,
messages=[{"role": "user", "content": prompt}])
return r.content[0].text.strip()
ROUTES = {
"REFUND": "You handle refund requests. Policy: {policy}. Draft a reply to: {msg}",
"TECHNICAL": "You are a product expert. Help with this care or usage question: {msg}",
"OTHER": "Politely answer or redirect this general enquiry: {msg}",
}
def handle(msg, policy):
category = call_llm(
"Classify this customer message as REFUND, TECHNICAL or OTHER. "
f"Reply with one word only.\n\nMessage: {msg}")
category = category if category in ROUTES else "OTHER" # never trust the label blindly
return category, call_llm(ROUTES[category].format(msg=msg, policy=policy))
3.3 Evaluator–optimiser in code
Python · generate, critique, revisedef write_with_review(brief, max_rounds=3):
draft = call_llm(f"Write a product description.\nBrief: {brief}")
for _ in range(max_rounds):
review = call_llm(
"Review this product description against the brief. If it fully meets "
"the brief, reply APPROVED. Otherwise list specific fixes.\n"
f"Brief: {brief}\nDraft: {draft}")
if review.startswith("APPROVED"):
break
draft = call_llm(f"Revise the draft using this feedback.\nFeedback: {review}\nDraft: {draft}")
return draft
Note the max_rounds limit: every loop needs a stopping condition.
3.4 Combining patterns
Real systems mix patterns: a router sends complex tickets to an orchestrator, whose workers run in parallel, with an evaluator checking the final reply. Start with one pattern, measure, and add complexity only when it demonstrably helps.
Key takeaways
- Chaining: fixed sequential steps with gates.
- Routing: classify, then specialise.
- Parallelisation: sectioning for speed, voting for confidence.
- Orchestrator–workers: dynamic decomposition.
- Evaluator–optimiser: generate, critique, revise — with a round limit.