The demo works. A user types a request, the model calls a tool, calls another, produces an answer. Then you put it in front of real traffic and the wheels come off: token spend that swings 5x run-to-run, failures nobody can reproduce, and a loop that occasionally never terminates. The pattern is familiar enough that it has a name attached to a remedy — 12-factor agents, a set of design principles that treat an LLM agent as ordinary production software rather than a clever prompt wrapped in a while loop. The core claim is simple and, once stated, hard to argue with: an agent is a piece of software, and it needs the same operational discipline any production system needs — explicit state, owned control flow, structured context, and stateless components you can restart and replay. The naive path bolts memory and tools onto a prompt as the demo grows. The 12-factor path decides, deliberately, which parts of the workflow need the model and which parts belong in deterministic code you can profile, cost, and debug. What does “12-factor agents” actually mean in practice? The name borrows from the 12-Factor App methodology that shaped how the industry builds cloud services — config in the environment, stateless processes, logs as event streams. The agent version, popularised by the HumanLayer team, adapts that same instinct to LLM systems. It is not a framework you install. It is a set of constraints on how you structure the code around the model. The single most consequential constraint is where the control flow lives. In a naive agent, the LLM owns the loop: you hand the model a goal and a toolbox, and it decides — token by token — what to do next, when to call a tool, and when to stop. That feels like the whole point of an agent. It is also the source of most production pain. A model owning the loop produces behaviour that is unbounded (nothing structurally caps how many steps it takes), non-reproducible (the same input can take different paths), and opaque (there is no state you can inspect between steps). The 12-factor reframe inverts this. Your code owns the loop. The model is called for scoped decisions — “given this state, which of these three actions fits?” or “extract the fields from this text” — and each call returns a structured result your code acts on. The loop, the branching, the retries, the termination conditions all live in deterministic code. This is the same instinct behind the profiling-first, restructure-before-you-scale discipline we apply to GPU simulation workloads: the win comes not from doing more of the expensive thing, but from restructuring the computation so the expensive component — here, the model call — is used deliberately. The factors, grouped by what they buy you There are twelve named factors in the canonical list, but memorising them in order is not the point. What matters is the four things they collectively enforce. Reading them as four groups is more useful than reading them as a numbered checklist. Group Factors it covers What it prevents Own the control flow Own your control flow; small, focused agents; the model is a stateless reducer Unbounded loops, non-reproducible paths, agents that sprawl into unmaintainable god-objects Structure the context Own your prompts; own your context window; tools are structured outputs; unify execution and business state Silent context bloat, per-run token cost you cannot predict, prompt drift you cannot version Make it inspectable Trigger from anywhere; contact humans with tool calls; compact errors into context Silent failures, un-debuggable runs, errors the model swallows and hallucinates around Keep it operable Launch/pause/resume with simple APIs; stateless design State you cannot checkpoint, agents you cannot restart cleanly, runs you cannot replay If you are moving a prototype toward production and can only adopt a few factors first, the two that pay off fastest in our experience are own your control flow and own your context window. Everything else gets easier once those two are in place, because you can then actually see what the agent is doing and what each step costs. This is an observed pattern across agent-hardening work, not a benchmarked ranking — but the ordering has held up consistently. Why should deterministic code own the loop rather than the model? Because “the model decides everything” trades away every property you need to run a system in production, and buys you flexibility you mostly do not want. Consider what “the LLM owns the loop” actually means at runtime. Each step, the full conversation plus tool results is packed back into the context window and sent to the model, which emits the next action as free-form text you then parse. The context grows every step, so token cost per step grows too — a five-step task and a fifteen-step task on the same request have wildly different bills, and nothing in the design caps the step count. When the model picks a bad action, there is no state boundary to catch it; the error just becomes more context for the next equally unconstrained decision. Now put the loop in code. Your code holds a state object — a plain data structure with the current step, accumulated results, and what remains to do. Each iteration, code decides whether to call the model at all; when it does, it sends only the context that step needs, not the entire history. The model returns a structured decision (a JSON action, a set of extracted fields), code validates it, applies it to the state, and moves on. Now the step count is bounded by your loop logic, the token cost per step is something you chose, and the state between steps is a value you can log, diff, and replay. The connection to the broader GPU-cost discipline is direct. In agentic AI benchmarking, where latency actually lives is rarely a single model call — it is the accumulation of calls, and the accumulation is exactly what an owned loop lets you measure and control. You cannot profile what you cannot bound. How does treating context as an explicit input change cost and reproducibility? This is the factor most teams under-appreciate, so it is worth being concrete. In the naive design, “context” is whatever has accumulated in the conversation — an emergent, ever-growing blob. In the 12-factor design, context is an explicit, structured input your code assembles for each model call. You decide what goes in: the relevant state fields, the specific documents retrieved, the tool schemas for this step. Nothing leaks in by accident. Two things change immediately. First, token cost per step becomes a design decision rather than an emergent property. If a step needs three retrieved passages and the current task state, that is what the model sees — not the full transcript. Second, reproducibility becomes achievable, because the input to each model call is a deterministic function of your state. Given the same state, you assemble the same context. Replay works. Worked example: a support-triage agent, both ways Assume a task that classifies an incoming ticket, retrieves relevant history, drafts a reply, and escalates if confidence is low. Assume roughly 800 tokens of accumulated conversation per naive step and four steps. Naive (model owns the loop): each of the four steps re-sends the growing transcript. Step 1 sees ~800 tokens; by step 4 the context has grown to carry all prior tool results — call it ~3,200 tokens on the final step. Total input across steps lands in the low five figures, and it is different every run because the model chooses the path. 12-factor (code owns the loop): step 1 (classify) sees the ticket plus a schema — ~400 tokens. Step 2 (retrieve) is deterministic code; the model is not called. Step 3 (draft) sees the classification, three retrieved passages, and the ticket — a bounded window you sized. Step 4 (escalation check) sees a compact state summary — ~300 tokens. The model is called three times on inputs you designed, and the total input is both smaller and the same across runs. The numbers here are illustrative — the exact tokens depend entirely on your prompts and data. The structural point is not: an owned loop with explicit context turns a variable, unbounded bill into a bounded, measurable one. That is the difference between “we don’t know what this agent costs” and “each completed triage costs about X tokens.” Cost-per-completed-task, not raw model capability, is the number the business actually cares about. If you want to model that number before deploying, our writeup on how tokenisation ratios affect inference cost covers the counting side. How do these principles make failures inspectable instead of silent? Silent failure is the quiet killer of production agents. A tool call fails, the model gets a garbled result, and instead of surfacing an error it confidently continues — producing an answer that looks fine and is wrong. Nobody notices until a user complains. The 12-factor answer is a combination of factors working together. Because errors are compacted into structured context rather than swallowed, a failed tool call becomes an explicit signal the model — or your code — can branch on. Because human contact is itself a tool call, “escalate to a person” is a first-class, logged action rather than a fallback that never fires. And because state is unified and inspectable, every run produces a trace: the sequence of states and decisions that led to the output. That trace is what makes an agent debuggable. When something goes wrong, you replay the state transitions and find the exact step where the decision diverged — the same way you would debug any deterministic pipeline. This is close in spirit to what ML model monitoring tracks for GPU inference workloads: you are instrumenting the boundaries so that when behaviour degrades, you can see where and why rather than guessing. In engagements moving agents to production, the sharpest observed improvement is usually the drop in silent failures — failures that used to surface as mysterious quality complaints become failures that surface as logged, inspectable events. That is an observed pattern from agent-hardening work, not a published rate. The profiling-first, restructure-before-you-scale methodology behind our GPU simulation redesign work on the /gpu practice is the same discipline underneath both: understand where the expensive, failure-prone work happens before you scale it, and structure the system so that work is deliberate and observable. For teams shipping agents into real-time media and telecom pipelines, the same reasoning shows up in our media and telecom work, where bounded latency and cost per task are non-negotiable. When is a full 12-factor agent overkill? Often, honestly. The methodology is a response to a specific problem — agents whose control flow is genuinely dynamic and whose failure modes are expensive. If your task is a fixed sequence of steps with occasional branching, you do not have an agent problem; you have a workflow, and a plain workflow with one or two scoped model calls is the right tool. Use this rubric before reaching for the full structure: The path is fixed or nearly fixed → build a workflow, not an agent. Deterministic pipeline, model calls at the steps that need judgement. Do not add a loop. The path is dynamic but low-stakes → a light agent with a bounded loop and structured context is enough. Skip the human-in-the-loop and replay machinery. The path is dynamic and failures are expensive → this is where the full 12-factor structure earns its cost. Own the loop, structure context, make every step inspectable, keep state replayable. You are still in the demo phase → do not over-engineer. Prove the task is worth doing first, then harden. The mistake in both directions is real. Teams that reach for a heavy agent framework when a workflow would do pay in complexity and cost they never recover. Teams that ship a model-owns-the-loop agent into high-stakes production pay in the reliability collapse the methodology exists to prevent. FAQ How should you think about 12-factor agents in practice? It is a set of design constraints, not a framework you install. In practice it means structuring the code around an LLM so that your code owns the control flow, context is an explicit structured input you assemble per call, the model is used for scoped decisions, and state is inspectable and replayable. The model does the reasoning; your software does the orchestration. What are the individual 12 factors, and which ones matter most when moving an agent from prototype to production? The twelve factors group into four jobs: own the control flow, structure the context, make failures inspectable, and keep the system operable. When moving from prototype to production, own your control flow and own your context window pay off fastest — once those are in place you can see what the agent does and what each step costs, and the remaining factors get easier to adopt. Why should deterministic code own the control flow rather than letting the LLM own the agent loop? Letting the model own the loop produces behaviour that is unbounded, non-reproducible, and opaque — nothing caps the step count, the same input can take different paths, and there is no state to inspect between steps. When code owns the loop and calls the model only for scoped decisions, the step count is bounded, token cost per step is a choice you made, and the state between steps is a value you can log, diff, and replay. How does treating context as an explicit, structured input change token cost and reproducibility? You assemble exactly what each model call needs rather than re-sending an ever-growing transcript, so token cost per step becomes a design decision instead of an emergent property. Reproducibility follows because the input to each call is a deterministic function of your state — given the same state you build the same context, so replay works. How do the 12-factor principles make an agent’s failures inspectable and debuggable instead of silent? Errors are compacted into structured context instead of being swallowed, human escalation is a first-class logged tool call, and unified state produces a replayable trace of every decision. When something goes wrong you replay the state transitions to find the exact step where the decision diverged, the way you would debug any deterministic pipeline. When is a full 12-factor agent overkill, and when is a simple workflow the right choice instead? When the path is fixed or nearly fixed, you have a workflow, not an agent — build a deterministic pipeline with model calls at the steps that need judgement. The full 12-factor structure earns its cost only when the path is genuinely dynamic and failures are expensive; in the demo phase, prove the task is worth doing before hardening it. How do these agent-design principles relate to the profiling-first, restructure-before-scaling discipline TechnoLynx applies to GPU workloads? Both start by identifying which parts of a workload actually need the expensive component — the model here, the GPU there — before scaling anything. The win in each case comes from restructuring the computation so the costly part is used deliberately and can be profiled, rather than doing more of the naive thing and hoping the numbers hold. The open question for most teams is not whether the 12-factor structure is correct — it plainly is, once you have watched a model-owned loop misbehave under load. The question is which parts of your specific workflow genuinely need the model versus deterministic code. Answering that is a profiling exercise, and it is the same restructure-before-you-scale discipline that separates a naive GPU port from one that actually pays off — the failure class here is the unbounded, un-profileable loop, and the artifact that catches it is an honest map of where model judgement is actually required.