by Nimruz · GGUF: Mizan-v3-3B-GGUF · all models: Mizan collection
A Persian-first decision model. You give it a record (text or JSON) and a set of typed questions. It returns a calibrated probability for every allowed answer, in one forward pass. There is no text generation, no parsing and no made-up labels.
/v1/systemone.| Model | Reads | Download (4-bit) | Use it for |
|---|---|---|---|
| Mizan v3 3B (this one) | text, JSON | 1.9 GB | The default: routing, policies, scores, moderation, reviews, intent |
| Mizan v2 4B | text + images | 2.7 GB + 0.37 GB | Camera frames, receipts, factory and product photos |
v1 and v1.5 tags of this repo, but v3
replaces both.
| Use | Example question |
|---|---|
| Ticket routing by your policy | "Following the policy in the record, which team gets this?" |
| Severity, urgency, risk on your rubric | "Impact 0–3: 3 = over 200 people or more than 50M toman lost" |
| Next step by priority rules | "Close / ask for details / escalate / assign, per the policy" |
| Fact checks against the record | "The customer gave a tracking number." (yes/no) |
| Moderation, sentiment, intent, topic | the classic Persian tasks, on reviews, tweets, messages |
Questions can be in Persian or English, and they can be new questions it has never seen. Options
can have meaningful keys (billing) or opaque ones (opt_1), because it reads the descriptions.
1. Install llama.cpp (build b11443 or newer):
brew install llama.cpp # macOS / Linux with Homebrew
# or: download a release for your OS from https://github.com/ggml-org/llama.cpp/releases
llama-server --version # check the build number
2. Download Mizan (1.9 GB):
pip install -U huggingface_hub
hf download Nimruz/Mizan-v3-3B-GGUF mizan-v3-3b-Q4_K_M.gguf --local-dir .
3. Start the server:
llama-server -m mizan-v3-3b-Q4_K_M.gguf -c 8192 -b 8192 -ub 8192 -ngl 99 --port 8080 # GPU
llama-server -m mizan-v3-3b-Q4_K_M.gguf -c 8192 -b 8192 -ub 8192 -ngl 0 --port 8080 # CPU only
4. Ask your first question. A request has a state (your record, as text or JSON) and questions
(each with a type and instructions):
import httpx
request = {
"state": {"review": "غذا سرد رسید و پیک هم خیلی بداخلاق بود"},
"questions": {
"sentiment": {"type": "choice", "instructions": "حس کلی مشتری چیست؟",
"criteria": {"positive": "راضی", "negative": "ناراضی"}},
"refund": {"type": "noul", "instructions": "آیا مشتری درخواست بازگشت پول کرده؟"},
},
}
answers = httpx.post("http://localhost:8080/v1/systemone", json=request, timeout=60).json()["answers"]
print(answers)
The real output of Mizan v3 Q4:
{"sentiment": {"type": "choice", "choice": "negative", "confidence": 0.989,
"probabilities": {"negative": 0.989, "positive": 0.011}},
"refund": {"type": "noul", "noul": 0.040}}
5. Read the answer. There are three question types:
type | You give | You get back |
|---|---|---|
choice | criteria: {key: description} | choice (the top key), confidence, probabilities for every key |
noul | a yes/no statement or question | noul: the probability of "yes" |
score | criteria: a list of levels, lowest first | score: the expected level, e.g. 2.3; probabilities per level; legend |
For a whole-number level, take the level with the highest probability. score is the probability-weighted
average, which is useful for sorting.
6. Give it your rules. This is where v3 shines. Put the policy and the rubric in the record:
{
"state": {
"message": "سلام، از دیروز اینترنت کل ساختمون ما قطعه، حدود ۴۰ واحد. کد پیگیری K48213",
"customer": {"tier": "ویژه", "previous_follow_ups": 1},
"policy": "ارجاع: قطعی و کندی ← شبکه؛ قبض ← صورتحساب. اگر شماره پیگیری نباشد، اول اطلاعات تکمیلی بخواه."
},
"questions": {
"route": {"type": "choice", "instructions": "طبق قواعد، این مورد به کدام تیم برود؟",
"criteria": {"network": "شبکه", "billing": "صورتحساب", "sales": "فروش"}},
"impact": {"type": "score", "instructions": "شدت پیامد کدام سطح است؟ بالاترین سطح صادق را انتخاب کن.",
"criteria": ["فقط یک نفر", "۲ تا ۱۰ نفر", "۱۱ تا ۱۰۰ نفر", "بیش از ۱۰۰ نفر"]},
"has_reference": {"type": "noul", "instructions": "کاربر شماره پیگیری داده است."}
}
}
Mizan v3 Q4 answers:
state as plain text. v3 follows the policy it's given, even when the policy is
unusual.opt_1 works as well as
billing.llama-server --parallel N serves several requests at once. We haven't benchmarked it
for Mizan yet, so load-test it on your hardware.میزان یک مدل تصمیمگیری است، نه یک چتبات. یک «رکورد» (متن یا JSON) و چند سؤال تایپشده میگیرد و برای هر گزینه احتمال برمیگرداند:
choice یعنی انتخاب یک گزینه؛noul یعنی بله/خیر؛score یعنی سطحبندی.قوانین و معیارهای کسبوکارتان را داخل خود رکورد بنویسید. میزان v3 طبق همان قوانین تصمیم میگیرد. برای تصویر از میزان v2 استفاده کنید. پیش از خودکارسازی، مدل را روی چند صد نمونهٔ واقعی خودتان بسنجید.
Unsloth (calibrated, same answers as the GGUF):
from unsloth import FastDecisionModel
model, processor = FastDecisionModel.from_pretrained("Nimruz/Mizan-v3-3B", max_seq_length=4096)
FastDecisionModel.for_inference(model)
answers = FastDecisionModel.predict(model, processor, state, questions)
Tested with unsloth==2026.10.3.
Plain transformers:
import sys
from huggingface_hub import snapshot_download
path = snapshot_download("Nimruz/Mizan-v3-3B")
sys.path.insert(0, path)
from joint_schema_model import load_release_model, systemone
model, processor = load_release_model(path, device="cuda")
print(systemone(model, processor, {"model": "mizan", "state": state, "questions": questions}))
Plain transformers gives uncalibrated probabilities: the same top answer, but sharper confidences. To
calibrate, divide each question's logits by the temperature in unsloth_decision_config.json for its
type (order: choice, score, noul).
3,000 real, human-labeled records from 7 public Persian datasets. Every model gets identical rows and questions, and none of these rows was used for training.
| Task | v3 Q8 | v3 Q4 | Mizan 1.0 | Clef-Flash 9B | Clef 27B | Jev (API) | Laya 322M |
|---|---|---|---|---|---|---|---|
| Food-review sentiment (SnappFood) | 87.0 | 87.0 | 87.0 | 88.0 | 85.8 | 86.0 | 80.8 |
| Product recommend (Digikala) | 71.8 | 72.2 | 74.8 | 76.2 | 75.2 | 74.8 | 44.5 |
| Aspect question in Persian (ParsiNLU) | 81.0 | 80.8 | 71.5 | 80.0 | 78.5 | 80.0 | 67.8 |
| Offensive moderation (Pars-OFF) | 86.0 | 83.8 | 80.8 | 82.2 | 86.8 | 87.5 | 72.2 |
| Intent routing, 60 intents (MASSIVE) | 77.5 | 76.8 | 72.5 | 87.7 | 85.7 | 78.2 | 27.2 |
| Article topic routing (Digikala Mag) | 81.2 | 80.5 | 80.8 | 77.0 | 81.0 | 82.2 | 62.5 |
| Claim supported by passage (FarsTail) | 93.0 | 92.2 | 83.5 | 90.0 | 90.8 | 97.5 | 81.8 |
| Mean | 82.5 | 81.9 | 78.7 | 83.0 | 83.4 | 83.7 | 62.4 |
| Calibration error (ECE, lower is better) | 0.058 | 0.061 | 0.110 | 0.078 | 0.075 | 0.092 | 0.210 |
| Size | 3.1 GB | 1.9 GB | 9.4 GB | ~19 GB | ~55 GB | hosted | 0.7 GB |
Earlier releases score 82.0 (1.5) and 82.8 (v2, which adds images). This benchmark is close to saturated: the top models all land at 82–84%, and about half of the "errors" we reviewed on SnappFood and Digikala were wrong dataset labels.

Tasmim-Bench v1.0.4 has 96 synthetic Persian cases: routing, next steps and severity scores under fictional policies, with colloquial text, Finglish, negation and taarof.
| Choice accuracy | Yes/no F1 | Score error (MAE) ↓ | Overall | |
|---|---|---|---|---|
| Mizan v3 Q4 | 0.917 | 0.970 | 0.458 | 91.1 |
| Mizan 1.0 Q4 | 0.870 | 0.970 | 0.490 | 89.2 |
| gpt-6-luna-decisions (API, #1) | 0.922 | 1.000 | 0.479 | 92.1 |
| Clef (API) | 0.958 | 0.979 | 0.729 | 89.8 |
| Jev 1.13 (API) | 0.943 | 0.989 | 0.760 | 89.3 |

Note: v3 never saw any Tasmim case, and the benchmark's domains were kept out of its training data. But part of that data borrows Tasmim's question format: a fictional policy in the record, 0–3 impact levels, a next-step question. So part of the gain is familiarity with the format. With 96 cases, differences of 1–2 points are within noise.
267 cases from a domain held out of training (school administration), built by the same generator as v3's rule data. Every answer is computed by code from the case's facts and its policy.
| Overall | Routing by policy | Next step | Impact / urgency scores | |
|---|---|---|---|---|
| Mizan v3 Q4 | 97.1% | 97% | 98% | 98–99% |
| Mizan 1.0 Q4 | 70.5% | – | – | – |
The domain is unseen, but the format isn't, so treat this as "it learned the skill", not as a real-world score.
Same 4-bit GGUF on llama.cpp, NVIDIA L4: 55 ms median per text request, against 80 ms for Mizan 1.0. CPU speed is not measured for v3 yet; expect about the same 1.5× gain.

Confidence gating: auto-decide what Mizan is sure about, and send the rest to a human.

A shorter backbone. We measured how well each layer of Qwen3.5-4B supports Persian decisions with a linear probe per layer:
| Layer | 8 | 12 | 16 | 18 | 20 | 24 | 28 | 32 (last) |
|---|---|---|---|---|---|---|---|---|
| Held-out mix | 64.7 | 71.8 | 78.2 | 80.3 | 79.9 | 78.5 | 77.6 | 76.1 |
| Unseen domain | 53.5 | 61.9 | 72.5 | 75.7 | 73.1 | 72.4 | 70.6 | 66.4 |
Order-aware scores. Score questions get an extra loss term: the expected distance from the right level. Being off by one level costs less than being off by three.
Training
| Base | Qwen/Qwen3.5-4B (via unsloth/Qwen3.5-4B), cut to its first 20 decoder layers |
| Head | Clef joint-schema head, trained from scratch |
| Method | Unsloth FastDecisionModel + DecisionTrainer; LoRA r=64 on all linear layers (lr 1e-4), head lr 3e-4, ordinal loss weight 0.5; LoRA merged |
| Schedule | 1 epoch, 1,336 steps, batch 16, cosine, max length 4,096 |
| Calibration | Per-type temperatures fitted on a held-out slice (ECE 0.027 there) |
| Compute | 1× H100, 34 min |
Data: 21.4k decisions, about 57% English and 43% Persian.
typed-decisions.Earlier releases are kept as tags of this repo: v1 (Mizan 1.0) and v1.5 (Mizan 1.5).
Apache-2.0, the same as the Qwen3.5 base model.