autoresearch — Karpathy's overnight AI-research loop
For NAO + anyone who wants to know what "AI doing AI research" actually looks like today — stripped of hype. In March 2026 Karpathy open-sourced a ~630-line repo that hands a coding agent a small-but-real LLM-training script and tells it: edit the code, train for 5 minutes, keep the change if the number went down, repeat all night. That's the whole thing. This article is the honest mechanics — the three files, the ratchet, the bits-per-byte metric, the local-search ceiling — and how the same loop shape already runs inside OpenAlice as our [[llm-maintained-wiki]] deep-research waves. Sources: the repo itself (README.md,program.md,train.py), plus the DataCamp and DataScienceDojo explainers.
The one-sentence idea
Give an AI agent a working LLM-training script, a fixed 5-minute compute budget, and a single number to minimise — then let it propose → train → measure → keep-or-revert, autonomously, hundreds of times while you sleep.
That is autoresearch. It is not a model, not a framework, not a product. It is a harness and a discipline: the smallest possible setup in which a general coding agent (Claude Code, Codex, Cursor) becomes an ML researcher instead of an ML assistant. The repo is deliberately tiny so the agent can hold the entire system in its context window at once.
Karpathy frames it half-jokingly in the README as an origin story — "This repo is the story of how it all began" — written from an imagined future where research is done by "autonomous swarms of AI agents" and the code has become a self-modifying binary "grown beyond human comprehension." The joke encodes the real thesis: the durable artifact of research is not the code, it's the instructions that direct the search.
Why it matters
Most "AI does science" demos are either closed (an internal lab's agent) or baroque (a multi-agent paper-writing pipeline with a dozen moving parts). autoresearch matters because it is the minimal honest version: you can read all of it in an afternoon, run it on one GPU, and watch a commodity coding agent genuinely improve a real training script.
The headline results are concrete, not vaporware (per the DataCamp writeup):
- Karpathy's own run: ~700 experiments overnight, ~20 genuine improvements, yielding an ~11% speedup on already-optimised GPT-2-class code.
- A widely-shared third-party run reported a ~19% quality improvement on a smaller model overnight, beating a hand-tuned baseline twice its size.
- The repo hit 21k+ GitHub stars within days of the March 7, 2026 release.
The deeper significance is a shift in where the human adds value. The researcher no longer hand-edits train.py; they edit program.md — the instructions that steer the agent. Leverage moves from execution to research design. This is the same division of labour that powers the [[llm-maintained-wiki]]: "The human's job is to curate sources, direct the analysis, ask good questions… the LLM's job is everything else." autoresearch applies that doctrine to experiments instead of knowledge.
How it works — the three files
The whole system is three files plus a lockfile, and the discipline lives in which file is allowed to change and by whom:
prepare.py ← constants, data prep, eval harness — NEVER modified (human-read-only)
train.py ← GPT model + optimizer + train loop — the agent edits THIS, constantly
program.md ← instructions for the agent — the HUMAN edits this (the "skill")
pyproject.toml ← pinned deps (no new deps allowed)A beginner's mental model: think of it as a science lab with a locked rulebook. prepare.py is the lab's fixed apparatus and scoring rubric — the agent may never touch it, so it can never cheat by redefining "good." train.py is the experiment bench — fully open, the agent rebuilds it however it likes. program.md is the lab director's standing orders.
prepare.py — the immutable apparatus (the anti-cheat layer)
prepare.py fetches the training data, trains a BPE tokenizer, and — crucially — defines evaluate_bpb(...) plus the fixed constants (TIME_BUDGET, MAX_SEQ_LEN, EVAL_TOKENS, vocab_size). The agent is explicitly forbidden from editing it or the evaluate_bpb function.
This is the single most important design decision in the whole repo. If the agent could edit the evaluator, it would (eventually, inevitably) "improve" the metric by making the metric easier — classic specification gaming. By locking the scoring function behind a read-only file, the goalposts can't move.
train.py — the experiment bench (everything the agent owns)
This is the only file the agent rewrites. It is a complete, modern, single-GPU GPT, and it is dense — the agent has a real search space, not a toy:
- Model:
GPT(nn.Module)configured byGPTConfig. DefaultDEPTH = 8layers; model width derives fromASPECT_RATIO = 64toward a targetHEAD_DIM = 128. Rotary positional embeddings; alternating layers carry ResFormer-style value embeddings; logit soft-capping at 15. - Attention pattern:
WINDOW_PATTERN = "SSSL"— three short (banded, half-context) sliding-window-attention layers then one long full-context layer, cycling. (The README tells small-GPU forkers to switch this to plain"L"because banded attention can be inefficient on non-Hopper hardware.) Attention uses Flash Attention 3 with a fallback path. - Optimizer: a hybrid
MuonAdamW. AdamW drives embeddings, unembeddings, and per-layer scalars; Muon (Nesterov-momentum orthogonalised updates) drives the 2-D matrix weights. Both aretorch.compile-fused. Muon momentum ramps —get_muon_momentum(step)goes 0.85 → 0.95 over the first ~300 steps. - Load-bearing constants (the knobs the agent plays with):
| constant | default | role | |---|---|---| | TOTAL_BATCH_SIZE | 2**19 (~524K tok) | tokens per optimizer step | | DEVICE_BATCH_SIZE | 128 | micro-batch per GPU | | EMBEDDING_LR | 0.6 | token-embedding LR | | UNEMBEDDING_LR | 0.004 | LM-head LR | | MATRIX_LR | 0.04 | Muon LR (2-D weights) | | SCALAR_LR | 0.5 | per-layer scalar LR | | WEIGHT_DECAY | 0.2 | decay (Muon params) | | WARMUP_RATIO / WARMDOWN_RATIO | 0.0 / 0.5 | LR schedule shape |
The 5-minute ratchet — the actual loop
The loop is enforced by wall-clock time, not step count — which is what makes runs comparable across changes. The budget starts after warmup so JIT compilation isn't charged to the clock:
# enforced inside train.py's loop:
if step > 10 and total_training_time >= TIME_BUDGET: # TIME_BUDGET ≈ 5 min, from prepare.py
break
progress = total_training_time / TIME_BUDGET # drives LR/momentum schedules
...
val_bpb = evaluate_bpb(model, tokenizer, DEVICE_BATCH_SIZE) # the one number that mattersAt ~5 min/run you get ~12 experiments/hour, ~100 overnight. The agent loop, spelled out in program.md, is:
- Create a git branch
autoresearch/<tag>(e.g.autoresearch/mar5). - Read
README.md/prepare.py/train.pyfor context; initresults.tsv. - Form a hypothesis, edit `train.py`, commit.
- Run
uv run train.py > run.log 2>&1;grepthe log forval_bpb+ peak VRAM. - Append a row to
results.tsv(commit, val_bpb, memory_gb, status, description). - If `val_bpb` improved → keep the commit. If not → `git reset` it away.
- On a crash, read the error log and try to fix it.
- `NEVER STOP` — keep iterating until the human interrupts.
That step 6 is the ratchet: the run state only ever moves toward lower val_bpb. It can never regress, because regressions are reverted.
The metric: validation bits-per-byte (val_bpb)
Why bits-per-byte and not plain loss/perplexity? Because bpb is normalised per *byte* of raw text, not per token — so it does not depend on the tokenizer's vocabulary size. If the metric were per-token loss, the agent could "win" by shrinking the vocab (fewer, fatter tokens → lower per-token loss) with no real modelling gain. bpb closes that exploit, so a run that changes the tokenizer, the architecture, and the batch size all at once is still directly comparable to the baseline. (See [[tokenization]] for why per-token metrics are treacherous, and [[scaling-laws]] for what bpb-vs-compute curves mean.)
Key ideas & tradeoffs
- Constraints as the product. ~630 lines is not a limitation, it's the feature. Small enough for the agent to hold whole-system context; locked evaluator so success can't be redefined; single editable file so every diff is reviewable. "One GPU, one file, one metric."
- Fixed wall-clock budget = comparability + portability. Every experiment is the same 5 minutes, so results are commensurable on your machine. The flip side: results are not comparable across hardware (an H100 run and a MacBook run optimise different operating points). autoresearch finds the best model for your platform in that budget, not a universal best.
- The instruction file is the real artifact. You're not really hand-tuning a model — you're tuning
program.md, the "research org code." Karpathy explicitly calls the defaultprogram.mda minimal "skill" and notes that iteratively growing it to discover better research-directing prompts is "the point" — a direct sibling of the [[llm-maintained-wiki]] doctrine where the durable value lives in the curated instructions, not the outputs. - git as the experiment ledger. Branches + commits +
results.tsvgive a fully reproducible audit trail of every hypothesis, for free. The revert-on- failure ratchet is implemented bygit reset. - It scales (a bit). Karpathy says he runs a "bigger cousin" of this on 8×H100 with the full nanochat — evidence the loop shape isn't only a toy.
Honest caveats
This is the part the hype skips. autoresearch is real and genuinely useful, but it is incremental optimisation, not scientific creativity — and its authors say so.
- The ratchet ceiling (local search). The greedy keep-only-if-better rule means the agent can never take a step backward to set up a larger gain. It gets trapped in local minima, cycling through minor hyperparameter wiggles (the explainers cite GitHub issues observing exactly this). No basin-hopping, no "get worse now to get much better later." It optimises within a research direction; it does not discover new ones.
- It automates iteration, not novelty. A human still defines the search space (the data, the model family, what's worth trying). The agent does the tireless methodical grinding humans hate — it is a better lab grad student, not a better Karpathy. "Human researchers still define the agenda."
- RLHF-induced timidity. Commentators note that alignment-trained agents are "cagy and scared" on genuinely open-ended problems — they prefer safe small edits over bold rewrites, which compounds the local-search trap.
- The 5-minute window hides slow-burn ideas. Any change whose payoff only shows up over a longer training horizon is invisible to a 5-minute eval and will be (wrongly) reverted. The budget that makes runs comparable also blinds the agent to a whole class of real improvements.
- Single NVIDIA GPU only (tested on H100). No CPU/MPS/AMD in the core repo by design (to avoid bloat) — community forks add macOS-MLX, Windows-RTX, AMD. Small-GPU users must hand-tune
DEPTH,WINDOW_PATTERN,MAX_SEQ_LEN,vocab_sizedown and use a low-entropy dataset (TinyStories) to see anything. - It is early/experimental. It's a baseline and a provocation, not a maintained platform. The "value" is the idea and the minimal harness, not a turnkey research engine.
Honesty check on this article: all five sources fetched cleanly (the repo README/program.md/train.py raw files plus two independent explainers). Exact code-shaped detail (constants, the if step > 10 and ... break budget gate, evaluate_bpb) is paraphrased from a fast-model read of train.py, not a line-by-line local checkout — treat the table values as the documented defaults, which match the README's small-GPU tuning advice. Where a number comes from a secondary source (the ~700-experiments / ~11% / ~19% figures, the 21k stars), it's attributed to the explainers, not the repo.
How it connects to OpenAlice + the Academy ladder
OpenAlice already runs the autoresearch loop shape — just over knowledge instead of training runs. The lab's deep-research waves (the M13 knowledge-compressor / deep-research skill that produced this very article) are an autoresearch analogue:
| autoresearch | OpenAlice deep-research wave |
|---|---|
propose edit to train.py | fan out parallel Sonnet readers over real sources |
5-min train → val_bpb | synthesize → Opus-level review for accuracy/coverage |
| keep if better, else revert | file the article back into the library if it passes |
program.md = the durable artifact | the curated wiki + skills = the durable artifact |
git + results.tsv ledger | [[llm-maintained-wiki]] cross-linked, version-controlled KB |
Both obey the same Karpathy doctrine of leverage: humans curate sources and direct; the LLM does the tireless bookkeeping. The difference is the metric — autoresearch's val_bpb is a hard scalar with a locked evaluator (great anti- cheat); a knowledge wiki's "is this article better?" is soft, which is exactly why OpenAlice keeps a human/Opus review in the loop rather than a blind ratchet.
Related substrate in the ecosystem:
- The ratchet + locked-evaluator pattern is precisely the safety scaffolding Alice's autonomous coding capability needs — propose a change, test against a fixed harness, keep only on green, audit trail in git. Compare to OpenAlice's own delegation + gate-on-green discipline (sub-agent edits re-gated against a fixed test target before merge). autoresearch is the clean reference design.
- Multi-agent escape from local minima — the single-agent ratchet's biggest weakness (local search) is exactly what [[fusion-and-llm-councils]] and [[mixture-of-agents]] address: many proposers + a judge can explore divergent directions the greedy loop can't, then reconcile. A council around an autoresearch loop is the obvious next iteration.
- Memory across runs —
results.tsvis a primitive episodic memory; durable cross-session learning ("we already tried wider-and-shallow, it lost") is the job of [[agent-memory-systems]]. A serious autoresearch swarm needs that to stop re-running dead hypotheses.
The Academy ladder
Where this sits in the OpenAlice Academy's learning path:
- Build the thing it optimises. Understand a GPT end-to-end first: [[llm-from-scratch]] → [[microgpt-build-an-llm-from-scratch]] → [[neural-network-from-scratch]]. autoresearch's
train.pyis a nanochat; you can't direct what you can't read. - Understand the moving parts the agent tunes. [[attention-and-transformers]] and [[flash-attention]] (the
WINDOW_PATTERN/FA3 it edits), [[positional-encoding]] (its rotary embeddings), [[tokenization]] (why bpb not perplexity), [[scaling-laws]] (what a 5-min budget can and can't reveal). - Understand the agent doing the research. [[rlhf-and-alignment]] (why the agent is "timid"), [[test-time-compute-reasoning]] (how it forms hypotheses), [[agent-memory-systems]] (what it forgets between runs).
- Understand the loop as a pattern. [[llm-maintained-wiki]] (same doctrine, knowledge domain), [[fusion-and-llm-councils]] + [[mixture-of-agents]] (escaping its local-search ceiling), [[model-routing]] (which model proposes vs. judges vs. grinds).
The one-line takeaway for a beginner: autoresearch is the smallest honest demonstration that a coding agent can do real, measurable ML research overnight — and the smallest honest demonstration of exactly where that ability stops.