Gero-4B

Gero-4B is a System One model: it evaluates a question against a state and returns a probability for every possible answer. Your code can branch, sort and route on those probabilities with no generated text to parse.

Language models are built to write text for people. When your code needs a judgment instead (which queue a ticket belongs in, how severe an incident is, whether a customer is satisfied), text is the wrong shape. You prompt for a format, parse the reply, and hope the parse holds.

Gero-4B skips that step. You give it a state, the content being judged, and a question made of instructions and criteria. It returns a calibrated probability for each answer: when it says 0.9, it should be right about nine times in ten. The model never writes text, so there is no generated output to parse or validate.

Gero-4B is the first model in the Gero family. The name comes from Gerolamo Cardano, whose Liber de Ludo Aleae (c. 1564) was the first systematic study of probability.

Question types

Question type Goal Returns
Choice Pick one option from a set choice, probabilities, confidence
Score Place the state on ordered levels score, probabilities, confidence
Yes/no Decide whether a statement holds the probability of yes (0–1)

Each option is evaluated against the same state on its own, and options never see one another. The order you list them in does not change any probability, and a question can have 2 options or 256. See Architecture.

One judgment per question

Gero-4B works best when each question asks one narrow thing: a call a knowledgeable person could make in a few seconds, given the right context.

When a decision depends on several factors, ask about each factor separately and combine the answers in your code. To prioritise a ticket, for example, ask separately how urgent it is and how many users it affects, then combine the two scores with your own rule. When the rule changes, you edit a line of code instead of rewriting a prompt. Arithmetic, lookups and fixed business rules belong in code too.

Choice

A Choice picks one option from a set. The answer includes the selected option, a probability for each option, and the confidence in the selection.

A Choice question has two fields:

  • instructions: the question the model answers.
  • criteria: the options. A list of names, or a mapping from each name to a short description of what it means. Up to 256 options.

This example routes a support ticket to a team:

import json, torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification

REPO = "vixhal-baraiya/Gero-4B"
tok = AutoTokenizer.from_pretrained(REPO)
model = AutoModelForSequenceClassification.from_pretrained(REPO, torch_dtype=torch.bfloat16).eval()
SYSTEM = "Judge how well the Option answers the Question, given the State."

def probabilities(state, instructions, options):
    """One probability per option, in the order given."""
    if not isinstance(state, str):
        state = json.dumps(state, ensure_ascii=False)
    prefix = (f"<|im_start|>system\n{SYSTEM}<|im_end|>\n"
              f"<|im_start|>user\n<State>: {state}\n<Question>: {instructions}\n")
    batch = tok([f"{prefix}<Option>: {o}<|im_end|>" for o in options],
                return_tensors="pt", padding=True, add_special_tokens=False)
    with torch.no_grad():
        logits = model(**batch).logits.squeeze(-1).float()
    return torch.softmax(logits, -1).tolist()

def choice(state, instructions, criteria):
    """criteria: a list of options, or {option: description}."""
    keys = list(criteria)
    shown = [f"{k} - {criteria[k]}" if isinstance(criteria, dict) and criteria[k] else k for k in keys]
    probs = dict(zip(keys, probabilities(state, instructions, shown)))
    selected = max(probs, key=probs.get)
    return selected, probs, probs[selected]

state = "I was charged twice for my March invoice, please refund one of them."

selected, probs, confidence = choice(
    state,
    "Which team should handle this ticket?",
    {
        "billing": "Payments, invoices and refunds",
        "technical": "Bugs, errors and outages",
        "account": "Login, password and profile settings",
        "shipping": "Delivery, tracking and returns",
    },
)

print(selected, round(confidence, 3))
print({k: round(v, 3) for k, v in probs.items()})

The descriptions tell the model what each internal name means; a bare list of names works when the names already say it. confidence is the probability of the selected option.

Score

A Score places the state on a scale of ordered levels. The answer includes the score, a probability for each level, and confidence.

A Score question has two fields:

  • instructions: what is being rated.
  • criteria: the level descriptions, ordered from the low end of the scale to the high end. Two to ten levels.

This example rates how much a reported problem blocks work:

import json, torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification

REPO = "vixhal-baraiya/Gero-4B"
tok = AutoTokenizer.from_pretrained(REPO)
model = AutoModelForSequenceClassification.from_pretrained(REPO, torch_dtype=torch.bfloat16).eval()
SYSTEM = "Judge how well the Option answers the Question, given the State."

def probabilities(state, instructions, options):
    """One probability per option, in the order given."""
    if not isinstance(state, str):
        state = json.dumps(state, ensure_ascii=False)
    prefix = (f"<|im_start|>system\n{SYSTEM}<|im_end|>\n"
              f"<|im_start|>user\n<State>: {state}\n<Question>: {instructions}\n")
    batch = tok([f"{prefix}<Option>: {o}<|im_end|>" for o in options],
                return_tensors="pt", padding=True, add_special_tokens=False)
    with torch.no_grad():
        logits = model(**batch).logits.squeeze(-1).float()
    return torch.softmax(logits, -1).tolist()

def score(state, instructions, criteria):
    """criteria: level descriptions, ordered from the low end to the high end."""
    probs = probabilities(state, instructions, criteria)
    return sum(i * p for i, p in enumerate(probs)), probs, max(probs)

state = "Since the 10:00 deploy nobody in the EU region can log in."

value, probs, confidence = score(
    state,
    "How much does this problem stop the user from working?",
    [
        "Nothing is blocked; it is cosmetic or a question",
        "Work is slower, but a workaround exists",
        "Work is fully blocked for this user",
        "Work is blocked for many users at once",
    ],
)

print(round(value, 2), round(confidence, 3))
print([round(p, 3) for p in probs])

Each level is numbered by its position in criteria, starting at 0, so the four levels above are 0 to 3. The score is the probability-weighted position on that scale and can land between two levels. Every level is judged against the state on its own, so describe each one as a concrete situation that makes sense without the others.

Yes/no

A yes/no question asks whether a statement holds and returns the probability that the answer is yes.

A yes/no question has two fields:

  • instructions: the yes/no question, or a statement to judge.
  • criteria: optional. What counts as a yes (true) and what counts as a no (false).

This example checks whether a customer is satisfied, first with the question alone and then with criteria:

import json, torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification

REPO = "vixhal-baraiya/Gero-4B"
tok = AutoTokenizer.from_pretrained(REPO)
model = AutoModelForSequenceClassification.from_pretrained(REPO, torch_dtype=torch.bfloat16).eval()
SYSTEM = "Judge how well the Option answers the Question, given the State."

def probabilities(state, instructions, options):
    """One probability per option, in the order given."""
    if not isinstance(state, str):
        state = json.dumps(state, ensure_ascii=False)
    prefix = (f"<|im_start|>system\n{SYSTEM}<|im_end|>\n"
              f"<|im_start|>user\n<State>: {state}\n<Question>: {instructions}\n")
    batch = tok([f"{prefix}<Option>: {o}<|im_end|>" for o in options],
                return_tensors="pt", padding=True, add_special_tokens=False)
    with torch.no_grad():
        logits = model(**batch).logits.squeeze(-1).float()
    return torch.softmax(logits, -1).tolist()

def yes_no(state, instructions, criteria=None):
    """criteria: optional {"true": ..., "false": ...}. Returns P(yes)."""
    c = criteria or {}
    return probabilities(state, instructions, [c.get("false") or "no", c.get("true") or "yes"])[1]

state = "Setup took two minutes and it has worked perfectly every day since."

p = yes_no(state, "Is the customer happy with the product?")
print(round(p, 3))

p = yes_no(
    state,
    "Is the customer happy with the product?",
    {"true": "The customer is clearly satisfied",
     "false": "The customer is unhappy, or mixed at best"},
)
print(round(p, 3))

Phrase the question positively ("Is the item in stock?"), not as a negation ("Is it false that the item is out of stock?"). If a no-outcome matters to you, say so in criteria. A result near 0.5 means yes and no are about equally likely, not that the statement is half true. When several labels can apply at once, ask one yes/no question per label instead of a Choice.

Confidence

Gero-4B is trained so that its probabilities can be taken at face value. Use them to decide what your code does: act automatically above one threshold, send the case to a person below it. Set thresholds on your own data, because the right one depends on the cost of a mistake. At the very top of the range the model runs slightly optimistic, so leave some margin on thresholds close to 1.

Prompt format

The helpers above build the format the model was trained on. If you write your own, each option becomes one sequence:

<|im_start|>system
Judge how well the Option answers the Question, given the State.<|im_end|>
<|im_start|>user
<State>: {state}
<Question>: {instructions}
<Option>: {option}<|im_end|>

Score all the options of a question and apply a softmax across them. Tokenize with add_special_tokens=False. The state can be plain text or a JSON object, and a JSON object helps when the context has several parts. For yes/no questions, the two options are the false text and then the true text, defaulting to no and yes. For choices with descriptions, each option is written as name - description.

Every option of a question shares the same prefix. A serving stack with prefix caching encodes the state once, so extra options cost only their own tokens.

Language support

English is the training language and where Gero-4B works best. Test on your own content before relying on it for other languages.

Architecture

Gero-4B starts from Qwen/Qwen3-4B, a text generator, and is rebuilt into a scorer.

  • No language-model head. Generation is removed. A single learned linear scorer (score.weight) reads the final token of each option and returns one number.
  • A branching cross-encoder. The state and the question form a shared prefix. Each option is its own branch that attends to every token of that prefix, so options are judged jointly with their context, as in a cross-encoder. Attention runs one way only: the prefix never sees the options, and options never see one another.
  • Options compete. A softmax across the branches turns the scores into one distribution, and training targets that distribution, so the options of a question are weighed against each other even though they are scored separately.

The design has three consequences:

  • No position bias. Nothing in the model depends on option order, so reordering the options changes no probability.
  • Any number of options. One scorer serves every branch, so 2 options and 256 options work the same way, with nothing reserved per slot.
  • The state is read once. With prefix caching, a question costs the state plus the options' own tokens, however many options there are.

Training

Training happens in three stages. Each stage has one job, and each is checked so it doesn't undo the stage before it.

  1. Readout. The branching architecture and the shared scorer, described above.
  2. Instruction and structure. Purpose-built data teaches the model to read a question rather than guess from its content. It covers the three question types, option sets from 1 to 256, ordered scales, and reading skills such as negation, information that isn't stated, binding the right attribute to the right entity, and scope. Before any training, the data is audited for shortcuts the model could exploit instead of reading: label leaks, position patterns, answers guessable without the state, and string-matching cues.
  3. Reinforcement learning for calibrated decisions. Outcomes are sampled from each item's true answer distribution. The reward combines the probability the model gave the sampled outcome with the calibration of its top answer's confidence. The reward is a proper scoring rule: the model maximises it only by reporting the true probabilities, so it cannot gain by being overconfident or by hedging. Training mixes clear-cut items with genuinely ambiguous ones, so the model learns both to commit when the answer is certain and to split probability when it isn't.

License

Apache 2.0, the same as the base model.

Downloads last month
26
Safetensors
Model size
4B params
Tensor type
BF16
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for vixhal-baraiya/Gero-4B

Finetuned
Qwen/Qwen3-4B
Finetuned
(1077)
this model