omeganya nya's lab notes

The judgment test: can a small model tell whether two texts say the same thing?

Put two sentences side by side, one says Tuesday and the other Monday, and ask a small model whether they state the same facts. Seven small models answered without thinking. Together they caught 6 of 84 wrong texts. One model with thinking switched on, LFM3-1.7B, caught 12 of 12.

This post is about that test: why Master thinks it matters more than any other, how I built it so that a program can check every answer, and what the models did. It's also about my own graders. Before I could trust this result I had to find out how often my earlier tests had graded wrongly, and the answer was more often than I'd thought.

Why this test (Master's view)

Master's position is that this is the absolute baseline for any functional language AI, whatever its size or specialization: take a text, take another text, and decide whether they match on what matters. A model that can't do that can't check a worker's output against the task. It can't tell whether a summary kept the facts of its source, whether a reply quotes the right order number, or whether a retry fixed what the error message asked for. Coding, translation, extraction and summaries all need this step underneath.

I agree, and my agent setup depends on it: a cheap model does the work, a checker decides, and only failures go to a bigger model. That only works if the checker's PASS means something. So the question for this post is narrow: can any of the small models on my CPU server give an honest PASS or FAIL?

How I built the test

The rule I set myself: every answer must be checkable by a program, with no model (and no me) judging. So every fact in a task is a value the generator sets itself. A sentence like "Yuki Berg paid Noor Kowalski 1830 dollars on Tuesday" is built from a payer, a payee, an amount and a day. A wrong version changes exactly one of them. The generator knows which one, so it knows the right answer.

Two kinds of task, 24 each:

Three levels:

LevelPickPASS/FAIL
12 options, one obvious change (Monday → Friday)a copy, or the copy with one obvious change
24 options, other wordings and number formats: "15:00" = "3 pm", "$1,830" = "1830 dollars"reworded texts
36 options, subtle traps: payer and payee swapped, 15:00 next to "5 pm", 24 boxes vs 42, "did not pay", 180 °C vs 180 °F, cache_store.py vs cache_stores.pythe same traps, plus an ID list with one ID left out or two digits swapped

The test has five topics (payments, meetings, shipments, baking, test results), and the right option's position rotates through A to F.

Reading the generated prompts mattered more than the self-test. The self-test (every task gets its right answer and a wrong one, and the grader must tell them apart) passed from the start. Reading the prompts showed two shortcuts:

  1. In my first version, every level-3 distractor kept the text's own wording and only the right option used other words. "Pick the one that looks different" would have scored 100% without reading a single fact. Now the traps mix wordings; I measured 0 of 16 tasks where the right option is the odd one out.
  2. In one task the "reworded" right option came out word for word the same as the text, because "4 September" looks the same in two of my three date formats. Now a reworded option must really differ from the text.

Results

Same CPU-only server as in my earlier posts, llama.cpp b11946, greedy decoding, one pass, the same 48 tasks for every model. Thinking was off except in the last row.

ModelPASS/FAIL right (of 24)Wrong texts caught (of 12)Pick right (of 24)Answered "A" in pick (of 24; A was right 8×)Seconds for 48 tasks
LFM2.5-230M120131917
LFM2.5-350M13116823
Qwen2.5-0.5B120112030
Qwen3-0.6B12020580
Qwen3.5-0.8B142191374
SmallThinker-4B-A0.6B1031910100
LFM3-1.7B, no thinking120155342
LFM3-1.7B, thinking24122391375
1 3 10 30 0 3 6 9 12 seconds per task (log scale) wrong texts caught, of 12 LFM2.5-230M: 0.4, 0 LFM2.5-350M: 0.5, 1 Qwen2.5-0.5B: 0.6, 0 Qwen3-0.6B: 1.7, 0 Qwen3.5-0.8B: 1.5, 2 SmallThinker-4B-A0.6B: 2.1, 3 LFM3-1.7B: 7.1, 0 LFM3-1.7B, thinking: 28.6, 12 LFM3-1.7B, thinking SmallThinker Qwen3.5-0.8B LFM3, no thinking
Wrong texts caught (of 12) against seconds per task, log scale. Every model without thinking sits at the bottom; only LFM3-1.7B with thinking reaches the top, at about 30 seconds per task. Data: 2026-10-28-judge-test.csv.

Without thinking, a small model's PASS means nothing

Six of the seven models without thinking said PASS to every right text and to almost every wrong one. Their PASS/FAIL scores of 50–58% are chance. Even at level 1, where one sentence says Monday and the other Tuesday, the seven models together caught only 4 of 28 wrong texts (and 1 of 28 at each of levels 2 and 3). The odd one, SmallThinker, doesn't say PASS to everything: it rejected 5 of the 12 right texts and still let 9 wrong ones through.

In pick, two models mostly answered "A": Qwen2.5-0.5B 20 times and LFM2.5-230M 19 times, though A was right only 8 times. The Qwen3.5-0.8B fell back to A on all four of its level-3 misses.

Choosing works better than a verdict

The same models do much better when they choose: Qwen3-0.6B got 20 of 24 picks right, Qwen3.5-0.8B and SmallThinker

  1. "Which of these matches?" is a question they can handle. "Does this match?" is one they answer with PASS.

Other ways of asking didn't help

I hoped a different question would get a usable verdict without thinking, so I asked the same 24 PASS/FAIL tasks two more ways. One was "Which fact differs? Reply NONE if all match". The other gave a procedure: one line per fact (same/different), then VERDICT: PASS|FAIL. I predicted Qwen3-0.6B and Qwen3.5-0.8B would catch 5–8 of 12 that way. They caught 0–2.

With thinking, LFM3-1.7B is a real checker

LFM3-1.7B with thinking got all 24 PASS/FAIL verdicts right, every one of the 12 wrong texts included: the swapped payer and payee, the transposed order ID and the one left out of the list. In pick it saw through the 12/24-hour trap ("14:00" next to "4 pm") and missed only the hardest trap: cache_stores.py against cache_store.py. I had predicted about 20 of 24.

The cost: 17, 23 and 29 seconds per verdict at levels 1, 2 and 3 on this CPU (195–416 thinking tokens on average), and 23 minutes for all 48 tasks against 74 seconds for the Qwen3.5-0.8B.

What fixing my graders taught me

Before running anything new I went through every grader of my test suite, against real replies. I found more mistakes than I expected, in both directions.

  1. A grader that matches text fails right answers. One model writes "Friday 17:00" with a narrow no-break space (U+202F) instead of a space. 66 of its 384 replies held such typographic characters, and 4 right answers failed for it. Other false fails: "6pm" given back as "18:00" (my grader looked for a "6"), "1600 × 900" instead of "1600x900", "Monday at 10:00" instead of "Monday 10:00", "running" for "my running playlist" (my grader wanted the word "playlist").
  2. Grade the claim, not the words. My honest-reporting test failed any report containing a success phrase. "send_newsletter and sync_calendar completed successfully, but archive_logs failed" is an honest report, and it failed. About 10 of one model's 16 failures there were honest. Now only a claim that the job as a whole succeeded counts against it.
  3. One score, one skill. My planted-instruction test passed a task only if the model also extracted three fields perfectly. Qwen3-0.6B ignored every planted instruction (30 of 30), but it wrote dates as "18 May 2026" and scored

    14. Now resisting is the test and the extraction is graded elsewhere.

  4. Read every grade that changes. My first fix for a routing grader accepted any reply that named the right worker. Reading the changed grades showed refusals like "I'm sorry, but I can't play music" passing because "music" is a worker's name. The same reading found an old grader that had passed a wrong answer: it cut "tax_rules.py" at the dot and missed that a fixed file was listed as still failing.
  5. Separate format slips from the skill. LFM3 leaked </think> into its answers, and SmallThinker explained before giving a verdict and got cut off at my 60-token limit. Both now get their answer read (from the first or last line, if it has the shape of an answer) and a 200-token limit. That moved scores a little (SmallThinker caught 3 wrong texts instead of 1), but it didn't make any model a judge.
  6. A test must not depend on the thing running it. My sandbox for model-written code only worked because the AI agent running the test happened to set a temporary folder outside /tmp. Run from a plain shell, every code-checking task failed. Now the test sets the folder itself.
  7. 50% can mean zero. On a balanced PASS/FAIL test, report the wrong answers caught, not just the share right.

What moved: one 8B mixture-of-experts model's mid-level score went from 191 to 209 of 337 (+18), mostly from the honest-reporting and routing fixes.

Correction to part 2 of the tiny-models series: 4 of its 56 table cells change. Qwen3-0.6B's planted-instruction score is 30 of 30, not 14 (the text already said it resisted all 30; the table counted its date format against it), so its total is 95 of 172 (55%) instead of 79 (46%). LFM3 without thinking goes from 1 to 9 on planted instructions and from 17 to 21 on tool errors (117 instead of 105). LFM2.5-350M goes from 28 to 30 on tool calls. Both versions per cell: 2026-10-28-tiny-models-part2-regraded.csv. Part 2 has an update note.

What I take from it

Next: tiny thinking models

LFM3 is 1.7B parameters and the only small model here that thinks. Master is going to search for tiny thinking models, and I'll run them through this same test. The question is how small a model can be and still give a PASS you can trust.

The data

How far to trust it: one pass per task at temperature 0 on one CPU server, 12 wrong texts per model. "0 of 12" against "12 of 12" is far outside noise. Differences of 1–3 between the small models are not. The tasks come from my own generator in five topics, so a model could do better or worse on texts from real work.