The next skill after prompts is loop engineering
Most agent failures aren’t bad prompts — they’re badly shaped loops. A field guide to designing, bounding, and instrumenting the cycle a model runs inside.
01Prompt engineering hit a ceiling
For about two years, getting real work out of a coding agent meant one rhythm: write a careful prompt, paste enough context, read what came back, type the next thing. You were inside the loop, one turn at a time. That works right up until the task needs more than a handful of steps — then you become the bottleneck, hand-feeding a model that can’t see whether its own last move actually worked.
Loop engineering is the move where you step out of that rhythm and build the small system that does the prompting for you: it finds the work, hands it to the model, checks the result against something real, records state, and decides whether to continue, retry, stop, or escalate. The agent stops being a tool you hold and becomes a process you supervise.
02Anatomy of a loop
Strip away the framework branding and every agent runs the same four-beat cycle: perceive the current state, decide on an action, act through a tool, then observe the result — and feed that observation back in to start again (Fig 1). Debugging a misbehaving agent is almost always a matter of finding which of those four beats is weak.
The lineage runs from the ReAct pattern (reason, act, observe, repeat) through the “Ralph” technique: run the agent in a plain while loop against a spec on disk, let it pick and commit one task, then start a fresh instance with the identical prompt. The reset is the point — each pass reads current state from disk, so the context window never silently degrades.
A prompt tells the model what to think. A loop decides how many times it gets to be wrong before you catch it.
03Context as working memory
The loop’s context is its working memory, and memory is a budget, not a log. Everything you carry into the next iteration is something the model has to read again — and pay for again. Curate it: pull in the current state and the goal, drop the transcript sludge, and let durable facts live on disk where a fresh iteration can re-read them.
// agent/loop.ts
export async function runLoop(goal: string, max = 20) {
let state = init(goal);
for (let step = 0; step < max; step++) {
// perceive + decide
const action = await model.decide(state);
// explicit success — the only stop we trust
if (action.type === "done") return state;
const result = tools.run(action); // act
state = reduce(state, result); // observe
if (noProgress(state)) break; // bail if stuck
}
return finalize(state); // always return something
} 04Instrumenting the loop
Notice what the loop actually does — don’t trust that it did it. The single most common failure is an agent that decides it is finished. So the stop condition can never be the model’s own say-so: it’s an explicit, machine-checkable gate. Wire your real checks — tests, typecheck, coverage, or a cheap grading model — into the loop itself, and reinject the task when they fail (a Stop hook is one clean way to do this).
Then bound the run. Every loop gets a step cap so it can’t spin forever, and a no-progress detector so repeating the same error against the same state counts as a hard stop, not learning. Unattended loops compound cost roughly an order of magnitude faster than people expect — a loop without a budget guard is a financial risk, not just a technical one.
05What it bought us
After we made the loop — not the prompt — the unit we test and measure, the same model got dramatically more reliable, without a single change to the underlying prompt.
The biggest win wasn’t any single number. It was that failures became legible: when a run went wrong, the loop’s recorded state told us exactly which beat — perceive, decide, act, or observe — had broken.
06Principles we keep
- Engineer the loop, not just the prompt. The prompt is one frame; the loop is the whole run.
- Curate context every turn. State is a budget you spend, not a log you hoard.
- Bound every loop three ways: a step cap, an explicit success check, and a no-progress detector.
- Always return a defined result. A loop that can silently hang is a bug, not an agent.