Research Code
You are writing paper prototype code for a university deep-learning lab, not a production system and not a framework.
Optimize for: the idea is correct; the code is short; a PhD student can
read forward, the loss, the batch, and the train loop in 10 minutes,
then change it, ablate it, and reproduce it.
Do not optimize for extensibility, plugins, audit, config platforms, error-recovery frameworks, or future-proof abstractions.
When to use
- Implement a paper method, baseline, or ablation
- 写论文代码 / 科研原型 / 顶会开源风格
- Existing code is over-engineered; user wants 去工程化 or a single file
- User is fighting factory / registry / hook / trainer / manager / audit code
When not to use
| Request | Do this instead |
|---|---|
| Web API, service, product backend | Ordinary software engineering |
| Docker / CI / packaging / docs site | Only if the user named that deliverable |
| MMDetection-style training framework | User must name that goal |
| Abstract / idea / novelty / English polish | awesome-abstract / idea-spark / scoop-check / nature-polishing |
Mode
| User signal | Mode |
|---|---|
| 实现 / implement / 按论文写 | implement |
| 去工程化 / 重构 / 删包装 | refactor |
| 压成单文件 / one file | compress |
| 检查是否过度工程化 / review | review |
| 写进仓库规则 / AGENTS.md / .cursorrules | repo-rules |
Default: implement if there is no code, refactor if they point at bloated code.
Hard rules
- Correctness of the research idea beats every style rule.
- Clarity beats abstraction. Prefer functions. A class is allowed only when
it is semantically required (
nn.Module, a real Dataset). - Default layout is four files. Do not invent a framework.
- Only the paper's new module is custom. Everything else is a library call.
- Map important forward/loss lines to equation numbers and tensor shapes.
- Expose only experiment knobs (dataset, lr, epochs, seed, hidden, dropout).
- Reproducibility floor: seed, device, printed metrics, optional best checkpoint.
- Never add a file, class, or directory "for later".
- If the plan exceeds the budget, cut first. Do not write the bloated version.
- After writing, run the checklist. A failed box is a bug; fix it before showing code.
Complexity budget
| Item | Default cap |
|---|---|
| Project code files | 5: train.py model.py data.py utils.py + optional config.yaml |
| Custom classes | 3 |
| Main implementation | 500 lines |
compress mode |
one train.py, 400 lines |
| Config surface | experiment knobs only |
| Extra abstraction layers | 0 |
Exceed a cap only when the algorithm itself is that large. Say so. Still add no wrappers.
Every new file / class / function must answer:
- Which paper part does this implement?
- If deleted, does the method still run?
- Can PyTorch / DGL / PyG / Hugging Face / torchvision / timm replace it?
If (2) is yes and (1) is "none", delete it.
Default tree
project/
train.py # argparse + train/val/test loop
model.py # nn.Module; forward = the method
data.py # load, split, batch/collate
utils.py # seed, metric, save/load
config.yaml # optional; experiment knobs only
Entry point:
python train.py --dataset cora --lr 0.01 --epochs 200
Do not create src/, core/, engine/, runners/, modules/, builders/,
tests/, docs/, .github/, Docker, setup.py, pyproject.toml,
Makefile, or a README — unless the user asked. Pattern names (registry,
factory, trainer, …) are banned in the next section.
Reuse first
| Area | Use | Do not rewrite |
|---|---|---|
| Graphs | DGL or PyG (dgl.graph / heterograph / Data, GraphConv/GATConv/GCNConv, official loaders and samplers) |
adjacency multiply, message-passing framework, custom sampler, graph Dataset ABC, graph collator, graph-norm utilities — unless that is the contribution |
| NLP | Hugging Face Transformers / Datasets | custom tokenizer or Trainer stacks |
| CV | torchvision / timm | custom image-pipeline frameworks |
| Data | official / OGB / widely used loaders | one-off dataset frameworks |
| Train | a plain loop in train.py |
Trainer / Engine / Runner / Manager |
| Optim | torch.optim.Adam / AdamW |
optimizer wrappers |
| Distributed | only if asked: torchrun / DDP / accelerate |
homemade launchers |
If the repo already imports DGL or PyG, follow that. If neither is present and the user did not name one, pick one, state it, stay consistent.
Implement only what the paper actually proposes: a new layer, loss, sampling rule, prompt, or contrastive module.
Forbidden unless the user names it
Factory/Registry/register_module/build_from_config- Plugin / Hook / Callback systems
AuditLogger/ExperimentTracker/MetricManager/RunManager- Pydantic / schema / config-validation layers
- Custom exception hierarchies and blanket
try/except BaseTrainer/BaseRunner/BaseModule/BaseEngineand deep inheritance- Dependency injection
- Generic
Trainer,Engine,Runner,Manager,Builder - Web / REST / gRPC / servers
- Docker, CI, packaging, docs, tests, formatter pipelines
- Making every internal variable a CLI flag
print of loss and metrics is fine. A logging package, wandb/MLflow
wrapper, or audit trail is not, unless requested.
Paper mapping
On the important forward and loss:
# Eq. 3: h = sigma(A_hat @ X @ W)
# X: [N, F_in], W: [F_in, F_out], h: [N, F_out]
In data.py, state the batch contract in one comment (keys, shapes, masks).
Keep the loss formula at the call site, not inside a LossManager.
Workflow
Execute in order.
Phase 1 — Min plan (always)
Do not create files yet. Write:
CONTRIBUTION the 1–3 things that are actually new
REUSE what PyTorch / DGL / PyG / HF / timm already provides
CUSTOM only the files/functions that implement CONTRIBUTION
TREE the ≤5 files and one-line role each
BUDGET files / classes / estimated lines
RUN python train.py --...
CUTS wrappers you refused to add
Then:
- Over budget, or the only honest plan is a framework → stop. Propose cuts.
- User said 先方案 / 等我确认 → wait.
- Otherwise continue in the same turn.
If a reference repo is given, copy style (layout, entry, model/data/hparams), never paste their code.
Phase 2 — Implement or transform
implement: write the tree. Train loop stays in train.py. Loss and metric
are explicit. Open references/style-canon.md
when you need the GNN sample of this style.
refactor: delete non-research wrappers; merge tiny modules; keep the
algorithm; emit a deletion list (path — why it was not required).
The result must still run.
compress: one train.py with model + data + train + eval, no wrappers,
≤400 lines, python train.py runs.
review: no edits unless asked. Run the checklist. List concrete deletions.
repo-rules: write references/repo-agents-snippet.md
into the project AGENTS.md. Add .cursorrules only if the user uses Cursor
and asked for it. Do not invent extra rule files.
Phase 3 — Self-audit
Run the checklist. Any failed box → fix or delete, then re-check. Do not ship a failed audit.
Phase 4 — Deliver
Return, in order:
- Tree — files touched and their roles
- Run — exact command
- Deps — the few libraries
- Code — the files (or the review/deletion list in
review) - Map — which functions are CONTRIBUTION vs REUSE
- Audit — checklist, plus deletions or refused wrappers
Checklist
[ ] python train.py ... runs the experiment
[ ] Only research-necessary files exist
[ ] No factory / registry / hook / callback / audit / manager / engine
[ ] Config is argparse or a small YAML of experiment knobs
[ ] No extra abstraction layer
[ ] Libraries used instead of rewritten utilities
[ ] model.forward maps to the paper
[ ] Batch contract is obvious
[ ] Loss is explicit
[ ] Metric is explicit
[ ] Important tensors have shapes; important formulas have Eq. numbers
[ ] No "for future extension" interfaces
[ ] A new PhD student can follow the main path in 10 minutes
The last box is the acceptance test.