Autonomous AI agent: how to let it work for hours without producing junk
An autonomous AI agent can work for hours without a human if the task has a machine-checkable goal, the agent runs in a sandbox with minimal permissions, progress is stored on disk, and readiness is judged by tests and a separate reviewer rather than by the agent itself. Attempt limits, a budget and an escalation rule are mandatory: if the task gets stuck, the loop stops and calls a human.
What an autonomous AI agent is and how it differs from a normal prompt
Normal work with an agent is step by step: you write a prompt, the agent does one step, you look at the result and decide what comes next. An autonomous AI agent works in a loop. You describe once what the final state should look like, and then the agent repeats "do, check, fix" until the goal is reached or the budget runs out. After you start it, you can walk away.
It sounds simple, but a typical first experience looks like this: the agent got the task in the morning, and in the evening you find that it spent several hours fixing a bug it had already fixed, because it forgot what it did an hour earlier. Nobody told it when to stop and when to call a human. Below is how to design a loop that does not allow this.
| Normal prompt | Autonomous loop | |
|---|---|---|
| What you specify | What to do next | What the final state looks like |
| What the agent does | Performs one action | Repeats until the goal is reached |
| Who decides that it is done | You | A checkable condition or an independent reviewer |
| Can you walk away | No | Yes |
An important caveat: the loop does not replace the harness. The harness makes a single run reliable, and the loop makes runs continuous. A loop on top of a poor harness just produces junk faster.
Is the task suitable for an autonomous agent
The first question is whether the work has a finish line that a machine can check. "All tests in tests/payments pass", "all features in the list are marked passing", "the metric grew relative to the baseline" are suitable. "Make the code better" or "keep improving payments" are not: such a goal has no end, and the loop will spin until it eats the budget.
It is useful to distinguish four types of loops:
- Step by step — you type every prompt; this is ordinary interactive work.
- By goal — you set a goal, and an independent check or a budget stops the loop. This is the autonomous agent that works for hours.
- By time — a run on a schedule (for example, a morning review of failed CI builds), stopped manually.
- By event — an external trigger (a new issue, a failed deploy) starts the work, and the end of event processing or a retry limit stops it.
A common mistake is to confuse a time-based loop with a goal-based one: "every 10 minutes, keep working on payments". A scheduled loop starts the instruction from scratch each time and does not know what has already been done. If the task has a finish line, you need a goal-based loop with memory of progress.
Step 1. Sandbox: where the agent is allowed to break things
An agent working unattended will sooner or later do something wrong: delete a file, rewrite a migration, run a heavy command. So the question is not "how do I make sure it never makes a mistake", but "how do I make sure a mistake costs nothing".
- A separate working copy.
git worktreegives the agent its own directory and its own branch on top of the same repository. The main branch is untouched, several agents do not interfere with each other, and a failed run is removed with one command. - A container or a virtual machine. If the agent needs full access to commands, run it in a container without access to your home directory, keys or production databases.
- Test data. A local database, payment-provider sandboxes, fake keys. Not a single production secret in the agent's environment.
# own branch and directory for an autonomous run
git worktree add ../shop-agent -b agent/f03-refunds
# after the run: look at the result or throw it away
git -C ../shop-agent log --oneline main..
git worktree remove ../shop-agent
Step 2. Permissions: the minimum the task needs
The longer the agent works alone, the narrower its permissions should be. In Claude Code, permissions are configured in settings.json (allow and deny lists for tools and commands). Explicitly allow what the task needs — running tests, the linter, editing files in the working copy — and deny what is dangerous: pushing to the main branch, working with secrets, network commands if they are not needed.
A mode in which the agent runs everything without confirmations is acceptable only inside an isolated sandbox, where the worst outcome is a discarded container. On a work machine with access to your keys and repositories, never do this.
Enforce the prohibitions that must not be broken with mechanisms, not text. The line "do not touch migrations/" in the instructions is a wish; a hook that blocks edits to that directory, or filesystem permissions, is a guarantee.
Step 3. Goal, check, stop: three lines before the run
The simplest working loop rests on three things, and you should write them down before starting, not along the way:
- Goal — what the final state looks like.
- Verification — a machine-checkable way to know that the goal is reached: a command that returns success or failure.
- Stop condition — a limit on attempts, time or money after which the loop stops even if the goal is not reached.
It is convenient to keep this in a separate methodology file that only a human edits. Example for a payments backend:
# program.md — methodology of the autonomous run
## Goal
All features in .agent/feature_list.json are in the passing state.
## Metric
Number of features in passing. Grows monotonically. A regression means an immediate rollback.
## Constraints
- One feature in progress at a time.
- Do not change migrations/ and infra/.
- Do not touch dependencies without a note in the journal.
## Work order
1. Take the first feature that is not passing.
2. Implement it, run pytest and the linter.
3. Green: commit and write to .agent/progress.md. Red: fix it.
## Stopping and escalation
- 3 failed attempts on one feature — mark it blocked and move to the next one.
- All remaining features blocked — stop and write a report.
- Any task about customers' money, access rights or data deletion — do not do it, escalate it.
Step 4. Check-and-fix loop with memory on disk
The context window does not survive long work: it fills up, gets compressed, the session restarts. So progress must live in files, not in the agent's "head": a task list with statuses, a journal of what was done and what did not work, the git history.
A good model is the "ratchet that only moves forward", as in Andrej Karpathy's autoresearch project, where an agent runs hundreds of ML experiments overnight. Each iteration: read the methodology, propose a hypothesis, make a change, run an experiment with a fixed time budget, compare with the baseline. If it improved, commit; if not, roll back. The result is written to the journal, then the next hypothesis follows.
Two decisions from this scheme are worth taking:
- A fixed budget per attempt. Attempts become comparable, and one failed hypothesis does not eat the whole night.
- Only improvements in the main branch. Everything that did not work is rolled back, not left lying around.
git logbecomes a verified work journal that is easy to read in the morning.
The same project shows the division of roles by files: nobody edits the infrastructure and the evaluation, the agent edits only the experiment code, and only a human edits the methodology. The agent cannot "improve" the result by tweaking the way it is measured.
Step 5. Who decides that it is done: a separate reviewer
When there is no human in the loop, the agent's self-assessment becomes the only brake, and this brake does not work. An agent that has just written code tends to consider it correct: it sees its own reasoning and "knows" what it meant to do.
Rules for separating the generator and the reviewer:
- The review is not done by the session that did the work. In Claude Code, subagents are convenient for this: each has its own context.
- The reviewer gets a clean context: the requirement, the diff and the test results — and nothing from the executor's reasoning.
- If possible, the reviewer is a different model, or at least a different prompt with a different role.
- The verdict is given by a rubric, and each item points to evidence: a test line, command output, a place in the diff.
Machine checks are still primary. The reviewer is needed for what tests do not catch: the requirement was understood wrongly, the test was fitted to the code, the change went beyond the task.
Stop criteria: when the agent should give up
A loop without stop conditions will loop; it is only a matter of time. The minimum set:
| Criterion | What it catches |
|---|---|
| Goal reached by the check | Normal completion |
| Attempt limit per task | Getting stuck in one place |
| Overall budget of time, steps or money | Endless work and the API bill |
| No progress over N iterations | Work that looks busy: attempts go on, the metric stands still |
| Metric regression | Fixing one thing that breaks another |
| Escalation trigger | Tasks that the loop must not be trusted with |
Check progress by substance, not by the number of attempts: "40 iterations done" means nothing, "the number of green features grew from 5 to 9" means something. If a task is stuck, that is a reason to hand it to a human, not to run the loop once more.
Risks of an autonomous agent that are not visible at first
The obvious risks — deleted files and a broken build — are covered by the sandbox and permissions. More dangerous are the quiet costs that accumulate over weeks:
- Verification debt. "Looks fine" instead of "confirmed by a machine". The only antidote is machine-checkable stop conditions, without exceptions.
- Eroded understanding. You know your own code less and less. The antidote is mandatory reading of diffs: the speed of the loop must not exceed the speed at which you can read.
- Cognitive surrender. You stop having an opinion about the result and accept everything. It is useful to honestly count the cases where you wrote "I'll look later" and never did.
- Context bloat. A long run accumulates history, and every next step becomes more expensive and worse. Context compression and external memory are needed from the first loop, not after everything has started to drift.
How to run your first autonomous loop in one evening
Do not start with "the agent will find and do the tasks by itself". Autonomy grows in steps: first, one task with a goal and a stop condition; then one automation on a schedule; then a separated executor and checker in an isolated working copy; and only after that loops that find work themselves and run in parallel.
- Find a task that you do by hand at least twice a week and that has a checkable finish line.
- Write down the goal, the verification and the stop condition — three lines.
- Create a separate worktree and set permissions: tests and file edits are allowed, push and secrets are not.
- Create a progress file and ask the agent to update it after each iteration.
- Separate the roles: the executor writes, and a checker in a separate context reviews the diff.
- Set an overall limit and check the first result after an hour, not in the morning.
Summary
An autonomous AI agent is only as reliable as its boundaries: a sandbox where a mistake costs nothing, minimal permissions, a machine-checkable goal, memory on disk, a separate reviewer and strict stop conditions with escalation to a human. Without these, a long run is not a time saver but a delayed cleanup of a mess. How to build such a loop on a running project — from program.md to an independent reviewer — is covered step by step in our harness engineering course.
FAQ
Can I leave an autonomous agent running overnight on my work laptop?
You can, if it is locked in a container or a separate working copy without access to keys and production data, with limited permissions and an overall budget limit. Running without confirmations on a machine that holds your secrets is a bad idea.
How much does it cost to run an agent all night?
It depends on the model and the length of the context, so set the budget in steps, time or money in advance. Context bloat makes late steps more expensive than early ones, so compression and external memory save money as well.
Why not let the agent decide by itself whether the task is ready?
The agent sees its own reasoning and tends to consider its result correct. Readiness should be confirmed by tests and by a reviewer in a clean context that sees only the requirement, the diff and the check results.
What should I do in the morning with the results of an autonomous run?
Read the progress journal and the git log of the branch, review the diffs and look at the tasks marked blocked. Merge into the main branch only what you have read and understood.
How is an autonomous agent different from a cron job?
A scheduled run starts the instruction from scratch every time; that is a time-based loop. An autonomous agent works toward a goal: it remembers progress in files and stops when the goal is reached or the budget runs out.
This topic is covered in depth with practice in the course “Harness Engineering: Engineering Reliability for AI Agents”.
- Autonomous AI Coding Agent for Hours: Loop Engineering and Stop Conditions
- Building an AI Agent Harness From Scratch: AGENTS.md, feature_list.json, verify.sh and an Autonomous Loop