A rules-based automation that reads supplier invoices works perfectly until a supplier
switches accounting software and starts writing Invoice Total where the parser
expected Total Due. The script does not degrade; it stops. Someone opens a ticket,
someone adds a branch, and the rule set grows one more special case.
An agent handed the same invoice reads it, understands that Invoice Total means the
same thing, and extracts the number. It also, occasionally, extracts the subtotal
instead — and it will not tell you which one it did unless you built the checking.
That is the trade in one paragraph. Traditional automation fails loudly on inputs it was not written for. Agents handle inputs nobody anticipated, and fail quietly on inputs you assumed were easy. Choosing between them is not a question of which is more advanced. It is a question of which failure mode your process can absorb.
Two different answers to "what happens next?"
Traditional automation — cron jobs, ETL pipelines, workflow engines, RPA scripts, integration platforms — encodes the sequence of steps at authoring time. The author enumerates the branches. At runtime the system looks up which branch applies and executes it. Given the same input, it produces the same output, forever.
An agent decides the sequence at runtime. As covered in the complete guide to AI agents, the model is given a goal and a set of tools, and it chooses which tool to call next based on what it has observed so far. The branches are not enumerated anywhere. They are constructed per run.
This is the only difference that matters. Everything else — cost, observability, testing strategy, failure behaviour, the shape of the on-call rota — follows from it.
What determinism buys, and what it costs
Determinism is not a nice-to-have. It is the property that makes a system testable, auditable and cheap to reason about.
| Property | Rule-based automation | Agent |
|---|---|---|
| Control flow decided | At authoring time | At runtime, per input |
| Same input, same output | Guaranteed | Not guaranteed |
| Unanticipated input | Hard failure, visible | Best-effort attempt, often silent |
| Cost per execution | Effectively fixed | Varies with steps and context size |
| Adding a case | Code change, review, deploy | Often none |
| Debugging | Read the branch that ran | Read a transcript and infer intent |
| Audit trail | The code is the trail | Requires deliberate instrumentation |
The row that surprises teams is the last one. A deterministic pipeline documents itself: to know what happened, read the code and the input. An agent's reasoning exists only in a transcript you chose to persist. If you did not capture the tool calls, the arguments and the observations, a bad outcome is unexplainable after the fact.
A worked comparison: invoice triage
Consider a queue of inbound supplier invoices that must be matched to purchase orders and routed for approval. The deterministic version is a parser plus a lookup.
from decimal import Decimal
FIELD_ALIASES = {
"total_due": ["Total Due", "Amount Due", "Balance Due"],
"po_number": ["PO Number", "Purchase Order", "PO #"],
}
def extract(document: dict) -> dict:
"""Pull known fields out of a parsed document, or raise."""
result = {}
for field, aliases in FIELD_ALIASES.items():
for alias in aliases:
if alias in document:
result[field] = document[alias]
break
else:
raise LookupError(f"missing required field: {field}")
return result
def triage(document: dict, orders) -> str:
fields = extract(document)
order = orders.get(fields["po_number"])
if order is None:
return "route:unmatched"
if Decimal(fields["total_due"]) > order.approved_total:
return "route:overage-review"
return "route:auto-approve"This is excellent code. It is fast, free to run, trivially unit-tested, and every
routing decision can be explained by pointing at a line. Its weakness is the else
clause on line 15 of the loop: an alias nobody listed raises LookupError, and the
invoice lands in a manual queue.
The agent version replaces extract with a model call over the raw document, keeping
triage deterministic:
EXTRACTION_TOOL = {
"name": "record_invoice_fields",
"description": (
"Record the fields extracted from the invoice. Call this exactly once. "
"total_due is the final amount payable including tax, not the subtotal. "
"If a field is genuinely absent, pass null rather than guessing."
),
"parameters": {
"type": "object",
"properties": {
"total_due": {"type": ["string", "null"], "pattern": r"^\d+(\.\d{2})?$"},
"po_number": {"type": ["string", "null"]},
"confidence": {"type": "string", "enum": ["high", "low"]},
},
"required": ["total_due", "po_number", "confidence"],
},
}
async def extract_with_model(raw_text: str, client) -> dict:
call = await client.call_tool(
system="Extract invoice fields. Do not infer values that are not present.",
user=raw_text,
tool=EXTRACTION_TOOL,
)
fields = call.arguments
if fields["confidence"] == "low" or None in (fields["total_due"], fields["po_number"]):
raise LookupError("low-confidence extraction")
return fieldsTwo design choices are doing the real work here. The tool schema forces a decimal
format, so a model that returns "1,240.00 USD" fails validation rather than
corrupting downstream arithmetic. And confidence gives the model an explicit way to
decline — without it, a model asked for a value will produce one.
Crucially, the routing logic did not become agentic. The model handles the part that is genuinely ambiguous — reading an unfamiliar document layout — and hands structured data back to deterministic code for the part that has real consequences.
The cost curves point in different directions
A rule-based pipeline is expensive to write and nearly free to run. Each new edge case costs engineering time; each execution costs a few milliseconds of CPU.
An agent inverts this. Getting to a working first version is fast — a prompt, some tool definitions, an afternoon. Every execution then costs tokens, and the cost is not constant: it scales with the number of loop iterations and, because the whole transcript is resent each step, with the square of the conversation length in the worst case. A run that takes eight steps instead of three does not cost two-and-a-half times more. It costs considerably more than that.
This matters for volume decisions. High-volume, low-variance work rewards rules. Low- volume, high-variance work rewards agents. The awkward middle — high volume and high variance — is where teams end up building the hybrid.
Where each one actually fails
Rule-based automation fails at the boundary of its enumeration, and it fails visibly. The exception is thrown, the pipeline halts, an alert fires. The system's ignorance is legible. The organisational failure mode is a slow accumulation of special cases until nobody understands the rule set and every change is risky.
Agents fail inside their competence, and they fail quietly. The model picks a plausible wrong tool, or extracts the subtotal, or decides a step already succeeded because a prior observation was ambiguous. There is no exception. The output looks well-formed. This is why agent systems need evaluation as a permanent fixture rather than a pre-launch gate: the failures do not announce themselves, so you have to go looking.
There is a second-order failure specific to agents. Because adding a capability is nearly free — write a tool, mention it in the description — agent codebases accumulate tools faster than they accumulate tests. Every tool widens what a single wrong decision can reach.
The hybrid most teams actually ship
The architecture that survives contact with production is rarely all-agent. It is a deterministic skeleton with model-powered joints:
- Deterministic orchestration. A workflow engine owns the sequence, retries, idempotency and the audit log.
- Model-powered steps. Individual steps that require judgement over unstructured input call a model with a constrained output schema.
- Agentic exception handling. When a deterministic step fails, an agent with read-mostly tools investigates and either proposes a fix or escalates with context.
That third pattern is the highest-value one and the least discussed. Most automation suites already have a dead-letter queue full of things that broke. An agent that reads the failure, checks the relevant systems, and writes a diagnosis into the ticket converts a queue nobody wants to work into a queue with a head start — without ever being trusted to write.
The same layering shows up in engineering workflows, where agents increasingly handle the ambiguous investigation and deterministic CI owns the gate. That shift is explored in how AI agents are changing software development.
A decision procedure
Four questions, in order. The first "yes" decides it.
- Are the steps knowable in advance? If yes, write them. An agent that rediscovers a fixed sequence on every run is a slow, expensive script.
- Is the input structured and stable? If yes, parse it. Models are for documents that vary, not for JSON with a schema.
- Is a wrong step recoverable? If no, the model may propose but must not execute. Put a human or a deterministic validator between the decision and the effect.
- Is the branching factor genuinely large? If the answer space is a handful of outcomes, a classifier with a fixed output set beats a tool-calling loop on cost, latency and testability.
If you reach the end still wanting an agent, you probably have a real use for one.
What a migration looks like
Teams that replace a working pipeline wholesale generally regret it. The pattern that works is narrower: find the single step where the exception queue is longest, replace only that step with a model call returning a constrained schema, and keep everything around it unchanged. Measure the exception rate before and after against the same inputs. If it drops without introducing silent wrong answers — which requires actually checking, not assuming — expand to the next step.
The knowledge that carries over is not prompt-writing. It is the operational discipline already built into good automation: idempotency, retries with backoff, dead-letter queues, structured logs. Agents need all of it and more, because a system that decides its own next move needs a record of why. That record is also the input to the evaluation harness that keeps it honest, which is where the discipline of retrieval-shaped systems and agent-shaped systems starts to diverge — and where the longer arc of agentic AI is heading.