Skip to content

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:

  1. 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.
  2. A decision the engine already makes — which parent, which task, which prior, merge or drop — belongs in a Policies field. This page's table.
  3. 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.