cribl-decision-1.0-0.8B
A parameter-efficient LoRA adapter for structured decision prediction. Given a state, one or more typed questions, and a set of candidate options, cribl-decision returns probability distributions over the supplied candidates.
This is the 0.8B variant of cribl-decision. It targets latency-sensitive
and resource-constrained deployments, including CPU-only environments.
This repository contains the LoRA adapter only. The Qwen base-model weights are not included.
| Property | Value |
|---|---|
| Base model | Qwen/Qwen3.5-0.8B |
| Adapter size | 7.5 M trainable parameters (0.99 % of backbone) |
| LoRA rank | 16 |
| Decision types | choice, noul, score |
| Max candidates | 255 per question |
| Readout | Hidden-state → lm_head → candidate softmax (no text generation) |
Highlights
- LoRA adapter — separate from the 0.8 B base; swap or freeze independently.
- Structured candidate scoring — probabilities come from projecting the
hidden state through the native
lm_head, restricted to candidate-letter tokens. No text is generated. - Three decision types — categorical choice (pick + distribution), noul (Boolean probability), score (ordinal distribution + expected value).
- Up to 255 candidates — labels A–Z, then two-letter single-token labels (AA, AB, …).
- Multi-slot readout — multiple questions packed in one forward pass, each
read at its own
Answer k: (slot, conditionally independent. - Small enough for CPU-only inference.
Evaluation
26-task held-out evaluation
Evaluated on 29,743 held-out records across 26 tasks.
| Metric | Result |
|---|---|
| Overall accuracy | 68.52 % |
| Overall Macro-F1 | 78.03 % |
| Task-macro accuracy | 69.33 % |
| Task-macro Macro-F1 | 69.02 % |
The held-out corpus was kept separate from training and was not used for checkpoint selection.
Comparison with other models
| Model | Params | Track A Acc | Track A F1 |
|---|---|---|---|
| cribl-decision (4B) | 4B LoRA | 77.3 % | 86.5 % |
| JPT-9B | 9B | 75.8 % | 31.4 % |
| JPT-4B | 4B | 73.8 % | 30.2 % |
| cribl-decision-1.0-0.8B | 0.8B LoRA | 68.5 % | 78.0 % |
| Jev-Omni | 12B | 68.2 % | 7.8 % |
| JPT-0.8B | 0.8B | 65.4 % | 24.9 % |
| Decision-1.0-Eos-0.8B | 0.8B | 51.2 % | 5.7 % |
At 0.8B parameters, this model matches the accuracy of the 12B Jev-Omni while achieving 10× higher F1 and 3× higher F1 than the next-best 0.8B model (JPT-0.8B).
Intended use
- Classification and intent routing
- Structured policy and safety judgments
- Tool and agent-action selection
- Preference and quality comparisons
- Knowledge and question answering over explicit candidates
- Ordinal or rubric-based scoring
- Local decision services that require probabilities
- Edge and CPU-only deployments where the 4B variant is too large
Not intended to replace human review in safety-critical, legal, medical, financial, or other high-impact decisions.
Quick start
Requirements
pip install torch transformers peft
Loading the adapter
Important: Use
Qwen3_5ForConditionalGeneration, notAutoModelForCausalLM. In transformers ≥ 5.19,AutoModelForCausalLMresolves toQwen3_5ForCausalLM, which silently ignores the LoRA adapter.
import torch
from transformers import AutoTokenizer, Qwen3_5ForConditionalGeneration
from peft import PeftModel
BASE_ID = "Qwen/Qwen3.5-0.8B"
ADAPTER = "cribl-ai/cribl-decision-1.0-0.8B"
tokenizer = AutoTokenizer.from_pretrained(BASE_ID)
base = Qwen3_5ForConditionalGeneration.from_pretrained(
BASE_ID, torch_dtype=torch.bfloat16,
)
model = PeftModel.from_pretrained(base, ADAPTER, is_trainable=False)
model = model.to("cuda").eval()
Prompt format
The model was trained on the following prompt layout. Using a different format will degrade accuracy significantly.
Context:
<state — JSON object, plain text, or any string>
Question: <question text>
Options:
(A) <candidate 1>
(B) <candidate 2>
(C) <candidate 3>
Answer: (
For multiple questions in one pass:
Context:
<state>
Question 1: <text>
Options:
(A) ...
(B) ...
Question 2: <text>
Options:
(A) ...
(B) ...
Answer 1: (
Answer 2: (
Structured readout
The model does not generate text. Instead:
- Run one forward pass through the transformer (
language_model). - Take the hidden state at the
(token position (the last token ofAnswer: (). - Gather the
lm_headweight rows for the candidate-letter token IDs only. - Compute
logits = candidate_weights @ hidden_state. - Apply softmax to get a probability distribution over the candidates.
# After loading the model as above:
base_model = model.get_base_model()
language_model = base_model.model.language_model
lm_head = base_model.lm_head
LETTERS = "ABCDEFGHIJ"
@torch.inference_mode()
def score(prompt, n_options):
ids = tokenizer.encode(prompt, add_special_tokens=False)
input_ids = torch.tensor([ids], dtype=torch.long, device="cuda")
mask = torch.ones_like(input_ids)
hidden = language_model(input_ids=input_ids, attention_mask=mask).last_hidden_state
h = hidden[0, -1] # hidden state at the "(" token
letter_ids = [tokenizer.encode(LETTERS[i], add_special_tokens=False)[0]
for i in range(n_options)]
cand_ids = torch.tensor(letter_ids, dtype=torch.long, device="cuda")
weights = lm_head.weight[cand_ids]
logits = (weights @ h).float()
probs = torch.softmax(logits, dim=-1).cpu().tolist()
return probs
Complete example
import json
state = {"message": "My card was charged twice for the same purchase."}
question = "Which department should handle this?"
options = ["billing", "technical support", "sales"]
prompt = (
f"Context:\n{json.dumps(state)}\n\n"
f"Question: {question}\n"
f"Options:\n"
+ "\n".join(f"({LETTERS[i]}) {opt}" for i, opt in enumerate(options))
+ "\nAnswer: ("
)
probs = score(prompt, len(options))
for i, (opt, p) in enumerate(zip(options, probs)):
print(f"({LETTERS[i]}) {opt:20s} {p:.4f}")
A standalone runnable script is provided in
example_readout.py.
Triton workaround
On some systems (e.g. DGX boxes without Python.h for Triton JIT), the first
forward pass may fail with a Triton compilation error. Add this before
importing transformers:
from torch._native import triton_utils
triton_utils.deregister_op_overrides()
Decision types
Choice
A categorical pick from the candidate set, plus the full probability distribution.
{
"type": "choice",
"choice": "network",
"probabilities": {"network": 0.999, "file": 0.001}
}
Noul
A Boolean probability for a yes/no proposition (two candidates: the proposition and its negation).
{
"type": "noul",
"probability": 0.91
}
Score
An ordinal distribution over ordered levels, plus the expected score.
{
"type": "score",
"distribution": {"low": 0.05, "medium": 0.30, "high": 0.65},
"expected": 2.60
}
Label table
| Options | Labels |
|---|---|
| ≤ 10 | A, B, C, D, E, F, G, H, I, J |
| 11–26 | A–Z |
| 27–255 | A–Z, then AA, AB, AC, … (two-letter single-token labels) |
Every label is guaranteed to be a single token in the Qwen3.5 tokenizer.
Local API usage
The decision server exposes a single endpoint:
curl -s http://localhost:8080/v1/systemone \
-H "Authorization: Bearer $DECISION_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "cribl-decision-1.0-0.8B",
"state": {"message": "My card was charged twice"},
"questions": [
{
"type": "choice",
"text": "Which department should handle this?",
"criteria": {
"billing": "Billing and payment issues",
"technical support": "Technical problems",
"sales": "Purchase and pricing questions"
}
}
]
}'
Training data
Trained on a broad mixture of public decision datasets spanning classification, intent, preference, safety, knowledge, reasoning, agent/tool, and rule families. Max context length: 8,192 tokens. Max candidates per question: 255. Option order shuffled; large label sets sub-sampled to 10 with the gold always kept. Task-balanced with inverse-sqrt frequency weights (capped at 4.0). Base transformer frozen; only LoRA parameters were trained.
Limitations
- Can produce incorrect or overconfident decisions.
- Accuracy varies substantially by task family.
- Sensitive to prompt formatting, candidate wording, candidate order, and tokenizer behavior.
- Generic text generation (
model.generate()) is not the intended use — use the structured readout described above. - Latency depends on hardware, precision, context length, and batching.
- The 26-task held-out result does not establish performance on every domain.
- Calibration should be rechecked on the target application's data.
- The 0.8B variant is less accurate than the 4B variant, especially on hard multi-class tasks.
Security and responsible use
- Deployers are responsible for protecting credentials (
DECISION_API_TOKEN, SSH keys,.envfiles) and keeping sensitive data out of logs. - Validate inputs and monitor calibration in production.
- Add human review for high-impact decisions.
License
Apache-2.0, where compatible with the base-model license. The Qwen3.5-0.8B model is subject to its own license terms.
Citation
@misc{cribl-decision-0.8b-2026,
title = {cribl-decision-1.0-0.8B: A LoRA Adapter for Structured Decision Prediction},
author = {{Cribl AI}},
year = {2026},
publisher = {Hugging Face},
howpublished = {\url{https://huggingface.co/cribl-ai/cribl-decision-1.0-0.8B}}
}