Skip to content
Poisson Labs Docs
GitHub poissonlabs.ai

Prove

The sweep spec

A sweep spec is a TOML file, prove.toml by default, that says what to run, under which conditions, and how many times each. poisson-prove init --example writes a working one, and poisson-prove init --target pkg.module:rollout a commented template. This page is every key and table it can hold. Every key is checked when the spec is read, before anything is imported or run: a key the spec does not have is refused with its line and the nearest key it does have.

A whole spec, with the optional tables:

target = "my_rollout:rollout"
target_id = "support-bot"
t_unit = "steps"
replicates = 200
retain_traces_for = ["unresolved", "timeout"]
[axes.ticket]
type = "categorical"
values = ["refund", "password", "shipping"]
[axes.temperature]
type = "continuous"
values = [0.0, 0.7]
[declare]
outcomes = ["resolved", "unresolved", "timeout"]
failures = ["unresolved", "timeout"]
successes = ["resolved"]
[capture]
instrument = ["openai"]
content = false
max_observation_bytes = 16384
keytypedefaultwhat it is
targetstring, module:functionno default; --target gives it on the command linethe rollout, imported with the spec’s folder first on the import path. A --target wins over it.
target_idstringno default, requiredthe name the runs go by: a run is named by it, the date and a letter
t_unitstringno default, requiredthe unit an episode’s t and horizon are counted in: steps, tool_calls, seconds
replicatesinteger, 1 or moreno default, requiredepisodes per condition; --replicates overrides it for one run
retain_traces_forlist of outcome namesevery trace is keptkeep the stored trace only of episodes that ended in one of these outcomes (an errored episode always keeps its trace)
[axes.<name>]table, one per axisat least one is requireda condition is one value of every axis; the grid is every combination
scenariosarray of tables, each headed by the table name in double bracketsnoneconditions added to the grid as written, one table each, with any keys
[declare]tablenonewhat the run expects: its outcomes and their kinds, its steps and gates
[capture]tableno capturestore the OpenTelemetry spans of a traced agent with each episode
[chaos]tableno faultsinject a fault into the target’s HTTP calls
[modal]tableModal’s defaults belowhow a --backend modal sweep runs
[burla]tablerequired for --backend burlahow a --backend burla sweep runs

A spec has no name: a run is named on the command line with sweep --name.

One table per axis, in the order the conditions are listed. The grid is every combination of the axes’ values, the last axis varying fastest; the cases your agent runs are one axis, so a condition runs one case (Your own agent says why).

keytypedefaultwhat it is
type"categorical" or "continuous"no default, requiredcontinuous for numbers whose order means something (a rate, a temperature), categorical for anything else
valuesnon-empty listno default, requiredthe axis’s values, passed to the rollout as they are written (5 stays an integer)

Each scenarios table is one more condition, with whatever keys it holds, added after the grid. A scenario equal to a grid condition is run once. Use it for a few hand-picked conditions the grid does not have:

[[scenarios]]
ticket = "refund"
temperature = 1.2

What the run expects of itself, recorded in its manifest. It is not part of what makes two sweeps the same sweep, so a run that declares and one that does not still share their seeds when paired.

keytypedefaultwhat it is
outcomeslist of namesnoneevery outcome the rollout can return; diff refuses two runs that declare different outcomes
failureslist of outcome namesnoneoutcomes that count against the agent: a rise of one is worse
successeslist of outcome namesnoneoutcomes that count for it: a rise of one is safer
stepslist of step namesnonethe steps the agent is expected to run, checked against its trace
gateslist of step names, among stepsnonethe steps that are checks the agent’s control flow consults

Refused: a name in both failures and successes; a failure or success that is not among outcomes, when outcomes is given; a gate that is not among steps; a name listed twice; and a table that declares nothing. Without failures or successes a comparison says higher and lower for an outcome, never worse and safer, and the plan says so. What steps and gates report is in What capture stores.

Turns on OpenTelemetry capture for a traced agent: the instrumentors you name are switched on in the process that runs the rollout, and each episode’s spans are stored with it. It needs the agent extra, pip install "poisson-prove[agent]".

keytypedefaultwhat it is
instrumentlist of names[]instrumentors to switch on, by the name they register under OpenTelemetry’s opentelemetry_instrumentor entry points (openai for opentelemetry-instrumentation-openai-v2); empty for an agent that makes its own spans. A name that is not installed is not refused: the run goes on without capture, and its manifest says so
contenttrue or falsefalsewhether prompts and completions are stored; with false they never leave the process
max_observation_bytesinteger, 1 or more, or "none"no default, requiredthe largest single value (one prompt, one tool result) stored whole; a larger one is stored as its head, its tail and its length. "none" stores every value whole
max_spansinteger, 1 or moreno limitthe most spans one episode keeps: the earliest-started, so the root survives

What capture stores says exactly what each setting keeps.

Injects a fault into the target’s HTTP calls, so you can measure how the agent copes. It needs no extra package: it hooks the httpx client the target’s model client uses. Fault injection lists every fault.

keytypedefaultwhat it is
faulta fault’s name"none"which fault to inject; an unknown name is refused when the sweep is planned
ratenumber from 0 to 11.0the share of eligible calls that get the fault
stepslist of step namesevery stepinject only inside these steps

Like [declare], it is not part of the sweep’s identity: a faulted run and a clean one paired with it share their seeds.

How a --backend modal sweep runs. Every key is optional; the command line’s --concurrency, --infra-retries, --max-cost, --est-seconds, --app-name, --batch-size, --episode-timeout and --max-rollouts override it for one run. Backends says what a Modal sweep needs.

keytypedefaultwhat it is
concurrencyinteger16containers at once
batch_sizeinteger64episodes per call to a container
episode_timeoutseconds, or none300the limit on one episode inside a container
infra_retriesinteger2retries of a batch that failed for an infrastructure reason
max_rolloutsinteger, or noneno limitstop after this many and leave the run partial
max_costdollars, or noneno caprefuse before anything starts when the estimate is above it
est_seconds_per_episodeseconds, or noneno estimatethe time one episode takes, for the cost estimate
app_namestring"poisson-prove"the Modal app’s name
secretslist of namesnoneModal secrets mounted on the function, by name; Prove never reads their values

The container image, a table under [modal]. The section named image, [modal.image], is used unless --image SECTION names another, so one spec can hold several images.

keytypedefaultwhat it is
pythonversion string"3.11"the image’s Python
piplist of requirementsnonepackages to install, your agent’s dependencies among them
aptlist of packagesnonesystem packages
dockerfilepathnonebuild from this Dockerfile instead
local_packageslist of package namesnoneimportable local packages shipped into the image (Prove itself always is); dockerfile excludes pip and apt
gpua GPU type, or noneno GPUthe GPU to attach, such as "A10G"

How a --backend burla sweep runs, on your own Burla cluster. The first ten keys are required: there is no default for a price, a cap or a cluster. Backends says what it needs.

keytypedefaultwhat it is
nodesintegerno default, requiredthe cluster’s nodes
machine_typestringno default, requiredthe nodes’ machine type
slots_per_nodeintegerno default, requiredepisodes run at once on a node
usd_per_node_hourdollarsno default, requiredthe price of a node for an hour
price_asofstringno default, requiredwhere and when that price was read
ledgerpathno default, requiredthe spend ledger, checked before the run starts and written as it goes
head_urlURLno default, requiredthe cluster’s head, a local http URL or an https one
stop_costdollarsno default, requiredthe spend at which the job is stopped
max_costdollars, at least stop_costno default, requiredthe estimate above which the sweep is refused before it starts
est_seconds_per_episodesecondsno default, requiredthe time one episode takes, for the estimate
packageslist of package namesnonelocal packages shipped to the workers, beside Prove itself
func_cpuinteger1CPUs per worker
func_ramgigabytes4memory per worker
boot_minutesminutes5.0billed time to boot the cluster, in the estimate
idle_minutesminutes5.0billed idle time before the cluster stops, in the estimate
batch_sizeinteger256episodes per worker call
episode_timeoutseconds, or noneno limitthe limit on one episode
infra_retriesinteger2retries of a batch that failed for an infrastructure reason
max_rolloutsinteger, or noneno limitstop after this many and leave the run partial

Every mistake is refused with exit 1 before anything runs, naming what to fix. Here is a misspelt key, made by renaming t_unit in the example spec:

Terminal window
poisson-prove init --example --out good.toml
sed 's/^t_unit =/t_units =/' good.toml > typo.toml
Terminal window
poisson-prove sweep typo.toml --dry-run # exits 1
Output1 line
error: typo.toml, line 10: "t_units" is not a key or table a sweep spec has; the nearest known key is "t_unit"

The others, each in one line: a required key missing (target_id, t_unit, replicates); no [axes.<name>] table; an axis without type or values, or with a type other than the two; an unknown key inside any table; a value of the wrong type or out of range (replicates = 0, a [chaos] rate of 2); a [capture] table without max_observation_bytes; a target that is not module:function, cannot be imported, or does not take (cfg, seed); and a design too small to detect the change you accept, which names the replicates it needs.