ACE#
ACE, Agentic Context Engineering (Zhang et al., ICLR 2026,
code), treats the context as a playbook that grows by incremental deltas
instead of being rewritten: bullets [id] helpful=N harmful=N :: content under sections, tagged by a Reflector and
extended by a Curator. Choose it when the adapter is a context document, you have the judge’s feedback on training
tasks, and you want the accumulated detail kept rather than compressed (the paper’s “brevity bias” and “context
collapse”). The answers come from the task’s own acting agent, which reads the playbook as part of its instructions,
where the authors’ code makes a separate LLM call; as in the paper, only the playbook is learned.
Setup#
template: ace@1.0
evolver:
name: ace
params: {feedback: true, max_num_rounds: 3, val_every: epoch}
data:
step_size: 1 # ACE's per-sample loop
val: splits/val.txt # optional: keeps the best validation playbook
llm: {model: <backend>/<model>} # the Reflector's and the Curator's model
The Reflector, the Curator and the merges are calls of the run’s model (llm). On a model reachable only through a
ChatGPT login, OfficeQA’s GPT-5.6-Luna setting makes each call one Codex session (llm: {cli: codex}).
The template’s ADAPTER.md is the acting agent’s context: any text the adapter started with (for example a task
skill), then the generator instructions and the playbook between markers. Starting from an adapter without a
playbook, the Evolver’s bootstrap appends the empty one. The de-duplication option needs
python -m pip install -e . --group ace (sentence-transformers).
One framework step#
The acting agent is ACE’s Generator: the Trainer’s rollouts are its attempts with the playbook as context, and it is
asked to name the bullet ids it used on a bullet_ids: line of its final message. Then, for each rollout in turn
(ACE._train_single_sample):
the ids the agent named are collected (the Generator’s regular-expression fallback; a copy of a playbook line, id followed by its counters, is not a citation);
a wrong answer: up to
max_num_roundsrounds of reflect, update the counters, run the agent again on the task with the reflection after its context (run_agent), stopping when it is right; a right answer: one reflection, to tag the bullets. The Reflector tags the named bullets helpful, harmful or neutral; the counters are updated without a model;every
curator_frequencysamples, the Curator turns the last reflection into ADD operations, merged into the playbook deterministically (new ids, a section that does not exist goes to OTHERS);with
use_bulletpoint_analyzer, bullets whose embeddings are at leastbulletpoint_analyzer_thresholdsimilar are merged by a model call.
flowchart TD
R[rollout: the agent answers with the playbook,<br/>names bullet_ids] --> C[collect the named ids]
C --> J{judge: right?}
J -- wrong --> RF[Reflector: diagnose, tag bullets] --> U1[update counters] --> A[run the agent again<br/>with the reflection]
A --> J2{right, or max_num_rounds?}
J2 -- no --> RF
J2 -- yes --> CU
J -- right --> RT[Reflector: tag bullets] --> U2[update counters] --> CU
CU[Curator, every curator_frequency samples:<br/>ADD bullets] --> CK{template check}
CK -- fails --> RB[roll the step back]
CK -- passes --> V[val_every: score on validation,<br/>keep the best] --> F[finalize: final = best,<br/>state/last_adapter = last]
The updated playbook must pass the template check, or the step is rolled back. With val_every, the playbook is
scored on validation and the best is kept; finalize hands it back. No validation: the current playbook is final.
Either way finalize also saves the playbook as training left it, as a complete adapter in state/last_adapter, so
it can be evaluated beside final when the validation split is too small to select on.
state/ace.json records every sample (named bullets, tags, retry scores, the Curator’s outcome, bullets added) and
the playbook’s size per step; output/calls/ holds every prompt and reply.
Parameters#
description: ACE, Agentic Context Engineering (Zhang et al., ICLR 2026, arXiv:2510.04618; ace-agent/ace) on the `ace`
template. The playbook (bullets with helpful/harmful counters under sections, in ADAPTER.md) grows by incremental
deltas. Each step, for each rollout in turn - the acting agent is ACE's Generator - the Reflector diagnoses the
attempt and tags the bullets the agent named; a wrong answer is reflected on and run again with the reflection
(run_agent) for up to max_num_rounds rounds; the counters are updated without a model; the Curator adds bullets
(ADD only, as upstream) every curator_frequency samples. Validation (val_every) keeps the best playbook, which
finalize hands back. Uses the run's `llm` tool; data.step_size 1 is upstream's per-sample loop.
class: evolver.py:ACEEvolver
needs: []
params:
feedback: true # the judge's verdict is the Reflector's environment feedback and gates the
# retry rounds (labels); false: label-free, the run's own status only, no retry
success_score: 1.0 # a judge score at or above this is a right answer
max_num_rounds: 3 # reflect -> run again rounds for a wrong answer (upstream code: 3; paper: 5)
curator_frequency: 1 # run the Curator every N samples
playbook_token_budget: 80000 # shown to the Curator, not enforced (as upstream)
total_samples: null # the Curator's "Sample k out of N"; null: the first epoch's size once known
val_every: epoch # validate at each epoch's end, every N samples (an integer), or never (0)
score_initial: false # also score the starting playbook on validation (upstream does not)
use_bulletpoint_analyzer: false # embedding de-duplication with model merges (upstream default: off)
bulletpoint_analyzer_threshold: 0.90
embedding_model: all-mpnet-base-v2
trace_chars: 12000 # characters of a trajectory shown to the Reflector (the middle is cut)
question_chars: 6000 # characters of the task shown to the Reflector and the Curator
max_tokens: 32000 # upstream: 4096, for a non-reasoning model
temperature: null # null: the endpoint's default (upstream: 0.0)
What it is not#
The playbook token budget is shown to the Curator but not enforced, as upstream; the template caps ADAPTER.md at
400,000 bytes, and a step that would pass it is rolled back, so learning stops there while the run goes on. The
Curator only adds: without de-duplication the playbook grows monotonically, as in the authors’ code.