Deterministic Control Flow, Not Prompt
also known as Own Your Control Flow, 12-Factor Control Flow
Branching decisions live in deterministic application code while the LLM is invoked at strategic points to produce structured signals that the code branches on.
Context
A team has an LLM-driven agent. The default temptation is to put branching logic in prompts ('if X then do Y, else do Z'). This makes control flow stochastic, hard to test, and hard to debug. The Polish/12-Factor-Agents 2026 source explicitly names this as a factor.
Problem
LLM-driven control flow is unreliable: the model may take the wrong branch, skip a branch, invent a branch. Tests cannot enumerate the paths. Debugging requires reading prompt traces. Distinct from spec-driven-loop (which specifies what the agent does at each step) by being specifically about keeping if/else logic out of prompts.
Forces
- LLM-driven branching is convenient — write 'choose action' in the prompt.
- Deterministic control flow requires the engineer to enumerate paths.
- Some branching legitimately depends on LLM judgment (intent classification).
Example
A support agent has paths: refund / technical / sales / escalate. Naive: prompt says 'choose path A/B/C/D'. With this pattern: LLM is asked 'classify intent as one of {refund, technical, sales, escalate}'. The deterministic router reads the enum and dispatches. Tests cover all 4 paths; if the LLM classifies as 'other' the deterministic code has an explicit fallback.
Diagram
Solution
Therefore:
Structure: deterministic application code drives the control flow. At each branching point, call the LLM to produce a structured signal (typed enum, numeric score). Deterministic code reads the signal and branches. The LLM never sees 'choose the team's next branch' as a prompt; it sees 'classify this' or 'score this'. Pair with structured-output, json-only-action-schema, spec-driven-loop, stateless-reducer-agent.
What this pattern forbids. LLM is invoked at branching points to produce structured signals only; no if/else logic in prompts.
And the patterns that stand alongside it, or against it —
- complementsSpec-Driven Loop★— Run the same prompt against a fixed spec in a deterministic outer loop until the spec is satisfied.
- complementsJSON-Only Action Schema✕— Anti-pattern: restrict the agent's action language to JSON tool-call dictionaries even for tasks where code-as-action (functions composing, loops, conditionals over results) would be the natural shape.
- complementsStructured Output★★— Constrain the model's output to conform to a JSON Schema (or similar typed shape).
- complementsStateless Reducer Agent★— Design the agent as a pure function (state, event) → newState; entire execution history is held in an external event log; enables pause / resume / replay / time-travel without bespoke checkpointing.
- complementsOwn Your Prompts (12-Factor Agents)★— Every prompt in a production agent is versioned, tested, and owned by the team in the application repo — never inherited as a framework default.
- complementsHybrid HTN + Generative Agent★— Hierarchical Task Network decomposition provides the procedural backbone; the generative LLM is invoked only at leaf nodes for the parts of the task that are genuinely open-ended.
- complementsBPMN/DMN Deterministic Shell Around Agent★— BPMN processes and DMN decision tables form the deterministic spine; LLM-driven agents are invoked only at explicit 'unstructured problem' nodes inside the process.
Neighbourhood
Click any neighbour to follow the language. Scroll to zoom, drag to pan.