Module 3 of 6 · 30 minutes

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.

PatternHow it worksUse when
Prompt chainingA sequence of LLM calls, each processing the previous output, with optional checks ("gates") betweenThe task splits cleanly into fixed steps — e.g. outline → check → draft
RoutingClassify the input, then send it to a specialised prompt, tool or modelDistinct categories need different handling — e.g. refunds vs technical support
ParallelisationRun calls simultaneously: sectioning (independent subtasks) or voting (several attempts, then aggregate)Speed, or more confidence through multiple perspectives
Orchestrator–workersA central LLM breaks the task down dynamically, delegates to worker LLMs, and synthesises resultsYou can't predict the subtasks in advance — e.g. changes across many files
Evaluator–optimiserOne LLM generates; another evaluates and gives feedback; repeatClear evaluation criteria and iterative refinement adds value — e.g. translation, search
Prompt chainingLLMgateLLMLLMRoutingRouterLLM ALLM BLLM CParallelisationInAggregateLLMLLMLLMOrchestrator–workersOrchestratorWorkerWorkerWorkerSynthesisEvaluator–optimiserGeneratorEvaluatorOutputdraftfeedbackapproved
Anthropic's five workflow patterns. Each is a predefined code path with LLM calls inside; real systems combine them.

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.