Stop hand-tuning your agent. From ACE's evolving playbooks to MCE's bi-level skill evolution, Meta-Harness's Pareto search over harness code, and AFlow's MCTS over workflow graphs — this is the ladder that climbs from prompts to optimizer code, one rung at a time.
You shipped a coding agent last month. You gave it a harness — tools, a loop, file-system memory, the works from the previous lesson — and it is genuinely useful. It is also failing about 30% of its tasks. You have one week to make it better. Where, exactly, do you spend that week?
Sit with the options, because they are not one option. You could rewrite the system prompt — sharper instructions, better examples. You could restructure what goes into the context window — what gets loaded, what gets summarized, what gets thrown away. You could redesign the workflow — maybe a plan step before the code step, maybe a review agent after it. You could rewrite the harness code itself — the actual program that decides what the model sees and does. Or — and this is the strange one — you could write code that rewrites the harness for you, and spend the week building the optimizer instead of the thing being optimized.
Those five doors are not just five chores. They form a sequence, and Weng (2026) states it in one line that this whole lesson unpacks: the progression in the object being optimized in a harness system is roughly instruction prompts → structured context → workflow → harness code → optimizer code. Read it left to right and two things grow at once: the complexity of the thing being tuned, and the generality of the method doing the tuning. As models become more intelligent and powerful, we move toward more complex targets and more generic methods.
Take the rungs one at a time. Rung 1: instruction prompts. The object is a string — the words handed to the model. "Optimizing" here means rewriting that string: hand-tuning, few-shot examples, or automated prompt search (systems like Promptbreeder evolve prompts with an LLM proposing mutations). This is the world of prompt engineering, and it was the frontier in 2022. The method is narrow because the object is small: you can only rearrange words.
Rung 2: structured context. The object grows from a string into a living document — a structured playbook of what the agent has learned, curated across many runs. Optimizing means deciding what enters, what merges, what gets deduplicated. This is where ACE and MCE live (Chapters 2–3). Notice the shift: nobody rewrites one prompt anymore; the system maintains a knowledge object that outlasts any single prompt.
Rung 3: workflow. Now the object is the shape of the run itself — which steps happen, in what order, with which checks. A workflow can be handcrafted by domain experts (AI Scientist, ScientistOne, Autodata — Chapter 5) or searched by algorithms (ADAS's meta-agent, AFlow's tree search — Chapters 6–7). The method gets more generic here: search algorithms do not care what domain the workflow serves.
Rungs 4 and 5: harness code, then optimizer code. At rung 4 the object is the repository — the actual program that orchestrates the model. Meta-Harness (Chapter 4) already stands here: a coding agent proposes whole new harnesses as code. At rung 5 the object is the optimizer itself — the loop that improves the improver — and that is the territory of the next lesson, Self-Improving Harnesses. Be honest about the boundary: it is soft. Meta-Harness optimizes harness code using an optimizer that is itself a harness, so it has one foot on each of the top rungs. The ladder is a map, not a fence.
Why does the industry climb at all? Because each rung has a capability threshold — the same theme you met in the previous lesson, where harness patterns only paid off once the model could exploit them. A weak model cannot rewrite harness code safely; hand it the repo and it will break the loop that feeds it. A strong model can grep through its own execution history, propose a code change, and evaluate the result. So as models strengthen, rungs that used to be reckless become tractable, and the community's energy visibly migrates upward: prompts were the game in 2022, structured context by 2023–24, workflow search through 2025, and harness-code optimization is the frontier of 2026. Higher rungs are not better — they are later, unlocked by capability.
And what does "more generic methods" mean, concretely? A prompt trick is bespoke: it works for this task, this model, this phrasing. A search algorithm is generic: MCTS does not know whether it is searching game moves or workflow graphs; a Pareto filter does not care what the two axes measure. As the optimized object becomes richer, the winning methods become less domain-specific, not more — because at rungs 3 and above the object is code, and code has universal structure that general-purpose search can grip.
Here is the whole ladder at a glance — keep this table in your pocket; every later chapter fills in one row:
| Rung | Object optimized | What "optimizing" means | Methods | Era |
|---|---|---|---|---|
| 1 · Instruction prompts | a prompt string | rewrite the words | hand-tuning, few-shot, Promptbreeder | 2022 |
| 2 · Structured context | a living playbook | curate what enters, merges, dedups | ACE, MCE | 2023–24 |
| 3 · Workflow | the shape of the run | rearrange stages, checks, retries | AI Scientist (hand), ADAS, AFlow (search) | 2024–25 |
| 4 · Harness code | the orchestrating repo | rewrite the program itself | Meta-Harness | 2025–26 |
| 5 · Optimizer code | the improver loop | improve the thing that improves | STOP-style (next lesson) | 2026→ |
The ladder also answers the opening question — where to spend your week — as a decision procedure. Read your failures first. If the agent misunderstands instructions, you are at rung 1: cheap, fast, do it today. If it forgets what it learned last run, rung 2: your context has no memory discipline. If it does the right steps in the wrong order, or never checks its work, rung 3: workflow. Only when the same class of fix keeps recurring across many tasks does it pay to climb to rung 4 and change the harness code — and only when you are making that kind of change weekly does rung 5, automating the changes themselves, earn its complexity. Climb no higher than your failure mode demands.
This lesson walks rungs 2 and 3 end to end — context engineering, then workflow design — with a working detour onto rung 4 for Meta-Harness. By Chapter 8 you will run an actual workflow search yourself, three strategies racing on one toy task, and you will see with your own eyes why tree statistics beat greedy hill-climbing when evaluations are noisy. Click the rungs below to start building the map.
Each rung shows the object being optimized, the methods that optimize it, and a capability gauge — how strong the model must be before this rung becomes tractable. Press Play the climb to watch the industry's migration 2022→2026.
Before any clever machinery, feel the problem it solves. The naive way to run an agent is to append everything: every tool response, every model generation, straight into the context, turn after turn. For a five-turn task this is fine. But agentic jobs are getting long — and as the job horizon increases significantly, appended context grows out of control.
Put numbers on it. A serious agentic rollout — debug a repo, run experiments, fix what breaks — can easily run 100 turns. Each turn adds a tool response plus a generation, call it ~2,000 tokens. That is 100 × 2,000 = 200,000 tokens of raw transcript, and the job is not even exotic. Three things now go wrong at once. First, you exceed the window the model was actually trained to use well, so attention over the early material degrades — the model starts missing things it "read." Second, cost balloons: every new turn re-pays for the entire history. Third, the signal drowns: the one insight that matters from turn 12 is buried under 88 turns of scaffolding noise.
The cost story is worse than it first looks, because context is re-paid. Turn t doesn't just add 2k tokens — it re-sends all t × 2k accumulated tokens as input. Total input tokens over n turns is 2,000 × (1 + 2 + … + n) = 2,000 × n(n+1)/2. For n = 100: 2,000 × 5,050 = 10.1 million tokens billed for a job whose final transcript is 200k. Appending is not linear-cost; it is quadratic:
| Turns | Window at the end | Cumulative tokens billed | Ratio billed/window |
|---|---|---|---|
| 10 | 20k | 2,000 × 55 = 110k | 5.5× |
| 50 | 100k | 2,000 × 1,275 = 2.55M | 25.5× |
| 100 | 200k | 2,000 × 5,050 = 10.1M | 50.5× |
Context management is the layer that fights this: it constructs a more structured and concise context for the LLM and manages persistent state, instead of letting the transcript pile up raw. You met the storage half in the previous lesson — the file system as persistent memory. This chapter is about the harder half: what earns a place inside the window at any given moment. In code, the difference is two data structures:
python # naive: the transcript IS the context — grows without bound context = [] def naive_turn(msg, tool_out): context.append(msg); context.append(tool_out) # 100 turns → 200k tokens return llm("\n".join(context)) # managed: a bounded, structured playbook + this turn's working set playbook = {} # {identifier: description} def managed_turn(msg, tool_out): working = select_relevant(playbook, msg, k=12) # what EARNS the window reply = llm(render(working) + msg + tool_out) return reply # persistent state lives in files
And it is not only money. A longer input is slower to process, so each turn's latency grows with the transcript; and models empirically attend unevenly across very long inputs — material buried in the middle of a huge window is recalled worse than material near either end. So the insight from turn 12, sitting mid-transcript at turn 90, is in the worst possible seat: paid for on every call, and least likely to be used.
A fair objection: "long-context research will fix this." Partly true — long-context modeling keeps making real progress, and every improvement raises the ceiling. But Weng (2026) is careful here: at the moment, long-context intelligence and context engineering intertwine. Better native long context means the model can hold more; engineering decides what deserves to be held at any budget. A bigger backpack does not pack itself. Even a 10-million-token window would leave the question of which 10 million tokens — and at what cost per step.
So the obvious fix is compression: every so often, have the model rewrite its own context into a shorter summary. Try it, and after a few cycles you meet the two demons this chapter is named for. Here is a true-to-life miniature. On turn 9 of a debugging session your agent earns a hard-won, precise insight: "the flaky test only fails when the CI machine is in UTC+9, because the date-rollover assertion runs at local midnight." The context gets summarized. Rewrite one: "the flaky test is timezone-dependent (UTC+9, midnight rollover)." Rewrite two: "there are timezone-related test issues." Rewrite three: "timezone issues exist." Every rewrite was locally reasonable. The load-bearing detail — which timezone, why midnight — is gone, and the agent will re-derive it from scratch next week, paying the full debugging cost again.
Lay the decay out as a table and you can see exactly which load-bearing atom vanishes at each step:
| Rewrite | What the context says | What just got lost |
|---|---|---|
| 0 (original) | "flaky test fails only when CI is in UTC+9 — the date-rollover assertion runs at local midnight" | — |
| 1 | "flaky test is timezone-dependent (UTC+9, midnight rollover)" | the causal mechanism — why midnight matters |
| 2 | "there are timezone-related test issues" | which timezone; which test; the trigger condition |
| 3 | "timezone issues exist" | everything actionable — a week of debugging, gone |
Name the two demons precisely, because ACE is built to fight exactly them. Context collapse is the loss of hard-won detail under iterative rewriting: each summarize step is lossy, and losses compound multiplicatively across cycles. Brevity bias is the direction of the loss: a model asked to "summarize" is being asked to shorten, so every rewrite drifts toward the vague and the general. Collapse says information disappears; brevity bias says it disappears toward mush. The UTC+9 story shows both at once — the fact decayed (collapse) and what survived got vaguer at every step (brevity bias).
Worse, the two demons feed each other. Collapse deletes the specific anchors (identifiers, numbers, conditions) that would have kept the next rewrite honest; brevity bias fills the vacated space with safe generalities that compress even further next time. Each cycle leaves the summarizer less to hold onto and more license to generalize — the decay accelerates. That is why the fidelity curve you are about to see bends downward instead of falling in a straight line.
A fair follow-up: what happens when the itemized playbook itself grows to thousands of items? Then it no longer all fits the window either — and the answer is the select_relevant line in the managed-turn code above: retrieve the top-k items relevant to this task, leave the rest safely on disk.
Itemization does not just stop decay; it makes retrieval possible at all, because discrete addressable items can be indexed and fetched, while a prose blob can only be included or excluded whole.
That last sentence is the entire preview of Chapter 2. If context is a single blob, every update must rewrite the whole blob, and every rewrite risks every item in it. If context is an itemized list — discrete entries with stable identifiers — then an update touches only the items it names, and the untouched 95% cannot decay, because nothing ever re-generates it. The playbook survives not because the model summarizes better, but because the update rule is structurally incapable of collapsing what it does not touch.
Play with the decay below. Drag the rewrite-cycles slider and watch seven real debugging insights pass through repeated monolithic rewrites — each item fades to vagueness, then vanishes, and the fidelity curve falls. Then flip to itemized mode and drag again: items persist untouched, new ones append, and the curve holds flat. The difference is not model quality. It is the update rule.
Seven hard-won insights sit in context. Drag rewrite cycles to pass them through repeated summarize steps. In monolithic mode items grey out (vague) then vanish (lost). In itemized mode items are edited individually — nothing untouched can decay, and new items append in green.
Chapter 1 ended with a promise: keep context as discrete items and the decay demons lose their grip. Agentic Context Engineering (ACE; Zhang et al. 2025, ICLR 2026) is that promise built out into a full system.
Its reframe is worth saying slowly: context is not an ever-lengthening prompt. It is an evolving playbook — a curated collection of bullet points, each carrying an identifier (a stable, unique name like pandas-groupby-copy) and a description (the actual insight).
The identifier is the item's address; the description is what lives there.
ACE maintains this one playbook with three components, and each is a distinct role with a distinct job. Think of a sports team between games: players play, an analyst watches the tape, and an editor updates the playbook binder. Nobody does all three, because each job corrupts the others when merged.
The Generator is the player. It produces task trajectories — actual attempts at actual tasks — and it does so with reference to the bullet points: the current playbook is in its context, so hard-won insights from past runs shape present behavior. This closes the loop that makes the playbook worth maintaining. A playbook nobody reads is a diary; a playbook the Generator consults every run is institutional memory.
The Generator's trajectories are raw material, not knowledge. A 60-turn trajectory contains maybe two sentences worth of reusable insight, buried in tool noise. Extracting them is the Reflector's job: it distills insights from trajectories — and, critically, from successful and failed trajectories alike. The failures matter as much as the wins, and it is worth pausing on why. A success tells you one path that worked; a failure tells you a whole region that does not, and often why it does not. "The retry loop must cap at 3 because the API rate-limits at 4" is a failure-born insight no success would ever surface. A Reflector that only mines wins builds a playbook of lucky paths; one that mines losses builds a map of the minefield.
Then comes the role where ACE's real design lives: the Curator updates the structured context with incremental, itemized entries. Note the two adjectives. Incremental: it adds and edits; it does not regenerate. Itemized: its output is a set of (identifier, description) bullets, not prose. The Curator is an editor with a strict format, not an author with a blank page.
| Role | Consumes | Produces | Why it must be separate |
|---|---|---|---|
| Generator | task + current playbook | task trajectories | doing the work would bias what gets recorded about the work |
| Reflector | trajectories (✓ and ✗) | candidate insights | distilling needs distance — the actor's context is full of self-justification |
| Curator | insights as (id, desc) deltas | merged playbook | state mutation must be boring and deterministic, never creative |
Why does deterministic merging beat asking a strong LLM to "produce the updated playbook"? Because a deterministic merge cannot collapse or vague-ify what it does not touch — not "is unlikely to," but cannot, structurally. An LLM rewriting the whole playbook re-generates every item, and every re-generation is a fresh roll of the brevity-bias dice from Chapter 1. The merge function never rolls those dice: an item it was not handed passes through as inert bytes. ACE moves the risky, creative work (distilling insights) into the Reflector, and makes the state-mutation step boring on purpose. Boring is what survives ten thousand cycles.
Items still need hygiene — a playbook that only ever appends will bloat with near-duplicates ("cap retries at 3" and "don't retry more than 3 times" from different runs). So ACE refines and deduplicates the context items periodically: an occasional, deliberate maintenance pass (embedding similarity catches near-duplicate descriptions), not a rewrite on every update. The dangerous operation happens rarely and in a controlled way, instead of constantly and incidentally.
Let's make the whole cycle concrete with a worked example. Suppose the Generator runs three trajectories on data-engineering tasks:
| Trajectory | Outcome | What the Reflector distills |
|---|---|---|
| T1: fix a pandas pipeline | ✓ success after a bug | (pandas-groupby-copy, "groupby().apply() returns a copy — mutations inside apply are silently lost; assign the result back") |
| T2: batch-call an API | ✗ failed twice, then passed | (api-retry-cap, "the vendor API rate-limits at 4 req/s; cap retries at 3 with exponential backoff") |
| T3: another pandas task | ✓ success | (pandas-groupby-copy, "...assign the result back; also true for transform on categorical columns") |
The Curator's merge is now pure mechanics. After T1: playbook gains pandas-groupby-copy. After T2: api-retry-cap appends — and note it was born from a failure.
After T3: the Reflector emitted an existing identifier, so the merge updates that one entry with the enriched description — the categorical-columns detail is folded in — while api-retry-cap is not re-generated, not re-summarized, not even read.
Three trajectories, two items, zero collateral damage. Trace the playbook state itself:
python # playbook state after each trajectory — the merge trace after_T1 = { "pandas-groupby-copy": "groupby().apply() returns a copy; assign result back", } after_T2 = { "pandas-groupby-copy": "groupby().apply() returns a copy; assign result back", "api-retry-cap": "vendor rate-limits at 4 req/s; cap retries at 3 + backoff", } after_T3 = { # same id arrived → THAT entry updates; the other is untouched bytes "pandas-groupby-copy": "...assign result back; also true for transform " "on categorical columns", # ← enriched "api-retry-cap": "vendor rate-limits at 4 req/s; cap retries at 3 + backoff", } # ← byte-identical
And here is the update step as real code:
python def reflect(trajectory, llm): # The ONLY generative step: distill (id, desc) deltas from a trajectory. # Works on successes AND failures -- failures map the minefield. prompt = ("Extract reusable insights from this trajectory as JSON " "[{identifier, description}]. Include lessons from failures.\n" + trajectory.render()) return json.loads(llm(prompt)) # -> [(id, desc), ...] def curate(playbook: dict, deltas: list) -> dict: # DETERMINISTIC merge -- plain code, no model, no dice. for d in deltas: key = d["identifier"] if key in playbook: playbook[key] = merge_desc(playbook[key], d["description"]) else: playbook[key] = d["description"] # append: new insight return playbook # untouched items: inert bytes def dedupe(playbook, embed, thresh=0.92): # PERIODIC hygiene pass (not every update): drop near-duplicates. keys = list(playbook) vecs = {k: embed(playbook[k]) for k in keys} for i, a in enumerate(keys): for b in keys[i+1:]: if a in playbook and b in playbook \ and cosine(vecs[a], vecs[b]) > thresh: del playbook[b] # keep the older identifier return playbook
And the compact version — the whole ACE update step is genuinely this small once you see the shape:
python def ace_step(playbook, task, llm): traj = generate(task, context=playbook, llm=llm) # Generator deltas = reflect(traj, llm) # Reflector return curate(playbook, deltas) # Curator (deterministic)
Now run the machine below. Fire rollouts and watch insight bullets fly into the grouped playbook — duplicates land on an existing row and bump its counter instead of adding noise. Then flip to monolithic blob mode and run ten more: the whole panel rewrites on every rollout, and you will watch existing items flicker out in red. That flicker is Chapter 1's photocopy decay, live.
Green rollouts succeeded, red failed — both emit bullets (failures map the minefield). The ×N badge counts deduplicated re-discoveries. Toggle to monolithic blob to watch a whole-panel rewrite lose items on every run.
One engineering edge case before moving on, because it is where itemized systems actually break in practice: identifier collisions.
What if two runs distill contradictory descriptions under the same identifier — T2's api-retry-cap says "cap at 3," a later run says "cap at 5, the vendor raised limits"? The deterministic merge must have a policy: last-write-wins (simple, risks thrash), merge-both-with-timestamps (safe, grows), or flag-for-reflection (route the conflict back to the Reflector as a task).
ACE's periodic refinement pass is where such contradictions get resolved. The lesson generalizes: deterministic merging removes accidental damage, but semantic conflicts still need a deliberate, occasional, model-in-the-loop pass — scheduled hygiene, not per-update roulette.
Now the honest limit, because it is the hinge to the next chapter. ACE learns insights from rollouts — that is real movement toward self-managed memory. But look at what is still frozen: the update rules and the overall workflow are handcrafted. A human decided that context should be bullets, that bullets should have identifiers, that merges should be deterministic, that dedup runs periodically. The content evolves; the mechanism is carved in stone. What if bullets are the wrong structure for some task — what if it needs a code library, or a retrieval index, or hierarchical notes? ACE cannot ask that question. Chapter 3 is about the system that can.
ACE ended on a confession: its update rules are handcrafted. Someone decided context should be an itemized playbook, and that decision never gets revisited — no matter how many rollouts disagree. Meta Context Engineering (MCE; Ye et al. 2026) is built on one clean separation: the mechanism (how context is managed) versus the artifact (what is in the context). ACE evolves the artifact under a fixed mechanism. MCE runs skill evolution at the meta level and context optimization at the base level — two loops, one machine, and the outer loop's job is to question exactly what ACE froze.
The unit the outer loop evolves is called a skill. A skill s ∈ S (the space of possible skills) defines a context function cs = (ρs, Fs) that maps an input x to a context c:
Every symbol, with its analogy. ρs = {ρ1, …, ρm} are the static components: prompts, knowledge bases, code libraries. This is the pantry — ingredients that sit on the shelf regardless of tonight's order. Fs = {F1, …, Fk} are the dynamic operators: search, selection, filtering, formatting. This is the cooking — the steps that turn shelf ingredients into a plated dish. And x is tonight's order: the actual input. So a skill is a recipe — pantry plus cooking steps — and the context c is the plated dish, prepared fresh for each input. ACE, in this vocabulary, is one fixed recipe. MCE breeds new recipes.
Two loops need two objectives, and here is the lesson's mathematical heart. Read it gently — every piece will be defined:
Symbol by symbol. Jtrain is a score — how well the agent performs on training tasks when given context cs. Jval is the same score measured on held-out validation tasks the inner loop never saw. argmax just means "the choice that makes the score biggest." The inner loop holds the recipe s fixed and perfects the dish: find the best context cs* this recipe can produce, scored on training data. The outer loop compares finished dishes across recipes and picks the best recipe s* — but scores them on validation data. If you know deep learning, this is precisely the classic split: inner = train the weights, outer = pick the hyperparameters/architecture. If you don't: inner = get the best meal out of one recipe; outer = decide which recipe deserves to stay in the family cookbook, judged by guests who didn't watch you cook.
Why Jtrain inside but Jval outside? This asymmetry is subtle and it is the single most important design decision in MCE, so take it in two steps. Step one: the inner loop should use training data — it runs many optimization steps, and it is allowed to squeeze everything it can out of the tasks in front of it. That is what optimization means. Step two: precisely because the inner loop squeezes so hard, its final Jtrain is a flattering, contaminated estimate. A skill can score brilliantly on training tasks by memorizing their quirks — hardcoding the pantry with answers, tuning the cooking to the exact menu. If the outer loop also judged by Jtrain, it would systematically breed quirk-memorizers: skills that look great and generalize terribly.
That failure has a name: meta-overfitting — overfitting one level up, where the mechanism (not the model) has specialized to the training distribution. The defense is exactly the one statisticians have used for a century: judge on data the optimizer never touched. The outer loop selects skills by Jval, so a memorizer's inflated training score buys it nothing — its validation score exposes it, and the crossover machinery breeds from honest winners instead. Watch it happen in numbers:
| Candidate skill | Jtrain | Jval | Outer-loop verdict |
|---|---|---|---|
skill-A: verbatim rollout log, keyword search | 0.81 | 0.79 | solid, generalizes (gap 0.02) |
skill-B: memorize per-task answer hints | 0.88 ← best on train | 0.64 | REJECTED — meta-overfit (gap 0.24) |
skill-C: itemized playbook + top-k retrieval | 0.84 | 0.82 ← best on val | WINNER — selected as s* |
Follow the arithmetic of the verdict. Greedy-on-train would rank B > C > A (0.88 > 0.84 > 0.81) and crown the memorizer. The outer loop instead ranks by validation: C > A > B (0.82 > 0.79 > 0.64), crowning C. And the gap Jtrain − Jval is the tell: A gaps 0.02, C gaps 0.02, B gaps 0.88 − 0.64 = 0.24 — twelve times wider. A big train–val gap at the skill level is the smoking gun of meta-overfitting, exactly as it is for an overfit neural net one level down.
How does the outer loop actually propose new skills? Not by random mutation — by informed breeding. MCE keeps a skill database tracking the full history of previous skills, their context functions, and both eval metrics:
Every recipe ever tried, with its dish and both report cards — the breeding stock. A meta-level agent then performs agentic crossover: given the task τ (tau — the task at hand), it reads the history and writes a new skill combining the strengths of prior ones:
The word agentic is load-bearing. In a genetic algorithm, crossover is blind splicing — half of parent A, half of parent B, hope for the best. Here the crossover operator is an LLM agent that reads the history: it can notice "A's verbatim logging scored well on debugging tasks, C's retrieval kept validation gaps small — combine A's logging with C's retrieval and drop B's memorization, which gapped 0.24." Then the base-level context engineer takes over and executes the new skill, learning the context from rollout feedback:
Read the arguments: it builds context for task τ, following recipe sk, warm-starting from the previous best context c*k−1 (no cold restarts — knowledge carries over), guided by rollout feedback Rk — what actually happened when the agent ran. The inner loop is itself agentic: try, observe, refine the files.
One degradation case to hold onto: what happens when the skill database itself grows large? The crossover agent must read Hk−1 to breed — and after hundreds of skills, the history no longer fits any window. The meta level has re-created the base level's context problem, one floor up, and needs its own retrieval and summarization over past skills. This recursion is not a flaw; it is the shape of the whole field — every optimization layer eventually needs the layer below it applied to itself.
Files — that word is literal, and this is the implementation detail that should make you smile. A context function c is instantiated as a collection of files in a dedicated directory: static components as skill.md, dynamic components as context and data rollout files.
And both the meta level and the base level run in ordinary agentic coding environments with the standard toolset:
No bespoke optimizer, no gradient machinery — skills are evolved by agents reading and editing files with the same seven tools a coding agent uses to fix your repo.
If you have used Claude Code skills — a directory holding a skill.md that reshapes how the agent approaches a task — you are looking at the same machinery.
This lesson was built by one. MCE's bet is that this machinery can breed itself: the recipe card is a file, files are editable, and editing files is the one thing coding agents are unambiguously good at.
What does such a skill directory actually look like on disk? Concretely, something like this — static pantry declared at the top, dynamic operators as instructions the base agent executes:
python # skills/debug-ctx-v3/ — one MCE skill, instantiated as files # # skill.md (static ρ: the mechanism, human-readable) # ## Static components # - knowledge/pitfalls.md (curated pitfall list) # - lib/retrieve.py (top-k retrieval over past rollouts) # ## Dynamic operators (F), run per input x: # 1. Grep rollouts/ for errors matching x's stack trace # 2. retrieve.py --k 8 --query "{x.summary}" # 3. Format: pitfalls first, then retrieved traces, then x # # context.md (dynamic: current best assembled context) # rollouts/run_017.log (dynamic: evidence the engineer learns from) # scores.json {"J_train": 0.84, "J_val": 0.82} inner_step = lambda skill_dir, feedback: agent( tools=["Read","Write","Edit","Bash","Glob","Grep","TodoWrite"], prompt=f"Improve the context files in {skill_dir} following skill.md, " f"guided by this rollout feedback:\n{feedback}")
Now the contrast that locks in the concept. ACE enforces a heuristic structure rule: context shall be itemized bullets, merged deterministically — a good rule, chosen by humans, forever. MCE enforces no such rule: skills are free-form, storing whatever knowledge form serves the task best — and the skill and the skill-conditioned context evolve together, iteratively. If bullets win on this task family, MCE's outer loop can discover bullet-playbooks and effectively reinvent ACE. If a code library or a retrieval index wins, it discovers that instead. The mechanism has joined the artifact on the optimization table — that is rung 2 reaching toward rung 3 of the ladder.
| Dimension | ACE | MCE |
|---|---|---|
| What evolves | context content only | mechanism (skill) + content, together |
| Structure rule | fixed: itemized bullets | none — free-form skills |
| Levels of optimization | one | two (bi-level, inner/outer) |
| Update executor | deterministic merge code | agents with T = {Read…TodoWrite} |
| Overfitting defense | n/a (mechanism fixed) | outer loop judged on Jval |
| Knowledge form | (identifier, description) bullets | whatever the skill defines (files) |
One caveat MCE inherits from every validation-judged system, worth stating because Chapter 8 will show it to you live: Jval is itself a noisy measurement. If the validation set is small, a mediocre skill can get lucky on it, and the outer loop will breed from a fluke — meta-overfitting's sneakier cousin, validation lottery. The defenses are the boring, honest ones: bigger validation sets, repeated evaluation, and preferring skills whose Jval is stable across re-runs over ones with a single shiny reading. Keep that in mind when you watch greedy search get fooled by noise in the showcase — the same disease, one rung down.
Inner step: the base-level engineer edits the context files; Jtrain climbs noisily. Outer step: the skill is scored on validation (Jval), shelved into the history H, and the two best-by-val parents crossover into a new skill. Skills with a train–val gap > 0.12 get the red meta-overfit stamp — watch the outer loop refuse to breed from them.
MCE put the context-management mechanism on the optimization table. Meta-Harness (Lee et al. 2026) moves one level deeper still: the optimized object is the code that determines and optimizes what information is stored, retrieved, and presented to the model. Not the playbook. Not the recipe for the playbook. The program — the harness source itself. The "Meta-" in the name says it plainly: it is a harness for optimizing harnesses. One agent system, whose job is to build and improve other agent systems, as code.
Four design details make this concrete, and each one earns its place. Design detail one: the proposer is itself a coding agent, and the final output is not a single winner but a collection of harness candidates on the Pareto frontier. Two ideas hide in that sentence, so unpack both. "The proposer is a coding agent" means new harnesses are written the way you would write them — an agent reads existing code, reasons about weaknesses, and writes a modified program. No mutation operators, no templates: the full expressiveness of code, wielded by something that understands code.
And the Pareto frontier? Build it from zero with two axes. A harness has (at least) two scores you care about: accuracy (higher = better) and token cost (lower = better). Plot every candidate as a dot: cost on the x-axis, accuracy on the y-axis. Candidate A dominates candidate B if A is at least as good on both axes and strictly better on one — cheaper and more accurate, say. If A dominates B, there is no reason B should ever exist: whatever you wanted from B, A gives you for less. The Pareto frontier is the set of candidates that nobody dominates. Concretely: a harness scoring 0.82 at 40k tokens and another scoring 0.74 at 9k tokens are both on the frontier — neither beats the other on both axes — while one scoring 0.73 at 30k tokens is dominated by each and gets dropped. Returning a frontier instead of a single champion is honest engineering: the "best" harness depends on your budget, so Meta-Harness hands you the whole menu of undominated trade-offs and lets deployment pick.
Work one tiny frontier by hand to make "dominated" mechanical. Five candidates, (cost in k-tokens, score): A(9, 0.74), B(30, 0.73), C(40, 0.82), D(22, 0.78), E(45, 0.80). Check each: A — nothing is both cheaper and better; frontier. D — cheaper than C/E, better than A/B on score… nothing beats it on both axes; frontier. C — highest score of all; frontier. B — D costs less (22 < 30) AND scores higher (0.78 > 0.73); dominated, drop. E — C costs less (40 < 45) AND scores higher (0.82 > 0.80); dominated, drop. The kept set {A, D, C} traces the staircase you will see in the sim. And note why the frontier beats collapsing to one number (like score − 0.01×cost): any fixed weighting bakes in a budget assumption, and a weighted sum can never even represent the deployment question "give me the best harness under 25k tokens" — the frontier answers every such question at once (here: D).
Design detail two: the entire execution history is accessible via the file system — and the proposer agent reads through it with commands like grep and cat, instead of shoveling everything into a single prompt context.
Stop and enjoy this one, because you have seen it before. It is Pattern 2 from the previous lesson — file system as persistent memory — paying rent at the meta level.
The history of every harness trial is far too large for any context window, so it lives as files, and the agent pulls exactly the slice it needs: grep "timeout" runs/*/log when investigating timeout-prone candidates.
The optimizer eats its own dogfood: the same context discipline it is trying to build into harnesses is the discipline that lets it function at all.
Design detail three: a proposed harness is a dictionary in the file system, containing its own source code, its scores, its rollout trajectories, and its state updates. Every candidate is a self-describing bundle — not a loose diff plus a spreadsheet row somewhere, but one addressable object holding the code and the evidence about the code. When the proposer wants to learn from candidate #23's failures, everything is in one place: what it was, how it scored, exactly what happened when it ran.
Design detail four: the meta-harness loop iteratively creates new harnesses, and only qualified ones are kept. Propose, evaluate, filter, repeat. The filter is the frontier logic from detail one: a candidate that is dominated — worse on every axis than something already kept — is discarded, and the archive stays a clean frontier rather than a junk drawer of every attempt.
Notice what the dictionary bundling (detail three) quietly buys beyond tidiness: reproducibility and rollback. Because a candidate carries its own source, scores, and trajectories, any past harness can be re-run exactly, audited after the fact, or restored wholesale if a newer one misbehaves in deployment. When the thing being optimized is the production system itself, "every candidate is a checkpoint" stops being a convenience and becomes the safety story.
Put the four details together and the outer loop is small enough to sketch faithfully:
python # Meta-Harness outer loop — a harness for optimizing harnesses kept = init_candidates() # e.g. Terminus-KIRA, Terminus-2 as strong seeds for t in range(MAX_ROUNDS): # (2) proposer reads history straight off the file system evidence = sh("grep -l 'timeout' runs/*/log | head; cat runs/best/scores.json") new_code = coding_agent("Improve on these harnesses. Evidence:\n" + evidence) scores = evaluate(new_code) # (score, token_cost, ...) # (3) each candidate = a self-describing dictionary in the fs save_dir(f"runs/h{t}/", source=new_code, scores=scores, trajectories=collect_rollouts(), state_updates=diff_log()) # (4) keep only the qualified — the undominated kept = pareto_filter(kept + [(new_code, scores)]) return kept # the whole frontier, not one champion
Now the results — reported honestly, because the honest version teaches more. On text classification, Meta-Harness improves within a small number of iterations: modest domain, real gains, quickly. On TerminalBench-2 — a hard terminal-agent benchmark — the search was initialized from Terminus-KIRA and Terminus-2, two very strong existing harnesses. Read that nuance carefully: the system did not conjure a great terminal harness from a blank file. It started from the best human-engineered designs available and pushed them further. That is genuinely impressive — improving on strong baselines is where human experts spend careers — but it is refinement of a strong seed, not creation from nothing. Keep both truths in your head; hype dies in that gap.
| Experiment | Starting point | Result | The honest read |
|---|---|---|---|
| Text classification | simple candidates | improves within a small number of iterations | real gains, fast, in a modest domain |
| TerminalBench-2 | initialized from Terminus-KIRA + Terminus-2 | improves on the seeds | refinement of elite human designs, not from-scratch creation |
The frontier output also changes how the result is consumed. A single-champion optimizer answers one question; the frontier answers a family of them — the accuracy-first team deploys the top-right candidate, the high-volume, cost-sensitive team the bottom-left one, both from the same search run. The optimizer's product is a menu, and the menu is reusable across deployments that the optimizer never knew about.
And then the sentence that survives the caveat, the takeaway Weng distills from this whole line of work: once harness design becomes an executable search space, a strong coding agent can exploit the same design space human engineers use. Nothing about harness engineering is privileged human ground — it is code, code is searchable, and a sufficiently strong coding agent can search it. The rest of this lesson, and the entire next one, is downstream of that sentence.
Run the search below. Each proposed harness lands as a dot in cost–score space; the frontier polyline pushes outward as batches arrive; dominated dots fade. Tap any dot to inspect its file-system dictionary. Then try the init from strong baselines toggle — that is the TerminalBench-2 nuance made visible: the frontier starts high and the search refines it, rather than climbing from the floor.
x = token cost per task (lower is better) · y = score (higher is better). Teal dots sit on the Pareto frontier; faded dots are dominated (something is better on both axes). Tap a dot to open its candidate dictionary below.
Climb from context (rung 2) to workflow (rung 3): the shape of the run itself — which stages happen, in what order, checked by what. Before anyone searched this space with algorithms, domain experts handcrafted workflows, and the flagship domain is auto-research: systems that try to do science. Three systems, three lessons — and together they set up exactly why search had to come next.
The AI Scientist (Lu et al. 2026 — published in Nature, a milestone in itself) is a pipeline of six handcrafted stages, each stage's output feeding the next stage's input:
Every arrow in that diagram is a human design decision: an expert decided research decomposes into these six stages, in this order, with review at the end. It works — the pipeline coordinates a large portion of a real research loop. But notice what it inherits: every blind spot of its designers, frozen in. If great research sometimes needs analysis before more coding, or two idea stages, this pipeline cannot discover that. Handcrafting buys reliability and pays in rigidity.
There is also a quiet connection back to Chapters 1–3 hiding in the pipeline's shape: a staged workflow is itself a context-management strategy. Each stage receives a purpose-built context — the coding stage sees the chosen idea, not the whole literature scan; the review stage sees the manuscript, not the raw experiment logs. Decomposing the job into stages with typed handoffs is what keeps any single context window small and on-topic. Rungs 2 and 3 of the ladder are not neighbors by accident; workflow design and context engineering are two grips on the same problem.
ScientistOne (Meng et al. 2026) keeps the handcrafted pipeline but redesigns it around one constraint: verifiability. The motivating fear is concrete — a paper-writing system can produce a beautifully plausible manuscript with fabricated citations, invented numbers, or conclusions its own experiments do not support. Fluent text is cheap; grounded text is not. So in ScientistOne, every claim must trace to an evidence source, and claims are audited by Chain-of-Evidence checks — an explicit chain from each sentence back to the artifact that justifies it. The four claim types, all named, all audited:
| Claim type | What must trace to evidence |
|---|---|
| Citation | "Smith et al. showed X" → the cited paper exists and actually says X |
| Numerical | "accuracy improved 4.2%" → the number appears in a real experiment log |
| Methodological | "we trained with lr=3e-4 for 10 epochs" → the config/code actually did that |
| Conclusion | "therefore X causes Y" → the inference is supported by the presented results |
Mechanically, a Chain-of-Evidence check is an audit pass over the manuscript: extract each claim, classify it into one of the four types, resolve its declared evidence pointer (a citation entry, a log line, a config file, a results table), and verify the claim against the artifact. A claim whose chain does not resolve fails the audit — and a failed audit blocks the manuscript, the same way a failing test blocks a merge. Trust becomes a build step.
Why this obsession? Because a research workflow's failure mode is not crashing — it is succeeding at the wrong thing: producing artifacts that look like science without being science. Hold that thought; the next lesson's catalog of failure modes (over-optimism, "p-hacking and eureka-ing") is this fear, observed in the wild. ScientistOne's answer is architectural: make the workflow itself demand receipts at every stage, rather than hoping the model is honest.
The third system moves from writing papers to making data. Autodata (Kulikov et al. 2026) is an agentic data scientist for generating training and evaluation data. Its handcrafted workflow is a main agent managing four roles: a challenger that proposes problems, a weak solver, a strong solver, and a verifier/judge that checks solutions. The goal is to synthesize data at the "just right" level of difficulty — defined with beautiful precision: the strong solver succeeds AND the weak solver fails.
| weak solver ✓ | weak solver ✗ | |
|---|---|---|
| strong solver ✓ | too easy — teaches nothing | GOLD: keep this problem |
| strong solver ✗ | suspicious — broken problem or lucky guess | too hard — no signal to learn from |
Why is that band the gold zone for training data? Run the four cases. Both solvers pass → the problem is too easy; the weak model already knows this; training on it teaches nothing. Both fail → too hard; there is no correct trace to learn from, no signal, just noise. Weak passes but strong fails → suspicious; likely a broken problem or a lucky guess — the verifier should frown. Strong passes and weak fails → exactly the material sitting at the edge of the weak model's ability, with a verified correct solution available from the strong solver to learn from. If you know curriculum learning, this is its automated cousin: keep the learner in the zone where problems are hard enough to stretch but solvable with the available guidance. And the workflow is adaptive: the challenger prompt is updated iteratively according to feedback from the solvers and the verifier — too many both-pass outcomes and the challenger is told to push harder; too many both-fail and it eases off. A thermostat for difficulty.
Notice also what the whole loop leans on: the verifier/judge. Every kept problem is kept because the verifier said the strong solver's answer was right and the weak solver's was wrong. If the verifier is weak — fooled by confident-sounding wrong answers, or exploitable by degenerate problems that look hard but have trivial shortcuts — then the "gold band" fills with fool's gold, and the fine-tuned weak solver learns to imitate whatever fooled the judge. This is your first sighting of a theme the next lesson makes central: a self-referential data or optimization loop is only as trustworthy as the evaluator standing outside it.
Now the limitation, and it deserves careful unpacking because Weng flags it explicitly: the synthesized tasks are used to fine-tune weak solvers, but not strong ones. Trace the consequence. The loop's ceiling is the strong solver — problems only count when the strong solver can crack them, so nothing in the data ever exceeds what the strong model already knows. If the loop cannot iteratively improve the strong model, then what is really happening is closer to indirect distillation: the strong model's capabilities are being compressed into the weak model, over a cleverly generated prompt distribution rather than a static dataset. Distillation is useful! But it is capability transfer, not capability creation — the system's total knowledge never rises above its strongest member. That is why Weng says it has "less RSI flavor": a recursive self-improvement loop must lift its own ceiling, and this one, by construction, polishes the floor.
The simulation below is Autodata's heartbeat. Problems flow from the challenger through both solver lanes; each outcome lands in a 2×2 grid; the difficulty trace at the bottom shows the controller steering into the gold band. Drag the difficulty slider to perturb the challenger and watch auto-adjust pull it back — then switch auto-adjust off and see the stream drift into worthless too-easy or too-hard territory.
Problems cross the weak and strong solver lanes and land in the 2×2 outcome grid — the glowing cell is the gold zone. The bottom trace shows challenger difficulty steering into the gold band. Perturb it; watch it re-converge.
Chapter 5 closed on handcrafting's tax: every workflow inherits its designers' blind spots. And the design space for workflows is enormous — every possible arrangement of stages, checks, retries, ensembles, delegations. Weng's framing is the pivotal move of this whole lesson: naturally, we can think of workflow design as a search problem — and therefore we should be able to find good solutions by algorithms rather than only crafting them by hand. The moment you say "search problem," a century of optimization machinery wakes up and asks for the job.
Automated Design of Agentic Systems (ADAS; Hu, Lu & Clune 2025) is the first system in our story to take that job seriously. It formulates agent design itself as an optimization problem — meta-agent search — where a meta-agent proposes new designs of agentic workflows. One agent whose entire occupation is inventing other agents. The loop has six steps, and every one carries a design decision worth understanding:
Walk the interesting steps. The archive (step 1) starts humble — chain-of-thought prompting and self-refine, the "hello world" agents of the field. It is not just storage; it is the gene pool. Every future design is proposed "inspired by existing solutions in the archive," so what the archive contains shapes what can be dreamed up next. As it fills with stranger, stronger designs, the inspiration pool compounds — late-iteration proposals combine ideas no single early agent contained.
The choice of seeds matters more than it looks, precisely because of that compounding. CoT and self-refine are good seeds for three reasons: they are simple (little code to imitate badly), they are well-understood (the meta-agent has seen thousands of descriptions of them in pretraining), and they are cheap to execute (early evaluations don't burn the budget). Seed the archive with one baroque, expensive agent instead, and every early proposal inherits its baroqueness — the gene pool is biased before the search begins.
Describe-then-implement (step 3) mirrors how good engineers work: design doc before code. Forcing the meta-agent to first write a high-level description of the new workflow — what it does and why it might win — and only then implement it in code separates invention from construction. A model that jumps straight to code tends to produce a mashup of syntax from its inspirations; a model that must first articulate the idea produces code in service of an idea.
Self-refine (step 4) is a named technique, so define it: ask the model to provide feedback on its own output, then ask the same model to refine that output based on the feedback (Madaan et al. 2023). ADAS runs the draft program through two self-refine passes, specifically checking novelty — is this genuinely a new design, or the archive's greatest hits reshuffled? Novelty checking is search-diversity maintenance: without it, the meta-agent drifts toward proposing tiny variations of the current best, and the search collapses into a crowd of near-clones (a failure you will meet again, formally, in the next lesson as diversity collapse).
Why exactly two passes, not one or five? Self-refine has sharply diminishing returns: the first pass catches most surface flaws (bugs, unoriginality the model can see once asked), the second catches what the first pass introduced — and beyond that, the model mostly rephrases its own approval while the token bill keeps growing. Two is the empirical knee of the curve: enough scrutiny to matter, cheap enough to run on every candidate.
Now the thesis hiding in three words of step 2: all in code. Why does it matter that ADAS's meta-agent writes programs rather than prompt strings? Three compounding reasons. First, the archive becomes executable — every stored design can be run, scored, and verified at any time; nothing is a vague idea. Second, new designs can compose old ones the way software composes: import the ensemble logic from one archived agent, wrap it in another's retry loop, add a fresh voting rule — try expressing that as a prompt-string edit. Third, the search space becomes the space of programs, which is universal: any workflow you can specify at all, you can specify as code. This is the same "code is the universal language" conviction that powers Meta-Harness one rung up, and it is the thesis of the entire optimization arc of this lesson.
python # ADAS meta-loop, structurally faithful sketch archive = [Agent("CoT", cot_code, score=eval_on(benchmark, cot_code)), Agent("SelfRefine", sr_code, score=eval_on(benchmark, sr_code))] for k in range(MAX_ITERS): # (2) propose, inspired by the archive -- in code inspiration = render(archive) # designs + scores as text # (3) describe FIRST, then implement desc = meta_agent("Given these agent designs and scores:\n" + inspiration + "\nDescribe a NOVEL agentic workflow likely to score higher.") code = meta_agent("Implement this design as runnable code:\n" + desc) # (4) two self-refine passes, checking novelty for _ in range(2): feedback = meta_agent("Critique this program. Is it truly novel " "vs the archive? Any bugs?\n" + code) code = meta_agent("Refine the program using this feedback:\n" + feedback + "\n" + code) # (5) evaluate; only successful candidates join the gene pool score = eval_on(benchmark, code) if score > min(a.score for a in archive): archive.append(Agent(name_of(desc), code, score))
A worked micro-run makes the dynamics concrete. Start: archive = {CoT: 0.62, SR: 0.66}. Run three iterations:
| Iter | Parents (inspiration) | Candidate | Score vs parent avg | Verdict | Best so far |
|---|---|---|---|---|---|
| 1 | CoT, SR | CoT+SC (self-consistency voting) | 0.69 > 0.640 | kept | 0.69 |
| 2 | SR, CoT+SC | Refl (reflection wrapper) | 0.63 < 0.675 | discarded | 0.69 |
| 3 | CoT+SC, SR | Ens3 (3-sample ensemble) | 0.73 > 0.675 | kept | 0.73 |
Two keeps, one discard — and the kept designs are already compositions the seed archive could not express: CoT+SC exists only because both parents were readable code. Also note the quiet cost column that isn't drawn: every iteration pays a full benchmark evaluation, which in practice dwarfs the cost of proposing. Evaluation, not generation, is the budget that limits archive search — the same economics you saw in Meta-Harness, and the reason ADAS stops at a max iteration count rather than searching to convergence. (Contrast AFlow next chapter, which stops on a measured plateau — a budget rule versus an evidence rule.)
Watch the loop run below. Each iteration draws inspiration lines from two or three archive parents, a candidate card appears in the eval bay, gets stamped with a score, and either slides into the archive with a green glow or fades away. The sparkline tracks the best score found so far — the signature staircase of an archive-driven search: flat stretches, then a jump when a genuinely new composition lands.
Purple lines = inspiration drawn from parent designs. The candidate is evaluated in the bay at right: kept designs join the archive shelf; discarded ones fade. Bottom: best-score-so-far sparkline.
ADAS searches the open space of programs with archive inspiration — maximum freedom, minimum structure. AFlow (Zhang et al. 2025) makes the opposite bet: constrain the representation, then bring a principled search algorithm. The representation: an agentic workflow is a graph, where nodes represent LLM-invoking actions (generate, critique, ensemble-vote, revise…) and edges implement the logical operations in code — the deterministic glue deciding what flows where, what gets retried, what gets merged. Model calls at the nodes, plain code on the edges. A workflow edit is a graph edit: add a node, rewire an edge, swap an operator. Suddenly "improve the workflow" has the crisp structure that search algorithms love.
The algorithm AFlow brings is MCTS — Monte Carlo Tree Search — and you can learn everything you need about it right here, in this context. Picture a tree. The root is a starting workflow. Each child is a modified version of its parent — one graph edit away. Every node carries a score from actually running that workflow on evaluation tasks. Searching means repeatedly deciding: which existing node should I modify next? That single decision — where to grow the tree — is the entire art of tree search.
Here are AFlow's six steps, in full fidelity. (1) Initialize the starting workflow W0 in the tree from a template. (2) Select a workflow node using a soft mixture of score and uniform exploration. (3) Expand: ask an LLM to produce a modified workflow, conditioned on its evaluation performance — the model sees how the parent scored and where it failed, so the edit targets actual weaknesses. (4) Execute and evaluate the new workflow on real tasks. (5) Add it back to the tree if it shows improvement, within a budget of N rounds. (6) Repeat steps 2–5; stop when the top-k average score plateaus or the budget is hit.
Step 2 hides the deepest idea, so slow down. Why not simply always pick the best-scoring node — pure greed? Because scores are noisy and the landscape has local optima. A workflow that scored 0.74 today might have been lucky on this eval batch; a 0.58 workflow might be one edit from greatness. Pure greed piles every expansion onto the current champion, digging one hole deeper and deeper while the actual peak sits two hills over, never visited. This is the exploration–exploitation trade-off: exploitation says "spend effort where scores are already high," exploration says "spend some effort everywhere, because your knowledge is incomplete." Every good search algorithm is a treaty between the two.
AFlow's treaty is the soft mixture: blend a score-proportional distribution (exploitation) with a uniform distribution (exploration), and sample from the blend. With mixing weight λ (lambda — the exploration knob, between 0 and 1) and n candidate nodes with scores si:
Read each piece. 1/n is the uniform vote: every node equally likely — pure curiosity, blind to scores. si / Σj sj is the score-proportional vote: each node weighted by its share of the total score — pure merit. λ is the trust slider between them: λ = 1 is a lottery, λ = 0 is a meritocracy with no second chances. Small λ (say 0.2) means "mostly follow the scores, but guarantee every node — even the worst — a real chance of being revisited." That guaranteed floor is what rescues the unlucky 0.58 node from oblivion.
Now the mandatory arithmetic — do it by hand once and the formula is yours forever. The tree holds four candidate workflows with scores 0.62, 0.71, 0.58, 0.74, and λ = 0.2. First the total score: 0.62 + 0.71 + 0.58 + 0.74 = 2.65. The uniform term is the same for everyone: λ · (1/n) = 0.2 × (1/4) = 0.050. Then each node's merit term, scaled by (1 − λ) = 0.8:
Sanity checks: the four probabilities sum to 0.237 + 0.264 + 0.225 + 0.273 = 0.999 ≈ 1 (rounding). The best node W4 leads at 27.3% — but the worst node W3 still gets 22.5%, not zero. Under pure greed W4 would get 100% and W3 would never be touched again. The mixture compresses a 0.16 score gap into a 4.8-point probability gap: merit tilts the odds, exploration keeps every door ajar.
Round two — watch the distribution update. Say the sampler picked W4, the LLM produced a modified child, and it evaluated to 0.70 — an improvement over some of the tree, so it is added (step 5). Now n = 5, total = 2.65 + 0.70 = 3.35, uniform term = 0.2 × (1/5) = 0.040. Recompute: P(W1) = 0.040 + 0.8×(0.62/3.35) = 0.040 + 0.148 = 0.188; P(W2) = 0.040 + 0.8×(0.71/3.35) = 0.040 + 0.170 = 0.210; P(W3) = 0.040 + 0.8×(0.58/3.35) = 0.040 + 0.139 = 0.179; P(W4) = 0.040 + 0.8×(0.74/3.35) = 0.040 + 0.177 = 0.217; P(W5) = 0.040 + 0.8×(0.70/3.35) = 0.040 + 0.167 = 0.207. Sum: 1.001 ≈ 1 ✓. Every incumbent's probability dropped — the newcomer claimed its share — and the ranking stayed merit-ordered with the floor intact. That is the algorithm breathing: each round redistributes attention over a growing tree.
What does "conditioned on its evaluation performance" (step 3) buy? Blind mutation would change the workflow at random and hope. AFlow instead hands the LLM the parent's report card: "this workflow scored 0.71 overall but failed 8 of 9 multi-hop questions." The natural edit — add a decompose node before the answer node — targets the actual weakness. It is ADAS's "inspired by the archive" idea sharpened into "informed by this exact parent's failures": expansion becomes directed repair, not random drift.
Here is the selection rule as running code — verbose with the arithmetic laid out, then the compact form:
python def soft_mixture_select(scores, lam, rng): # P(i) = lam * (1/n) + (1 - lam) * s_i / sum(s) n, tot = len(scores), sum(scores) probs = [] for s in scores: uniform = lam * (1.0 / n) # curiosity: same for everyone merit = (1 - lam) * (s / tot) # each node's share of total score probs.append(uniform + merit) r, acc = rng.random(), 0.0 # SAMPLE — never argmax for i, p in enumerate(probs): acc += p if r <= acc: return i return n - 1 soft_mixture_select([0.62, 0.71, 0.58, 0.74], lam=0.2, rng=rng) # probs computed inside: [0.237, 0.264, 0.225, 0.273] ← the worked example # compact (numpy): probs = lam / len(s) + (1 - lam) * np.array(s) / sum(s) pick = np.random.choice(len(s), p=probs)
To feel what λ does, recompute round one at λ = 0.6 and compare. Uniform term: 0.6 × 0.25 = 0.150; merit scale: 0.4. P(W4) = 0.150 + 0.4 × 0.279 = 0.262; P(W3) = 0.150 + 0.4 × 0.219 = 0.238. Side by side:
| Node (score) | λ = 0 (pure greed) | λ = 0.2 | λ = 0.6 | λ = 1 (lottery) |
|---|---|---|---|---|
| W1 (0.62) | 0 | 0.237 | 0.244 | 0.250 |
| W2 (0.71) | 0 | 0.264 | 0.257 | 0.250 |
| W3 (0.58) | 0 | 0.225 | 0.238 | 0.250 |
| W4 (0.74) | 1.000 | 0.273 | 0.262 | 0.250 |
Read the columns as a dial: λ = 0 is a monarchy (the champion gets everything), λ = 1 is a lottery (scores mean nothing), and the useful settings in between let merit lead by a margin proportional to (1 − λ). At λ = 0.2 the best node is 1.21× likelier than the worst; at λ = 0.6 only 1.10×. Tuning λ is tuning how loudly the scores are allowed to speak.
Two guardrails complete the design. The add-back rule (step 5) admits a new workflow only if it shows improvement within a budget of N rounds — a candidate that cannot demonstrate progress within its allowance is dropped, keeping the tree from silting up with mediocrity. And the stopping rule (step 6) watches the top-k average: not just the single best score (one lucky outlier could mask stagnation) but the average of the k best. When that average plateaus — several rounds without meaningful movement — the search has converged; spend no more.
Results: in experiments on QA, code, and math tasks, AFlow showed decent improvement over manually designed workflows — and over ADAS. That second comparison is the instructive one. ADAS searches the open space of arbitrary programs with archive inspiration — unbounded freedom, but its search signal is loose ("be inspired, be novel"). AFlow constrains everything to graph edits guided by tree statistics — a smaller space, but every candidate is one principled step from a scored parent, and selection provably balances exploration with merit. On these benchmarks, structure beat freedom: the constrained, statistics-guided search found better workflows than the unconstrained, inspiration-guided one. Not a universal law — but a deep lesson about search: the value of a bigger space is bounded by your ability to navigate it.
Grow the tree yourself below. Step through SELECT → EXPAND → EVALUATE → KEEP/PRUNE and watch the live soft-mixture arithmetic under the canvas — the same computation you just did by hand, updating as the tree grows. Push λ to the extremes: at 0 the search tunnels on one branch; at 1 it wanders aimlessly. The sweet spot is where merit leads and curiosity chaperones.
Node color runs low score → high score. SELECT highlights the sampled node (purple ring) using the soft mixture below; EXPAND asks the LLM for a modified child; EVALUATE stamps its score; KEEP/PRUNE applies the add-back rule. A banner announces the top-3 plateau.
Everything so far has been someone else's search. Now run your own. The task: an agent must answer a 20-question quiz, and you get to design its workflow by chaining up to three operators from a library of six — direct (just answer), CoT (think step by step), self-refine (critique and revise), ensemble-3 (three samples, majority vote), verify-then-fix (check the answer, repair if wrong), and decompose (split the question first). Each operator carries a hidden true quality and a token cost, and some pairs have synergies — refinement after reasoning is worth more than refinement alone. With sequences of 1 to 3 operators, the space holds 6 + 36 + 216 = 258 workflows. Small enough to draw, big enough that brute force at ~5 tokens a pop would bankrupt the point.
The catch that makes this a real search problem: you never see true scores. Every evaluation returns true score + noise — a finite eval batch, so a mediocre workflow can test brilliantly once and a great one can stumble. Your budget is limited (10–100 evals). Three strategies compete, and you can run them side by side on identical random seeds: random search samples a fresh workflow every eval and keeps the best measurement it has seen. greedy hill-climb starts somewhere, mutates one operator at a time, and accepts any mutation that measures better — chasing single noisy readings. MCTS-lite is Chapter 7 distilled: a tree over operator prefixes, soft-mixture selection, and — the quiet superpower — revisit averaging: visiting the same branch again and again, so each branch's estimate is a mean of many noisy readings instead of one lucky roll.
The y-axis below plots the true quality of each strategy's current champion — the score you would actually get deploying its pick (the strategies themselves never see this line; neither would you in production). Two experiments to run. Experiment one: low noise, budget 60, all three strategies. Greedy should look respectable — with clean measurements, hill-climbing climbs. Experiment two: crank eval noise to high, reset seeds, run again. Watch greedy's curve stall or even sag: it accepts mutations on flukes and then can't escape, because its inflated champion measurement beats every honest challenger. MCTS-lite keeps grinding upward — averaging repeated visits burns the noise out of its estimates. You are watching, live, why AFlow carries tree statistics instead of a greedy pointer.
Toggle strategies (all three can race at once), set the eval budget and noise, then Run. Top strip: the leading strategy's champion pipeline. Chart: true quality of each champion vs evals spent. Right meter: tokens burned. Reset seeds replays the identical randomness for fair A/B tests.
Three structured experiments, if you want the guided tour:
Debrief — what you should have seen. At low noise, all three strategies climb, and greedy often climbs fastest early: local moves on trustworthy measurements are efficient. Random search rises in bursts — it has no memory, but 60 darts at 258 targets land somewhere decent. The separation happens at high noise. Greedy's acceptance test — "did this single measurement beat my champion's single measurement?" — becomes a coin flip decided by luck, and one inflated fluke poisons it permanently: the champion's lucky score becomes a bar no honest measurement clears. Random search is noise-tolerant but wasteful, never concentrating effort. MCTS-lite alone reduces noise, because a branch visited five times has its estimate averaged over five draws — the standard error shrinks by √5 ≈ 2.2×. Tree statistics are not decoration; they are a noise filter.
Now that you have searched blind, here is the ground truth you were searching over — the hidden operator table (reading it before searching would have spoiled the point):
| Operator | True quality pull | Token cost | Synergy |
|---|---|---|---|
| direct | 0.55 | 1 | — |
| CoT | 0.68 | 3 | +0.03 when followed by refine |
| refine | 0.72 | 5 | (receives the CoT bonus) |
| ens-3 | 0.74 | 9 | +0.02 after decompose |
| verify | 0.76 | 6 | +0.04 when placed last |
| decompose | 0.70 | 4 | (feeds the ens-3 bonus) |
Each added operator pulls the workflow score 55% of the way toward its own quality (diminishing returns — the second good operator helps less than the first), stacking the same operator three times is penalized, and the synergies reward ordered pairs. The best short pipelines live around CoT → refine → verify: reasoning, then repair, then a final check — which is, not coincidentally, the shape of most production agent workflows. If your search found something in that family, it found the real optimum.
Second observation worth pausing on: the tokens meter. Ensemble-heavy workflows score well but burn 2–3× the tokens of lean ones — on a fixed token budget (rather than eval count), the "best" workflow changes entirely. You have rediscovered why Meta-Harness returns a Pareto frontier instead of a single champion: score-at-any-cost and score-per-token crown different kings, and only the frontier holds both answers.
Third: everything you just searched was fixed in advance — six operators, hand-defined, immutable. The search chose arrangements; it could not invent a seventh operator. Now imagine handing that limitation to a coding agent: "here are the six operators as code; write new ones." The search space stops being 258 points and becomes the space of programs — and you have walked the exact conceptual bridge from AFlow (search over a fixed graph vocabulary) to ADAS and Meta-Harness (search over code itself). The ladder's top rungs are this playground with the walls removed.
For the record, here is the machinery under the hood — the same seeded-noise evaluation and the greedy/MCTS-lite cores you just raced (abridged to essentials):
python def evaluate(wf, rng, noise): # true_score is HIDDEN from strategies; they see only this noisy reading return true_score(wf) + rng.gauss(0, noise), token_cost(wf) def greedy_step(state, rng, noise): cand = mutate(state.champion, rng) # swap/add/drop one operator meas, cost = evaluate(cand, rng, noise) if meas > state.champ_meas: # trusts ONE noisy reading -- state.champion, state.champ_meas = cand, meas # flukes get enthroned return cost def mcts_step(tree, rng, noise, lam=0.25): node, path = tree.root, [] while node.children and rng.random() > 0.3: node = soft_mixture_pick(node.children, lam, rng) # ch.7 formula path.append(node) child = node.expand_or_revisit(rng) meas, cost = evaluate(child.workflow, rng, noise) for n in path + [child]: # REVISIT AVERAGING: n.n += 1 # running mean over many n.mean += (meas - n.mean) / n.n # noisy draws -> noise shrinks return cost
Eight systems crossed this lesson. Lay them on one table and the ladder from Chapter 0 reappears as the first column — every method is defined by the object it optimizes, who proposes changes, who judges them, and how the search moves. (Scroll the table sideways on a phone.)
| Method | Object optimized (rung) | Who proposes | Who evaluates | Search method | Key design choice | Main limit |
|---|---|---|---|---|---|---|
| ACE | context playbook (2) | Reflector (LLM) | rollout outcomes | incremental accumulation | deterministic itemized merge | mechanism is handcrafted |
| MCE | context mechanism + content (2–3) | meta-agent crossover | Jtrain inner, Jval outer | bi-level skill evolution | free-form skills as files | needs clean train/val splits |
| Meta-Harness | harness code (4) | coding agent | multi-objective evals | iterate + keep qualified | Pareto frontier output | strong-seed init on hard tasks |
| AI Scientist | nothing — fixed workflow (3) | human experts | peer-review stage | none (handcrafted) | 6-stage research pipeline | designers' blind spots frozen in |
| ScientistOne | nothing — fixed workflow (3) | human experts | Chain-of-Evidence audits | none (handcrafted) | every claim traces to evidence | verification adds cost/rigidity |
| Autodata | challenger prompt (1–3) | challenger agent | verifier + solver pair | iterative feedback | keep (strong ✓, weak ✗) band | never improves the strong solver |
| ADAS | workflow programs (3) | meta-agent (code) | benchmark eval | archive-inspired proposals | all in code + novelty self-refine | loose search signal |
| AFlow | workflow graph (3) | LLM expansion | execute + evaluate | MCTS, soft-mixture select | graph nodes/code edges | bounded by graph vocabulary |
The Chapter 0 staircase, one last time — now with every method you've met pinned where it lives. Note MCE straddling rungs 2–3 and Meta-Harness reaching from 4 toward 5: the boundaries were always soft.
And a decision guide — when you face a real system next week, which of these do you actually reach for?
| Your situation | Reach for |
|---|---|
| Agent re-derives the same insights every run; summaries keep losing detail | ACE-style itemized playbook + deterministic merge |
| You suspect your context format itself is wrong, and you have train/val task splits | MCE-style bi-level skill evolution |
| Fixed set of workflow building blocks, cheap noisy evals, want principled search | AFlow-style MCTS with soft-mixture selection |
| Open-ended workflow design, budget for many benchmark evals | ADAS-style archive + meta-agent proposals |
| Multiple objectives (score, cost, latency); strong existing harness to improve | Meta-Harness-style Pareto search seeded from your baseline |
| Need training data for a weaker model, have a stronger one + a trustworthy verifier | Autodata-style challenger/solver/judge loop |
| Output artifacts must be trustworthy (reports, papers, claims) | ScientistOne-style Chain-of-Evidence gating |
Now the cheat sheet — every formula and list from the lesson, compressed to what you should be able to reproduce on a whiteboard from memory.
Where to go next, and why:
Harness Engineering — lesson 1 of this trilogy: the design patterns (workflow, file-memory, sub-agents) that everything here optimizes.
Self-Improving Harnesses — lesson 3: the optimizer becomes the optimized — STOP, Self-Harness, AlphaEvolve, DGM, and the failure modes.
Loop Engineering — the discipline of running these optimization loops unattended, and what gets to say "no."
Prompt Engineering — rung 1 of the ladder, where all of this began.
AI Evaluation — the hidden prerequisite: every Jtrain, Jval, and score badge in this lesson presumes an evaluator you can trust.
Agent Architectures — the workflow shapes (ensembles, debate, decomposition) that ADAS and AFlow search over.
Monte Carlo Tree Search — the full MCTS lesson: UCB selection, rollouts, backpropagation — the deep end of Chapter 7's pool.
You now hold the ladder: playbooks that refuse to decay, skills that breed under a validation judge, harnesses on a Pareto frontier, archives of executable workflows, and a tree search that samples with curiosity. Next lesson, the loop turns all the way around — the optimizer optimizes itself.