Run everything, and extend it¶
Install and first run covers getting set up. This page is the complete list of what you can run, the configuration reference, and how to plug your own domain into the loop.
Looking for something specific?
Module map indexes every module and its page ·
API reference has every signature ·
The evolve method is the entry point's own page.
1. Run the demos¶
The demos need a checkout, not just pip install
The examples/ directory ships with the repository, not the wheel, so
python -m examples.… requires git clone. pip install agentdescent gives
you the library only.
RQ1 — merge vs fork (synchronous DP)¶
Runs the merge-based AgentDescent loop and a DGM-style fork baseline on the same
budget, then prints the learning curve and the comparison:
round dev_acc stable commit fused stale confl oracle
0 0.828 0.000 1 1 0 0 0
3 1.000 0.000 1 0 0 1 0 ← a contradiction dropped
8 1.000 1.000 0 0 0 0 0 ← stable branch catches up
AgentDescent (merge) held-out accuracy : 1.000
Fork/archive best-fork accuracy : 0.379
merge advantage : +0.621
Async stage orchestration (FlashEvolve-style)¶
Compares the three staleness policies and sweeps the async_ratio lag budget.
RQ2 — staleness tolerance sweep¶
Evolving a directory (a skill folder a real agent reads)¶
Materialises a candidate skill directory into a throwaway workspace at
.claude/skills/csv-total/ and lets a real (sub)process agent read it. Runs
offline in seconds; --agent claude-code swaps in the real CLI agent, and
--install-to <dir> writes the evolved skill back. See
evolving a directory.
Self-evolution algorithm ports (real datasets)¶
Faithful ports of the latest skill- and harness-self-evolution algorithms — ACE,
GEPA, EvoSkill, SkillOpt, ADAS, DGM (see
the catalog). Each loads a real benchmark through
the agentdescent.dataloader data layer on a real run.
--dry-run prints the requested dataset and runtime configuration, then returns
before loading data or models: it needs no network and no API key.
python -m examples.ace.ace_context_evolution --dry-run # ACE / FiNER-139
python -m examples.gepa.gepa_prompt_evolution --dry-run # GEPA / HotpotQA
python -m examples.dgm.dgm_self_improve # DGM / SWE-bench Verified (offline surrogate)
Tests¶
pytest # the whole suite, no external services
pytest -q tests/test_async.py # just the async runtime
2. Programmatic use¶
The entry point — evolve()¶
Covered in full elsewhere, and not repeated here:
- Quickstart — a dataset to an evolved skill in one call, with the measured result of running it.
- The
evolvemethod — the entry point underneath, with every parameter and what it plugs into. - Connecting agents & LLMs — any
prompt -> textis a backend, including tool-using CLI agents. - Measured results — every empirical claim with its setup.
What a run cost¶
EvolutionResult carries the artifact and the bill. Every field below defaults
to zero and is read back with .get, so a result saved before they existed still
loads.
r = evolve(tasks, reward, agent=agent)
r.cost_summary() # one line: rollouts, wall-clock, model calls, ratios
r.time_to_quality(0.9) # seconds to the first round at >= 0.9, or None
r.cost_to_quality(0.9) # rollouts to that same round, or None
r.stale_rate() # discarded / considered -- the ratio needs both
r.duplicate_rate() # evaluation cache hit rate
| what | field |
|---|---|
| time | wallclock, and RoundInfo.elapsed_s per round |
| work | rollouts, rollout_seconds (a sum across workers, so it exceeds wallclock when they overlap), eval_seconds |
| model | usage.calls / .seconds / .failures |
| staleness | stale_considered, stale_discarded |
| recovery | redispatched, duplicates_dropped |
| recomputation | cache_hits, cache_misses |
| sandboxes | sandbox_wait_s, sandbox_setup_s, sandboxes_created / _reused / _failures |
Two things worth knowing before reading these numbers:
usage.callscounts actor invocations — onerunorpropose— not provider requests. Acli_agentrollout is one call here and many requests to the model.- Token counts need a shared
Usage.runis(rendered, task) -> str, so an opaque actor cannot report tokens. Pass the same object to both and they accumulate together:
from agentdescent import Usage
u = Usage()
r = evolve(tasks, reward, agent=LLMAgent(claude(usage=u)), usage=u)
print(r.usage.total_tokens, r.usage.estimated_cost(3.0, 15.0))
The sandbox fields are zero on the default path: one throwaway workspace per rollout, nothing to queue for and no image to warm. They exist so that when a pool does, "8 workers only bought 2x" can be attributed rather than guessed at.
The reference stack — AgentDescent / AsyncAgentDescent¶
A separate runtime used by the RQ1/RQ2 and efficiency experiments on the
built-in synthetic router domain. It has the TaskScheduler / EvidenceBuffer /
duration-estimator machinery that evolve() does not — see the
two-stack note. Reach for it to reproduce
those experiments, not to evolve your own artifact.
import tempfile
from agentdescent.domains.router import make_task_universe
from agentdescent import AgentDescent
universe = make_task_universe(seed=7)
with tempfile.TemporaryDirectory() as repo:
system = AgentDescent(repo, universe, n_workers=6, noise=0.15, seed=1)
history = system.run(rounds=40)
print(system.final_accuracy()) # held-out accuracy on the dev branch
import tempfile
from agentdescent import AsyncAgentDescent, AsyncConfig
from agentdescent.domains.router import make_task_universe
from agentdescent import get_policy
universe = make_task_universe(seed=7)
cfg = AsyncConfig(n_workers=6, async_ratio=4, target_accuracy=0.98, max_seconds=15.0)
with tempfile.TemporaryDirectory() as repo:
system = AsyncAgentDescent(repo, universe, config=cfg,
staleness_policy=get_policy("reflective"))
stats = system.run()
print(stats.final_dev_accuracy, stats.commits, stats.discarded_stale)
3. Configuration reference¶
AggregatorConfig (aggregator)¶
| Field | Default | Meaning |
|---|---|---|
batch_trigger |
4 | B: fire a bucket once it holds this many cards |
max_wait_rounds |
3 | T_max: fire a cold bucket after this many sweeps |
base_delta |
0.5 | acceptance risk; threshold is 1 − δ, annealed by version |
alpha_head |
5 | staleness tolerance α for hot artifacts |
alpha_tail |
1 | staleness tolerance α for cold artifacts |
trust_region_ops |
6 | max edits per diff (trust region) |
trust_region_chars |
32 000 | max characters in one op's value — for a FileTree that is a per-file cap, and TreeSpec.max_file_bytes must stay under it |
anneal_half_life |
64 | versions over which the acceptance risk decays |
promote_after_k |
3 | dev→stable survival rounds (EMA) |
AsyncConfig (async)¶
| Field | Default | Meaning |
|---|---|---|
n_workers |
6 | worker threads |
async_ratio |
3 | ROLL Flash lag budget: max head-drift before a worker refreshes |
noise |
0.15 | per-op probability that a noisy worker proposes a wrong label (every third worker is noisy; the rest are clean) |
target_accuracy |
0.98 | stop early when the dev branch reaches this |
max_seconds |
20.0 | wall-clock safety bound |
aggregator_interval |
0.002 | sleep between aggregator sweeps |
worker_pause |
0.001 | sleep between worker rollouts |
oracle_budget |
400 | oracle calls the AuditScheduler may spend |
stall_patience |
150 | no-commit sweeps before a backpressure sync |
AsyncStats.error is None on a clean run and carries the backend failure that ended it otherwise — check it, since a run whose workers all died otherwise returns normal-looking zeros.
Staleness policy (staleness)¶
from agentdescent import get_policy
get_policy("full") # accept stale diffs directly
get_policy("guarded") # version-gated (default)
get_policy("reflective") # always rebase + re-verify
4. Plug in your own domain¶
Most callers never need this
A strategy over a flat {key: value} state covers nearly
everything — including a whole directory. Implement
Evolvable yourself only when your artifact genuinely is not
that shape; domains/router.py is the worked example.
The whole framework is domain-agnostic: what evolves is decided by registration, not hard-coded. To evolve something new, provide four things.
4.1 An Evolvable¶
Implement the protocol from agentdescent/evolvable.py
(reference: RouterSkill):
from agentdescent import Contract, Diff, EvidenceCard
class MyArtifact:
def __init__(self, id, state, version=1, blast_radius=0.2):
self.id = id
self.version = version
self.blast_radius = blast_radius # → auto L0/L1/L2 layering
self.contract = Contract(major=1)
self.state = state
def diff(self, other) -> Diff: ... # difference to another instance
def apply(self, diff: Diff) -> "MyArtifact": # return a NEW instance, version+1
...
def cheap_eval(self, evidence: EvidenceCard) -> float:
# score on the tasks the evidence card carries (used by rebase re-verify)
...
def full_eval(self, task_set) -> dict: # ground-truth metrics
...
apply must be pure
apply returns a new instance with version + 1; it never mutates
self. The aggregator relies on this to test candidates without side
effects.
4.2 Serialize / deserialize for the Ledger¶
The Ledger stores artifacts as JSON blobs in git, so give it two functions:
def serialize_mine(a) -> dict: # → JSON-friendly dict
return {"state": a.state, "blast_radius": a.blast_radius}
def deserialize_mine(artifact_id, version, state) -> MyArtifact:
return MyArtifact(artifact_id, state["state"], version, state["blast_radius"])
ledger = Ledger(repo_path, serialize_mine, deserialize_mine)
ledger.register(MyArtifact("my-art", initial_state))
4.3 An eval function for the verifier¶
Ground-truth scorer over a held-out task set:
def my_eval(artifact, tasks) -> float: # accuracy / reward in [0, 1]
...
verifier = ThreeLayerVerifier(eval_fn=my_eval, held_out=held_out_tasks)
4.4 A worker that proposes diffs¶
Workers turn observed failures into a Diff + EvidenceCard. The reference
Worker (agentdescent/worker.py) is a deterministic corrector; in a real system
this is where an LLM reflects on a trajectory and proposes an edit. Emit a card
with the base_version you read, the touched artifacts, and a local
before_after_delta, then aggregator.ingest(card).
Once these four pieces exist, everything else — the Ledger, the aggregator, the schedulers, the governance layers, both runtimes, and all three parallel paradigms — works unchanged.
5. Building the documentation site¶
These docs render as a website via MkDocs: