Deep dive

Option-marker scoring: how Laya reads a question and its answers together

Most AI classifiers are trained on a fixed list of categories and must be retrained when the list changes. Laya instead reads your list of possible answers alongside the text and scores every option at once, so you can add or rename a category on the very next request.

6 min readLast updated

Swiss-hosted inference. Nothing you send is ever stored.Swiss data residency

In 30 seconds

  • Your answer options are written into the model's input, each with its own marker.
  • The model reads the question, the options and the text together, and scores every option in one pass.
  • Adding or renaming a category needs no retraining: just change the request.
  • All options share a limited amount of space, so accuracy drops with very long lists (published results fall sharply at 77 options).
  • Short, well-described option lists work best; split big lists into two questions.

Code and dense tables are folded away. Open any of them on demand.

What is option-marker scoring?

Option-marker scoring is the technique Laya uses to classify with labels supplied at request time. The options are written into the input, each preceded by a [MASK] marker; the encoder reads the question, all options and the text together, and a scorer reads one logit per marker. A softmax over those logits gives the answer probabilities.

A classic fine-tuned classifier has a fixed output layer: one neuron per label, learned during training. That is efficient, but it ties the model to one label set. Add a department, rename a category or start a new workflow and you need new labelled data, a new training run and a new deployment.

Several techniques let the label set change at request time:

Show technical details· 5 rows × 4 columns
ApproachHow labels enterPasses per decisionLabel-text interaction
Fixed classification headBaked into weights1None (labels are indices)
Zero-shot NLI (entailment)One hypothesis per labelk (one per label)Full cross-attention, but each label seen alone
Embedding similarityLabel and text embedded separately1 + label embeddings (cacheable)None at inference (bi-encoder)
Generative LLMListed in the prompt1 prefill + decode stepsFull, via generation
Option-marker scoring (Laya)Written into the input with a marker each1Full cross-attention, all labels seen together

Option-marker scoring aims for the best cell in each column: one pass, labels supplied per request, and full attention between the labels, the question and the text.

How the sequence is built

For every question, Laya builds one token sequence in this format (from build_sequence in the package source):

Show technical details· text sample
text
[CLS] <type> question: <instructions> [SEP] [MASK] option0 [MASK] option1 ... [MASK] optionK [SEP] <state> [SEP]

Concretely, for a choice question:

Show technical details· text sample
text
[CLS] choice question: Which department should handle this request? [SEP]
[MASK] billing: invoices, payments, refunds
[MASK] technical: bugs, outages, system errors
[MASK] other: everything else [SEP]
{"subject": "Duplicate charge", "body": "We were billed twice for March."} [SEP]

Options are rendered according to the question type:

  • choice: label: description, or just label when no description is given;
  • score: level 0: <description>, level 1: <description>, and so on;
  • noul: always two options, false: <description> then true: <description>, with default wording if you omit criteria.

Object and list states are serialised to JSON. Any literal [MASK] text inside your instructions, options or state is replaced with a space so it cannot be confused with a marker.

How the markers are scored

The model then does four things:

  1. Encode. The encoder (ModernBERT-large or mmBERT-base) runs over the whole sequence with bidirectional attention. Every option marker attends to the instructions, to the other options and to the state.
  2. Condition on question type. A learned type embedding (one vector each for choice, score and noul) is added to every token, and two further transformer layers refine the representation.
  3. Read the markers. The vector at each [MASK] position is gathered and passed through a small scorer (LayerNorm, Linear, GELU, Linear) that outputs a single logit.
  4. Normalise. The logits for that question's options are divided by a calibration temperature and softmaxed into the returned probabilities.
Show technical details· text sample
text
logit_i = scorer( h[marker_i] )
p_i     = exp(logit_i / T) / Σ_j exp(logit_j / T)

The [MASK] token is a deliberate choice. In masked-language-model pretraining, the vector at a [MASK] position is trained to summarise its surroundings in order to predict the hidden token. Reusing it as an option slot gives the scorer a representation that is already context-aware.

Because every option sits in the same sequence, the model compares options directly. "Billing" and "other" are scored with knowledge of each other, which NLI-style zero-shot classification (one pass per label, each label judged alone) cannot do.

All questions in one batch

Each question gets its own sequence, but all sequences in a request are padded into one batch and run through the model together. The model card reports that ten questions cost 158.6 ms on the English checkpoint and 72.3 ms on the multilingual one on a T4, against 39.5 ms and 32.8 ms for one. Per-question cost falls as you batch more.

The state is repeated in each question's sequence, so input tokens scale with the number of questions. That is visible in the usage.input_tokens field of the response.

The token budget, and why large label sets struggle

The sequence has two budgets:

Show technical details· 2 rows × 4 columns
Checkpointmax_len (whole sequence)head_max_len (question + options)Left for state
English512192~320
Multilingual, typed-decisions1,024256~768

Inside the head budget, the rules in the source are:

  • each option is truncated to 48 tokens plus its marker;
  • if the options leave fewer than 16 tokens for the instructions, every option is cut to max(4, (head_max_len − 16) // k) tokens, marker included;
  • the instructions get whatever remains, with a floor of 8 tokens;
  • the state fills what is left of max_len, truncated from the end.

Work through a 77-option question on the multilingual checkpoint: (256 − 16) // 77 = 3, so each option gets the floor of 4 tokens: one marker and three tokens of label text. The instructions are cut to 8 tokens. Many banking intents are indistinguishable in three tokens ("card payment not recognised" vs "card payment wrong exchange rate"). This is the mechanism behind the model card's Banking77 result: 0.425 for Laya against 0.870 published for Jev.

The card suggests two fixes: raise head_max_len (for example to 512) and max_len when self-hosting, or split the decision into a coarse question followed by a fine one. On a hosted API, the second is the practical option. If the markers would not fit in the sequence at all, Laya rejects the question with an error rather than silently dropping options.

Practical rules for writing options

  • Keep option counts modest. Under about 20 per question, and fewer on the English checkpoint.
  • Front-load the distinguishing words. Truncation cuts from the end of each option.
  • Keep descriptions short and parallel. "invoices, payments, refunds" beats a full sentence.
  • Put the decisive part of the state first. State truncation keeps the beginning.
  • Test for order sensitivity. Options are scored in the order you give them. Shuffle on a validation set and check that answers are stable.
  • Use neutral keys when noul sticks. The model card documents noul sometimes following its false:/true: labels; a two-option choice with keys like A/B avoids that.

Sending options per request to Laya Studio

Because options are part of the input, you can change them on every call. This example uses a coarse-to-fine split for a large intent space:

Show technical details· bash sample
bash
curl -s https://api.laya.studio/v1/systemone \
  -H "Authorization: Bearer $LAYA_STUDIO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "state": {"message": "My card payment in euros was charged at a strange exchange rate."},
    "questions": {
      "area": {
        "type": "choice",
        "instructions": "Which area is message about?",
        "criteria": {
          "cards": "card payments, card delivery, lost or stolen cards",
          "transfers": "bank transfers, top-ups, direct debits",
          "account": "identity checks, account access, personal details",
          "fees_and_rates": "fees, charges, exchange rates"
        }
      }
    }
  }'

Then send a second request with only the fine-grained options under the chosen area. Two small questions typically beat one enormous one on this architecture, and each is billed only the input tokens it reads. See /docs for request limits and /signup for a key.

Frequently asked questions

Do I need to retrain Laya to add a new label?
No. Labels are part of the request. Add the option to criteria and it is scored on the next call. Whether the model scores it well depends on how clearly the label and its description relate to the text, so validate new labels on examples.
Is option-marker scoring the same as zero-shot NLI?
No. NLI-based zero-shot classification runs one forward pass per candidate label and judges each label in isolation. Option-marker scoring puts all options in one sequence, so they are scored in a single pass and with awareness of each other.
Why does Laya struggle with 77 labels?
All options share a fixed budget of 192 or 256 tokens. At 77 options each label gets about three tokens of text, so similar labels become indistinguishable. Split large label sets into two questions.
Does the order of options matter?
Options are placed in the sequence in the order you provide, and a model can pick up positional habits. Test by shuffling options on a labelled sample. If answers change, keep a fixed order and calibrate on it.
Why does input token usage grow with the number of questions?
Each question gets its own sequence containing the full state, so the state is encoded once per question. They still run in one batched forward pass, so latency grows much more slowly than token count.
How does Laya classify text without a fixed label set?
It writes your options into the input, each with its own marker token, and scores every marker in one forward pass. The label set is part of the request, not the model's weights, so new labels need no retraining.

Sources

Last updated . Laya Studio is an independent hosted service for the open-source Laya model (Apache-2.0, © Convai Innovations) and is not affiliated with Convai Innovations or TypeSafe.