Area 03 // AI AgentsLesson 47 / 14014 min read
Workflows vs agents
A workflow is a fixed sequence of steps your code controls, with LLM calls filling in specific blanks; an agent lets the model decide the steps itself in a loop; most products should start with a workflow and only use an agent where the path really can't be planned in advance.
Think of it like this
Analogy
A workflow is a recipe: chop, fry, add sauce, serve. Anyone can follow it, and the dish comes out the same every time. An agent is a chef handed a fridge and told "make dinner": they look at what's there, decide what to cook, taste as they go, and adjust.
You want the recipe for your restaurant's signature dish. You want the chef when a customer has an unusual allergy and nothing on the menu fits.
The problem it solves
"Agents" are exciting, so many teams build one for every feature. Then they discover the agent sometimes skips a step, sometimes loops, costs ten times more than expected, and is hard to test because it does something different each run. Meanwhile most of the task was the same five steps every time.
Choosing the right level of autonomy is the first design decision for any LLM feature, and it's often the most important.
How it works, step by step
- Start from the task, not the technology. Can you write down the steps in advance? Is the path the same for most inputs?
- If yes, build a workflow. Your code runs fixed steps; each LLM call does one narrow job (classify, extract, draft). Common patterns:
- Chain: step A's output feeds step B (outline → draft → polish).
- Router: classify the input, then send it down the right path.
- Parallel: run independent calls at once (check tone, check facts, check length) and combine.
- Evaluator-optimizer: one call drafts, another checks against criteria, repeat until it passes.
- If the steps genuinely can't be known in advance, consider an agent: give the model tools and a goal, and let it decide the next step in a loop until it's done (the agent loop).
- Check the four agent criteria before choosing one: the task is complex and hard to specify step by step; it's valuable enough to justify extra cost and latency; models are actually capable at it; and mistakes can be caught and recovered (tests, review, undo).
- Mix them. Real systems are usually a workflow with one agentic step where flexibility is needed (e.g. a fixed support pipeline where only "investigate the bug" is an agent).
| Workflow | Agent | |
|---|---|---|
| Who decides the steps | Your code | The model |
| Predictability | High | Lower |
| Cost and latency | Low, bounded | Higher, variable |
| Testing | Unit-test each step | Evaluate trajectories and outcomes |
| Best for | Known, repeatable processes | Open-ended problems, unknown paths |
Flow engineering
Flow engineering means designing the flow around the model: break the task into steps, give each step one clear job and a check, and decide in code what happens on failure. Instead of one huge prompt ("read this email, find the order, check the policy, write a reply"), you build small steps with validation between them.
- Decompose the task into steps a model (or code) can do reliably.
- Put code where code is better: routing, validation, lookups, arithmetic.
- Add a check after each risky step: schema validation, a test, a rule, or a judge call.
- Decide failure paths: retry, ask the user, or hand to a human.
- Measure each step separately, so you know which one to improve.
See it in code
- Needs
- Nothing: plain Python 3. The LLM is a stub so the flow runs offline.
A support workflow where code controls every branch and each LLM call does one small job.
# A WORKFLOW: your code decides every step; the model only fills in specific blanks.
def fake_llm(task: str, text: str) -> str:
"""Stand-in for one small, focused LLM call per step."""
if task == "classify":
return "refund" if "refund" in text.lower() else "other"
if task == "extract_order":
return next((w for w in text.split() if w.startswith("A") and w[1:].isdigit()), "none")
return "Hi! Your refund for order {order} is being processed."
def support_workflow(email: str) -> list[str]:
trace = []
category = fake_llm("classify", email) # step 1: classify (LLM)
trace.append(f"classify -> {category}")
if category != "refund": # step 2: route (plain code)
trace.append("route -> human queue")
return trace
order = fake_llm("extract_order", email) # step 3: extract (LLM)
trace.append(f"extract -> {order}")
if order == "none": # step 4: validate (plain code)
trace.append("validate -> ask customer for order number")
return trace
reply = fake_llm("draft", email).format(order=order) # step 5: draft (LLM + template)
trace.append(f"draft -> {reply}")
return trace
for email in ["I want a refund for A1234 please", "Refund please!", "Love the product"]:
print(email, "=>", support_workflow(email))I want a refund for A1234 please => ['classify -> refund', 'extract -> A1234', 'draft -> Hi! Your refund for order A1234 is being processed.']
Refund please! => ['classify -> refund', 'extract -> none', 'validate -> ask customer for order number']
Love the product => ['classify -> other', 'route -> human queue']Every path is visible, testable and cheap. Replace fake_llm with real calls and you have a production-shaped pipeline; nothing here needed an agent.
A real example
Example
An insurance claims team that un-agented its system
A team built an autonomous agent to process car insurance claims: read the claim, check the policy, check for fraud signals, decide, and write to the customer. It worked in demos but in production took 40–120 seconds, occasionally skipped the fraud check, and cost about 30× a workflow.
They rebuilt it as a workflow: extract fields (LLM + schema), look up the policy (code), run fraud rules (code + a small classifier), decide (rules for clear cases), and draft the letter (LLM). Only unusual claims, about 8%, went to an agent that could investigate with tools, and then to a human. Average time fell to 6 seconds and nothing got skipped.
The easy way
Start here
Default to a workflow. Write the steps on paper; make each one a function; use an LLM only inside the steps that need language understanding; add a check after each. Reach for an agent only for the parts you truly can't write down.
When you grow: the production setup
Mature teams treat autonomy as a dial: deterministic workflows for the main path, constrained agents (few tools, small step limits) for specific sub-tasks, and full agents only where outcomes are verified (tests, human review). They trace both, evaluate each step and each trajectory, and regularly turn agent behaviour that became predictable back into workflow code.
Trade-offs
What you gain
- Workflows: predictable, cheap, fast, easy to test and debug.
- Agents: handle open-ended tasks and unexpected situations without hand-coding every path.
- Mixing both gives flexibility only where it pays off.
What it costs you
When not to use it
- Avoid agents for high-volume, well-understood processes, and where a wrong action can't be undone without human review.
- Avoid rigid workflows for genuinely exploratory tasks (research, debugging) where they'd need hundreds of branches.
Common mistakes
- Building an agent because it's fashionable when the steps are always the same.
- One giant prompt that does five jobs, instead of five small checked steps.
- Letting the model do routing, arithmetic or lookups that plain code would do perfectly.
- Agents without step limits, budgets or verification.
- Never revisiting: agent behaviour that became predictable should become workflow code.
Interview questions
Try answering out loud first, then open the model answer.
When would you build an agent instead of a workflow?
Model answer
When the task is open-ended so the sequence of steps can't be specified in advance, the value justifies higher cost and latency, current models are capable at it, and errors are recoverable through tests, review or undo. Otherwise I'd use a workflow, where code controls the path and LLM calls handle narrow steps, because it's cheaper, faster, more predictable and easier to test. Often the answer is a workflow with one agentic sub-step.
Describe common LLM workflow patterns.
Model answer
Prompt chaining (sequential steps, each checking the previous), routing (classify then dispatch to specialised paths or models), parallelisation (independent calls run concurrently, or several attempts voted on), orchestrator-worker (dynamic sub-task delegation, edging into agents), and evaluator-optimizer (generate, critique against criteria, revise in a loop with a cap).
What is flow engineering?
Model answer
Designing the end-to-end flow around model calls: decomposing a task into small, well-defined steps, using code for deterministic parts, adding validation or tests after risky steps, defining retry and fallback paths, and measuring each step. It treats the LLM as one component in an engineered process rather than expecting a single prompt to do everything reliably.
Quiz
Pick an answer to see whether it’s right and why.
0 of 5 answered
Key terms
- Workflow (LLM)
- A fixed sequence of steps controlled by code, where LLM calls fill in specific blanks such as classifying or drafting.
- Agent
- A system where a model decides, in a loop, which tools to call and what to do next until a task is done.
- Flow engineering
- Designing the steps, checks and retries around model calls, instead of relying on one big prompt to do everything.
- Tool calling (function calling)
- A model replying with a request to run one of your functions, with arguments; your app runs it and sends the result back.
Before and after
Progress is saved in this browser only.