Agent or workflow: a decision rule that holds up in production
When a model should choose the next step and when your code should, four tests that decide it, worked examples, and how the two shapes nest inside each other.
Use a workflow when the sequence of steps is known before the input arrives, and an agent when it is not. Most production systems need both: a deterministic spine that owns ordering and side effects, with model-driven decisions at a few named points inside it. The unit of the decision is the step, not the project.
That framing is unpopular because "agent" is the default answer. A model that chooses its own path demos well, absorbs vague requirements, and needs no one to sit down and write the process out. The same properties make it slow, expensive, hard to test and capable of taking a route nobody reviewed. A team that reaches for an agent on a process it could have written down pays for flexibility it does not use, every run, forever.
Should I build an AI agent or a deterministic workflow
Answer four questions about the step in front of you. Three yes answers or more means code should decide the next step. Two or fewer means a model should, inside limits the runtime enforces. The questions are about one step, so a process usually splits: some steps go one way, some the other.
Is the sequence of steps known before the input arrives?
Write the process on a whiteboard without looking at any particular input. If you can, the sequence is known, and a model choosing it at runtime is sampling from a space whose right answer you already have. Refund handling is usually known: check the order, check the return window, check the payment method, issue or decline, notify. A support conversation is not known, because the second step depends on what the customer said in the first.
The tell is a process document. If your operations team already has a runbook for this, the runbook is the workflow, and the only genuinely open question in it is the one the runbook phrases as "use judgment".
Is the input structured?
An order ID, a webhook payload and a row from a table are structured. An email, a screenshot, a support ticket and a chat message are not. Code handles structured input better than a model does, because a field access cannot hallucinate.
Where the input is unstructured, the model earns its place at exactly one job: turning it into structure. A classification step that returns {"intent": "refund", "orderId": "..."} converts an open input into a value the rest of the process can branch on with an expression your team wrote and can unit test.
Is the action reversible?
Sending a draft to a reviewer is reversible. Issuing a refund, deleting a record, emailing a customer and posting to a public channel are not. Irreversible actions belong in code, at a fixed position, with the conditions checked before them, because a model deciding when to take an irreversible action means the timing of that action is sampled. When the action genuinely has to be model-chosen, put an approval gate in front of it and treat the agent's stated reason as context for the approver rather than as an input to any automatic decision.
Can you write the test?
Say out loud what a correct run looks like. If the sentence is "it updates the record and then sends the notification, in that order, and never sends twice", you have an assertion, and an agent will fail it intermittently for reasons no prompt edit fixes.
If the sentence is "the reply addresses what the customer actually asked and does not promise a refund we do not offer", you have a judgment, which is scored rather than asserted, and a workflow cannot produce it. The shape of the test tells you the shape of the step. Non-determinism is the reason the first sentence and the second need different machinery, and the mechanics of that are in why LLM output is non-deterministic.
The four shapes
The two primitives combine into four arrangements, and most teams need three of them somewhere in the same product.
| Shape | Who picks the next step | How you test it | How it fails |
|---|---|---|---|
| Fixed workflow | Your code, always | Assert on the path and the output | A branch nobody wrote; unhandled input falls through |
| Workflow, model at a step | Code, except one judgment | Assert the path, score the model step | The model step returns a shape the next step cannot read |
| Agent, bounded tool set | The model, from few tools | Score outcomes; assert on limits and side effects | Wrong tool chosen; repeated calls until a cap stops the turn |
| Open agent | The model, from many | Score outcomes only | Unreviewed routes, runaway cost, a failure you cannot reproduce |
An open agent is the right answer less often than it is chosen. It is the right answer for research and triage over messy inputs, for internal tools where a wrong path costs a person two minutes, and for the exploratory phase where you are still learning what the process is. It is the wrong answer for anything with money or customer-visible side effects in it.
Worked examples
Each row applies the four tests. The verdict is the shape, not the whole system: a single product usually contains several of these rows.
| Step | Sequence known | Input structured | Reversible | Testable as an assertion | Verdict |
|---|---|---|---|---|---|
| Nightly invoice reconciliation | Yes | Yes | No | Yes | Fixed workflow |
| Classify an inbound support email | Yes | No | Yes | Partly | Workflow, model at a step |
| Decide whether a refund is within policy | Yes | Yes | No | Yes | Code, never the model |
| Draft the reply the customer receives | Yes | No | Yes | No | Workflow, model at a step |
| Answer a product question from docs and history | No | No | Yes | No | Agent, bounded tool set |
| Investigate why a specific customer churned | No | No | Yes | No | Open agent, internal use |
| Post the refund to the payment provider | Yes | Yes | No | Yes | Code, behind an approval gate |
The third and seventh rows are where teams most often go wrong. Both look like decisions, so they get handed to the model along with the rest of the conversation, and the process ends up with its ordering sampled. The same failure turns up anywhere ordering carries a rule: an agent asked to cancel a subscription, holding tools for each part of it, will on one run read the contract term, compute the proration and then cancel. On another it cancels first and prorates against a plan that no longer exists, with nothing wrong in the prompt and nothing to fix in it either.
How the two nest
The arrangement that covers most products puts each shape inside the other. A flow can include a model step whose job is one bounded judgment: classify, draft, extract, score. The step declares an output variable, the next step branches on a field of it, and the branch condition is an expression your team wrote rather than a regex over prose.
Going the other way, an agent can call a whole flow as a single tool. "Look up the order, check the return window, compute the amount" becomes one action the model can invoke and cannot reorder. Two things improve at once. The irreversible parts move back into code, and the agent's tool set gets smaller, which is the main lever on wrong-tool selection.
A deliberate second pass sits inside a loop with a hard iteration cap, and the cap is a property of the runtime rather than a sentence in the prompt:
{
"type": "loop",
"name": "Draft and review",
"config": {
"steps": [
{ "type": "prompt", "name": "Draft reply", "config": { "outputVariable": "draft" } },
{ "type": "prompt", "name": "Review", "config": { "responseFormat": "json", "outputVariable": "review" } }
],
"until": "review.verdict === 'pass'",
"maxIterations": 3
}
}
A reviewer that never says "pass" costs three rounds and then the loop exits, which the next step handles as an ordinary case. Without the cap, the same arrangement runs until a token budget stops it. The agent-side equivalent is a cap on turns and on tool calls per turn, because a tool that returns an empty array on a miss reads to the model like a failed call and invites another attempt with a slightly reworded query. Cost and duration controls for that pattern are in long-running agents.
Nesting also gives you a migration path in both directions. A path the agent takes on most runs is a candidate to become a flow it calls as one tool. A branch in a flow that keeps meeting inputs its conditions do not cover is a candidate to become a model step.
Splitting work across several agents is a third option and a later one; it adds a lossy handoff at every hop, which multi-agent systems covers in detail. Both primitives, and the rule for combining them, are the subject of the agent orchestration platform overview.
Where this gets easier
Runtype treats flows and agents as separate primitives that nest, so the decision above is made per step rather than per project. A flow can be called as a tool from an agent and an agent can be wrapped inside a flow, which makes moving one decision from the model to code, or back, a change to a single step. The bounds the four tests keep pointing at are runtime-enforced rather than requested in a prompt: a loop step requires a maxIterations value capped at 10, maxTurns runs from 1 to 100, per-turn maxToolCalls defaults to 10 with a ceiling of 100, tools time out at 30 seconds by default, and each turn has a wall-clock budget.
Frequently asked questions
- Is an agent always more capable than a workflow?
- An agent covers a wider input space, which is a different property from being more capable at a given task. On a task whose steps are known in advance, a workflow does the same work faster, cheaper and with a path you can assert on. The agent adds coverage for inputs you did not anticipate, and pays for it in latency, tokens and testability.
- Can I start with an agent and tighten it later?
- That is a reasonable way to learn what the process actually is. Run the agent over real inputs, read the traces, and look for the paths it takes repeatedly. Each recurring path is a candidate for a flow the agent can call as one tool, which removes that path from the sampled space without removing the agent.
- Does a low temperature make an agent deterministic enough to test like a workflow?
- No. Temperature zero reduces sampling variance without removing it, and the usual explanations are batching effects on the provider's hardware and silent model updates. Tool results that differ between runs move the output as well, whatever the temperature. Assert on outcomes and invariants rather than on an exact path.
- How many tools is too many for an agent?
- There is no fixed number, and the ceiling that matters is behavioral rather than technical. The signal shows up in traces: the model starts picking a plausible but wrong tool, or picks between two tools whose descriptions overlap. Grouping several fine-grained calls behind one deterministic flow the agent invokes as a single action shrinks the choice without removing the capability.