The line between an agent and a workflow is control flow: who picks step two, you at author time or the model at run time? A decision tree and two architectures.
A decision tree, two reference architectures, and the hybrid pattern for production systems
MewCP · 30 Days of AI Agents · Day 06 companion resource · Keyword: FLOW
This is a build document, not another definitions post. It assumes you already know roughly what an agent is and you are now trying to decide whether you need one.
It works best read out loud with your team. Open Section 2, answer the questions honestly about the system you are actually building rather than the one described in the roadmap, land on a terminal node, then jump to the architecture that node points at. Section 8 is a printable checklist for recording the decision along with the reasoning behind it, so that in six months nobody has to reverse engineer why the system ended up shaped this way.

A ──▶ B ──▶ C ──▶ D
└──────────────────┘
defined before runtimeA workflow follows a path you wrote. You decided the sequence before anything ran. A model can do the work inside a step (summarise, classify, extract, rewrite) but it never chooses which step comes next.
GOAL ──▶ DECISION ──▶ ACTION ──▶ OBSERVATION
▲ │
└──────────────────────────┘
n iterations
resolved during runtimeAn agent runs a loop. You define the goal and the tools. The model decides each step from what it just observed and keeps deciding until a stop condition is met.
Who picks step two, you at author time or the model at run time?
Not autonomy. Not intelligence. Not model size. Not whether it calls tools, because both shapes call tools. The line is control flow, and the things you actually care about (cost, testability, failure modes, what you have to monitor) all follow from it.
A workflow keeps the path in the source:
doc = ingest(file)
text = llm.extract(doc) # model does work
kind = llm.classify(text) # model does work
if kind == "invoice": # engineer chooses the branch
result = handle_invoice(text)
else:
result = handle_generic(text)
store(result)Every route through this program is visible by reading it. The LLM calls are steps. They are not decisions about steps.
An agent keeps the path in the loop:
state = {goal: goal, history: []}
while not stop(state):
step = llm.decide(state, tools) # model chooses the next action
obs = tools.run(step) # execute whatever it chose
state.history.append((step, obs))
return stateNobody wrote the sequence. The tools list bounds what can happen, not the order it happens in. The route only exists once the run is finished, which is why the trace is your only record of what the system really did.
A fixed path does not give you a deterministic output. A workflow guarantees the route, not the result. Every LLM step along that route is still stochastic. Reproducible output comes from structured outputs, constrained decoding, low temperature and validation. It does not come from picking a workflow.

Start at the top.
Can you enumerate the steps in advance?
│
┌──────────────────┴──────────────────┐
YES NO
│ │
Does the order ever change? Do you know the steps but
│ not the order?
┌───────┴───────┐ │
NO YES, but ┌────┴────┐
│ among known YES NO
▣ WORKFLOW paths │ │
│ ▣ CONSTRAINED Is the scope
▣ ROUTER AGENT bounded, with a
(fixed toolset) reachable stop
condition?
│
┌────┴────┐
YES NO
│ │
▣ WORKFLOW (steps enumerable, order fixed)
You can draw the whole thing on a whiteboard before writing any code. Build the rail and put LLM calls inside the steps that need them. Go to Section 3.
▣ ROUTER (one choice up front, among known paths)
This is the tier most articles skip. A model picks one of N predefined workflows, then that workflow runs to completion. The model touches control flow exactly once, at the top, and never again. It covers a surprising amount of work that gets pitched as "we need an agent", at a fraction of the variance and cost, and it stays testable: N deterministic paths plus one classification. If your walk through the tree landed here, sit with it before reaching for something bigger. Go to Section 3 and put a classification step in front of the rail.
▣ CONSTRAINED AGENT (steps known, order unknown)
The model sequences a closed set of operations. Because the toolset is fixed and small, you can still enumerate the failure surface even though you cannot enumerate the path. It is a loop on a short leash: tight step budget, no tool discovery at runtime. Go to Section 4 and lock the tool layer.
▣ AGENT LOOP (steps unknown until runtime, scope bounded)
The real thing. It needs all four stop conditions and trace led observability from the first day it runs. Go to Section 4.
▣ STOP AND REDESIGN (unbounded scope, no reachable end state)
If you cannot say in one sentence when the system is finished, you do not have an agent design yet. You have a budget with a prompt attached. Narrow the goal until a stop condition exists, then walk the tree again.

trigger
│
▼
┌─────────────────────────────────────────────┐
│ ORCHESTRATOR (owns sequence + retries) │
└─────────────────────────────────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
[step A] [step B] [step C] [step D]
│ │ │ │
└────────┴────────┴────────┴──▶ trace spans
each step: input contract → work → output contract
Orchestration layer. Owns the sequence, the retries and the state handed between steps. Use durable execution (a queue, a state machine, a workflow engine) if runs are long or need to survive a deploy. A plain function is fine if they are short. The orchestrator never asks a model anything.
Step contracts. Every step declares typed input and typed output. This is what makes the workflow testable, because each step becomes a unit you can exercise with fixtures. Validate at the boundary rather than deep inside the step.
Retry and idempotency. Steps retry independently, so any step with a side effect needs an idempotency key. Sort your steps into two piles: safe to retry (pure computation, reads, most LLM calls) and unsafe without a key (writes, payments, anything that sends a message to a human). Retrying an LLM call is cheap and usually correct. Retrying a write creates a duplicate.
Where the LLM calls sit. Inside steps, never between them. An LLM call is a transformation with an input contract and an output contract like anything else. The moment you catch yourself asking the model "what should we do next?", you have left this architecture. That is fine, but do it on purpose and go to Section 4.
Structured output validation. Every LLM step returns a schema validated object. A validation failure is a step failure and follows the normal retry path. Never let unvalidated model output flow into the next step's input contract.
Error propagation. Decide per step whether a failure kills the run, skips with a default, or routes to a fallback, and write that decision into the step definition. The best thing a workflow gives you is a failure list you can enumerate. You throw that away by swallowing exceptions.
Tracing. One span per step, a parent span per run, with token counts, latency, payload sizes and retry count on each. Because the path is known, the dashboard can be a fixed funnel: how many runs reached step C today.
Testing. You can test this deterministically, per step, in CI. Mock the LLM calls with recorded fixtures to cover paths, and run a small live evaluation set to cover quality. End to end tests are finite here because the paths are finite. Count them and cover them.

goal ──▶ ┌───────────────┐
│ DECISION │◀───────────────┐
│ (model) │ │
└───────┬───────┘ │
│ chosen action │
▼ │
┌───────────────┐ ┌───────────────┐
│ TOOL LAYER │───────▶│ OBSERVATION │
│ (bounded) │ │ (+ compact) │
└───────────────┘ └───────────────┘
│
stop? ─┴─▶ step budget · token budget
wall clock · terminal stateGoal specification. This is the highest leverage artifact in the whole system and it is usually written in five minutes. It needs the objective, the definition of done, the constraints and the things the agent must not do. A vague goal does not produce a creative agent. It produces a long one.
Decision layer. The model call that picks the next action from the goal, the history and the available tools. Keep it separate from the tools themselves so you can swap models, tune the prompt or add a cheaper pre filter without touching execution.
Tool layer and selection surface. Tools are the real boundary on what your agent can do. Keep the set small and keep the tools semantically distinct, because overlapping tools are the most common reason an agent wanders. Each tool needs a precise description (the model reads this, so treat it as prompt rather than documentation), a typed schema, its own auth, its own timeout and its own audit record. Destructive tools sit behind an approval gate or do not exist.
Observation handling and context growth. Every iteration adds to the context. Left alone, that degrades quality and inflates cost at the same time. Truncate big tool outputs at the boundary, compact or summarise history past a threshold, and keep an explicit scratchpad for the facts that have to survive compaction.
The four stop conditions. Ship all four.
Each one catches a different failure: infinite tool calling, context bloat, a hung tool call, and never recognising success. An agent without a stop condition is not an agent, it is a bill.
Retry semantics inside a loop. Retry the tool call, not the iteration. A failed tool should hand its error back as an observation, because a good agent recovers from "file not found" by choosing something else, and that recovery is the adaptability you paid for. Retrying the whole iteration throws it away.
Human approval checkpoints. Put them in front of anything irreversible: external sends, writes to a system of record, payments, deletions. The checkpoint pauses the loop and shows the proposed action along with the reasoning. Treat it as part of the design, not as training wheels you remove later.
Trace design when the path is not fixed. You cannot build a funnel dashboard here because there is no funnel. Watch the shape of runs instead: iterations per run, tool call distribution, tokens per run, which of the four stop conditions fired, and how often the agent reached a genuine terminal state. Your alerts live on shifts in those distributions. A rising share of runs hitting the step budget usually tells you quality is slipping well before a customer does.
Most production systems end up here, as a workflow skeleton with agentic steps embedded in it. This is not a compromise. It is what maturity looks like, because it puts the unpredictable part exactly where it earns its keep and nowhere else.

┌────┐ ╭────╮ ┌────┐ ╭────╮ ┌────┐
│ 01 │──▶│ ◯ │──▶│ 03 │──▶│ ◯ │──▶│ 05 │
└────┘ ╰────╯ └────┘ ╰────╯ └────┘
fixed agentic fixed agentic fixed
└───────────────── one system ─────────────────┘Where to draw the boundary. Make a step agentic when its sequence is genuinely unknowable in advance, not when its content is hard. Hard content inside a known step is still a workflow step. It just has a good prompt in it.
How to bound an agentic step. Treat it as a workflow step that happens to loop inside. Same typed input contract, same typed output contract, its own step and token budget, its own timeout, validated result. The surrounding pipeline should not be able to tell whether the step looped three times or thirty, only whether it returned something valid in time.
How to fall back. Every agentic step needs a deterministic fallback for when it burns its budget or fails validation: a simpler heuristic, a default value, or a human queue. That is what stops one unpredictable step from destabilising an otherwise predictable pipeline. Track fallback rate as a first class metric, because it is the cheapest early warning you will get.
A worked example, document intake:
| # | Step | Shape | Why |
|---|---|---|---|
| 1 | Document intake | Deterministic | Receive, store, hash, queue. Nothing varies. |
| 2 | Extraction | Agentic | Layout varies wildly, so the agent picks which tools to run and in what order: OCR, table parse, re crop, read again. Bounded to 8 steps. |
| 3 | Validation | Deterministic | Schema, totals, cross field rules. Pure code, fails loudly. |
| 4 | Discrepancy resolution | Agentic | Only runs when step 3 fails. Queries source systems, checks history, proposes a correction. Bounded to 6 steps, and it proposes rather than writes. |
| 5 | Write | Deterministic | Idempotent, audited, one path. |
Two agentic steps, three deterministic ones, one predictable system end to end. Steps 2 and 4 can be budgeted and improved on their own. Steps 1, 3 and 5 can be tested exhaustively. Notice where the irreversible action sits: the write is deterministic, and the agentic step before it proposes instead of writing. That pairing is the heart of the pattern.
| Workflow | Agent | |
|---|---|---|
| Failure 1 | An unhandled input variant hits a missing branch | Loops without converging and burns the step budget |
| Failure 2 | An LLM step returns off schema output | Picks the wrong tool from an overlapping set |
| Failure 3 | A downstream service times out mid run | Context bloat degrades decisions late in the run |
| Failure 4 | A retry duplicates a side effect | Acts on a stale or misread observation |
| Failure 5 | Quality drifts quietly inside one step | Declares success without meeting the goal |
| How it shows up in traces | A named step fails and the span tells you which | The run shape is wrong: iteration count, tool mix, tokens |
| Cost profile | Fixed per run, forecast from volume | Variable per run, forecast from distribution, budget at p95 |
| Latency | Sum of the steps, tight variance |
There is an asymmetry here worth internalising. Workflow failures get caught by tests. Agent failures get caught by traces. Budget your engineering time accordingly, because a team that ships an agent with workflow grade observability will find its failures in production, reported by customers.

You do not rewrite a workflow into an agent. You make one step's selection dynamic, instrument it, bound it, then widen.
Three signs a workflow has outgrown its shape
else branch is quietly handling a rising share of real traffic.Two signs an agent should go back to being a workflow
Demoting an agent is not an admission of failure. It is the same evidence driven move as promoting one, pointed the other way.
Walk these in a review and record the reasoning, not just the verdict.
Shape
If it is a workflow
If any part of it is agentic
Both
Record three things at the end: the decision, the rationale, and the signal from Section 7 that should make you revisit it. A date is a weaker trigger than a signal.
Both shapes call tools, and both need those tools to be reliable, authenticated and observable. That layer is what MewCP handles. The shape above it is your call, and after walking this guide it should be a recorded one.
Day 07 begins agent architecture. Follow @mewcp_ai.
| Iterations times decision latency, long tail |
| What to alert on | Step error rate, step latency p95, retry rate, funnel drop off | Step budget hit rate, tokens per run p95, tool error rate, terminal state rate, fallback rate |