ADAS — Meta Agent Search¶
Harness self-evolution. Evolve the agentic system itself — the control flow that orchestrates the model. Runs through
evolve()with a customStrategy+aggregator_factoryat L1 governance. Example:examples/adas/adas_meta_agent_search.py.
| Paper | Automated Design of Agentic Systems — Hu, Lu, Clune, 2024 (arXiv:2408.08435) |
| Repo | ShengranHu/ADAS |
| Dataset | MGSM (Multilingual Grade-School Math) |
| Layer | L1 harness (blast_radius=0.6) |
The algorithm¶
Meta Agent Search:
- Seed an archive with hand-designed building blocks (CoT, Self-Consistency, Reflexion, Debate, Step-back, Quality-Diversity, Role-Assignment).
- A meta-agent, conditioned on the entire archive (designs + fitness), proposes the next agent, then does two Reflexion refinement rounds.
- Evaluate it on the MGSM validation set; fitness = bootstrap-CI mean.
- Keep-all append to the archive; repeat. Return the best.
How it plugs into evolve()¶
strategy=AgentDesignStrategy()— a proposed agent (JSON) → aDiffon the one-slot "agentic system";renderreturns the program for the interpreter.propose— the meta-agent, conditioned on the whole archive (shared viaAdasContext), so it does not depend on the specific per-task inputevolve()hands it.aggregator_factory→MetaSearchAggregator— the keep-all archive; it scores each candidate with bootstrap-CI fitness and keeps the best design as the dev head.--select dgmswaps archive conditioning for the DGM parent-selection rule.
classify() prints L1_SLOW — a harness change is high-blast-radius.
Safety substitution (documented)¶
ADAS execs model-written Python forward() functions. To avoid arbitrary code
execution, an agent here is a composable control-flow program in a small
validated DSL (AGENT_BLOCKS) run by a safe interpreter. The Meta Agent Search
loop, the seed archive, MGSM scoring, and the keep-all archive are faithful;
only the agent substrate is a safe DSL instead of raw exec.
Plug-ins implemented¶
In examples/adas/adas_meta_agent_search.py:
| Plug-in | evolve() slot |
What it does |
|---|---|---|
AgentDesignStrategy |
strategy= |
a proposed agent (JSON) becomes a Diff on the one-slot agentic system; render returns the program for the interpreter |
MetaSearchAggregator |
aggregator_factory= |
ADAS's keep-all archive with bootstrap-CI fitness |
make_propose(...) |
propose= |
the meta-agent, conditioned on the whole archive (+ two Reflexion rounds) |
Interpreter + seed_archive |
(agent substrate) | the safe control-flow DSL (cot/cot_sc/reflexion/debate/step_back/role_assignment/ensemble) and the seven MGSM seeds |
dgm_parent_weights |
--select dgm |
DGM's sigmoid×novelty rule as an alternative archive-conditioning strategy |
Measured — MGSM with DeepSeek¶
MGSM is saturated for a strong model, so the search has no gradient without
--hard. Measured with deepseek-v4-flash over the whole benchmark — all 11
languages, 250 items each:
| pool | 2750 items |
| direct, structure-free single call | 0.919 |
| items it answers incorrectly | 222 (8.1%) |
Those 222 are what the search runs on, split 15 / 50 / 35:
The train share only triggers proposals — the meta-agent conditions on the
archive, not on the task evolve() hands it, so a generation consumes --workers
items and ignores the rest. Everything else measures. Splits are stratified by
language, because MGSM's languages differ by 8 points of baseline accuracy (en
0.964, ja 0.880) and an unstratified draw hands validation one mixture and test
another.
On a --hard subset the structure-free baseline is 0.000 by construction, so the
searched agent's test accuracy on its own says nothing. The run scores the best
hand-designed seed on the same split and reports both:
val (search) test (held out)
best hand-designed seed ?.??? ?.???
best searched design ?.??? ?.???
lift +?.??? +?.???
The lift row is not filled in yet
A run over the split above is ~2 hours and 6k–17k model calls. One has not been completed against the current code, so this page does not claim a demonstrated lift — the table is the shape of the answer, not the answer.
Give a reasoning model a real token budget¶
deepseek-v4-flash spends its budget on hidden reasoning first; visible content
is what is left. At the library default of 4096 the meta-agent returns empty
content on every call, so no design ever reaches the archive:
--max-tokens |
meta-agent replies | solver blank rate | solver CoT |
|---|---|---|---|
| 4096 | 0 / 4 | 13 / 40 | 0.275 |
| 16384 (default here) | 4 / 4 | 2 / 40 | 0.325 |
An empty completion does not raise — _extract_int("") is None and that scores
as a wrong answer, so a starved run reports a low accuracy indistinguishable from
a model that cannot do the problems. The run counts blank replies and warns, and
the pre-flight check sends a reasoning prompt and aborts if it comes back
empty. You are billed for tokens generated, not for the cap.
This is by far the most expensive example
Every candidate is scored on every validation item, and each score is a
multi-step program. Wall-clock is set by the serial chains, not by
fan-out, so past --eval-concurrency >= |val| more concurrency buys nothing:
| chain | length |
|---|---|
| seed archive | 19 sequential calls per item (7 seeds) |
propose |
3 Reflexion rounds, ~84 s |
| candidate evaluation | program_cost calls per item, candidates run one after another |
A proposed design may cost up to MAX_PROGRAM_CALLS (10) calls per question
against a seed average of 2.7, which is what dominates a generation. The
budget line reports both ends of the range for exactly this reason.
| knob | effect |
|---|---|
--generations, --workers |
candidates searched |
--hard-keep N |
caps the pool, and with it every sweep |
--eval-concurrency N |
set it to at least |val| |
--max-tokens, --timeout |
see above |
Run it¶
Point the example at your own endpoint with two environment variables — they are read at call time and never stored by the repo (full list, including Claude and local servers: Configuring your provider and key):
Inspect the setup before it costs something: --dry-run prints the requested
MGSM/runtime configuration and returns before loading data or models, so it needs
neither network nor an API key:
python -m examples.adas.adas_meta_agent_search --dry-run
python -m examples.adas.adas_meta_agent_search --select dgm --langs en,es
The settings the numbers above come from — the whole benchmark, hard subset, a split that leaves something to measure. The baseline pass is cached per (model, question), so only the first run pays for it:
python -m examples.adas.adas_meta_agent_search \
--provider openai --model deepseek-v4-flash \
--hard --langs bn,de,en,es,fr,ja,ru,sw,te,th,zh --per-lang 250 \
--generations 4 --workers 3 --eval-concurrency 128 \
--train-frac 0.15 --test-frac 0.35 --yes
Offline tests: tests/test_adas_example.py.