Back to Writing

Production AI is mostly workflow design

Everyone argues about which model is smartest. In the systems I've shipped, into government, enterprise and consumer, almost none of the hard problems were the model. They were what you feed it and what you do when it fails.

5 min read

The model is the smallest part of a production AI system that works. Most of the engineering that decides whether anyone trusts the thing sits in the plumbing around it: what you retrieve before you call it, how you check what comes back, what happens when it fails, and who has to approve the result. Swap a good model for a great one and the system gets slightly better. Get the workflow wrong and the best model available still ships you something nobody will rely on.

I’ve built this in three settings that have almost nothing in common, and it held in all of them.

Government: the model interprets, the engine decides

At VicRoads I worked on pricing custom number plates, a product sitting on a P&L north of $100M and serving more than six million drivers. The hard part is that a requested plate means something. “GOAT” is worth more than “X7QJZ” and a person can see why instantly. The naive design asks a model to read the plate and name a price.

That design can’t ship in government, and the reason is the whole point here. A price a model produced can’t be explained, can’t be audited, and can’t be defended when a customer or a minister asks why this plate cost that much. So the workflow splits the job. The model interprets meaning, reading the requested string and classifying it into features a pricing model can use. A deterministic engine then sets the price from those features. The model never touches the number. Prices stay explainable because a rule produced them, and the model does the one thing rules are bad at, which is reading intent out of arbitrary text. The reliability came from where we put the boundary, and none of it came from model intelligence.

Enterprise: retrieval and evaluation are the product

At Brand Ninja the model generated brand content for serious accounts, the kind that sign six-figure contracts. What the customer experiences as quality mostly happens upstream and downstream of the generation itself.

Upstream is retrieval. A model with no access to a brand’s guidelines, prior campaigns and tone will write generic, off-brand content. What moved quality was assembling the right context before the call: what this brand sounds like, what they’ve published, what they’ve rejected. Get retrieval right and an average model writes on-brand. Get it wrong and a frontier model writes fluent nonsense.

Downstream is evaluation. You can’t ship generated content to an enterprise account on the assumption that it’s fine. You need a loop that scores output against the brand’s constraints, flags what fails, and routes it for human review before it goes anywhere near a customer. An evaluation harness is what lets you change a prompt or a model and know within minutes whether you broke something, instead of finding out when an account complains. It’s unglamorous and it’s most of the reliability.

Consumer: fallbacks are the experience

At hey anna, solo-built and bootstrapped, the discipline is sharpest, because there’s no team to absorb a failure. The model can be slow, can rate-limit, can return something malformed, can be wrong. A consumer product that assumes none of that happens is a demo. The workflow has to answer every one of those cases: retry with backoff, degrade to a smaller path, show a clear state instead of a spinner that never resolves, and never present a fabricated answer as a real one. Fallback handling is what keeps the “analyst, not chatbot” promise, because an analyst you can’t trust when the data is thin isn’t much of an analyst.

The parts that actually move reliability

Strip the three settings down and the same components carry the weight:

ComponentWhat it doesWhat breaks without it
Orchestrationsequences steps, decides what runs whenone giant prompt doing five jobs badly
Retrievalassembles the right context before the callconfident, fluent, generic wrong answers
Evaluationscores output, catches regressionsyou find out it broke when a customer does
Approvalsputs a human on irreversible actionsthe system ships mistakes at machine speed
Fallbackshandles failure, timeout, malformed outputa demo that dies on the first malformed response

A smarter model improves the text inside each box. It doesn’t build the boxes and it doesn’t connect them. That connective work is ordinary software engineering applied to a probabilistic component, and it’s where production AI gets won.

Designing the pieces the model composes

Orchestration itself is shifting, and it changes what the design work is. The traditional shape is a linear workflow. You decide the steps and their order in advance, step one feeds step two feeds step three, and the path is fixed before any request arrives. That’s still right when the path has to run the same way every time, the way the VicRoads split has to interpret first and price second in order to stay auditable.

The newer approach inverts it. Rather than designing the sequence, you design the tools and let the model compose the workflow as it goes. It decides, for the request in front of it, which tools to call and in what order, fitting them to a task you never explicitly wired up. The design work moves from drawing the path to shaping the pieces, where each tool is a clean, well-bounded interface to something more complex underneath: a query engine, a retrieval index, a pricing service, an external API. The model never sees the mess behind the tool. It sees a handle it can pull.

That moves the hard part rather than removing it. Designing a good tool surface is its own discipline. A tool that’s ambiguous, leaky, or quietly does three things gets composed into confident nonsense, the same way a bad function signature breeds bugs downstream. Shape the pieces well, each one legible and honest about what it does, and the model covers paths you’d never have enumerated by hand. Most real systems end up with both: a deterministic spine for the parts that mustn’t vary, and composable tools at the edges where the range of tasks is too wide to wire in advance.

I’ve argued elsewhere that the model should be a dumb renderer at the point it touches the user. This is what has to be true underneath for that to hold, because the render is only ever as trustworthy as the workflow feeding it. Spend your effort on the workflow and the model you already have is usually good enough.