Skip to content

Labeling Engine Configuration

Every key in the labeling_engine block, covering sources and confidence methods, the human pools, crowd spend, budgets, runtime limits, features and what validation refuses.

Everything the labeling engine does is set in one labeling_engine block. An invalid block stops the server at startup and lists every problem at once. To have annotators served the engine's requests, also set assignment_strategy: engine. Setting that strategy without an enabled engine is a validation error.

The block has these sections, each covered below:

yaml
assignment_strategy: engine
 
labeling_engine:
  enabled: true
  schemas: [sentiment]            # default: every scheme a source can label
  sources: []
  cascade: [laya, gemma]          # default: the first source alone
  contracts: []
  humans: {}
  crowd: {}
  budgets: {}
  runtime: {}
  features: {}
  redaction: {}
  distill: {}

Top-level keys

Five keys sit directly under labeling_engine:

KeyDefaultMeaning
enabledfalseTurn the engine on. An enabled engine needs at least one source.
schemasallThe annotation schemes the engine labels. Under assignment_strategy: engine, a scheme left out is answered only on the items the engine sends to people for its own schemes, not on every item, and validation warns about it. A scheme no source can label (free text, for one) cannot be in the engine, so under this strategy it is never answered on every item. Use another assignment strategy if it must be.
sources[]The models. See Sources.
cascadefirst sourceSource ids in the order they are tried. People come after the last.
contracts[]What the labels must satisfy. See Contracts.

Sources

A source is a model, or an existing set of labels, that proposes a label and a confidence for each item. Every source has a unique id and a kind, and an unknown key on a source is an error:

KeyDefaultMeaning
idrequiredName used in cascade, the call log and the certificate.
kindrequiredOne of llm, vlm, decision, detector, judge, jury, trained, static, fixture, asr, lf, simulator.
endpoint_typerequired for llm, vlm, decision, judgeThe AI endpoint, such as vllm, openai, anthropic, gemini, ollama, openrouter, openai_vision, ollama_vision, laya or systemone. detector defaults to yolo.
modelendpoint defaultModel name.
base_urlnoneServer URL for self-hosted or OpenAI-compatible endpoints.
api_keynoneNever hashed into a version and never exported. ${ENV_VAR} is substituted.
temperature0.00 to 2.
max_tokens256Output token limit per call.
timeoutendpoint defaultSeconds per call.
promptbuilt from the schemePrompt template. Changing it makes a new source version.
confidence{method: auto}How the source states confidence. See Confidence methods.
concurrencyruntime.concurrencyParallel calls to this source.
schemasall engine schemesRestrict the source to these schemes.
options{}Kind-specific options, listed below.
judge{}judge only. The same options as options, under their own key, and options wins where both set one.
membersnonejury only. At least two other source ids.
aggregationpluralityjury only. Plurality is the one rule.
field / predictions_filenonestatic only. Where the existing labels are. The field holds {scheme: label}, or a bare label when the source labels one scheme. A field of the wrong shape on every item fails the run with a message naming it.
error_rate, seednonefixture only. A synthetic source with a known error rate, for tests.
revisionnoneA model revision to record in the version, for example a checkpoint hash.

Everything that can change an answer (model, prompt, temperature, options) goes into the source's version hash. A new version makes every contract that depends on the source stale.

An answer that is not a value the scheme can hold, such as a label not in its list, a number field that is not a number, or a list on a one-of-N scheme, is recorded as an error and never as a label, and the run's message counts these errors. An item whose calls fail is asked again on later runs, at most three times per source version, and after that it waits for a changed source (a fixed key or prompt is a new version). A run in which one source fails does not stop the other tiers. The next tier labels every item, and potato label run exits with status 1 and names the failed source.

Confidence methods

The engine's acceptance rules threshold on each prediction's confidence, so how a source states confidence decides how much the engine can accept. confidence.method takes one of five values:

confidence.methodApplies toWhat it does
verbalizedllm, vlm, judgeThe model states 0-100 in its reply. One call, and the least reliable method.
k_samplellmsamples replies (5 by default) at sample_temperature (0.7 by default). The label is the most common answer and the confidence is its share. Costs k calls.
logprobllmLabels are lettered and the model answers with one letter, and the confidence is read from the first token's probabilities. One call, usually the best calibrated. Needs a backend that returns logprobs (vLLM, OpenAI-compatible) and at most 20 labels, and a scheme with more is a validation error.
nativedecision, detector, trainedThe model's own probabilities. On an llm source it is a validation error.
autoallnative where the source has it, otherwise verbalized.

Kind-specific options

Five source kinds read options of their own:

  • decision covers Laya in-process, or any /v1/systemone server such as hosted Jev or OpenJev. One forward pass returns a probability for every option, and it works on radio, select, likert and slider schemes. endpoint_type must be laya or systemone, and systemone needs base_url, the server root (a URL ending in /v1/systemone is accepted too). The state is sent as {"text": ...} in both cases, so one checkpoint gives the same answers in-process and behind laya-serve, and options: {state_key: ""} sends the bare string instead. On rating scales (likert, slider), Laya's scores can barely follow the scale, so check a few items before relying on it. The in-process laya endpoint needs pip install 'potato-annotation[decision]'.
  • detector reads boxes from an object detector on image_annotation schemes. keep_threshold (0.35) sets the score at or above which a box is part of the prediction. Boxes between candidate_threshold (0.05) and keep_threshold lower the image's confidence. label_map maps detector classes to scheme labels, and unmapped classes are ignored. image_field names the item field with the image path or URL. Paths resolve as the annotation page resolves them, with /media/x.jpg under the task's media_directory and a relative path under task_dir.
  • judge takes rubric (the scheme's description by default), fields (the item fields shown to the judge, in order) and swap_order, which asks twice with the label order reversed and turns a verdict that flips into an abstention. On agent-trace schemes (process_reward, trajectory_eval, ...) the judge labels each step.
  • vlm uses a vision endpoint (openai_vision, anthropic_vision, ollama_vision). For a self-hosted model behind an OpenAI-compatible server, use openai_vision with base_url.
  • trained takes artifact_dir, trainer (sklearn-text by default) and run_id. Distillation usually creates this source, rather than a hand-written config.

humans

The humans block sets who labels and how the engine samples them:

KeyDefaultMeaning
pools[team]Who labels: team, crowd, solo.
calibration_rateautoShare of items sampled for blind calibration. auto picks a rate from the contracts' projections, enough labels per class to be testable, at most half of a small study and a fifth of one over 1,000 items.
dev_fraction0.5Share of calibration labels used to choose thresholds. The rest test them.
min_calibration_per_class10Floor per class before per-class contracts are tested.
audit_rate0.02Share of newly accepted machine labels sent back to people, blind, to watch for drift.
overlap0.0Share of calibration items labeled by several people.
overlap_k3People per overlap item. The no_worse_than_human contract needs 3.
distribution_k5People per item for distributional contracts.
truthmajorityHow several people's labels on one item become the label contracts test against, majority or dawid_skene.
verify_visibilityshownWhether a verify request shows the machine's answer (shown) or not (blind).

crowd

The crowd block applies when crowd is in humans.pools, and Prolific is the only crowd platform the engine can drive:

KeyDefaultMeaning
providerprolificThe crowd platform.
study_idnoneThe Prolific study to resize.
reward_usdnoneReward per place, for cost projections.
per_worker_quota20Requests per worker. Places are ceil(open requests / quota).

Crowd recruiting is a dry run by default, and the planner reports what it would do. Spending money needs features.crowd_live_spend: true and a budgets.human_usd cap, and a config that turns on live spend without the cap is rejected. Crowdsourcing covers setting up the Prolific study itself.

budgets

The budgets block stops spending before a limit is exceeded:

KeyMeaning
model_usdStop calling priced models once this much is spent. Each call reserves its projected cost first. Models with no price, and self-hosted models on localhost, are not limited by it.
human_usdCeiling on crowd spend. Required for live crowd spend.
human_labelsMaximum human requests the engine may open, counted when each request is opened.

runtime

The runtime block controls batching, concurrency and how long a person may hold a request:

KeyDefaultMeaning
batch_size32Items per committed chunk. A killed run resumes at the next chunk without repeating calls.
concurrency4Parallel calls per source, overridden by a source's own concurrency.
cachetrueReuse replies to identical requests from the same source version.
retries2Extra attempts after a timeout, connection error, 429 or 5xx. Every attempt is logged.
planner_interval30Seconds between planner passes in the server.
hold_minutes60Minutes a person may hold a request without answering before it goes back to the pool. The item also leaves their page, so a tab left open cannot answer it later.

features

Every feature is off by default except drift:

KeyWhat it turns on
driftOn by default. Acts on audit results. When audits show the accepted labels' error rate is above target, the contract reads violated, machine labels on that scheme are no longer accepted, and fresh calibration labels are drawn. Each certification starts a new audit sequence, so certifying again after a fix restores acceptance. Set it to false to report a breach only, and the contract then reads "met, audits breached".
distillTrain a model on the study's labels and put it first in the cascade (see distill).
optimizeHave each planner pass score every subset of the cascade on dev labels and report the cheapest one that keeps the contracts' coverage, at GET /api/engine/v1/schemes/<name>/optimize. Nothing is applied, because adopting a cascade is a config change and a new certification.
crowd_recruitLet the planner size the Prolific study, as a dry run unless crowd_live_spend is on.
crowd_live_spendActually change the Prolific study. Needs the crowd pool and budgets.human_usd.
pii_redactionMask emails, URLs, IP addresses, card numbers, SSNs, phone numbers and @handles before text goes to a third-party API. The call log records what was masked as categories and hashes, never the text.
payload_captureKeep full prompts and replies under <task_dir>/engine_payloads/. Off by default, and the call log keeps hashes only.

redaction

redaction.categories lists which of email, url, ip, card, ssn, phone and handle to mask, and defaults to all of them. Masking applies only with features.pii_redaction, and only to third-party APIs. Self-hosted endpoint types (vllm, ollama, laya, yolo, ...) are not masked wherever they run, and neither is any endpoint whose base_url is on the same machine.

distill

With features.distill on, the engine trains a cheap model on the study's labels and puts it first in the cascade:

KeyDefaultMeaning
trainersklearn-textA trainer from potato/training.
min_labels200Train only once this many labels exist.
include_silvertrueAlso train on machine labels a contract certified, marked silver.
every2.0Retrain when the label count has grown by this factor.

Items in the cert and audit splits never go into the training set, so the distilled model is never tested on labels it was trained on.

Validation

potato validate config.yaml checks the labeling_engine block along with the rest of the config, and each error names its key, for example labeling_engine.sources[1].kind must be one of ... or labeling_engine.contracts[0].error is required for selective_risk. Validation also refuses:

  • a contract key the contract kind does not read, such as a typo of error;
  • a contract on a scheme no cascade source can label, with each source's reason;
  • a contract on a scheme the engine cannot score (free text, pairwise);
  • a decision source whose endpoint_type is not a decision model, and an llm, vlm or judge source whose endpoint_type is one; and
  • no_worse_than_human with humans.overlap_k below 3.

Validation also warns when budgets.human_labels is too small to certify a selective_risk contract even if every machine answer were right. Raise the budget, or loosen error, until that warning goes away.

Further reading

For implementation details, see the source documentation.