Multi-Step Autonomy: How Agents Plan, Act, Verify, and Retry
A single-shot AI call takes an input and returns an output. Multi-step autonomy means the system can take a goal — “fix the failing test in the billing module” — and work through however many steps that actually requires, checking its own progress along the way, without a human typing a new prompt after every action.
The core pattern is a loop, not a pipeline: plan → act → verify → retry (or stop). Understanding that loop, and where it needs guardrails, is most of what it takes to build autonomy that’s useful instead of dangerous.
The Loop, Concretely
Goal: "Fix the failing test in test_billing.py"
Plan: Read the test, read the code it exercises, form a hypothesisAct: Read test_billing.py and billing.pyVerify: Hypothesis — off-by-one error in tax roundingAct: Edit billing.py to fix the roundingVerify: Run test_billing.py → still failing, different assertionPlan: Revise hypothesis — the fix exposed a second bugAct: Read the new failing assertion, edit againVerify: Run test_billing.py → passesVerify: Run full test suite → no regressionsStop: Goal achieved, open PREach verify step is what makes this autonomy rather than blind repetition — the agent isn’t just generating a plausible-looking edit, it’s checking real ground truth (test output) before deciding whether to continue, retry, or stop.
Why Verification Has to Be Real, Not Self-Reported
The single biggest failure mode in early agent systems was letting the model judge its own success — “I believe this fix is correct” is not verification, it’s a guess with confidence. Real multi-step autonomy ties the “verify” step to something outside the model’s own output: test results, linter exit codes, a schema validator, a second model acting as a checker. This connects directly to feedback loops — autonomy without a real feedback signal is just an agent confidently repeating its own mistakes.
The Guardrails That Keep Loops From Running Away
| Guardrail | What it prevents |
|---|---|
| Max iteration count | Infinite retry loops burning time and money on an unsolvable task |
| Cost/token budget per task | A single task consuming disproportionate compute |
| Escalation trigger after N failed verifications | Agent silently giving up or looping instead of asking for help |
| Diff size limit | A “small fix” ballooning into an unreviewable rewrite |
| Scope boundary (allowed files/directories) | An agent wandering outside the task’s intended blast radius |
A production system should treat “the agent hit its iteration limit” as a normal, expected outcome — not a bug — and route it to a human with full context of what was tried, exactly like a person would escalate a task they got stuck on.
Plan-and-Execute vs. Reactive Loops
Two common architectures handle the plan step differently:
- Reactive (ReAct-style): the agent decides its next action one step at a time, reasoning fresh after every observation. Flexible, but can wander on long tasks.
- Plan-and-execute: the agent commits to a multi-step plan upfront, then executes it, replanning only if a step fails. More predictable and auditable, better for tasks where the shape of the work is well understood in advance (e.g., “upgrade this dependency across the repo”).
Most production coding agents use a hybrid: plan-and-execute for the overall task shape, reactive reasoning within each step.
Practical Starting Point
Don’t design for full autonomy on day one. Start with a loop capped at 2–3 iterations and a hard escalation to a human on any verification failure — this gives you real production data on where the agent actually gets stuck before you invest in more sophisticated planning or higher iteration limits. Autonomy budgets should expand based on observed reliability, not assumed capability.