[{"data":1,"prerenderedAt":457},["ShallowReactive",2],{"mdc-idbums-key":3},{"data":4,"body":5},{},{"type":6,"children":7},"root",[8,17,24,30,35,42,47,60,65,71,198,212,218,223,228,273,278,283,289,294,356,361,367,372,385,390,402,408,413,418,430,434],{"type":9,"tag":10,"props":11,"children":13},"element","h3",{"id":12},"agents-in-production",[14],{"type":15,"value":16},"text","Agents in production",{"type":9,"tag":18,"props":19,"children":21},"h1",{"id":20},"an-agent-is-a-state-machine-with-an-llm-picking-the-transitions",[22],{"type":15,"value":23},"An agent is a state machine with an LLM picking the transitions",{"type":9,"tag":25,"props":26,"children":27},"p",{},[28],{"type":15,"value":29},"Most systems described as agents are state machines where a language model chooses the next transition. That is a useful definition because it makes the design question obvious: for each transition, do you already know the answer? If you do, write it in code. The model should do work inside the states, not decide the order of them.",{"type":9,"tag":25,"props":31,"children":32},{},[33],{"type":15,"value":34},"Almost every reliability problem I have seen in a production LLM feature traces back to a decision that was handed to the model when it did not need to be. Not because the model is bad at deciding — often it decides correctly — but because a decision the model makes is one that can go differently on a Tuesday, and a decision in code is one that cannot.",{"type":9,"tag":36,"props":37,"children":39},"h2",{"id":38},"what-is-the-actual-difference-between-orchestration-and-reasoning",[40],{"type":15,"value":41},"What is the actual difference between orchestration and reasoning?",{"type":9,"tag":25,"props":43,"children":44},{},[45],{"type":15,"value":46},"Orchestration is choosing which step runs next. Reasoning is doing something with unbounded input. They get conflated because a loop that calls tools looks like thought, and it usually isn't.",{"type":9,"tag":25,"props":48,"children":49},{},[50,52,58],{"type":15,"value":51},"Here is the test I apply. Write down the steps your agent takes on a successful run. Then ask, for each arrow between them: ",{"type":9,"tag":53,"props":54,"children":55},"strong",{},[56],{"type":15,"value":57},"was that ever really in question?",{"type":15,"value":59}," In most pipelines the honest answer is no. Parse the request, retrieve the context, generate the draft, validate it, save it. That sequence was fixed before anyone wrote a prompt. Letting the model rediscover it on every run buys nothing and costs you a class of failure where it skips a step, repeats one, or invents a sixth.",{"type":9,"tag":25,"props":61,"children":62},{},[63],{"type":15,"value":64},"The model earns its keep at the two ends: turning messy input into structured intent on the way in, and turning structure into something readable on the way out. In the middle, where lookups and rules and arithmetic happen, deterministic code is faster, cheaper, testable, and behaves the same next quarter — which the model you deployed this year may not.",{"type":9,"tag":36,"props":66,"children":68},{"id":67},"where-does-the-model-actually-belong",[69],{"type":15,"value":70},"Where does the model actually belong?",{"type":9,"tag":72,"props":73,"children":74},"table",{},[75,94],{"type":9,"tag":76,"props":77,"children":78},"thead",{},[79],{"type":9,"tag":80,"props":81,"children":82},"tr",{},[83,89],{"type":9,"tag":84,"props":85,"children":86},"th",{},[87],{"type":15,"value":88},"Job",{"type":9,"tag":84,"props":90,"children":91},{},[92],{"type":15,"value":93},"Give it to",{"type":9,"tag":95,"props":96,"children":97},"tbody",{},[98,112,125,137,149,161,173,186],{"type":9,"tag":80,"props":99,"children":100},{},[101,107],{"type":9,"tag":102,"props":103,"children":104},"td",{},[105],{"type":15,"value":106},"Turning free text into a structured intent",{"type":9,"tag":102,"props":108,"children":109},{},[110],{"type":15,"value":111},"The model",{"type":9,"tag":80,"props":113,"children":114},{},[115,120],{"type":9,"tag":102,"props":116,"children":117},{},[118],{"type":15,"value":119},"Deciding which of four known steps runs next",{"type":9,"tag":102,"props":121,"children":122},{},[123],{"type":15,"value":124},"Code",{"type":9,"tag":80,"props":126,"children":127},{},[128,133],{"type":9,"tag":102,"props":129,"children":130},{},[131],{"type":15,"value":132},"Retrieving records from your own database",{"type":9,"tag":102,"props":134,"children":135},{},[136],{"type":15,"value":124},{"type":9,"tag":80,"props":138,"children":139},{},[140,145],{"type":9,"tag":102,"props":141,"children":142},{},[143],{"type":15,"value":144},"Judging whether a draft matches a rubric",{"type":9,"tag":102,"props":146,"children":147},{},[148],{"type":15,"value":111},{"type":9,"tag":80,"props":150,"children":151},{},[152,157],{"type":9,"tag":102,"props":153,"children":154},{},[155],{"type":15,"value":156},"Computing a total, a date, a score",{"type":9,"tag":102,"props":158,"children":159},{},[160],{"type":15,"value":124},{"type":9,"tag":80,"props":162,"children":163},{},[164,169],{"type":9,"tag":102,"props":165,"children":166},{},[167],{"type":15,"value":168},"Drafting prose from a structured brief",{"type":9,"tag":102,"props":170,"children":171},{},[172],{"type":15,"value":111},{"type":9,"tag":80,"props":174,"children":175},{},[176,181],{"type":9,"tag":102,"props":177,"children":178},{},[179],{"type":15,"value":180},"Routing into a fixed taxonomy",{"type":9,"tag":102,"props":182,"children":183},{},[184],{"type":15,"value":185},"Code, usually a classifier",{"type":9,"tag":80,"props":187,"children":188},{},[189,194],{"type":9,"tag":102,"props":190,"children":191},{},[192],{"type":15,"value":193},"Handling the residue the rules could not cover",{"type":9,"tag":102,"props":195,"children":196},{},[197],{"type":15,"value":111},{"type":9,"tag":25,"props":199,"children":200},{},[201,203,210],{"type":15,"value":202},"The pattern in the right-hand column: the model is at the edges. Everything with a knowable answer sits in the middle, written down. This is the same argument as ",{"type":9,"tag":204,"props":205,"children":207},"a",{"href":206},"/blog/where-ai-belongs-in-your-architecture",[208],{"type":15,"value":209},"where AI belongs in your architecture",{"type":15,"value":211},", applied one level down — inside a single feature rather than across a system.",{"type":9,"tag":36,"props":213,"children":215},{"id":214},"how-do-you-build-it-as-an-explicit-graph",[216],{"type":15,"value":217},"How do you build it as an explicit graph?",{"type":9,"tag":25,"props":219,"children":220},{},[221],{"type":15,"value":222},"Declare the nodes, declare the edges, and give the run a state object that every node reads and writes. That is it — the pattern is old and boring and it is exactly what makes generative pipelines debuggable.",{"type":9,"tag":25,"props":224,"children":225},{},[226],{"type":15,"value":227},"The shape I use for every generation workflow on my platform is the same four parts, repeated:",{"type":9,"tag":229,"props":230,"children":231},"ul",{},[232,243,253,263],{"type":9,"tag":233,"props":234,"children":235},"li",{},[236,241],{"type":9,"tag":53,"props":237,"children":238},{},[239],{"type":15,"value":240},"A state object.",{"type":15,"value":242}," Everything the run knows so far, in one typed structure. Nodes take state and return state. Nothing is hidden in a closure.",{"type":9,"tag":233,"props":244,"children":245},{},[246,251],{"type":9,"tag":53,"props":247,"children":248},{},[249],{"type":15,"value":250},"A planner.",{"type":15,"value":252}," The node that makes the model call — given the state, propose something. This is where reasoning lives.",{"type":9,"tag":233,"props":254,"children":255},{},[256,261],{"type":9,"tag":53,"props":257,"children":258},{},[259],{"type":15,"value":260},"A presenter.",{"type":15,"value":262}," Formats the proposal for the client. Deliberately separate from the planner, because how a thing is generated and how it is shown to a human are different concerns that change at different times.",{"type":9,"tag":233,"props":264,"children":265},{},[266,271],{"type":9,"tag":53,"props":267,"children":268},{},[269],{"type":15,"value":270},"A commit step.",{"type":15,"value":272}," Takes what the human chose and persists it as a draft. Nothing reaches published state without passing through here.",{"type":9,"tag":25,"props":274,"children":275},{},[276],{"type":15,"value":277},"Four workflows — proposing a course structure, proposing the units inside it, writing an article, generating assessments — and all four have that identical skeleton. That is not an accident of tidiness; it is what lets me test them the same way, instrument them the same way, and fix a bug in one place.",{"type":9,"tag":25,"props":279,"children":280},{},[281],{"type":15,"value":282},"The graph is explicit. Edges are declared, not inferred. When a run misbehaves I can point at a node.",{"type":9,"tag":36,"props":284,"children":286},{"id":285},"what-do-you-get-from-making-the-graph-explicit",[287],{"type":15,"value":288},"What do you get from making the graph explicit?",{"type":9,"tag":25,"props":290,"children":291},{},[292],{"type":15,"value":293},"Five things, and they are the five things that are hardest to retrofit:",{"type":9,"tag":295,"props":296,"children":297},"ol",{},[298,308,326,336,346],{"type":9,"tag":233,"props":299,"children":300},{},[301,306],{"type":9,"tag":53,"props":302,"children":303},{},[304],{"type":15,"value":305},"Testable transitions.",{"type":15,"value":307}," An edge in code is a branch you can write a test for. An edge in a prompt is a behaviour you can only sample.",{"type":9,"tag":233,"props":309,"children":310},{},[311,316,318,324],{"type":9,"tag":53,"props":312,"children":313},{},[314],{"type":15,"value":315},"Bounded cost.",{"type":15,"value":317}," A declared graph has a maximum number of model calls. A loop where the model decides whether to continue does not, which is how ",{"type":9,"tag":204,"props":319,"children":321},{"href":320},"/blog/the-most-expensive-call-is-the-one-that-fails",[322],{"type":15,"value":323},"interactions that fail expensively",{"type":15,"value":325}," happen.",{"type":9,"tag":233,"props":327,"children":328},{},[329,334],{"type":9,"tag":53,"props":330,"children":331},{},[332],{"type":15,"value":333},"Resumability.",{"type":15,"value":335}," Persisted state plus a known node means a failed run restarts where it stopped rather than from the beginning.",{"type":9,"tag":233,"props":337,"children":338},{},[339,344],{"type":9,"tag":53,"props":340,"children":341},{},[342],{"type":15,"value":343},"Attributable failures.",{"type":15,"value":345}," \"Generation failed\" is not a bug report. \"The assessment node returned output that failed schema validation twice\" is.",{"type":9,"tag":233,"props":347,"children":348},{},[349,354],{"type":9,"tag":53,"props":350,"children":351},{},[352],{"type":15,"value":353},"Cheap model swaps.",{"type":15,"value":355}," When each node has one job and a checkable output, you can move a node to a smaller model and measure the effect on that node alone.",{"type":9,"tag":25,"props":357,"children":358},{},[359],{"type":15,"value":360},"Point five is worth dwelling on. In a monolithic agent, changing models is an all-or-nothing bet you evaluate by vibes. In a graph, it is a per-node decision with a per-node measurement — and most nodes turn out not to need the expensive model.",{"type":9,"tag":36,"props":362,"children":364},{"id":363},"where-does-the-human-fit-in-the-graph",[365],{"type":15,"value":366},"Where does the human fit in the graph?",{"type":9,"tag":25,"props":368,"children":369},{},[370],{"type":15,"value":371},"As a node, not as a reviewer at the end.",{"type":9,"tag":25,"props":373,"children":374},{},[375,377,383],{"type":15,"value":376},"This is the design choice I would defend hardest. My pipeline does not generate a course and then ask someone to approve it. It proposes candidate structures and ",{"type":9,"tag":378,"props":379,"children":380},"em",{},[381],{"type":15,"value":382},"stops",{"type":15,"value":384},". A person picks one, or removes items from it, or asks for different ones. Only then does the next stage run.",{"type":9,"tag":25,"props":386,"children":387},{},[388],{"type":15,"value":389},"That pause is doing enormous work. It bounds error propagation — a bad plan cannot silently become forty bad articles, because the plan is confirmed before the articles exist. It puts the judgment call where judgment actually lives. And it makes the whole thing feel like a tool rather than a slot machine, because the person is steering rather than grading.",{"type":9,"tag":25,"props":391,"children":392},{},[393,395,400],{"type":15,"value":394},"The general principle: ",{"type":9,"tag":53,"props":396,"children":397},{},[398],{"type":15,"value":399},"put the human where a wrong answer would be expensive to undo, not where it is convenient to add a review step.",{"type":15,"value":401}," Approval at the end is the most expensive place to catch an error and the least useful place to make a decision.",{"type":9,"tag":36,"props":403,"children":405},{"id":404},"when-is-a-loop-actually-right",[406],{"type":15,"value":407},"When is a loop actually right?",{"type":9,"tag":25,"props":409,"children":410},{},[411],{"type":15,"value":412},"When the number of steps genuinely cannot be known in advance. Open-ended research over a corpus, iterative debugging where each result determines the next probe, exploration where the goal is discovery — those are real, and forcing them into a fixed graph would be the mistake in the other direction.",{"type":9,"tag":25,"props":414,"children":415},{},[416],{"type":15,"value":417},"But even there, the loop wants a frame around it: a hard step limit, a budget ceiling, a required progress signal, and a defined thing that happens when it hits any of them. A loop with no exit condition other than the model's own judgement is not an agent, it is an outage with a spinner.",{"type":9,"tag":25,"props":419,"children":420},{},[421,423,428],{"type":15,"value":422},"The rule I would give a team: ",{"type":9,"tag":53,"props":424,"children":425},{},[426],{"type":15,"value":427},"fixed graph by default, loops where you can name the reason.",{"type":15,"value":429}," If someone cannot articulate why the step count is unknowable, it is knowable and it should be written down.",{"type":9,"tag":431,"props":432,"children":433},"hr",{},[],{"type":9,"tag":25,"props":435,"children":436},{},[437],{"type":9,"tag":378,"props":438,"children":439},{},[440,442,447,449,455],{"type":15,"value":441},"Part 4 of ",{"type":9,"tag":204,"props":443,"children":445},{"href":444},"/blog?series=agents-in-production",[446],{"type":15,"value":16},{"type":15,"value":448},". Start from ",{"type":9,"tag":204,"props":450,"children":452},{"href":451},"/blog/what-it-takes-to-ship-an-llm-agent",[453],{"type":15,"value":454},"the pillar post",{"type":15,"value":456}," if you came in here.",1787908866326]