qbrixqbrix

Pools & Experiments

Pools and experiments are the two core objects in qbrix. A pool defines what you're optimizing (the variants), and an experiment defines how (the algorithm).

Pools

A pool is a named collection of arms (variants). Think of it as a container for the options you want to optimize between.

Creating a Pool

pool = client.pool.create(
    name="checkout-buttons",
    arms=[
        {"name": "green-cta", "metadata": {"color": "#22c55e", "text": "Buy Now"}},
        {"name": "blue-cta", "metadata": {"color": "#3b82f6", "text": "Add to Cart"}},
        {"name": "orange-cta", "metadata": {"color": "#f97316", "text": "Get It Now"}},
    ],
)

Arms

Each arm in a pool has:

FieldTypeDescription
namestringHuman-readable identifier
indexintAuto-assigned position (0-based), used internally for selection and feedback
metadataobjectArbitrary JSON — store anything your application needs to render the variant

Arms are immutable after creation. To change the variant set, create a new pool. This prevents mid-experiment data corruption where historical feedback no longer maps to the correct arm.

Arm Metadata

Metadata is a free-form JSON object attached to each arm. qbrix doesn't interpret it — it's passed through to your application in selection responses.

Common patterns:

// visual variants
{"image": "hero-summer.png", "headline": "Summer Sale"}
 
// pricing experiments
{"price": 9.99, "currency": "USD", "discount_label": "20% off"}
 
// feature flags
{"show_reviews": true, "layout": "two-column"}
 
// model variants
{"model_id": "gpt-4o", "temperature": 0.7}

Pool Rules

  • A pool must have at least 2 arms (otherwise there's nothing to optimize)
  • Pool names must be unique within your workspace
  • A pool can be used by multiple experiments simultaneously (e.g., same variants, different algorithms)
  • Deleting a pool that has active experiments is not allowed — disable the experiments first

Experiments

An experiment links a pool to a policy (bandit algorithm). It defines how arms are selected and how feedback updates the policy's parameters.

Creating an Experiment

experiment = client.experiment.create(
    name="checkout-optimization",
    pool_id=pool.id,
    policy="BetaTSPolicy",
    policy_params={"alpha_prior": 1.0, "beta_prior": 1.0},
)

Experiment Fields

FieldTypeDescription
namestringHuman-readable name
pool_iduuidThe pool this experiment operates on
policystringAlgorithm to use (see Policies)
policy_paramsobjectAlgorithm-specific configuration
enabledboolWhether the experiment is active (default: true)

Experiment Lifecycle

Experiment lifecycle

  • Enabled: The experiment is live. Selections use the bandit policy, feedback triggers training.
  • Disabled: Selections fall back to the default arm (index 0). Useful for pausing an experiment without losing learned parameters.
  • Deleted: The experiment and its learned parameters are permanently removed.

Policy Parameters

Each policy accepts different configuration. Parameters are set at experiment creation and can be updated later:

client.experiment.update(
    "<experiment-id>",
    policy_params={"alpha_prior": 2.0, "beta_prior": 1.0},
)
Warning

Changing policy parameters resets the learned state. The experiment starts learning from scratch with the new configuration.

See Policies for the full parameter reference for all 12 algorithms.


How Pools and Experiments Relate

Pool and experiments relationship

Multiple experiments can share the same pool. This is useful for:

  • A/B testing algorithms — compare how different policies perform on the same variant set
  • Segmented experiments — use feature gates to route different user segments to different experiments

Each experiment maintains its own independent parameter state. They don't interfere with each other.


What's Next