In Part 2 you saw the agent loop: observe, think, act, repeat, until the task is done. In Part 3 you saw how tools give that loop something to act with. Both of those parts assumed you already know you want an agent. This part steps back and asks a question that should come earlier: do you actually want one, or would a simpler, fixed sequence of steps do the job just as well, for less money and with fewer surprises? Part 1 already warned against reaching for an agent when a single prompt or a short script would do. This part goes further and gives you a second, more structured tool: a family of patterns called workflows, which sit between a single LLM call and a full autonomous agent, and a precise way of telling the two apart.
The distinction matters because the word agent has become a label people attach to almost anything that uses an LLM more than once. That looseness has a cost. If you build an open-ended agent loop for a task that really only needed three fixed steps, you pay for it in unpredictable cost, unpredictable latency, and a system that is much harder to test and debug. If you build a rigid fixed pipeline for a task that genuinely needs the model to decide its own path, you end up with brittle special-case code that breaks the moment a real input does not match the cases you anticipated. Knowing which situation you are in is one of the more practical skills in this path.
Two ways to build the same assistant
Imagine you are building a tool that handles incoming customer support tickets. Every ticket needs to be read, categorised, checked against policy, and answered or escalated. There are two quite different ways to build this.
Version one: a fixed pipeline
You write code that always does the same four things in the same order: read the ticket, classify it into billing, technical, or general, check a policy rule that depends on the category, and draft a reply. An LLM might be used inside step one (to classify) and step four (to draft), but the order of steps, and which step runs after which, is written by you, in advance, in code. No matter what the ticket says, the system always classifies first, always checks policy second, always drafts third. The LLM is doing work inside steps, but it never decides which step comes next or whether a step should be skipped.
Version two: an open loop
Alternatively, you give the model a goal ("resolve this ticket") and a set of tools (look up the customer's account, check refund policy, search past tickets for similar cases, draft a reply, escalate to a human). At each turn, the model looks at what it has learned so far and decides for itself what to do next: maybe it looks up the account first, maybe it searches past tickets, maybe for a simple ticket it skips straight to drafting a reply. The number of steps is not fixed in advance. For an easy ticket it might take one tool call; for a tangled billing dispute it might take six. This is the agent loop from Part 2, applied to this task.
Both versions can produce a correct answer for the same ticket. The difference is not in what they can achieve in the best case, but in who is in control of the path taken to get there, and what that implies for cost, speed, predictability, and how easy the system is to reason about when something goes wrong.
The precise difference: who decides what happens next
Here is the definition to hold onto for the rest of this path. In a workflow, the control flow, meaning the sequence of steps, the branches, the loops, is written by the developer ahead of time, in code or in a diagram. The LLM may be called inside one or more of those steps to generate text, classify something, or extract data, but it never decides which step executes next. In an agent, the control flow is decided at run time by the model itself. The loop structure (observe, think, act) is fixed, as you learned in Part 2, but the path taken through that loop, which tool gets called, in what order, how many times, and when to stop, is chosen by the model based on what it has seen so far.
A useful short test: if you can draw the system as a flowchart with fixed arrows before you have seen a single real input, and the model only fills in the boxes, you have a workflow. If the arrows themselves depend on what the model decides while the system is running, you have an agent.
This test also explains why a workflow with an if/else branch based on a classification is still a workflow, not an agent. The branch condition ("if billing, go to policy check A; if technical, go to policy check B") was decided by the developer in advance. The model fills in which branch applies for this particular ticket, but it did not invent the branch, and it cannot decide to take a third path that nobody coded.
A fixed pipeline in code
The example below shows the ticket triage system as a workflow. To keep it runnable without an API key, the classify and draft steps are simple rule-based stand-ins for what would normally be LLM calls; the structure, three functions always called in the same order, is what matters here.
# A fixed, three-step workflow for triaging support tickets.
# In a real system, classify() and draft_response() would call an
# LLM. check_policy() is deterministic business logic. The order
# of steps never changes, no matter what the ticket says.
def classify(ticket_text):
text = ticket_text.lower()
if "refund" in text or "charge" in text:
return "billing"
if "error" in text or "crash" in text:
return "technical"
return "general"
def check_policy(category, ticket_text):
if category == "billing" and "refund" in ticket_text.lower():
return "needs_manager_approval"
return "auto_handle"
def draft_response(category, policy_result):
if policy_result == "needs_manager_approval":
return f"This {category} ticket has been forwarded to a manager."
return f"Here is a standard {category} response, generated automatically."
def run_workflow(ticket_text):
category = classify(ticket_text)
policy_result = check_policy(category, ticket_text)
response = draft_response(category, policy_result)
return {
"category": category,
"policy_result": policy_result,
"response": response,
}
tickets = [
"I was charged twice for my subscription, please refund me.",
"The app crashes every time I open settings.",
"What are your opening hours?",
]
for t in tickets:
print(run_workflow(t))
Running this prints one dictionary per ticket, each with a category, a policy result, and a drafted response. Every ticket goes through exactly three steps, in exactly the same order. You could swap the rule-based classify function for a real LLM call tomorrow and the shape of the system would not change at all. That stability is the main appeal of a workflow: you can test each step in isolation, you know exactly how many model calls a batch of a hundred tickets will cost, and you can trace a wrong answer back to the exact step that produced it.
An open loop in code
Now compare that with a minimal agent loop answering a question that needs two separate facts. Again, the decision step is faked with simple rules instead of a real LLM call, purely so the example runs without an API key. What matters is that the number of iterations is not fixed before the loop runs; it depends on what the question asks and what has already been found.
# A minimal agent loop. Unlike the workflow above, which tool gets
# called next, and how many times the loop runs, is decided while
# the loop is running, based on what has been learned so far.
def search(query):
# Pretend search engine returning canned results for this example.
fake_index = {
"population of Iceland": "About 390,000 people, as of a recent estimate.",
"capital of Iceland": "Reykjavik is the capital of Iceland.",
}
return fake_index.get(query, "No results found.")
def decide_next_action(question, memory):
# In a real agent this would be an LLM call that reads the
# question and the memory gathered so far, then returns either
# a tool call or a signal to stop. Here the same decision is
# faked with simple rules so the example needs no API key.
if "capital" in question and "capital" not in memory:
return {"tool": "search", "args": "capital of Iceland"}
if "population" in question and "population" not in memory:
return {"tool": "search", "args": "population of Iceland"}
return {"tool": "finish", "args": None}
def run_agent(question, max_steps=5):
memory = {}
for step in range(max_steps):
action = decide_next_action(question, memory)
if action["tool"] == "finish":
break
result = search(action["args"])
key = "capital" if "capital" in action["args"] else "population"
memory[key] = result
print(f"step {step + 1}: searched '{action['args']}' -> {result}")
return memory
answer = run_agent("What is the capital and population of Iceland?")
print("final memory:", answer)
Running this prints two search steps, because the question asks for two facts, then stops once decide_next_action sees both have been found. If you change the question to ask for only the capital, the loop would stop after one step instead of two. Nothing in the code tells it in advance how many steps to take; the stopping condition is evaluated fresh at every turn. That is the defining trait of an agent, and it is also exactly why an agent is harder to reason about in advance than a workflow: you cannot read the code and know how many model calls a given input will cost, because that depends on the input and on what the model decides while running.
Five workflow patterns you will keep seeing
Workflows are not all the same shape. A handful of patterns cover most of what people build when the control flow is fixed but LLM calls do real work inside it. Recognising these patterns is useful because they are reusable building blocks, the way sorting and searching are reusable building blocks in ordinary programming.
Prompt chaining
One LLM call's output becomes the next call's input, in a fixed sequence. A common example is writing: generate an outline, then expand the outline into a draft, then edit the draft for clarity. Each step is a separate, focused call, which tends to produce better results than asking for a finished document in one shot, because each call has a narrower job and a clearer success criterion. The chain is fixed: outline always comes before draft, draft always comes before edit.
Routing
An early step classifies the input, and the classification picks which fixed path handles the rest. The ticket triage example above is routing: classify into billing, technical, or general, then follow the policy and drafting logic that matches that category. Routing is useful whenever different categories of input genuinely need different handling, and the categories are known in advance, even if you let an LLM do the classifying.
Parallelisation
Several LLM calls run at the same time instead of one after another, and their results are combined afterwards. Two common versions: sectioning, where a long document is split into chunks, each summarised independently, then the summaries are combined; and voting, where the same question is sent to the model several times (or to several different prompts) and the final answer is chosen by majority or by picking the best one. Parallelisation is a workflow because the number of parallel calls and how they are combined is decided in advance, not by the model.
Orchestrator plus workers
A first call looks at the task and produces a plan, for example a list of subtasks. Fixed code then dispatches each subtask to its own LLM call, and a final step combines the results. This pattern can look agent-like because the first call is doing some of what feels like planning, but if the dispatch and combination logic is fixed code that just loops over whatever list the planning step produced, the control flow is still fixed; only the content of the plan varies. It starts to blur into agent territory when the orchestrator itself can revise the plan mid-way based on what the workers return, rather than running the plan once through.
Evaluator and optimizer
One call generates something, a second call evaluates it against criteria, and if it fails, the first call runs again with the feedback, up to a fixed maximum number of attempts. This is a bounded loop with a clear stopping condition decided in advance (either the evaluator approves, or the attempt limit is reached), which keeps it a workflow rather than an open-ended agent. A useful mental anchor: this pattern has a loop, but the loop's shape (generate, then evaluate, repeat up to N times) is fixed, unlike an agent loop where the shape of what happens on each turn is itself decided by the model.
- Prompt chaining: fixed sequence, each step's output feeds the next step's input.
- Routing: classify first, then follow one of several fixed paths.
- Parallelisation: run several calls at once, then combine with fixed logic.
- Orchestrator plus workers: one call plans subtasks, fixed code dispatches and combines them.
- Evaluator and optimizer: generate, evaluate, revise, bounded by a fixed attempt limit.
None of these five patterns require the model to decide, mid-run, what kind of step comes next. They all let the model do real reasoning work inside a step, which is often most of the value you wanted from an LLM in the first place, without handing it control over the overall structure.
What makes something an agent, by contrast
An agent, in the sense used throughout this path, is the loop from Part 2: observe the current state, let the model think about what to do, let the model act (usually by calling one of the tools from Part 3), observe the result, and repeat, with the model itself deciding when enough has been done to stop. The defining feature is not that it is complicated, or that it uses many tools, or that it runs for a long time. A single-tool agent that sometimes stops after one call and sometimes after five is still an agent, because the number of calls and which tool gets used was not fixed by the developer; it was decided by the model, turn by turn, based on what it observed.
This is also why agents are harder to test exhaustively than workflows. A workflow has a fixed number of paths through it (one per branch), so you can write a test for each path and be reasonably confident you have covered the system's behaviour. An agent can, in principle, take a different number of steps and a different order of tool calls for every input, so testing shifts from "does each step work" toward "does the loop behave sensibly across a wide sample of situations", which is a harder and less complete kind of testing. This does not make agents a bad idea; it makes them a deliberate trade, taken on in exchange for flexibility you could not have hard-coded in advance.
A spectrum, not a binary choice
It helps to picture these options as points on a single spectrum of how much control you hand to the model, rather than as two unrelated categories.
- A single LLM call with no surrounding logic: you write a prompt, you get a response, done.
- A workflow using one of the fixed patterns above: multiple calls, fixed order, branches decided by the developer.
- A workflow where one step is itself a small, tightly scoped agent loop, for example a research step that can search and re-search an unknown number of times before handing a summary back to the fixed pipeline.
- A full agent: the model decides the whole path, from first action to the decision to stop, using whichever tools it has access to.
Most real systems that people call agents in casual conversation actually sit at point 3: a workflow with an agentic step embedded inside it, rather than a pure, unconstrained agent from end to end. That is often the right place to sit, because it gives you a predictable outer shape while still letting the model handle the one part of the task that genuinely cannot be fully planned in advance.
When a workflow is the better choice
Reach for a workflow, or push your design further left on the spectrum above, when most of the following are true.
- The task is well understood and repeats in roughly the same shape every time, like classifying a support ticket or filling in a standard form from a document.
- You can list the possible categories or branches in advance, even if you are not sure which one a given input will fall into.
- Cost and latency need to be predictable, for example because you are processing a large batch overnight or you have a strict response-time budget.
- You need to audit or explain exactly why the system produced a given output, which is easier when every input goes through the same fixed, inspectable steps.
- Mistakes are expensive enough that you want a human-reviewable trail of fixed steps rather than an open-ended trail of model decisions.
Going back to the expense report example sometimes used in this path's earlier parts: checking a receipt against a reimbursement policy is a strong fit for a workflow. Extract the amount and category (an LLM can help here), compare against a fixed rule such as "anything under a set threshold is auto-approved, anything over goes to a manager", and generate a confirmation message. There is no part of this task that benefits from letting the model decide whether to check the policy before or after extracting the amount; the order never needs to change.
When an agent earns its complexity
Reach for an agent, or push your design further right on the spectrum, when most of the following are true.
- The number of steps needed cannot be known before you see the input. A simple question might need one search; a tangled one might need five searches and two follow-up lookups.
- The best next action genuinely depends on what the previous action returned, in a way that is too varied to write down as a fixed set of branches.
- The task resembles research, investigation, or troubleshooting, where you are narrowing down an answer rather than filling in a template.
- You are willing to trade predictable cost and latency for the chance of a better or more complete answer on harder inputs.
- You have a way to bound the loop, such as a maximum number of steps or a time limit, so an unexpected input cannot send the system into an unbounded or very expensive run. Part 8 covers this in more depth.
A travel-planning assistant that needs to check flight availability, compare it against a calendar, consider a hotel's cancellation policy, and possibly revise earlier choices once a constraint is discovered partway through, is a reasonable fit for an agent. You could try to enumerate every branch in advance, but the number of ways these constraints can interact grows quickly, and a workflow built to handle all of them in advance would end up re-implementing a general-purpose planner in brittle if/else logic. Letting the model decide the next step, one at a time, based on what it has already learned, is a more honest match for how unpredictable the task actually is.
Mixing the two
In practice the two are often combined rather than chosen between. Two combinations come up often enough to name.
An agent as one step inside a workflow
The outer shape of the system is a fixed pipeline, but one of its steps is itself an open-ended agent loop. For example, a document-processing pipeline might have fixed steps for intake, formatting, and filing, with a research step in the middle that is a small agent allowed to search and read an unknown number of times before handing a summary back to the fixed pipeline. From the outside, this still behaves like a workflow: it always runs intake, then research, then formatting, then filing, in that order. The unpredictability is contained inside one box, rather than spread across the whole system.
A workflow as one tool an agent can call
The outer shape is an agent loop, but one of the tools it can call is itself a small fixed pipeline rather than a single action. For example, an agent that is told to "resolve this customer's issue" might have a tool called process_refund that, once invoked, runs a fixed, three-step refund workflow internally (check eligibility, calculate amount, issue the refund) rather than exposing each of those three steps as separate tools the agent has to sequence itself. This keeps a sensitive, well-understood sub-process predictable and testable, even though the agent decides when to invoke it.
Both combinations are a way of containing the part of the system that genuinely benefits from an open loop, while keeping the rest of the system as predictable as it can afford to be. This is usually a better default than an all-or-nothing choice, and it is close to how most production systems that use the word agent are actually built.
Common mistakes
A few patterns of getting this wrong show up repeatedly enough to call out directly.
- Building an agent because the word sounds more impressive, for a task that a three-step workflow would have handled with far less cost and far more predictable behaviour. Part 1 covered this trap in general terms; this is the specific, structural version of it.
- Building a rigid workflow for a task whose real complexity is in the number and order of steps, then patching it with an ever-growing pile of special-case branches as new input shapes show up. If you find yourself adding a new if/else branch every week to cover another edge case, that is a sign the task wanted an agent's flexibility, not more branches.
- Calling something an agent just because it contains a loop. An evaluator-optimizer pattern with a fixed retry limit is a workflow with a bounded loop, not an agent; the distinction is whether the model decides the shape of what happens next, not whether anything repeats.
- Giving an agent an unbounded loop with no maximum number of steps, which means a single unusual input can turn into a very long, very expensive run. Part 8 covers stopping conditions in detail, but the short version is: every open loop needs a hard limit, decided in advance, even if the model is the one choosing what happens inside that limit.
- Underestimating how much harder an agent is to test. A workflow can be tested step by step, with one test per branch. An agent needs to be tested across a sample of realistic inputs and judged on whether its behaviour stays sensible, which is a different and generally harder kind of testing to get confidence from.
- Assuming more autonomy always means a better result. On tasks that are genuinely well understood, a fixed workflow using a well-chosen prompt at each step often matches or beats an agent's output, while costing less and running faster, simply because there was no ambiguity for the extra flexibility to resolve.
A decision checklist
When you are not sure which way to build something, these questions, asked in order, usually settle it quickly.
- Can you draw the full set of steps and branches on paper before seeing a single real input, and be reasonably confident it covers what will actually happen? If yes, build a workflow.
- If you cannot draw it fully in advance, is the uncertainty confined to one part of the task, such as one research or lookup step, while the rest is predictable? If yes, build a workflow with an agent embedded in that one step.
- Does the task as a whole resemble open-ended investigation, where the next useful action genuinely depends on what the last one revealed, across most of the task rather than one part of it? If yes, a full agent is a reasonable choice, with a hard limit on how many steps it can take.
- Either way, can you state a clear stopping condition and a maximum cost or time budget before you start, even if the model is the one deciding what happens inside that budget? If you cannot, that is worth resolving before you write any code, not after.
Note that the checklist does not have a step that says "pick whichever is more exciting to build." The two approaches solve the same underlying problem, predictability versus flexibility, and the right answer is whichever one the task actually needs, which is often less exciting and more boring than it might feel.
Summary and what comes next
A workflow is a system where the developer fixes the sequence of steps in advance, even if LLM calls do real reasoning work inside individual steps. An agent is a system where the model itself decides, turn by turn, what happens next, using the observe-think-act loop from Part 2 and the tools from Part 3. Five common workflow patterns, prompt chaining, routing, parallelisation, orchestrator plus workers, and evaluator-optimizer, cover most fixed-structure designs you will meet. Most real systems sit somewhere on a spectrum between a single LLM call and a fully open agent, often as a workflow with one agentic step embedded inside it, or an agent with one workflow-shaped tool it can call. Choosing between them comes down to how predictable the task's shape is: well-understood, repeatable tasks favour workflows; open-ended, investigative tasks favour agents, bounded by a hard limit on how far the loop is allowed to run.
The next part in this path, Planning and Task Decomposition, picks up exactly where the harder end of this spectrum leaves off: once you have decided a task needs an agent, or an agentic step inside a workflow, how does the model go about breaking that task into a sensible sequence of actions in the first place, and what can go wrong in that planning process.
Comments (0)
No comments yet. Be the first to share your thoughts.