HACO (Human–AI Co-discovery) is an AI co-scientist that discovered a state-of-the-art algorithm for crystal structure prediction (CSP). The key idea is co-discovery: rather than fully automating research, HACO works together with a human expert. It searches generative paradigms and architectures across fields, transfers a promising one into CSP, and refines it under sparse human guidance to discover new algorithms (e.g., MaskGXT). See the blog post for the full story.
Mechanically, it is an autonomous loop that searches for the best train.py,
running indefinitely if no human intervenes. To intervene, a human injects a prompt
into the Orchestrator, the sole interface for human steering, which routes the
intervention into the operator it dispatches. Interventions took two forms:
- Objective level — give the agent a new goal it wasn't chasing, and let it figure out how (e.g., "recover sub-bin coordinate precision").
- Mechanism level — inject domain knowledge the agent missed (e.g., "one composition has many polymorphs, but i.i.d. sampling misses them — spread the draws across them").
A prompt may cite a paper reference, or supply auxiliary information the human prepared by hand (e.g., data preprocessing).
Tree search over a git DAG of train.py variants. idea+draft open new
(paradigm, architecture) branches, improve refines, debug repairs a crash.
Sparse human interventions inject objective / domain mechanisms.
Validation METRe across search trials. Blue: autonomous agent steps. Orange: sparse human interventions.
Explore the full search tree interactively at kiyoung98.github.io/HACO.
- Task — given a composition, generate a crystal structure (lattice + fractional coordinates).
- Data — MP-20 polymorph split (
data/mp20_ps_{train,val,test}.pt): polymorphs of one composition stay on the same split. The search scores onval;testis held out. - Metric — METRe (arXiv:2509.12178): the fraction of references matched by generated crystals. Higher is better.
- Orchestrator (LLM) — the loop: decides operator / parent / strategy, calls
scripts/*.sh, dispatches subagents, commits each result. Never writes code. - 6 subagents
idea— pick a (paradigm, architecture) pairdraft— instantiatetrain.pyimprove— one changedebug— fix a crashsmoke— pre-run checkanalyze— judge the run
- Memory = git — every node is a commit under
agent/<run_tag>/*;git logis the whole state. Heavy artifacts live inruns/<run_tag>/<sha>/.
Repo layout (tracked files only):
HACO/
├── program.md the orchestrator loop
├── contract.md shared rules read by every subagent
├── prepare.py one-time data setup → data/mp20_ps_{train,val,test}.pt
├── evaluate.py METRe score function
├── csp_methods.md novelty boundary read by the idea subagent
├── requirements.txt Python dependency
├── scripts/ bootstrap + parallel-slot plumbing (bash)
│ ├── bootstrap.sh build base venv (uv), prepare data, detect GPUs, write campaign.json
│ └── slot_*.sh register / run / parse / reconcile / finalize a run slot
├── .claude/agents/ the 6 subagent prompts
│ ├── idea.md draft.md improve.md
│ └── debug.md smoke.md analyze.md
└── assets/ README figures
1. Bootstrap — human, in the shell: clone, branch a fresh agent/root
trunk off main, then bootstrap (clean tree required).
git clone https://github.com/kiyoung98/HACO && cd HACO
git checkout -b agent/root
scripts/bootstrap.sh run_tag=<run_tag> [gpus=all] [train_minutes=120] [sample_minutes=60] [smoke_seconds=120] [noise_sigma=0.004]
Only run_tag is required; omitted arguments use their defaults (gpus=all,
train_minutes=120, sample_minutes=60, smoke_seconds=120, noise_sigma=0.004).
noise_sigma is the run-to-run standard deviation of METRe across seeds for an
unchanged train.py.
2. Launch — start Claude Code with --dangerously-skip-permissions in tmux
and send:
You are the orchestrator of the HACO campaign run_tag=<run_tag>.
Read program.md, then run the loop.
@misc{seong2026haco,
title={Discovering Crystal Structure Prediction Algorithms with an AI Co-Scientist},
author={Kiyoung Seong and Nayoung Kim and Sungsoo Ahn},
year={2026},
eprint={2606.22866},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2606.22866},
}MIT (see LICENSE). Portions of evaluate.py and prepare.py are
derived from the OMatG project (MIT); see
the attribution comments in those files.

