description: The decision plane of AgentDescent: eight named policy slots covering selection, sampling, proposal, staleness, conflict, fusion, acceptance and promotion.¶
Choosing policies — the decision plane¶
Module: agentdescent.policies
Every decision evolve() makes is an object you can swap, and they all travel
in one argument. This page is the catalogue; the guide
is the how — where each slot sits, what it is handed, how they compose, and
how to prove one ran.
from agentdescent import Policies, evolve
from agentdescent.selection import Beam
from agentdescent.sampling import DifficultyWeighted
from agentdescent.advantage import AdvantageAcceptance
from agentdescent.fusion import reflective_merge
evolve(tasks, reward, agent=agent, policies=Policies(
selection=Beam(4),
task_sampler=DifficultyWeighted(),
acceptance=AdvantageAcceptance(inner=my_gate),
**reflective_merge(completion),
))
Two guarantees make the bundle trustworthy. Nothing is silently ignored:
each engine declares what it honours, and Policies.require_supported raises
on anything else — a custom acceptance rule either runs or refuses loudly.
None means today's behaviour: Policies() and passing nothing are the
same run, so adding a policy never changes an existing measurement.
Each field is independent: swap one and the other seven stay at their defaults.
The one deliberate pair is conflict + fusion for model merging, which is why
reflective_merge() returns both. A wrapper that defers to a shipped default —
AdvantageConflict(), AdvantageAcceptance() — needs nothing from you: the
aggregator hands the engine's verifier and config to any policy exposing the
optional bind(verifier) / configure(config) hooks
(details).
Which seam is my mechanism?¶
An algorithm's distinctive mechanism lives in exactly one of three layers:
- The artifact's shape — what a proposal is — belongs in the strategy, not a policy. Append-only memory, a keyed library, a single replaced slot.
- A decision the engine already makes — which parent, which task, which
prior, merge or drop — belongs in a
Policiesfield. This page's table. - Pure actor text — prompts and tools — belongs in the definition and needs no seam at all.
| Field | The decision | Page | Shipped implementations |
|---|---|---|---|
task_sampler |
which task the next rollout spends | Task sampling | RoundRobin, DifficultyWeighted |
selection |
which candidate the next batch starts from | Candidate selection | SingleHead, Beam, ParetoFrontier, Archive, MCTS (+ examples-level BinaryTournament, SoftMixed) |
proposal |
how rollout evidence becomes proposals | Proposal policies | protocol only — write your own |
conflict |
what happens to contradicting diffs | Conflict policies | DefaultConflict, KeepContradictions, AdvantageConflict |
fusion |
whether and how survivors merge | Fusion policies | DefaultFusion, ReflectiveFusion |
acceptance |
whether the merged candidate commits | Acceptance policies | DefaultAcceptance, AdvantageAcceptance, StableDistanceAcceptance |
promotion |
when dev reaches stable | Promotion policies | DefaultPromotion |
staleness |
what a lagging diff is worth | Staleness policies | Full, Guarded, Reflective |
The remaining fields are machinery, not algorithm: verifier, ledger,
eval_cache, executor, evaluator, sandbox_* — see
the verifier, the ledger, and
execution.
When one decision is not enough¶
Some optimizers change how the decisions compose — a population with its own
admission rule, per-instance score rows, an archive driving parent switches.
That is the aggregator_factory exit: replace (or
subclass) the whole optimizer while every policy above stays available to the
replacement. Rule of thumb: swap a field first; reach for the factory when
your mechanism needs state the pipeline does not keep.