Autonomous AI Agents: From Assistants to Automation

📚 Series Background: This is Part 2 of the AI Workflows series. Part 1 covered the daily workflows where you prompt an AI assistant and review its output. The November 2025 post set up the MCP infrastructure that gives an agent access to project state. This post is the next step: removing yourself from the loop on the work that doesn't need you.

In Part 1 I argued that AI assistants are Junior+ teammates: great at boilerplate, sharp at patterns, and dependent on you for context and review. Every workflow in that post had a human in the inner loop: you prompt, it generates, you review, you apply.

This post is about the work where that loop is the bottleneck. Not the feature you're designing, but the maintenance that piles up around it. Dependency bumps. A flaky service that needs a restart. A backlog that drifts out of priority order. A production error that's been firing for six hours while you sleep. None of that needs your judgment. It needs attention, on a schedule, forever.

So I gave that attention to an agent. It runs every 15 minutes, unattended, on my Mac. It reads the state of my projects, decides what's worth doing, does a bounded slice of it, and writes down what happened. I review its work the way I'd review a junior's: in batches, after the fact, not in real time.

This is the part everyone gets wrong: the hard problem isn't making an agent capable. The models are already capable. The hard problem is making an unattended agent safe and cheap enough that you'll actually leave it running. That's where most of this post lives.

From Assistant to Agent

The difference between an assistant and an agent is who closes the loop.

An assistant waits for you. It has no clock and no initiative. You bring it a task, it produces output, and the interaction ends. Value is gated entirely on your attention. If you don't prompt, nothing happens.

An agent has a clock and a goal. It wakes up on its own, looks at the world, and decides whether there's anything worth doing. You're still in control, but you've moved from the inner loop (approve each step) to the outer loop (set the goals, set the guardrails, review the results).

It's not capability; a frontier model is plenty capable of running a maintenance task end to end. The dividing line is initiative under constraint: an agent acts without being asked, which means every safety question you could previously answer with "well, a human is watching" now needs a real answer. Autonomy is a security posture before it's a feature.

That reframing matters because it tells you where to spend your effort. With an assistant, you invest in prompting: better context, clearer requirements. With an agent, you invest in bounding: what can it touch, what can it spend, and how do you stop it. The model is the easy part.

I'll make this concrete with the agent I actually run. I'll call it the daemon, because that's what it is: a background process that never logs off.

Anatomy of an Autonomous Loop

Every useful autonomous agent I've seen converges on the same five-stage loop, whether or not its authors named it that way. Mine runs the cycle every 15 minutes:

Observe → Triage → Think → Act → Record
↑ │
└──────────────────────────────────┘

Each stage exists to answer one question, and this is the important part: each stage runs on the cheapest tool that can answer it.

Observe — gather state

The cycle starts by reading the world: open work items, recent git activity, service health, production errors, anything queued since the last cycle. No model is involved yet; this is plumbing. The output is a structured snapshot of "what's true right now."

The trap here is doing too much work on the hot path. My first version regenerated a pile of subsystem summaries inline every cycle. It was 99% of the observe time and dominated the whole loop. The fix was to move that fan-out to a separate scheduled job that writes snapshots to disk, and have the cycle read the freshest snapshot under a staleness gate. An autonomous loop should read cached state, not compute it.

Triage — filter cheaply

Most cycles, the answer is "nothing worth doing." You do not want to pay a frontier model to reach that conclusion 96 times a day. So a small local model does the first pass: given the snapshot, is there anything here that deserves real reasoning? It's a classifier, not a planner. Cheap, fast, private, and good enough to throw away 90% of cycles before they cost anything.

Think — plan one bounded action

When triage says "yes, look at this," a capable model gets the snapshot and produces a plan for one bounded action: not a sprint, not a refactor, one slice. Bounding the output is deliberate: a small action is easy to review, easy to roll back, and hard to turn into a disaster.

The Think stage doesn't apply changes. It emits a proposal: here's what I observed, here's the one thing I'd do, here's why, here's how to undo it. That proposal is what the Act stage executes against, and what gets recorded for review.

Separating "decide" from "do" buys two things:

A review surface. Every action has a written rationale attached before it runs. When I audit a day's work, I'm reading decisions, not reverse-engineering diffs.

A natural gate. High-trust actions execute automatically; anything novel or risky stops at the proposal and waits for me. The boundary is a config line, not a code change.

This is the same "humans own the what and why, AI owns the how" split from Part 1, enforced structurally instead of by you sitting there.

Act — execute in a sandbox

The action runs inside a sandbox with hard limits (much more on this below). Shell commands come from an allowlist. File writes are restricted to a couple of directories. Network egress is filtered. If the action is outbound-facing (sending a message, opening a PR) it passes through a kill switch first.

Record — write it down

Finally, the cycle writes an auditable trail: what it observed, what it decided, what it did, what changed. This is the outer loop's input. I don't watch the agent work; I read what it recorded, in batches, and that's where I catch drift, correct bad patterns, and decide what to let it do next.

You don't need my exact stack to use this. The shape (observe cached state, triage with something cheap, reason with something capable, act inside a sandbox, record for later review) is the reusable part. A cron job that checks your error tracker, asks a model "is this worth paging a human," and posts a triaged summary is the same loop with four of the five stages collapsed. Start there.

The Economics of Running It

Here's the constraint that shapes everything: an unattended agent runs whether or not it's producing value. An assistant only costs money when you choose to use it. An agent on a 15-minute clock fires ~96 times a day, ~2,900 times a month, forever. If each cycle calls a frontier model, you've built a very expensive way to mostly discover there's nothing to do.

My self-evolution budget for this is $20/month. That number isn't aspirational frugality. It's a design constraint that forces the whole architecture. At $20/month, you cannot call a frontier model every cycle. You have to route.

Tiered model routing

I run a tier ladder, cheapest first, and most cycles never leave the bottom rung:

Tier | Where | Used for
0 | Local (on-device) | Triage, classification, the 90% of cycles that get filtered out
1 | A private GPU box on my network | Heavier reasoning and code tasks that want a real model but not a frontier one
2 | Included/free remote models | Overflow and specific tool-calling
3 | Frontier API | Only the cycles that genuinely earn it

The triage stage lives entirely on Tier 0. It's private (project state never leaves the machine), fast (no network round trip), and free. By the time anything reaches the paid frontier tier, a cheap model has already decided it's worth the money.

Cache discipline is load-bearing

The other half of the economics is the prompt cache. Every compatible provider keys its cache on the prefix of the request: the system prompt, the tool schema, and the earlier messages. Hit the cache and you pay ~10% of the cost at ~3x the speed. Mutate any part of that prefix mid-conversation and you invalidate the cache for the rest of the session.

The expensive mistakes are subtle: injecting a timestamp into the system prompt, reformatting it "harmlessly," adding a tool mid-conversation, rewriting earlier messages to compress them. Each one looks innocent and each one quietly torches your cache-hit rate. In a 30-turn conversation, one cache-busting mutation can 10x the cost.

The cost ceiling is what lets the agent be autonomous. Because most cycles cost nothing, I can afford to run it constantly, which means it's actually there when something does need doing. An agent you can only afford to run occasionally isn't autonomous; it's a cron job with a credit-card bill. I care enough about this that prompt-cache discipline gets its own post (Part of the series, September). It's that load-bearing.

Guardrails: What Makes "Unattended" Safe

This is the section I'd have wanted before I built mine. An autonomous agent is, bluntly, a process holding an API key, a shell, and the ability to send messages as me, running while I'm not looking. The security question isn't "can it be jailbroken." It's "what is the blast radius when it does something wrong," because eventually it will. You design for the misbehaving cycle, not the well-behaved one.

Four controls do most of the work.

1. Kill switches that actually kill

Every outbound path (every send, every external action) gates on a kill switch before it fires. Flipping that switch is a hard "stop" that takes effect within about a second, across every process, no deploy required. There's a one-command pause and a one-command resume. The first thing I build for any new outbound capability is its off switch; the capability comes second.

2. Hard spend caps, fail-closed

Spend is governed by a budget kill switch that trips automatically when a ceiling is breached, and critically, it fails closed. If the system can't confirm you're under budget, it stops spending rather than assuming the best. A subtlety I learned the hard way: any calibration or estimate feeding the budget check has to be freshness-gated. Stale "we're fine" data is worse than no data, because it green-lights spending on bad information. When the inputs are stale, the safe default is to fall back to raw numbers and let the kill switch act alone.

3. A real sandbox around Act

The Act stage runs under concrete limits:

Allowlist-only shell — a fixed set of binaries, no shell metacharacters, no piping into rm/sudo/eval.

Restricted write paths — the agent can only write to a couple of explicitly-blessed directories. It cannot touch source it isn't supposed to.

SSRF protection — any fetch the agent makes blocks localhost and private IP ranges, so it can't be tricked into hitting internal services.

Secrets resolved per-process, at execution time — never injected session-wide. A leaked or logged environment shouldn't hand over a credential.

4. A trust ladder, not a trust leap

New automations don't get to act for real on day one. They run in shadow first: they propose what they would do and log it, but don't execute. Only after an automation has demonstrated a track record of correct proposals does it graduate to running automatically. Promotion is earned with evidence (a threshold of real successes), not granted because the code looks done. It's the same instinct as Part 1's "review AI code like a junior's", formalized into a graduation gate.

Early on, two processes were both allowed to send on the same channel. They saw each other's messages as new input and replied: a recursive echo loop that spammed before I caught it. The lesson generalized into a rule I now treat as non-negotiable: one sender per channel, with cross-process deduplication. Per-process safety isn't enough when multiple agents share a substrate. Every "the agent went haywire" story I've heard reduces to a missing boundary like this one. Draw the boundaries first.

What It Actually Does All Day

Concrete is better than abstract, so here's the real work the daemon handles without me:

Service health. A watchdog checks that background services are alive and restarts the ones that aren't, with a cap on restarts per hour so a genuinely broken service quarantines itself and escalates to me instead of flapping forever.

Self-healing on known issues. For routine, fingerprinted failures, it runs a vetted remediation playbook automatically. For anything it hasn't earned trust on, it alerts me with the playbook attached so I can run it with one decision.

Backlog hygiene. A planning sub-engine keeps work items ranked by priority and context, so the top of the list is actually the next thing worth doing instead of whatever was added last.

Production triage. When errors fire, it pulls the context, classifies severity, and either handles the routine ones or surfaces a triaged summary, so I wake up to "here are the two things that matter," not 200 raw alerts.

Self-review. A metacognition pass looks back over recent cycles for drift and surfaces its own highest-priority findings. The agent audits the agent, and I audit the audit.

None of these are glamorous. That's the point. This is exactly the maintenance toil that used to fragment my attention all day, and it's precisely the work that doesn't need my judgment, only consistent, scheduled follow-through. Offloading it is what frees the attention for the work in Part 1: architecture, design, the decisions that are actually mine to make.

Where Autonomous Agents Fail

Same honesty as Part 1's failure modes: these have all bitten me, and guarding against them is the difference between "useful daemon" and "automated foot-gun."

Failure Mode 1: Confidently wrong, at scale

An assistant's mistake is one bad suggestion you reject. An agent's mistake repeats every cycle until you notice. The mitigation isn't smarter prompts. It's bounded actions plus a fast kill switch. Keep each action small enough to undo, and keep the stop button one command away.

Failure Mode 2: Cost creep

A subtle prompt change tanks your cache-hit rate and the bill quietly climbs. Or a retry loop hammers a paid API. You won't feel it in the moment; autonomy means no one's watching the meter. Hard caps that fail closed are the only reliable defense; alerting alone is too slow when the agent runs unattended.

Failure Mode 3: Doing too much on the hot path

The instinct to compute fresh state every cycle will wreck your loop latency, as it did mine. Read cached state; compute it on a separate schedule. The hot path should be cheap enough to run constantly.

Failure Mode 4: Shared-substrate collisions

The recursive-echo loop above. When multiple agents or sessions share infrastructure (a queue, a channel, a key) per-process safety is insufficient. Coordinate through shared, deduplicated state, and give every contended resource exactly one owner.

Failure Mode 5: Silent scope drift

Give an agent a goal and enough cycles and it'll find creative interpretations of "helpful." The trail it records is your early-warning system, but only if you actually read it. Schedule the review. An outer loop you never close is just an inner loop you stopped supervising.

The pattern across all five: an autonomous agent removes the human from the inner loop, so every safety property you used to get for free from "someone's watching" now has to be designed in. Bounded actions, hard caps, sandboxing, deduplication, and a review you actually do. Skip any one and the agent will eventually find the gap.

Building Your Own: Where to Start

You don't need a 15-minute daemon with four model tiers to get value from this. Start with the smallest version of the loop and grow it as you earn trust in it.

Pick one toil task that runs on a clock and doesn't need judgment: triaging error alerts, summarizing overnight CI failures, flagging stale dependencies. One task, well-bounded.

Write the loop, collapse the stages you don't need yet. A scheduled job that reads state, asks a model one question, and posts a result is a legitimate autonomous agent. You can add triage and a real Act stage later.

Build the off switch before the on switch. Whatever outbound thing it does, make sure you can stop it instantly. If you can't articulate how you'd halt it, you're not ready to run it unattended.

Run it in shadow first. Have it propose and log for a week before it acts for real. You'll learn its failure modes on your terms instead of in production.

Cap the spend. Even a tiny agent should have a hard ceiling that fails closed. Decide the number before you turn it on.

Schedule the review. Put a recurring slot on your calendar to read what it did. The outer loop is the whole point; if you don't close it, you've just built an unsupervised process.

Part 1 was about doing the work faster with a teammate in the loop. This is about taking yourself out of the loop on the work that never needed you there. The goal isn't a fully autonomous engineering org. It's reclaiming the attention that maintenance toil was quietly taxing, and spending it on the decisions only you can make.

Conclusion & Key Takeaways

Autonomous agents are transformative on the right work, and dangerous on the wrong work, run the wrong way. The model was never the hard part. Bounding it was.

The Shift

Assistant = human in the inner loop (you prompt, you approve each step).

Agent = human in the outer loop (you set goals + guardrails, review results in batches).

Point it at scheduled toil, not at the work that needs your judgment.

The Loop

Observe → Triage → Think → Act → Record — and run each stage on the cheapest tool that can answer its question.

What Makes It Safe

Kill switches that halt outbound action in ~1 second.

Hard spend caps that fail closed.

A real sandbox: allowlist shell, restricted writes, SSRF guards, per-process secrets.

A trust ladder — shadow before real, promotion earned with evidence.

What Makes It Cheap

Local-first tiered routing — most cycles never touch a paid API.

Prompt-cache discipline — a single prefix mutation can 10x a conversation.

Escalate to a frontier model only when the task earns it.

The Honest Caveats

Confidently-wrong-at-scale, cost creep, hot-path bloat, shared-substrate collisions, and silent scope drift. Every one is a missing boundary. Draw the boundaries first, then turn it on.

Next in this series: "MCP in Production: Wiring Tools Into Agents Without Busting the Cache" (Part 3, November): how an agent actually reaches its tools, why your tool schema is part of the cache prefix, and how to add capabilities without quietly wrecking cost.

Running an autonomous agent of your own, or thinking about it? Tell me what you pointed it at and where it bit you. The failure modes are where the real learning is.