InterviewPrepKit

Home / Cheat Sheet / AI Agent System Design

Cheat sheet

Autonomous Agent

Read the full lesson →

Design an agent that runs 8 hours unattended, no human in the loop, and produces a report you can trust by morning: a ReAct loop wrapped in five harness mechanisms the model cannot reach.

Preconditions (build autonomy only if all three)

  • Reversible actions replace a human’s undo; irreversible mistake at 3am has unbounded cost.
  • Machine-checkable success replaces a human’s judgment; a model asked “did you succeed?” just predicts what a successful assistant says.
  • Hard budget ceiling replaces a human’s attention; a stuck loop burns 8 hours at full token price.
  • If the success predicate can’t be written as code, the task is not autonomy-eligible.
  • Human on the loop (watches, can intervene, run never waits) is the better default when the constraint allows.

Five mechanisms (what makes it more than ReAct)

  • Verification after every task: machine-checkable, never the model’s opinion.
  • Checkpoint before and after every task.
  • Drift check against the immutable original goal.
  • Dual budgets: one the model sees, one it cannot.
  • Termination set where every exit, including failures, is reported.

Termination set (exhaustive + prioritized)

STOP has 7 members; the set is exhaustive (every exit, including a raised exception, maps to exactly one) and prioritized (order of checks is code, not convention). GOAL_MET is checked first so a run that meets the goal on its last dollar reports success. The harness runs the goal predicate, never the agent.

StopClassExitAlert
GOAL_METSUCCESS0
BUDGET_EXHAUSTEDPARTIAL2alert
TIME_LIMITPARTIAL2alert
DRIFT_DETECTEDFAILURE3page
NO_PROGRESSFAILURE3alert
QUEUE_EMPTY_GOAL_UNMETFAILURE3alert
UNRECOVERABLE_ERRORFAILURE4
  • Only drift pages (wakes someone now); it’s the ending spending money on the wrong problem.
  • while True + one stop check at top; try classifies exceptions (the likely exit); module-level assert STOP == set(STOP_CLASS) catches a class with no producer at import.
  • Empty-queue trap: while queue: makes “empty queue, goal unmet” the normal exit, so it reports FAILURE as completed. A false success is worse than a crash.

Drift detection

  • Drift: each self-generated task is locally reasonable but the chain isn’t; it’s a property of the path, so no local check catches it. Compare the queue against the origin.
  • Goal must be immutable: held in the harness (no tool reaches it), SHA-256 hashed, assert_intact on every check. Unreachability is the real lock; the hash is a cheap secondary check.
  • Judge runs in a fresh context: only (original_goal, queue), never the agent’s reasoning.
  • reasoning field emitted before drift_score (constrained decoding emits in schema order), so the number is conditioned on analysis.
  • Cadence DRIFT_EVERY = 5 (full Opus call, slow-moving property). Strict >: > 0.5 halt, > 0.3 prune off-goal tasks, <= 0.3 aligned.
  • False-negative bias; calibrate on 20 labeled queues (10 aligned, 10 drifted) before trusting a threshold.

Checkpointing (atomic or nothing)

  • A torn write that still parses is worse than no checkpoint: silent, confident wrong resume.
  • Safe write: temp file on same filesystemflush + fsync(fd)os.replace (atomic rename) → fsync the directory.
  • Frame the payload with a SHA-256; every refusal raises RuntimeError naming UNRECOVERABLE_ERROR. os.replace defeats a torn file; the checksum covers bit rot after commit. Neither catches a body wrong before serialization.
  • Store artifacts by path, history as a compacted summary (not raw transcript). Checkpoint per task, not on a timer.
  • Resume verifies, never re-executes an IN_PROGRESS task. Three-way outcome: done → complete; nothing happened → re-queue; partially_applied → human. Re-running non-idempotent work (a post, a payment) does damage.

Budgets, cost, evals

  • Hard cap: harness ledger, invisible to model, reserve() refuses the call before it’s made (worst case = prompt + max_tokens), sets blocked so should_stop can report BUDGET_EXHAUSTED (spend stalls below the cap, e.g. $27.75 of $28).
  • Task budget (task_budget, tokens): visible to model, pacing only; a runaway loop sails past it. Run both.
  • record counts all four usage fields (cache reads 0.1x, writes 1.25x) or the ceiling is far from where you think.
  • A night ≈ $22 (40 tasks, 6 calls each); 83% is task execution. Input cost grows with the square of loop length. Set cap at $28; track cost per verified task ($0.73), halt if pass rate drops below ~70%.
Want the full picture? The lesson has the derivations, worked examples, and diagrams this card compresses into bullets. Read the full lesson →
Report a bug