
Autoresearch
- 227 installs
- 40 repo stars
- Updated August 4, 2026
- akillness/oh-my-skills
Automate literature and market research loops so agents gather sources, synthesize findings, and surface decisions before scoping work.
About
Autoresearch skill automates early discovery: agents run repeatable research loops, collect sources, summarize evidence, and output actionable briefs so teams decide what to validate or build with less manual searching.
- Automated multi-source research loops
- Structured synthesis of findings
- Agent-driven question refinement
- Citation and evidence tracking
- Faster early discovery before build
Autoresearch by the numbers
- 227 all-time installs (skills.sh)
- Ranked #2,712 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/akillness/oh-my-skills --skill autoresearchAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 227 |
|---|---|
| repo stars | ★ 40 |
| Last updated | August 4, 2026 |
| Repository | akillness/oh-my-skills ↗ |
What it does
Automate literature and market research loops so agents gather sources, synthesize findings, and surface decisions before scoping work.
Files
autoresearch
Autoresearch is a closed-loop ML experimentation workflow:
- human writes
program.md - agent edits
train.py prepare.pystays fixed- every run gets the same 300-second budget
- lower
val_bpbwins - regressions get reverted
This skill should behave like a routing-first front door, not a giant tutorial. Pick the user's mode, enforce the immutable-harness rules, then hand them to the smallest useful script or reference.
When to use this skill
- Set up
karpathy/autoresearchon a real GPU machine - Write or refine
program.mdbefore a session - Run a bounded overnight
train.pysearch loop - Interpret
results.tsvafter a session - Adapt the workflow to tighter VRAM constraints without invalidating comparisons
- Explain the ML-specific boundary between
autoresearchand nearby eval tooling
Do not use this skill when
- The user wants to optimize a
SKILL.md, prompt, or repo-local workflow with frozen prompts/evals — useskill-autoresearch - The user wants app-level tracing, dataset-backed LLM evals, feedback review, or observability — use LangSmith, Braintrust, Weave, Promptfoo, or similar tools
- The job does not involve a real training repo,
program.md,train.py, fixed runtime budget, andval_bpbkeep/revert ratcheting - The user is really asking for a paper survey, general benchmark scan, or literature review with no intention to run the training loop
Core boundary
| Concern | autoresearch owns | Route elsewhere |
|---|---|---|
| Mutable target | train.py in a real training repo | prompts, app configs, SKILL.md, product behavior |
| Fixed evaluator | prepare.py, validation shard, TIME_BUDGET=300, chosen MAX_SEQ_LEN / EVAL_TOKENS for the session | prompt/eval datasets, app scorecards, observability dashboards |
| Acceptance rule | keep only lower val_bpb; revert ties/regressions | human review queues, app-level release gates |
| Main artifacts | program.md, results.tsv, kept/discarded commits | prompt suites, traces, feedback datasets |
If that boundary does not fit, do not stretch this skill.
Required intake packet
Before acting, identify: 1. Mode — setup, program.md, run loop, results interpretation, or constrained hardware 2. Repository state — cloned or not, dependencies installed or not 3. Hardware state — GPU / VRAM / CUDA / MLX / Windows path 4. Session state — first baseline, active loop, or completed run 5. Constraint state — target VRAM ceiling, whether prepare.py has already been frozen for this session
Instructions
Step 1: Pick exactly one operating mode
Choose the smallest mode that answers the request:
1. Setup readiness
- install
uv - clone repo
- sync dependencies
- verify GPU/CUDA/uv with
scripts/check-hardware.sh - run the first baseline experiment
2. `program.md` authoring
- write or refine the human research charter
- record current baseline
val_bpb - prioritize hypotheses
- list what has already been tried
- freeze constraints before the loop starts
3. Bounded run loop
- confirm the evaluator is already fixed
- use
train.pyas the only mutable search surface - run the loop with keep/revert discipline
- log every experiment to
results.tsv
4. Results interpretation
- summarize best kept runs
- identify repeated failures or crash patterns
- extract what belongs in the next
program.md - distinguish genuine gains from one-off anomalies
5. Constrained-hardware adaptation
- set
MAX_SEQ_LENandEVAL_TOKENSbefore the session - keep them unchanged once the session starts
- adjust model/search strategy instead of cheating the evaluator mid-run
- route to community forks when CUDA assumptions do not hold
Do not answer all five modes at once unless the user explicitly asked for a full end-to-end walkthrough.
Step 2: Re-state the immutable harness
Every mode must preserve these rules:
program.mdis human-authored and read-only during a sessiontrain.pyis the main mutable search surfaceprepare.pyis read-only once the session startsTIME_BUDGET=300stays fixedval_bpbis the main keep/revert metricresults.tsvis append-only- dependency set in
pyproject.tomlstays locked
If the user wants to change the evaluator, start a new comparison track, not the current session.
Step 3: Execute the chosen mode
Mode A — Setup readiness
Use this path when the repo is not yet runnable.
curl -LsSf https://astral.sh/uv/install.sh | sh
git clone https://github.com/karpathy/autoresearch
cd autoresearch
uv sync
bash scripts/check-hardware.sh
uv run prepare.py
uv run train.py > run.log 2>&1
grep "^val_bpb:\|^peak_vram_mb:" run.logSuccess condition: one baseline run completes and prints both val_bpb and peak_vram_mb.
Mode B — program.md authoring
Use this path when the loop exists but direction is weak.
Minimum sections:
- goal tied to lower
val_bpb - current baseline
val_bpb - directions to explore in priority order
- what has been tried already
- constraints:
TIME_BUDGET=300, noprepare.pymutation, no new packages, VRAM ceiling, one meaningful change per experiment
For fuller templates and update patterns, use references/program-md-guide.md.
Mode C — Bounded run loop
Use this path only after setup and program.md are ready.
Loop contract: 1. read program.md + current train.py 2. form one hypothesis 3. edit train.py 4. commit 5. run one 300-second experiment 6. extract val_bpb 7. keep if improved, otherwise git reset HEAD~1 8. append result to results.tsv
Typical commands:
bash scripts/run-experiment.sh
bash scripts/run-loop.sh --max 20 --desc "session-1"Do not encourage multi-change hero rewrites. Clean ablations matter more than flashy edits.
Mode D — Results interpretation
Use this path after a completed run or checkpoint.
Helpful commands:
bash scripts/show-results.sh --top 10
awk -F'\t' '$4=="keep"' results.tsv | sort -t$'\t' -k2 -n
awk -F'\t' '{print $4}' results.tsv | sort | uniq -cSummarize only four things: best gains, repeated failures, what should move into What Has Been Tried, and the next narrow experiment family.
Mode E — Constrained-hardware adaptation
Use this path when VRAM, platform, or runtime constraints dominate.
Rules:
- choose
MAX_SEQ_LENandEVAL_TOKENSbefore the session - never change them mid-session
- lower model/search ambition before mutating the evaluator
- prefer route-outs to community forks for Apple Silicon / non-CUDA paths
For concrete values and troubleshooting, use references/hardware-config.md.
Step 4: Route out aggressively when the request is adjacent
Route out when:
- the user wants to optimize instructions, prompts, or repo-local skills →
skill-autoresearch - the user wants app-level traces, feedback review, observability, or online/offline eval dashboards → LangSmith / Braintrust / Weave / Promptfoo
- the user wants general literature synthesis rather than a runnable ML loop → research or survey tooling
Step 5: Keep the heavy detail in support files
Use support files instead of re-explaining everything inline:
references/operating-modes-and-route-outs.md— fast routing table, minimal response shape, and handoff logicreferences/architecture.md— immutability contract, file map, metric rationalereferences/program-md-guide.md— templates and update rulesreferences/hardware-config.md— VRAM tables and platform troubleshootingscripts/*.sh— runnable setup / loop / reporting helpers
Available scripts
Run from inside the autoresearch repository directory:
| Script | Purpose | Usage |
|---|---|---|
setup.sh | One-time environment setup | bash scripts/setup.sh [--seq-len 512] |
run-experiment.sh | Single 5-minute experiment + metric extraction | bash scripts/run-experiment.sh |
run-loop.sh | Autonomous loop: run → keep/revert → repeat | bash scripts/run-loop.sh [--max 20] |
show-results.sh | Human-readable results.tsv report | bash scripts/show-results.sh [--top 10] |
check-hardware.sh | GPU/CUDA/uv readiness check (JSON output) | bash scripts/check-hardware.sh |
References
Detailed documentation in references/:
| File | Contents |
|---|---|
references/operating-modes-and-route-outs.md | Mode picker, adjacency boundaries, and minimal output contract |
references/architecture.md | System design, immutability contract, git ratcheting, metric rationale |
references/program-md-guide.md | How to write and update effective program.md directives |
references/hardware-config.md | VRAM settings by GPU, memory optimization, platform troubleshooting |
Examples
Example 1: First 40GB GPU session
Request: “Help me run Karpathy autoresearch on a 40GB GPU.”
Expected behavior:
- choose Setup readiness first
- verify hardware and dependencies
- run one baseline experiment
- route to
program.mdauthoring only after the baseline exists
Example 2: User wants to optimize a skill instead
Request: “Can autoresearch help me improve this SKILL.md with binary evals?”
Expected behavior:
- route out immediately to
skill-autoresearch - explain that this skill is for real ML training search on
train.py
Best practices
1. Start with the smallest mode that fits — setup, authoring, run loop, interpretation, or hardware adaptation 2. Baseline before bravado — confirm one successful run before talking about overnight loops 3. Freeze the evaluator before the session — prepare.py, TIME_BUDGET, MAX_SEQ_LEN, and EVAL_TOKENS must stay comparable 4. One meaningful experiment at a time — ablations beat mystery bundles 5. Keep `results.tsv` append-only — discarded runs are still evidence 6. Push deep detail into references/scripts — the front door should classify and route, not duplicate every table 7. Route adjacent jobs away early — prompt/app eval and SKILL.md optimization are different lanes
References
{
"skill_name": "autoresearch",
"evals": [
{
"id": 1,
"prompt": "Help me set up Karpathy autoresearch on a 40GB GPU, verify the machine, and run the first baseline experiment.",
"expected_output": "The skill chooses setup-readiness mode, preserves the immutable harness, and points to concrete setup / hardware / baseline commands instead of dumping every downstream detail at once.",
"assertions": [
"The workflow clearly identifies setup or readiness as the chosen mode.",
"The workflow includes hardware verification or baseline-run commands such as `check-hardware.sh`, `uv sync`, `uv run prepare.py`, or `uv run train.py`.",
"The response keeps the immutable 300-second / `prepare.py` / `val_bpb` contract visible."
]
},
{
"id": 2,
"prompt": "My repo runs, but my `program.md` just says 'try improvements'. Help me fix it before tonight's run.",
"expected_output": "The skill chooses `program.md` authoring mode, asks for or fills in the baseline/priority/constraints structure, and keeps the loop tied to `train.py` rather than generic eval tooling.",
"assertions": [
"The workflow explicitly selects `program.md` authoring or an equivalent mode.",
"The response includes sections such as current baseline, directions to explore, tried-already notes, or constraints.",
"The response does not drift into prompt / app observability tooling as the main answer."
]
},
{
"id": 3,
"prompt": "My smaller-GPU autoresearch run keeps crashing, and I want to change the evaluator halfway through to get something working.",
"expected_output": "The skill keeps the evaluator immutable inside the session, recommends constrained-hardware adaptation, and treats any evaluator change as a new comparison track.",
"assertions": [
"The skill warns against modifying `prepare.py`, `TIME_BUDGET`, or the active evaluator mid-session.",
"The response recommends hardware-aware adaptation such as lowering `MAX_SEQ_LEN`, adjusting `EVAL_TOKENS` before the session, or using the hardware reference guidance.",
"The response frames evaluator changes as a new comparison track rather than part of the current run."
]
},
{
"id": 4,
"prompt": "Can autoresearch help me improve this SKILL.md with binary evals and keep-or-revert scoring?",
"expected_output": "The skill routes the request away from ML search toward `skill-autoresearch` or adjacent eval tooling instead of pretending this training-loop skill is the right fit.",
"assertions": [
"The skill explicitly says this is not the right tool for repo-local `SKILL.md` optimization.",
"The response names `skill-autoresearch` as the preferred route-out.",
"The response preserves the ML-specific `program.md` / `train.py` / `val_bpb` boundary."
]
}
]
}
autoresearch Architecture Reference
Source: karpathy/autoresearch · MIT License
---
Overview
autoresearch is a closed-loop ML experimentation system. A human authors program.md; an AI agent reads it and autonomously modifies train.py, executes experiments, and commits only improvements — creating a monotonically improving research branch overnight.
---
File Map
autoresearch/
├── train.py ← Agent's ONLY editable file (~630 lines)
├── prepare.py ← Immutable: data pipeline + evaluate_bpb() + MAX_SEQ_LEN/TIME_BUDGET/EVAL_TOKENS
├── program.md ← Human-written research directives (agent reads this)
├── pyproject.toml ← Locked dependencies (no new packages allowed)
└── results.tsv ← Persistent experiment log (all runs)Immutability Contract
| File | Agent Access | Rationale |
|---|---|---|
train.py | Read + Write | The search space — architecture, optimizer, hyperparameters |
prepare.py | Read-only | Contains evaluate_bpb() plus MAX_SEQ_LEN, TIME_BUDGET, and EVAL_TOKENS — must never change for fair comparison |
program.md | Read-only | Human's intent — agent follows, never modifies |
pyproject.toml | Read-only | Locked deps — no pip install during search |
results.tsv | Append-only | Monotonic experiment log — never delete rows |
---
The Experiment Loop
┌─────────────────────────────────────────────────────────┐
│ AGENT LOOP │
│ │
│ 1. Read program.md + current train.py │
│ 2. Formulate hypothesis (architecture / optimizer) │
│ 3. Edit train.py → git commit │
│ 4. uv run train.py (exactly 300 seconds) │
│ 5. grep "^val_bpb:" run.log → extract metric │
│ │
│ ┌─── improved? ──────┐ ┌─── not improved? ──────┐ │
│ │ git commit stays │ │ git reset HEAD~1 │ │
│ │ update baseline │ │ baseline unchanged │ │
│ └────────────────────┘ └─────────────────────────┘ │
│ │
│ 6. Append row to results.tsv │
│ 7. Repeat from step 1 │
└─────────────────────────────────────────────────────────┘---
Key Design Decisions
1. Fixed 300-Second Budget
TIME_BUDGET = 300 lives in prepare.py. Every experiment runs for exactly 300 seconds wall-clock time regardless of GPU or model size.
Why: Ensures every row in results.tsv is directly comparable. A val_bpb of 0.97 in experiment 3 means the same thing as 0.97 in experiment 97.
Throughput: ~12 experiments/hour → ~100 experiments in an overnight session.
2. Immutable Evaluation Harness
evaluate_bpb() in prepare.py is never modified. It always evaluates on the same validation shard (the last FineWeb-Edu parquet file), with the same tokenizer, for the same EVAL_TOKENS value chosen before the session starts.
Why: Without a fixed harness, a clever agent could modify the evaluation to make its model appear better — "metric hacking." The immutable harness prevents this.
3. Single Metric: val_bpb
Validation bits-per-byte (val_bpb) measures how many bits on average the model uses to predict each byte of validation text. Lower = better.
Why val_bpb over perplexity: val_bpb is vocabulary-size-independent. If the agent experiments with different tokenizers (different vocabulary sizes), perplexity scores would be incomparable; val_bpb remains a fair metric across all configurations.
4. Git Ratcheting
Every experiment is a git commit. On improvement: keep. On regression: git reset HEAD~1.
Result: The main branch is a clean, monotonically improving history of algorithmic discoveries.
Side effect: results.tsv retains the full record including all discarded experiments, providing a complete picture of the search.
5. Fits in Context Window
At ~630 lines, train.py fits within a modern LLM's context window. The agent always has the complete file in view — no partial reads, no retrieval augmentation needed.
---
train.py Structure
The default train.py contains:
1. Imports and device setup
2. Hyperparameters (model, training, eval)
3. GPT model definition
- Transformer blocks
- Attention (default: multi-head)
- FFN layers
- Layer normalization
4. Optimizer setup (Muon + AdamW)
5. Training loop
- Forward pass
- Loss computation
- Backward pass
- Optimizer step
6. Evaluation call
7. Metric output: val_bpb, peak_vram_mbAgent's search space (everything in train.py is fair game):
- Model architecture: depth, width, attention variants, FFN type
- Positional encoding: learned, RoPE, ALiBi, none
- Normalization: LayerNorm, RMSNorm, position
- Optimizer: learning rate, schedules, momentum values, weight decay
- Training: batch size, gradient accumulation, mixed precision
---
results.tsv Format
commit val_bpb peak_vram_mb status description| Column | Type | Values |
|---|---|---|
commit | string | 7-char git hash |
val_bpb | float | Lower = better; crash if OOM/error |
peak_vram_mb | integer | Peak GPU memory in MB |
status | enum | keep, discard, crash |
description | string | Free text summary of the change |
---
Karpathy's Documented Results
From the original repo and public statements:
| Session | Experiments | Improvements | Start val_bpb | Best val_bpb |
|---|---|---|---|---|
| Session 1 | 126 | 18 | 0.9979 | 0.9697 |
| Tobi Lütke (Shopify CEO) | 37 | ~19% gain | — | — |
Key observation: Improvements found on depth-12 transferred cleanly to depth-24, suggesting genuine algorithmic discoveries rather than overfitting to a particular scale.
---
Platform Notes
autoresearch is designed for a single NVIDIA GPU on Linux. Community forks extend this:
| Platform | Status | Notes |
|---|---|---|
| H100 80GB + Linux | Official | Default config |
| A100 40GB | Supported | May need MAX_SEQ_LEN reduction |
| RTX 4090 24GB | Community | MAX_SEQ_LEN ≤ 512 |
| GTX 1660 Ti 6GB | Community | MAX_SEQ_LEN=256, reduced EVAL_TOKENS |
| Apple Silicon (M-series) | MLX fork | Different optimizer API required |
| Windows | Community | WSL2 + CUDA recommended |
---
What the Agent Should Never Do
1. Modify prepare.py mid-session — breaks evaluation fairness 2. Change TIME_BUDGET — makes comparisons invalid 3. Add new packages via pip — pyproject.toml is locked 4. Delete rows from results.tsv — permanent record 5. Push to main before human review — results branch only
Hardware Configuration Reference
Configure autoresearch for your GPU. The key levers are MAX_SEQ_LEN and EVAL_TOKENS in prepare.py.
Rule: Never change these values mid-session. A session'sresults.tsvis only internally comparable if all rows use the sameMAX_SEQ_LENandEVAL_TOKENS.
---
Recommended Settings by GPU
| GPU | VRAM | MAX_SEQ_LEN | EVAL_TOKENS | ~Experiments/hr | Notes |
|---|---|---|---|---|---|
| H100 80GB | 80 GB | 2048 | 20,971,520 | ~12 | Default config |
| A100 80GB | 80 GB | 2048 | 20,971,520 | ~12 | Same as H100 |
| A100 40GB | 40 GB | 1024 | 10,485,760 | ~12 | Halve both |
| RTX 4090 | 24 GB | 512 | 5,242,880 | ~12 | Quarter both |
| RTX 3090 | 24 GB | 512 | 5,242,880 | ~12 | Same as 4090 |
| RTX 3080 Ti | 12 GB | 256 | 2,097,152 | ~12 | Eighth both |
| GTX 1660 Ti | 6 GB | 256 | 2,097,152 | slower | Community tested |
| Apple M-series | unified | — | — | — | MLX fork required |
EVAL_TOKENSshould scale proportionally withMAX_SEQ_LENto maintain evaluation quality.
---
How to Apply Settings
Edit prepare.py before running uv run prepare.py (one-time setup):
# prepare.py — find and edit this line:
MAX_SEQ_LEN = 2048 # change to your value
# prepare.py — find and edit this line:
EVAL_TOKENS = 20_971_520 # change to your value from the table aboveOr use the setup script with --seq-len:
bash scripts/setup.sh --seq-len 512The setup script currently patchesMAX_SEQ_LENonly. UpdateEVAL_TOKENSinprepare.pyyourself using the table above so runs stay internally comparable.
---
Checking Available VRAM
Before running any experiment:
# Live VRAM status
nvidia-smi --query-gpu=name,memory.total,memory.free --format=csv
# Run the built-in check script
bash scripts/check-hardware.sh---
Memory Optimization Techniques (when at VRAM limit)
These can be added to train.py by the agent if VRAM is tight:
1. Gradient Checkpointing
Trades compute for memory — recomputes activations during backward pass instead of storing them.
# In model's forward() call inside the training loop:
from torch.utils.checkpoint import checkpoint
output = checkpoint(block, x) # instead of: output = block(x)Effect: Reduces VRAM by ~30-40% at cost of ~20% slower training.
2. Mixed Precision (BF16)
Most modern GPUs support BF16 natively.
# In training loop:
with torch.autocast(device_type='cuda', dtype=torch.bfloat16):
logits = model(x)Effect: ~40-50% VRAM reduction for activations.
3. Reduce Batch Size + Gradient Accumulation
# Instead of batch_size=64:
batch_size = 16
accumulation_steps = 4 # equivalent effective batch size = 64Effect: Linear VRAM reduction. Training unchanged if gradient accumulation compensates.
4. Reduce Model Depth Before Width
Depth scales VRAM more than width (intermediate activations). Try:
n_layer = 8 # instead of 12
n_embd = 768 # keep or increase slightly---
VRAM Estimation Formula
A rough estimate for transformer VRAM at training time:
VRAM_GB ≈ (parameters × 4 bytes) # model weights (fp32)
+ (parameters × 4 bytes) # gradients
+ (parameters × 8 bytes) # optimizer state (AdamW = 2× fp32)
+ (batch × seq_len × hidden × layers × 4) # activations (fp32)For a depth-12, hidden-768, MAX_SEQ_LEN=2048, batch=32 model:
- Parameters: ~85M × 16 bytes = ~1.4 GB
- Activations: 32 × 2048 × 768 × 12 × 4 bytes ≈ ~24 GB
- Total: ~26 GB (fits on A100 40GB, tight on RTX 4090 24GB)
With BF16 activations: activations halved → ~14 GB total.
---
Apple Silicon (MLX) Setup
The official repo requires NVIDIA CUDA. Community MLX port:
# Clone MLX fork (community maintained)
git clone https://github.com/[community-fork]/autoresearch-mlx
cd autoresearch-mlx
# Install mlx
pip install mlx mlx-lm
# Setup and run
python prepare_mlx.py
python train_mlx.pyNote: MLX uses different optimizer APIs and may have different architectural constraints. results.tsv values from MLX runs are NOT comparable to CUDA runs.---
Multi-GPU Notes
autoresearch is designed for single-GPU training. The 300-second budget and evaluate_bpb() assume a single device.
For multi-GPU:
- Do NOT use
DistributedDataParallelin experiments (changes training dynamics) torch.nn.DataParallelcan be used as a workaround but introduces overhead- Results from multi-GPU runs may not be comparable to single-GPU baseline
If you have multiple GPUs, run parallel independent autoresearch sessions on different GPUs with different program.md research directions — then merge insights.
---
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
| OOM crash immediately | MAX_SEQ_LEN too large | Halve MAX_SEQ_LEN and re-run prepare.py |
| OOM mid-training | Model too large for seq len | Reduce n_layer or n_embd |
| val_bpb = nan | Learning rate too high | Reduce max_lr by 10× |
| All experiments crash | CUDA not available to PyTorch | Check nvidia-smi + uv run python -c "import torch; print(torch.cuda.is_available())" |
| Very slow training | CPU fallback active | Confirm CUDA available; check torch.cuda.current_device() |
| prepare.py takes forever | Network slow / many shards | Set MAX_SHARDS=100 in prepare.py for a smaller dataset |
Operating Modes and Route-Outs
Use this page when SKILL.md has already chosen the lane and you need the smallest next move.
Mode picker
| User situation | Choose this mode | Immediate next artifact / command |
|---|---|---|
| Repo not installed or hardware unknown | Setup readiness | bash scripts/check-hardware.sh |
| Repo runs but search direction is weak | program.md authoring | edit program.md using program-md-guide.md |
| Baseline exists and loop is ready | Bounded run loop | bash scripts/run-loop.sh --max N --desc session-name |
| Session finished and results need meaning | Results interpretation | bash scripts/show-results.sh --top 10 |
| VRAM / platform constraints dominate | Constrained-hardware adaptation | update prepare.py before the session and cross-check hardware-config.md |
Immutable-harness reminders
Carry these into every mode:
program.mdis human-authored for the sessiontrain.pyis the main mutable search surfaceprepare.pyis read-only once the session startsTIME_BUDGET=300stays fixedresults.tsvis append-only- lower
val_bpbwins
If any of those must change, start a new comparison track instead of mutating the active session.
Route-outs
| If the request is really about... | Route to... | Why |
|---|---|---|
Improving a SKILL.md, prompt packet, or repo-local instruction artifact | skill-autoresearch | Same ratchet idea, different mutable artifact and evaluator |
| App-level evals, prompt regression suites, traces, feedback review, dashboards | LangSmith / Promptfoo / Braintrust / Weave | Observability/eval infrastructure, not train.py ML search |
| Literature review or broad research scan | survey or research skills | No runnable training loop yet |
| Generic GPU setup without the autoresearch workflow | environment / MLOps skill in the host repo | Hardware setup alone is not the full lane |
Minimal response shape
A good front-door answer usually fits this template:
1. Mode: the one mode you chose 2. Immutable rules: what must not change 3. Next step: the file or command to touch now 4. Route-out: only if the request is adjacent or mixed
program.md Authoring Guide
"The researcher's job shifts from writing Python to writing Markdown." — Andrej Karpathy
program.md is the most important file in an autoresearch session. The agent reads it at the start of every loop iteration. A vague program.md wastes GPU hours; a precise one focuses the search.
---
What program.md Controls
The agent uses program.md to decide:
- What to try next — which hypotheses to form
- What to avoid — directions already explored or known to fail
- What to prioritize — VRAM efficiency vs. val_bpb vs. speed
- What constraints to respect — VRAM limit, MAX_SEQ_LEN, banned packages
The agent does NOT use program.md to determine the evaluation metric (always val_bpb), the time budget (always 300s), or which file to edit (always train.py).
---
Minimal Template
# Research Program
## Goal
Minimize val_bpb on the FineWeb-Edu validation set.
## Current Baseline
val_bpb: [FILL IN after first run]
## Directions to Explore
[What to try]
## Constraints
- TIME_BUDGET=300s (fixed)
- Peak VRAM must stay under [X] GB
- Do not modify prepare.py
- No new packages (pyproject.toml is locked)---
Full Template with All Sections
# Research Program
## Goal
Minimize val_bpb on FineWeb-Edu within the 300-second training budget.
Lower val_bpb is always better. Do not optimize for anything else.
## Current Baseline
val_bpb: 0.9979
Model: depth-12 GPT, Muon + AdamW optimizer, RoPE, SwiGLU
Hardware: H100 80GB
## Directions to Explore
### High Priority (try these first)
1. Attention variants: GQA (grouped-query), MLA (multi-head latent), sliding window
2. Layer types: MoE (mixture of experts) FFN, SwiGLU vs. GeGLU
3. Optimizer: Muon momentum values 0.90–0.98, AdamW β1/β2 grid
4. Normalization: RMSNorm vs. LayerNorm, pre-norm vs. post-norm
### Medium Priority
5. Learning rate schedule: cosine vs. linear warmup + decay ratios
6. Weight tying: tie embedding and output projection weights
7. Depth/width tradeoffs: same FLOP budget, different aspect ratios
### Low Priority / Exploratory
8. Positional encoding: ALiBi, T5-style relative, sinusoidal
9. Residual connection variants: pre-gate, scaled residuals
## What Has Been Tried (Do Not Repeat)
- Learned positional embeddings: worse than RoPE by ~0.008
- Depth-8 with wider hidden: worse than depth-12
- Pure AdamW (no Muon): worse by ~0.015
## Constraints
- Must complete in 300 seconds (TIME_BUDGET is fixed, do not change)
- Peak VRAM must stay under 39 GB
- Do not modify prepare.py or pyproject.toml
- Do not add new packages
- Each experiment should change ONE thing at a time (clean ablations)
## Notes
- depth-12 improvements transfer to depth-24 — focus on algorithms, not scale
- Previous session found SwiGLU activation reliably helps---
Writing Principles
1. Record the Current Baseline
Always include the current best val_bpb and what model configuration produced it. The agent needs this to decide whether a new experiment is an improvement.
## Current Baseline
val_bpb: 0.9697
Model: depth-12, GQA (4 KV heads), SwiGLU, Muon lr=0.012. List What Has Been Tried
This prevents the agent from re-running experiments that already failed. Be specific.
## What Has Been Tried
- GQA with 2 KV heads: DISCARD (val_bpb=0.981, worse than baseline)
- MoE with 8 experts: CRASH (OOM at MAX_SEQ_LEN=2048)
- cosine LR schedule: KEEP (val_bpb=0.971)3. One Change at a Time
Instruct the agent to change ONE architectural component per experiment. Combined changes make it impossible to attribute improvements.
## Constraints
- Each experiment must change exactly ONE component of train.py
- Do not combine architecture changes with optimizer changes4. Specify VRAM Budget
The agent needs to know the GPU's headroom to avoid OOM crashes.
## VRAM Constraint
Peak VRAM must stay under 38 GB.
If an experiment would exceed this, try reducing hidden_size by 20%
before abandoning the approach.5. Prioritize Directions
An ordered list focuses the agent's first experiments on the most promising directions.
## Exploration Priority
1. Attention: GQA (most likely to help, low risk)
2. Optimizer: Muon hyperparameters (easy, known to matter)
3. FFN: SwiGLU variants (medium risk)
4. Architecture: depth/width (expensive, do last)---
Updating program.md Between Sessions
After each session, update program.md with:
## Session Log (append at bottom)
### Session 2026-03-11 (126 experiments, 18 improvements)
Best achieved: val_bpb=0.9697 (commit a3f2c91)
Top gains:
- SwiGLU activation: -0.012 (commit 8e2a1b3)
- GQA 4 KV heads: -0.009 (commit 5d7f9c2)
- Muon momentum 0.95: -0.006 (commit 2c8e4a1)
What to try next:
- MLA (multi-head latent attention) — not yet explored
- Mixture of Depths — promising from literature---
Common Mistakes
| Mistake | Problem | Fix |
|---|---|---|
| No baseline recorded | Agent doesn't know what "improvement" means | Add Current Baseline: val_bpb: X.XXXX |
| Too vague ("try things") | Agent wastes experiments on random changes | List specific directions with priority order |
| No VRAM constraint | Agent causes OOM crashes | Add Peak VRAM must stay under X GB |
| Allowing multi-component changes | Can't attribute improvements | Add Change exactly ONE component per experiment |
| Never updating after sessions | Agent re-explores exhausted directions | Append session log after each run |
| Contradictory instructions | Agent gets confused | Review for internal consistency before each session |
#!/usr/bin/env bash
# check-hardware.sh — Verify GPU setup for autoresearch
# Checks NVIDIA GPU, CUDA, VRAM, Python, and uv availability
#
# Usage:
# bash scripts/check-hardware.sh
#
# Output:
# JSON to stdout with fields: gpu_name, vram_mb, cuda_version, python_ok, uv_ok, ready
# Human-readable summary to stderr
#
# Exit codes:
# 0 All required checks pass
# 1 Missing required components (no GPU, no CUDA, no uv)
set -uo pipefail
log() { echo "[check-hardware] $*" >&2; }
warn() { echo "[check-hardware] WARN: $*" >&2; }
err() { echo "[check-hardware] ERROR: $*" >&2; }
GPU_NAME="none"
VRAM_MB=0
CUDA_VERSION="none"
PYTHON_OK=0
UV_OK=0
READY=1
# ── NVIDIA GPU ────────────────────────────────────────────────────────────────
if command -v nvidia-smi &>/dev/null; then
GPU_NAME=$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1 | xargs || echo "unknown")
VRAM_MB=$(nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits 2>/dev/null | head -1 | xargs || echo "0")
CUDA_VERSION=$(nvidia-smi | grep "CUDA Version" | awk '{print $NF}' | head -1 || echo "unknown")
log "GPU: ${GPU_NAME} | VRAM: ${VRAM_MB} MB | CUDA: ${CUDA_VERSION}"
else
warn "nvidia-smi not found — no NVIDIA GPU detected"
READY=0
fi
# VRAM check: autoresearch default requires ~38-40GB for MAX_SEQ_LEN=2048
if [[ "${VRAM_MB}" -gt 0 ]]; then
if [[ "${VRAM_MB}" -ge 39000 ]]; then
log "VRAM: ${VRAM_MB} MB — sufficient for default config (MAX_SEQ_LEN=2048)"
elif [[ "${VRAM_MB}" -ge 20000 ]]; then
warn "VRAM: ${VRAM_MB} MB — reduce MAX_SEQ_LEN to 512 or lower in prepare.py"
elif [[ "${VRAM_MB}" -ge 6000 ]]; then
warn "VRAM: ${VRAM_MB} MB — low-VRAM mode: set MAX_SEQ_LEN=256 in prepare.py"
else
err "VRAM: ${VRAM_MB} MB — insufficient (minimum ~6GB required)"
READY=0
fi
fi
# ── Python ────────────────────────────────────────────────────────────────────
if command -v python3 &>/dev/null; then
PY_VER=$(python3 --version 2>&1 | awk '{print $2}')
log "Python: ${PY_VER}"
PYTHON_OK=1
else
warn "python3 not found"
fi
# ── uv ────────────────────────────────────────────────────────────────────────
if command -v uv &>/dev/null; then
UV_VER=$(uv --version 2>&1 | head -1)
log "uv: ${UV_VER}"
UV_OK=1
else
warn "uv not found — run: curl -LsSf https://astral.sh/uv/install.sh | sh"
READY=0
fi
# ── PyTorch CUDA check (if in autoresearch repo) ──────────────────────────────
if [[ -f "train.py" ]] && command -v uv &>/dev/null; then
TORCH_CUDA=$(uv run python3 -c "import torch; print(torch.cuda.is_available())" 2>/dev/null || echo "unknown")
log "torch.cuda.is_available(): ${TORCH_CUDA}"
if [[ "${TORCH_CUDA}" == "False" ]]; then
warn "PyTorch cannot see CUDA. Check CUDA drivers and pytorch install."
READY=0
fi
fi
# ── Structured JSON output (stdout) ──────────────────────────────────────────
cat <<JSON
{
"gpu_name": "${GPU_NAME}",
"vram_mb": ${VRAM_MB},
"cuda_version": "${CUDA_VERSION}",
"python_ok": ${PYTHON_OK},
"uv_ok": ${UV_OK},
"ready": ${READY}
}
JSON
# ── Exit with appropriate code ────────────────────────────────────────────────
if [[ "${READY}" -eq 1 ]]; then
log "All checks passed — ready for autoresearch"
else
err "One or more checks failed — fix the issues above before running experiments"
exit 1
fi
#!/usr/bin/env bash
# run-experiment.sh — Run a single 5-minute autoresearch experiment
# Executes uv run train.py, captures output, extracts val_bpb and peak_vram_mb
#
# Usage:
# bash scripts/run-experiment.sh [--repo <path>] [--log <logfile>]
#
# Options:
# --repo <path> Path to autoresearch repo (default: .)
# --log <file> Log file path (default: run.log in repo root)
#
# Output (stdout, tab-separated):
# val_bpb peak_vram_mb duration_s log_path
# Exit codes:
# 0 Experiment completed, metrics extracted
# 1 Missing val_bpb in output (likely crash/OOM)
# 2 Experiment timed out (> 360s)
set -uo pipefail
REPO_DIR="."
LOG_FILE=""
while [[ $# -gt 0 ]]; do
case "$1" in
--repo) REPO_DIR="$2"; shift 2 ;;
--log) LOG_FILE="$2"; shift 2 ;;
--help)
sed -n '2,16p' "$0" | sed 's/^# //'
exit 0
;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
esac
done
cd "${REPO_DIR}"
LOG_FILE="${LOG_FILE:-run.log}"
TIMEOUT=360 # 60s grace over the 300s TIME_BUDGET
log() { echo "[run-experiment] $*" >&2; }
err() { echo "[run-experiment] ERROR: $*" >&2; }
# ── Sanity checks ────────────────────────────────────────────────────────────
if [[ ! -f "train.py" ]]; then
err "train.py not found. Are you in the autoresearch repo? Use --repo <path>"
exit 1
fi
if ! command -v uv &>/dev/null; then
err "uv not found. Run setup.sh first."
exit 1
fi
# ── Run experiment ───────────────────────────────────────────────────────────
log "Starting experiment (TIME_BUDGET=300s)..."
START_TS=$(date +%s)
if ! timeout "${TIMEOUT}" uv run train.py > "${LOG_FILE}" 2>&1; then
EXIT_CODE=$?
END_TS=$(date +%s)
DURATION=$(( END_TS - START_TS ))
if [[ "${EXIT_CODE}" -eq 124 ]]; then
err "Experiment timed out after ${DURATION}s (timeout=${TIMEOUT}s)"
exit 2
fi
# Non-zero but not timeout — might still have written metrics before crash
log "train.py exited with code ${EXIT_CODE} after ${DURATION}s — checking for partial metrics..."
fi
END_TS=$(date +%s)
DURATION=$(( END_TS - START_TS ))
# ── Extract metrics ───────────────────────────────────────────────────────────
VAL_BPB=$(grep "^val_bpb:" "${LOG_FILE}" | tail -1 | awk '{print $2}')
PEAK_VRAM=$(grep "^peak_vram_mb:" "${LOG_FILE}" | tail -1 | awk '{print $2}')
if [[ -z "${VAL_BPB}" ]]; then
err "val_bpb not found in ${LOG_FILE} — experiment likely crashed (OOM or syntax error)"
err "Last 10 lines of log:"
tail -10 "${LOG_FILE}" >&2
exit 1
fi
PEAK_VRAM="${PEAK_VRAM:-N/A}"
log "Experiment complete in ${DURATION}s"
log "val_bpb=${VAL_BPB} peak_vram_mb=${PEAK_VRAM}"
# ── Structured output (stdout) ────────────────────────────────────────────────
printf "%s\t%s\t%s\t%s\n" "${VAL_BPB}" "${PEAK_VRAM}" "${DURATION}" "$(realpath "${LOG_FILE}")"
#!/usr/bin/env bash
# run-loop.sh — Autonomous autoresearch experiment loop
# Runs experiments, evaluates val_bpb, keeps improvements, reverts failures.
# Appends every run (keep/discard/crash) to results.tsv.
#
# Usage:
# bash scripts/run-loop.sh [--repo <path>] [--max <N>] [--desc "<text>"]
#
# Options:
# --repo <path> Path to autoresearch repo (default: .)
# --max <N> Max experiments to run (default: 20, 0 = unlimited)
# --desc <text> Experiment description prefix for results.tsv
#
# The loop:
# 1. Run train.py for 300 seconds
# 2. Extract val_bpb from output
# 3. Compare against current best
# 4. Keep commit if improved, git-reset if not
# 5. Append to results.tsv
# 6. Repeat
set -uo pipefail
REPO_DIR="."
MAX_EXPERIMENTS=20
DESC_PREFIX="auto"
while [[ $# -gt 0 ]]; do
case "$1" in
--repo) REPO_DIR="$2"; shift 2 ;;
--max) MAX_EXPERIMENTS="$2"; shift 2 ;;
--desc) DESC_PREFIX="$2"; shift 2 ;;
--help)
sed -n '2,17p' "$0" | sed 's/^# //'
exit 0
;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
esac
done
cd "${REPO_DIR}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
log() { echo "[run-loop] $*"; }
err() { echo "[run-loop] ERROR: $*" >&2; }
warn() { echo "[run-loop] WARN: $*" >&2; }
# ── Sanity checks ─────────────────────────────────────────────────────────────
for f in train.py results.tsv; do
if [[ ! -f "${f}" ]]; then
if [[ "${f}" == "results.tsv" ]]; then
log "Creating results.tsv..."
printf "commit\tval_bpb\tpeak_vram_mb\tstatus\tdescription\n" > results.tsv
else
err "${f} not found. Are you in the autoresearch repo? Use --repo <path>"
exit 1
fi
fi
done
if ! command -v uv &>/dev/null; then
err "uv not found. Run setup.sh first."
exit 1
fi
# ── Baseline ──────────────────────────────────────────────────────────────────
# Read best val_bpb from results.tsv (kept experiments only)
get_best_bpb() {
awk -F'\t' 'NR>1 && $4=="keep" {print $2}' results.tsv \
| sort -n | head -1
}
BEST_BPB=$(get_best_bpb)
if [[ -z "${BEST_BPB}" ]]; then
log "No kept experiments found in results.tsv — running baseline first..."
BEST_BPB="99.0" # sentinel: accept first experiment unconditionally
fi
log "Current best val_bpb: ${BEST_BPB}"
# ── Loop ──────────────────────────────────────────────────────────────────────
EXPERIMENT=0
while true; do
EXPERIMENT=$(( EXPERIMENT + 1 ))
if [[ "${MAX_EXPERIMENTS}" -gt 0 && "${EXPERIMENT}" -gt "${MAX_EXPERIMENTS}" ]]; then
log "Reached max experiments (${MAX_EXPERIMENTS}). Stopping."
break
fi
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
log "Experiment ${EXPERIMENT}/${MAX_EXPERIMENTS} | best_so_far=${BEST_BPB}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
LOG_FILE="run_${EXPERIMENT}.log"
# ── Run experiment ────────────────────────────────────────────────────────
RESULT=$(bash "${SCRIPT_DIR}/run-experiment.sh" \
--repo "." \
--log "${LOG_FILE}" 2>/dev/null) || RUN_EXIT=$?
RUN_EXIT="${RUN_EXIT:-0}"
# ── Handle crash / timeout ────────────────────────────────────────────────
if [[ "${RUN_EXIT}" -ne 0 ]]; then
COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
STATUS="crash"
BPB="crash"
VRAM="N/A"
DESCRIPTION="${DESC_PREFIX}: experiment ${EXPERIMENT} — crashed (exit ${RUN_EXIT})"
warn "Experiment crashed (exit ${RUN_EXIT}). Reverting commit..."
git reset HEAD~1 2>/dev/null || true
else
BPB=$(echo "${RESULT}" | cut -f1)
VRAM=$(echo "${RESULT}" | cut -f2)
# ── Compare and decide ────────────────────────────────────────────────
COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
if awk "BEGIN { exit !(${BPB} < ${BEST_BPB}) }"; then
STATUS="keep"
IMPROVEMENT=$(awk "BEGIN { printf \"%.4f\", ${BEST_BPB} - ${BPB} }")
BEST_BPB="${BPB}"
DESCRIPTION="${DESC_PREFIX}: experiment ${EXPERIMENT} — improved by ${IMPROVEMENT}"
log "IMPROVED val_bpb=${BPB} (delta=-${IMPROVEMENT}) KEEPING commit ${COMMIT}"
else
STATUS="discard"
DESCRIPTION="${DESC_PREFIX}: experiment ${EXPERIMENT} — no improvement (val_bpb=${BPB} >= ${BEST_BPB})"
log "NO IMPROVEMENT val_bpb=${BPB} >= best=${BEST_BPB} REVERTING..."
git reset HEAD~1 2>/dev/null || warn "git reset failed — may be at initial commit"
fi
fi
# ── Append to results.tsv ─────────────────────────────────────────────────
printf "%s\t%s\t%s\t%s\t%s\n" \
"${COMMIT}" "${BPB}" "${VRAM}" "${STATUS}" "${DESCRIPTION}" \
>> results.tsv
log "Logged: ${STATUS} | ${DESCRIPTION}"
done
# ── Summary ───────────────────────────────────────────────────────────────────
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Run loop complete"
KEPT=$(awk -F'\t' 'NR>1 && $4=="keep" {c++} END {print c+0}' results.tsv)
TOTAL=$(awk -F'\t' 'NR>1 {c++} END {print c+0}' results.tsv)
echo " Experiments run : ${EXPERIMENT}"
echo " Improvements kept: ${KEPT} / ${TOTAL} total"
echo " Best val_bpb : ${BEST_BPB}"
echo " Results : results.tsv"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
#!/usr/bin/env bash
# setup.sh — One-time autoresearch environment setup
# Installs uv, clones karpathy/autoresearch, syncs dependencies, prepares dataset
#
# Usage:
# bash setup.sh [--dir <target-dir>] [--skip-data] [--seq-len <N>]
#
# Options:
# --dir <path> Clone into this directory (default: ./autoresearch)
# --skip-data Skip uv run prepare.py (dataset download, ~2 min)
# --seq-len <N> Override MAX_SEQ_LEN in prepare.py before running it
# (useful for low-VRAM GPUs: try 256 or 512)
set -euo pipefail
TARGET_DIR="./autoresearch"
SKIP_DATA=0
SEQ_LEN=""
# ── Argument parsing ────────────────────────────────────────────────────────
while [[ $# -gt 0 ]]; do
case "$1" in
--dir) TARGET_DIR="$2"; shift 2 ;;
--skip-data) SKIP_DATA=1; shift ;;
--seq-len) SEQ_LEN="$2"; shift 2 ;;
--help)
sed -n '2,12p' "$0" | sed 's/^# //'
exit 0
;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
esac
done
log() { echo "[setup] $*"; }
err() { echo "[setup] ERROR: $*" >&2; }
warn() { echo "[setup] WARN: $*" >&2; }
# ── Step 1: Install uv if missing ───────────────────────────────────────────
if ! command -v uv &>/dev/null; then
log "Installing uv..."
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="${HOME}/.local/bin:${PATH}"
if ! command -v uv &>/dev/null; then
err "uv install succeeded but binary not found. Restart shell and retry."
exit 1
fi
log "uv installed: $(uv --version)"
else
log "uv already installed: $(uv --version)"
fi
# ── Step 2: Clone repo ──────────────────────────────────────────────────────
if [[ -d "${TARGET_DIR}/.git" ]]; then
log "Repository already exists at ${TARGET_DIR} — skipping clone"
else
log "Cloning karpathy/autoresearch into ${TARGET_DIR}..."
git clone https://github.com/karpathy/autoresearch "${TARGET_DIR}"
fi
cd "${TARGET_DIR}"
# ── Step 3: Sync dependencies ───────────────────────────────────────────────
log "Syncing dependencies (uv sync)..."
uv sync
log "Dependencies installed"
# ── Step 4: Optional MAX_SEQ_LEN override ───────────────────────────────────
if [[ -n "${SEQ_LEN}" ]]; then
warn "Overriding MAX_SEQ_LEN → ${SEQ_LEN} in prepare.py"
# Backup original
cp prepare.py prepare.py.bak
sed -i "s/MAX_SEQ_LEN\s*=\s*[0-9]*/MAX_SEQ_LEN = ${SEQ_LEN}/" prepare.py
log "MAX_SEQ_LEN set to ${SEQ_LEN} (backup: prepare.py.bak)"
fi
# ── Step 5: Prepare dataset ─────────────────────────────────────────────────
if [[ "${SKIP_DATA}" -eq 1 ]]; then
warn "Skipping data preparation (--skip-data). Run 'uv run prepare.py' manually before training."
else
log "Preparing dataset (FineWeb-Edu shards + BPE tokenizer). This takes ~2 minutes..."
uv run prepare.py
log "Dataset ready"
fi
# ── Done ────────────────────────────────────────────────────────────────────
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " autoresearch setup complete"
echo " Directory : ${TARGET_DIR}"
echo " Next step : bash scripts/run-experiment.sh"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
#!/usr/bin/env bash
# show-results.sh — Parse and display autoresearch results.tsv
# Shows statistics, best experiments, and improvement history.
#
# Usage:
# bash scripts/show-results.sh [--repo <path>] [--top <N>] [--kept-only]
#
# Options:
# --repo <path> Path to autoresearch repo (default: .)
# --top <N> Show top N experiments by val_bpb (default: 10)
# --kept-only Show only experiments that were kept
set -uo pipefail
REPO_DIR="."
TOP_N=10
KEPT_ONLY=0
while [[ $# -gt 0 ]]; do
case "$1" in
--repo) REPO_DIR="$2"; shift 2 ;;
--top) TOP_N="$2"; shift 2 ;;
--kept-only) KEPT_ONLY=1; shift ;;
--help)
sed -n '2,13p' "$0" | sed 's/^# //'
exit 0
;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
esac
done
cd "${REPO_DIR}"
if [[ ! -f "results.tsv" ]]; then
echo "results.tsv not found. Have you run any experiments yet?" >&2
exit 1
fi
# ── Overall statistics ────────────────────────────────────────────────────────
TOTAL=$(awk -F'\t' 'NR>1 {c++} END {print c+0}' results.tsv)
KEPT=$(awk -F'\t' 'NR>1 && $4=="keep" {c++} END {print c+0}' results.tsv)
DISCARD=$(awk -F'\t' 'NR>1 && $4=="discard" {c++} END {print c+0}' results.tsv)
CRASHED=$(awk -F'\t' 'NR>1 && $4=="crash" {c++} END {print c+0}' results.tsv)
BEST_BPB=$(awk -F'\t' 'NR>1 && $4=="keep" && $2~/^[0-9]/ {print $2}' results.tsv \
| sort -n | head -1)
FIRST_BPB=$(awk -F'\t' 'NR>1 && $2~/^[0-9]/ {print $2; exit}' results.tsv)
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " autoresearch — Results Summary"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
printf " Total experiments : %s\n" "${TOTAL}"
printf " Kept (improved) : %s\n" "${KEPT}"
printf " Discarded : %s\n" "${DISCARD}"
printf " Crashed : %s\n" "${CRASHED}"
echo ""
if [[ -n "${FIRST_BPB}" && -n "${BEST_BPB}" ]]; then
DELTA=$(awk "BEGIN { printf \"%.4f\", ${FIRST_BPB} - ${BEST_BPB} }")
printf " Starting val_bpb : %s\n" "${FIRST_BPB}"
printf " Best val_bpb : %s (delta: -%s)\n" "${BEST_BPB}" "${DELTA}"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# ── Top experiments ───────────────────────────────────────────────────────────
echo ""
echo "Top ${TOP_N} experiments by val_bpb:"
echo ""
printf " %-10s %-8s %-14s %-8s %s\n" "COMMIT" "VAL_BPB" "PEAK_VRAM_MB" "STATUS" "DESCRIPTION"
echo " ─────────────────────────────────────────────────────────────────"
AWK_FILTER='NR>1 && $2~/^[0-9]/'
if [[ "${KEPT_ONLY}" -eq 1 ]]; then
AWK_FILTER='NR>1 && $4=="keep" && $2~/^[0-9]/'
fi
awk -F'\t' "${AWK_FILTER} {print}" results.tsv \
| sort -t$'\t' -k2 -n \
| head -"${TOP_N}" \
| while IFS=$'\t' read -r commit bpb vram status desc; do
# Truncate description to 40 chars
short_desc="${desc:0:40}"
[[ "${#desc}" -gt 40 ]] && short_desc="${short_desc}..."
printf " %-10s %-8s %-14s %-8s %s\n" \
"${commit}" "${bpb}" "${vram}" "${status}" "${short_desc}"
done
# ── Improvement timeline ──────────────────────────────────────────────────────
echo ""
echo "Improvement timeline (kept only):"
echo ""
printf " %-10s %-8s %-14s %s\n" "COMMIT" "VAL_BPB" "DELTA" "DESCRIPTION"
echo " ─────────────────────────────────────────────────────────────────"
PREV_BPB=""
awk -F'\t' 'NR>1 && $4=="keep" && $2~/^[0-9]/ {print}' results.tsv \
| while IFS=$'\t' read -r commit bpb vram status desc; do
if [[ -z "${PREV_BPB}" ]]; then
delta="baseline"
else
delta=$(awk "BEGIN { printf \"%.4f\", ${PREV_BPB} - ${bpb} }")
delta="-${delta}"
fi
PREV_BPB="${bpb}"
short_desc="${desc:0:35}"
[[ "${#desc}" -gt 35 ]] && short_desc="${short_desc}..."
printf " %-10s %-8s %-14s %s\n" \
"${commit}" "${bpb}" "${delta}" "${short_desc}"
done
echo ""
N:autoresearch
D:Run Karpathy-style autonomous ML search on a real training repo: choose the right mode (setup, program.md, bounded loop, results interpretation, or constrained-hardware adaptation), preserve the immutable prepare.py / 300-second / val_bpb contract, and route prompt/skill eval work away to LangSmith, Promptfoo, Braintrust, or skill-autoresearch.
G:autoresearch ml-experiments autonomous-research karpathy gpu train val-bpb overnight ratcheting
U[5]:
Set up karpathy/autoresearch on a real GPU machine and verify the first baseline run
Write or refine a program.md charter with baseline, priorities, tried-already notes, and constraints
Run a bounded overnight train.py search loop with keep/revert ratcheting
Interpret results.tsv to identify real gains, repeated crashes, and next experiment families
Adapt the workflow to constrained VRAM without mutating the evaluator mid-session
S[5]{n,action,details}:
1,ChooseMode,Pick exactly one mode first: setup readiness, program.md authoring, bounded run loop, results interpretation, or constrained-hardware adaptation
2,FreezeHarness,Restate the immutable contract: human-authored program.md, train.py mutable, prepare.py read-only, TIME_BUDGET=300, val_bpb keep/revert, results.tsv append-only
3,ExecuteMode,Use the smallest relevant script or reference: check-hardware/setup, program-md-guide, run-loop, show-results, or hardware-config
4,RouteOut,Send prompt/app eval and repo-local SKILL.md optimization to LangSmith/Promptfoo/Braintrust or skill-autoresearch instead of stretching this skill
5,Summarize,Return an Autoresearch Run Brief with chosen mode, immutable rules, next command/file, and any route-outs
R[5]:
Never modify prepare.py during an active session; start a new comparison track if the evaluator must change
Never change TIME_BUDGET=300 inside the session
Never treat prompt or SKILL.md optimization as part of this ML lane
Prefer one meaningful train.py experiment at a time; clean ablations beat bundled rewrites
Keep results.tsv append-only because discarded runs still matter
E[3]{desc,in,out}:
"First 40GB GPU session","Help me run Karpathy autoresearch on a 40GB GPU","Chooses setup-readiness mode, verifies hardware/deps, runs one baseline, then points to program.md authoring"
"Weak program.md","My repo runs but my program.md just says try improvements","Chooses program.md authoring mode and rewrites the charter with baseline, priorities, and constraints"
"Wrong lane","Can autoresearch help me improve this SKILL.md with binary evals?","Routes immediately to skill-autoresearch and explains the ML-specific boundary"