description: A runnable reproduction of DGM (Darwin Godel Machine): a coding agent that edits its own codebase into an open-ended archive. Measured on vendored bugs under real pytest.¶
DGM — Darwin Gödel Machine¶
Harness self-evolution. A coding agent that edits its own codebase, keeping every variant in an open-ended archive. Runs through
evolve()with a customStrategy+aggregator_factoryat L1 governance. Example:examples/dgm/dgm_self_improve.py.
| Paper | Darwin Gödel Machine — Zhang, Hu, Lu, Lange, Clune, 2025 (arXiv:2505.22954) |
| Upstream code | jennyzzt/dgm |
| Example | examples/dgm/dgm_self_improve.py |
| Domain | SWE-bench Verified instance ids against a transparent surrogate (default), or 64 vendored bugs with real pytest runs (--objective real, which the rows below used) |
| Layer | L1 harness (blast_radius=0.6) |
| Fidelity | benchmark_faithful — what the classes mean |
The algorithm¶
Faithful to DGM_outer.py:
- Keep-all archive of agents — stepping stones are retained, not just the best.
- Parent selection =
p_i ∝ sigmoid(10·(score−0.5)) · 1/(1+children_i)— favour high performers, discount already-explored parents (open-endedness). Ported exactly asdgm_parent_weightsand unit-tested. - Self-modification: a parent inspects its own eval logs, diagnoses a weakness, and adds "the next feature" to its harness → a child.
- Staged empirical validation: small=10 → medium=50 if score > 0.4 → big=140.
How it plugs into evolve()¶
strategy=HarnessStrategy()— a proposed capability → aDiffon the harness's capability set.run— the surrogate objective;reward— resolved / not.propose— inspect the failed instance, add the most-needed capability.aggregator_factory→DGMArchiveAggregator— the keep-all archive with staged eval and the sigmoid×novelty parent selection; it commits the sampled parent as the dev head, soevolve()mutates it open-endedly next round (not the greedy best).
Honesty boundary¶
DGM's real objective runs each candidate patch inside the SWE-bench Docker
harness (per-task containers, real test suites, arbitrary code execution) —
out of scope for a dependency-free example. The objective here is a transparent
surrogate (each real SWE instance has a latent required-capability set an agent
must cover), so the DGM algorithm runs and is tested offline while the scores
are simulated, not SWE-bench results. Pass a real evaluate_fn to run_dgm to
plug in the actual Docker harness.
Plug-ins implemented¶
In examples/dgm/dgm_self_improve.py:
| Plug-in | evolve() slot |
What it does |
|---|---|---|
shipped Archive(sampling="sigmoid_novelty") |
selection (seam) | sigmoid(10·(s−0.5)) × 1/(1+children) parent sampling; default of DGMArchiveAggregator, with the aggregator's own rng |
HarnessStrategy |
strategy= |
a proposed capability becomes a Diff on the harness capability set |
DGMArchiveAggregator |
aggregator_factory= |
keep-all archive + staged eval (10→50→140) + sigmoid×novelty parent selection; sets the sampled parent as the dev head |
dgm_parent_weights / choose_selfimproves |
(selection) | the exact DGM rule p_i ∝ sigmoid(10·(score−0.5)) · 1/(1+children_i). The formula is now agentdescent.selection.sigmoid_novelty_weights; examples/adas carried a byte-identical copy until it was shared |
propose + make_surrogate_evaluator |
propose= / objective |
add the most-needed capability; the transparent surrogate objective (swap in a real Docker harness via evaluate_fn) |
Measured results — vendored bugs (--objective real)¶
--objective real evolves the agent's own Python source and scores it by running
pytest. 64 vendored bugs in examples/dgm/tasks/ (32 train / 32 held-out),
16 rollouts, --staleness full, deepseek-v4-flash, one seed:
| resolve rate | |
|---|---|
| seed agent, held out | 0.844 (27 of 32) |
| best archived child | 0.906 (29 of 32) |
| archive | 3 agents: 0.906, 0.906, 0.844 |
the agent's own solve.py |
18 lines → 79 lines |
These numbers predate a fixture fix (two ordering bugs asserted on strings, whose
hashing Python randomises per process) and so carry up to ±1 task of seed luck
in each 32-task split;
test_a_bug_about_ordering_fails_on_every_hash_seed pins it against a repeat.
What it wrote for itself. The seed agent sends lib.py and the pytest output
to a model and writes the reply straight back. Its own diagnosis produced three
changes, all of them aimed at failures that actually occurred:
def _strip_code_fences(reply): # replies arrived wrapped in ``` and broke the import
if text.startswith("```"): ...
match = re.search(r"```[a-zA-Z0-9_+-]*\s*\n(.*?)```", text, re.DOTALL)
def _is_success(out): # "passed" alone is not success
return " passed" in out and " failed" not in out and " error" not in out
test_source = (task_path / "test_lib.py").read_text() # read the tests too
The fence stripper is the one that mattered: measured before the run, six of the
sixteen tasks the seed failed came back as 1 error rather than 1 failed --
the reply had been written into lib.py complete with its Markdown fence, so the
module would not import. Reading the test file is not a fix for anything that
failed; it is the agent giving itself an input the seed never had.
This is not SWE-bench
64 hand-written bugs are not 500 repository issues, and a number here cannot be compared with the paper. What this reproduces is the shape: real source, real execution, real pass/fail, and self-edits that can leave the agent worse -- or unable to run at all.
The surrogate objective remains the default, and its
limitation is worth stating precisely: it is monotone. Adding a
capability can never un-resolve a task, so a self-modification can never
regress -- which removes the reason to keep an archive. Open-ended search
retains stepping stones because a worse intermediate can lead somewhere
better, and under a monotone objective there are no worse intermediates.
Under the real objective there are: an earlier run archived children at
0.875 and 0.500, the worse one kept, which is the behaviour keep-all
exists for and which the surrogate cannot produce.
Run it¶
python -m examples.dgm.dgm_self_improve # offline (surrogate)
python -m examples.dgm.dgm_self_improve --generations 12 --archive keep_all
python -m examples.dgm.dgm_self_improve --model claude-haiku-4-5 # LLM proposes modifications
# the real objective, as measured above
python -m examples.dgm.dgm_self_improve --yes --seed 0 --objective real \
--selfimprove-size 2 --generations 9999 --budget-rollouts 16 \
--staleness full --async --async-ratio 3 --max-seconds 2700 \
--eval-concurrency 16 --model deepseek-v4-flash \
--write-agent /tmp/evolved-agent
--write-agent is not optional if you want to see what the run produced: the
ledger is a scratch git repo that evolve reaps on exit, and for a self-editing
agent that source is the result.
Offline tests: tests/test_dgm_example.py, tests/test_dgm_real_objective.py.