Enterprise AI Workflow Automation That Survives Production

Most failed AI automations I've seen did not fail because the model was bad. They failed on the unglamorous parts: nobody could see what the system did at 3 a.m., nobody knew who approved what, and the bill doubled after a retry loop nobody noticed. In my experience, most of the work in a production workflow is the engineering around the model, not the model itself, and that is what this post is about.
Start with a workflow, not an agent
A workflow is a predefined code path that calls an LLM at specific steps. An agent is a system where the LLM decides the path. Anthropic's "Building effective agents" essay draws exactly this line, and it recommends the simplest solution possible, adding complexity only when needed. It also notes that agentic systems trade latency and cost for better task performance, which may mean not building an agent at all (https://www.anthropic.com/engineering/building-effective-agents).
I agree with that more every year. Most enterprise processes are not open-ended. Invoice triage, support ticket routing, document extraction, report assembly: the steps are known, and what the LLM adds is judgment at specific nodes (classify this, extract that, draft this reply). Wrapping that in a free-roaming agent buys you unpredictability and nothing else.
The same essay catalogues five workflow patterns: prompt chaining, routing, parallelization, orchestrator-workers and evaluator-optimizer. In my own systems, nearly everything reduces to the first two plus an evaluator step. A rough decision rule I use:
| Situation | Use |
|---|---|
| Steps are known, order is fixed | Prompt chain with code between steps |
| Inputs vary in type, handling differs | Router, then specialized chains |
| Independent subtasks, speed matters | Parallelization |
| Subtasks unknown until runtime | Orchestrator-workers (an agent, earn it) |
| Output quality must be verified | Evaluator step before any side effect |
If you cannot write down why the path must be chosen at runtime by the model, it should be code.
Design for failure before you design for success
A production workflow is a state machine with durable state, idempotent steps and explicit failure paths. The LLM call is one fallible node among many. Treat it like any flaky external dependency, because it is one.
The pattern I rely on, from serverless integrations I've shipped on AWS (EventBridge, Lambda, API Gateway), looks like this:
- Every run gets an ID and a persisted state record. Not in memory. In a database row or a state machine execution. If the process dies mid-run, you can see exactly where.
- Every step is idempotent. Retrying "create ticket" must not create two tickets. Use a deterministic idempotency key derived from the run ID and step name.
- Side effects happen last and only once. Read, reason and draft freely. Write to external systems only after validation, and record that the write happened.
- Failures route somewhere. A dead-letter queue or a "needs human" state, never a silent drop.
- Retries have budgets. A maximum attempt count and a maximum token spend per run, enforced in code.
A skeleton of the step wrapper I use in most pipelines:
async function runStep<T>(ctx: RunContext, name: string, fn: () => Promise<T>): Promise<T> {
const key = `${ctx.runId}:${name}`;
const done = await store.getResult(key);
if (done) return done as T; // idempotent replay
if (ctx.spentTokens > ctx.tokenBudget) {
throw new BudgetExceeded(ctx.runId, name); // hard stop, routed to review
}
const result = await withRetry(fn, { max: 3, backoff: "exponential" });
await store.saveResult(key, result);
return result;
}
Boring, and it is the difference between a demo and something that runs unattended. My content pipelines run on schedules with no human watching, and the reason they keep working is this layer, not prompt cleverness.
Put approvals where the risk is, not everywhere
Human approval gates belong in front of irreversible or high-impact actions, and they must be designed so the human can actually say no. In Anthropic's agent model, agents can pause for human feedback at checkpoints or when they hit blockers. That is the right primitive. The mistake is using it as a blanket.
Approve-everything workflows fail in a predictable way: reviewers get flooded, start rubber-stamping, and your "human in the loop" becomes theater. OWASP names a sharper version of this in its Top 10 for Agentic Applications (ASI09, Human-Agent Trust Exploitation): agents whose persuasive or misleading outputs push people into unsafe approvals. A fluent, confident draft is exactly what makes a tired reviewer click approve.
What works better:
- Tier actions by reversibility. Read-only and reversible actions run autonomously. Irreversible ones (sending money, deleting records, emailing customers at scale) require approval.
- Show the evidence, not just the conclusion. The approval screen should present the source data, the diff of what will change, and the confidence signals, not a polished summary written by the same model.
- Sample instead of gating, once trust is earned. After a workflow proves itself, move from approving every item to reviewing a random slice plus all low-confidence items.
- Log every decision. Who approved, what they saw, when. This is your audit trail and your training data for tightening the system.
On regulation: if your workflow touches the EU, check the current text of Regulation (EU) 2024/1689 on EUR-Lex and the official timelines for the high-risk and transparency obligations, because the Digital Omnibus has been changing them (law-firm summaries such as https://cdp.cooley.com/digital-ai-omnibus-delays-key-deadlines-introduces-new-rules/ are a useful starting point, but confirm against the official text or with legal counsel). If your workflow produces generated content that reaches the public, build the marking and disclosure step in now, not later.
Make it observable before it is clever
You cannot operate what you cannot replay. For LLM workflows, observability means more than logs and uptime: it means being able to reconstruct, for any run, the exact inputs, prompts, model version, tool calls, outputs and cost.
My minimum trace per step:
- Run ID, step name, attempt number
- Model identifier and prompt version (prompts are code; version them)
- Input and output, or hashes plus pointers if they contain sensitive data
- Latency, tokens in and out, computed cost
- Validation result and any routing decision
Two things this buys you. First, debugging by replay: when something goes wrong in production, you re-run that exact step against the same input instead of guessing. Second, evals from real traffic: the traces become your regression set. The self-learning content loop I run (measure, learn, target, generate, publish) only works because every stage records what it did and what happened afterward. Without that record there is nothing to learn from.
Alert on the things that actually break these systems: spend per run above a threshold, retry rate climbing, validation failure rate shifting after a model or prompt change, and queue depth in the human-review state. Uptime alone will tell you the system is "up" while it quietly produces garbage.
Security: least agency, scoped tools, no ambient authority
Give each workflow the minimum tools and permissions for its job, and make autonomy something the system earns. OWASP's agentic guidance extends least privilege into "Least Agency": autonomy should be earned rather than granted by default (https://auth0.com/blog/owasp-top-10-agentic-applications-lessons/). The OWASP Top 10 for Agentic Applications lists risks including Tool Misuse (ASI02), Identity & Privilege Abuse (ASI03), Cascading Failures (ASI08) and Rogue Agents (ASI10) (https://cycode.com/blog/owasp-top-10-agentic-applications/).
Translated into design rules I actually apply:
- Per-workflow credentials, not shared service accounts. If one workflow is compromised or confused, the blast radius is that workflow's scope.
- Tools are narrow functions, not generic shells. "Create a draft reply on ticket X" is a tool. "Run arbitrary HTTP request" is a liability.
- Treat all retrieved and user-supplied text as untrusted. Documents, emails and web pages can carry instructions. Validate what the model proposes against a schema and an allowlist before executing anything.
- Authorize actions outside the model. The LLM proposes; deterministic code or a policy layer decides. AWS offers a managed version of this in Bedrock AgentCore, and its pricing is worth checking on AWS's own page rather than relying on third-party summaries.
- Contain cascades. Put limits between stages so one bad output cannot fan out into a thousand downstream actions. Circuit breakers are cheap insurance.
Cost control is an architecture decision
Most LLM cost is decided by routing and batching, not by negotiating price. Verify current numbers on Anthropic's official pricing page before you plan around them, because third-party aggregators can lag behind. Two structural facts matter more than any specific price.
First, tier your models. Aggregators describe a lineup where the smallest model is aimed at classification, extraction and routing. That matches how I build: a cheap model handles routing and extraction, a stronger model handles the few steps that need real reasoning, and the expensive tier is reserved for the evaluator or the genuinely hard cases. Most steps in most workflows do not need the top model.
Second, use batch processing for anything that is not latency sensitive. The Claude Message Batches API offers a discount for asynchronous processing compared with standard calls, in exchange for results that arrive later (see https://agentdex.app/blog/anthropic-api-pricing/ and confirm the current terms on Anthropic's pricing page). Overnight enrichment, backfills, bulk classification and scheduled content jobs are all natural batch candidates. Design for it from day one, because retrofitting async onto a synchronous pipeline is painful.
Beyond price tiers, the controls that save real money are structural:
- A per-run token budget enforced in code (shown in the wrapper above)
- Caching deterministic results so identical inputs don't trigger new calls
- Short, structured outputs instead of free-form prose where a schema will do
- Cost attached to every trace, so a regression shows up as a number on a dashboard
Gartner has predicted that a large share of agentic AI projects will be canceled, citing escalating costs, unclear business value and inadequate risk controls (reported via https://www.digitalapplied.com/blog/agentic-ai-project-cancellations-gartner-40-percent-2026). That is a forecast, not a measurement, but the three causes match what I see: no cost ceiling, no defined value metric, no risk controls. All three are design choices you can make up front.
A phased path from pilot to production
Move in phases, and don't advance until the current phase's exit criteria are measured, not felt. This is the sequence I'd use for an enterprise engagement.
Phase 1: Narrow pilot (weeks, not months). Pick one workflow with a clear owner, a known manual baseline and a bounded blast radius. Instrument it fully from day one. Run it in shadow mode: the system produces outputs, humans still do the real work, and you compare. Exit when you have a measured accuracy number against real cases and a baseline for hours spent and cost per item.
Phase 2: Supervised production. Turn on real actions behind approval gates. Every irreversible step needs a human yes. Collect the approval and rejection data, because it tells you exactly where the system is weak. Exit when rejection rates are stable and low on the action types you want to automate.
Phase 3: Selective autonomy. Remove gates for action types that have earned it, moving to sampled review. Keep gates on anything irreversible or high-value. Add the budget caps, circuit breakers and alerting described above. Exit when the system runs through a full period without a human having to intervene on the autonomous path.
Phase 4: Expansion and hardening. Add the next workflow, reusing the run store, trace format, approval UI and cost controls. This is where the investment compounds: the platform layer is built once. Revisit security scoping and model tiering as volume grows.
Throughout, measure in business terms: hours saved per month, cost per processed item, SLA compliance. That is the same discipline behind the 73+ hours per month saved by a four-system automation ecosystem I built (192% Year-1 ROI), and behind the serverless AWS and Zendesk integration that delivered first-ever SLA compliance. Neither result came from a clever prompt. Both came from a boring, observable pipeline that did one job reliably.
What I'd do
If I were handed an enterprise AI automation project tomorrow:
- Write the workflow as code first. Add an agent only where the path truly cannot be known in advance, and justify it in writing.
- Build the run store, idempotency and trace layer before the first prompt. It is the part that makes everything else debuggable.
- Gate by reversibility, and show reviewers evidence, not summaries. Assume reviewers get tired.
- Enforce token budgets and per-workflow scoped credentials in code, not in policy documents.
- Route by model tier and batch everything that can wait. Then confirm prices against Anthropic's official page, not a blog.
- Run shadow mode before supervised mode before autonomy, with measured exit criteria at each step.
None of this is exotic. That is the point. The teams that ship dependable AI systems are mostly the ones that treat the LLM as one unreliable component inside a well-engineered system.
If you're planning an AI workflow project and want to compare notes, you can reach me at lazar-milicevic.com/#contact, or keep reading on the blog for more production write-ups.
Frequently asked questions
What is the difference between an AI workflow and an AI agent?
A workflow is a predefined code path that calls an LLM at specific steps, while an agent is a system where the LLM decides the path itself. Anthropic's "Building effective agents" essay draws this same line and recommends the simplest solution possible. Agents trade latency and cost for better task performance, so they are not always worth it. I default to workflows because most enterprise processes have known steps and only need LLM judgment at specific nodes.
Why do enterprise AI automations fail in production?
In my experience they rarely fail because the model is bad. They fail on the engineering around it: no visibility into what the system did overnight, no record of who approved what, and cost spikes from unnoticed retry loops. Most of the work in a production workflow is this surrounding layer, not the model. Fixing it means persisted state, observability, approvals and enforced budgets.
How do you make an LLM workflow reliable and safe to retry?
I treat it as a state machine with durable state, idempotent steps and explicit failure paths. Every run gets an ID and a persisted record, and each step uses a deterministic idempotency key built from the run ID and step name, so retrying "create ticket" never creates two tickets. Side effects happen last and only once, after validation. Failures route to a dead-letter queue or a needs-human state instead of being silently dropped.
Where should human approval gates go in an AI workflow?
Put them in front of irreversible or high-impact actions, such as sending money, deleting records or emailing customers at scale. Read-only and reversible actions can run autonomously. Approving everything floods reviewers and turns the human in the loop into rubber-stamping. The approval screen should show source data and a diff of what will change, not just a polished model-written summary.
How do you control cost and retries in an AI automation pipeline?
I enforce retry budgets in code: a maximum attempt count and a maximum token spend per run. In my step wrapper, exceeding the token budget throws a hard-stop error that routes the run to review instead of continuing to spend. Retries use exponential backoff with a cap of three attempts. This prevents the kind of unnoticed retry loop that can double a bill.
Building something hard with AI or automation? I am open to talk.
Get in touch