Mova0.5 pre-release⌕
☰
home / docs / prag — Mova pragmatics: LLM silver and a transparent SLM

prag — Mova pragmatics: LLM silver and a transparent SLM

prag is Mova's pragmatics: the frames reader (who did what to whom, why, what came next), speech acts and their labeller, implicatures, irony candidates, expressions read by their meaning, and the agreer that checks our labels against open witnesses. Pragmatics: frames — who did what to whom, why, and what came next is the guide to the frames.

The small model learns pragmatics with memory models (something like a graph) next to a perceptron. The criterion: if the perceptron gives better quality and even speed, the little graph is not pulling its weight yet.

A separate crate that depends on en, not the other way round. It holds:

Schema (Smart annotator — layers, context, memory)

fieldvalues
formdecl · q · wh · imp · excl · frag · intj · other
actassert · narrate · ask · request · offer · suggest · promise · thank · apologize · greet · agree · disagree · evaluate · express · warn · other
indirect0/1 — the form is not the usual one for the act ("Can you open the window?" — q, but request)
hedge0/1 — maybe, I think, sort of, perhaps
polaritypos · neg · none — the speaker's evaluation; grammatical negation by itself is not neg
voicenarrator · character (direct speech) · none (a standalone sentence outside a story)
meanswhat was meant, up to ~15 words in English; "-" means nothing beyond the literal. Written only by the LLM

A sentence with direct speech inside a narrator frame ("‘Pay me my wages,’ said Hans.") is labelled by the speech itself: voice=character, the frame is ignored. Full definitions are in the prompt (src/silver.rs, RULES). Closed sets are enums (src/schema.rs).

Data

Only texts with a commercial licence. The gate is en::license::commercial_gate; the sources are added to en/data/train-licenses.tsv:

LLM silver and its cost

Run parameters:

sentences sent2,999
accepted by gates2,986
rejects13 (0.43 %)
calls57 (pilot 2 + wave 55), no retries
time~10 min wall clock (pilot 47 s + wave 9 min 09 s); 2,228 s summed over calls — 0.74 s per sentence
tokensoutput 204,999 (68 per sentence), cache: read 485,311, write 1,154,458; uncached input 114

Cache writes are mostly the built-in system prompt of claude -p, ~20k tokens per call. Our prompt is ~4k.

Gates (silver::gate), all with negative controls in tests:

Rejects go to rejects.tsv, with no repeated calls. All 13 are about voice:

There were no format failures and no number shifts.

Label distribution (2,986):

Database (mova.duckdb, schema v2 — migration in db/schema.sql, command db migrate):

SLM

Features (src/feats.rs) — 32 slots with symbolic values, the same for all three models:

If a sentence contains direct speech, the words and the tree are taken from the quoted text. Quotes are tracked across the sentences of a paragraph. "’" between letters is an apostrophe. Titles in quotes ("the title of “Gentlemen Weavers.”") are not treated as direct speech.

Models (src/model.rs) — a shared trait Classifier (predict → class, confidence, explanation; bytes; info). The models run side by side on the same features:

Results

Held-out silver (80/20: tales split by document, Tatoeba by sentence; test 538)

Accuracy / macro-F1, %. Baseline — majority class of the training set. Control — the same model on shuffled labels: it must land at baseline level.

fieldbaselineperceptronIGTreek-NNcontrol: perceptronIGTreek-NN
form77.3 / 12.594.1 / 70.988.7 / 55.891.6 / 63.576.4 / 12.474.3 / 14.077.0 / 12.4
act33.3 / 3.668.8 / 30.065.6 / 25.167.5 / 29.424.5 / 5.429.2 / 5.728.1 / 5.2
indirect90.5 / 47.589.8 / 57.488.7 / 54.090.1 / 52.590.3 / 47.589.0 / 48.790.5 / 47.5
hedge96.8 / 49.297.4 / 74.398.5 / 84.298.0 / 77.596.8 / 49.296.3 / 49.196.8 / 49.2
polarity51.5 / 22.756.1 / 48.248.9 / 31.153.5 / 37.546.8 / 32.246.5 / 27.947.2 / 26.8
voice33.3 / 16.699.6 / 99.698.5 / 98.598.9 / 98.934.4 / 34.531.6 / 27.532.9 / 32.3

The negative control goes red as it should: with shuffled labels the accuracy of all three models is at baseline level (no more than 1.1 pp above) or below, far from the real models.

Cross-validation on the whole silver (5 folds, 2,986 sentences)

A test of 538 sentences is small for rare acts, so cross-validation gives a more stable comparison. Each sentence is predicted by a model that has not seen it. These predictions are what is written to the database: sets pragmatics / rust, runs prag-2026-09-26:perceptron|igtree|knn, with decision traces in pragmatics.trace.

fieldbaselineperceptronIGTreek-NN
form78.9 / 11.092.7 / 70.489.6 / 64.592.5 / 68.4
act28.9 / 3.970.3 / 38.064.1 / 23.968.1 / 26.0
indirect92.7 / 48.192.3 / 59.992.2 / 58.292.5 / 53.9
hedge97.3 / 49.398.1 / 76.398.0 / 75.597.8 / 73.4
polarity52.6 / 23.055.4 / 45.749.0 / 31.854.3 / 38.9
voice29.4 / 27.498.4 / 98.497.6 / 97.698.2 / 98.2

Speed and size (all 6 fields together)

modeltrainingprediction per sentencesize
perceptron0.12 s0.016 ms906 KB (dense f32 weights, ~4,700 features)
IGTree0.01 s0.008 ms72 KB (3,800 nodes)
k-NN3.9 s (choosing k)1.6 ms1,880 KB (the whole memory)

Shared feature extraction takes 0.45 ms per sentence: tokens, the en tagger and parser, cues. The en model loads in 0.11 s. The whole prag eval takes ~40 s: three models, controls, 5 folds and GUM.

Agreement of perceptron and memory — the "ask the LLM" signal

Cross-validated, 2,986 sentences. "All three" means agreement of the three models; in that case the last two columns show the perceptron's accuracy outside agreement.

fieldpairagreement, %accuracy when agreeing, %disagreeingperceptron when disagreeing, %memory when disagreeing, %
formperceptron ~ k-NN94.795.315945.341.5
formall three90.096.329859.7—
actperceptron ~ IGTree73.280.980041.118.1
actperceptron ~ k-NN77.680.567034.825.1
actall three67.284.597941.2—
polarityall three57.361.81,27546.7—
indirectall three95.993.912154.5—

Act:

Model disagreement is a natural "ask the LLM" trigger: a third of the sentences go to Opus and take two thirds of the errors with them. On disagreements the perceptron is mostly more accurate than memory (exception — indirect: 46.8 vs 53.2 for k-NN), so on disagreement it is the one to trust, or the LLM.

Full tables (all pairs, confusion of each model) — report.md.

Confusion matrix for act — perceptron, cross-validated

Rows — silver, columns — prediction; the 12 most frequent of the 16 acts are shown.

silver \ predictionassertnarrateaskrequestoffersuggestpromiseagreedisagreeevaluateexpresswarn
assert7064061842541249243
narrate19938·3····152·
ask3115342··1192·
request32861125116251292
offer8·56326·1414
suggest171·19·13112523
promise13··53·10311·3
agree7··32229·22·
disagree28·2122·130832
evaluate1398181115··763182
express5039511·2418512
warn15·110135··455

What the matrix shows:

form vs GUM test s_type (evaluation only)

Label mapping:

We compare 1,413 of 1,464 sentences.

modelaccuracymacro-F1accuracy on decl/q/wh/imp
baseline (all decl)69.411.788.5
perceptron76.146.795.1
IGTree72.534.692.2
k-NN75.245.395.1

On the main sentence types the transfer from tales and Tatoeba to GUM genres is good: 95 %. The main discrepancy is in the definition of a fragment:

To compute GUM metrics, the frag/intj definitions need to be aligned with the GUM scheme, or labels mapped at the schema level.

Decision examples (test and cross-validated predictions)

Numbers next to perceptron features are the contribution to the margin of the chosen class over the runner-up. In an IGTree path — the node's default label and count.

Correct, all three. The Innkeeper said, “Why do you howl so fearfully?” — silver: wh, ask, character.

Indirect request. The Hare said to him, “I wish you would act sincerely by me, and show yourself in your true colors.” — silver: decl, request, indirect=1; means: "the Hound should stop acting two ways and show his true intent".

An indirect "I wish you would…" as a request is a job for the LLM. So far the SLM has learned only the classic "Can you…?":

Indirect request where there is none (teasing). ‘Can your name be RUMPELSTILTSKIN?’ said the lady slyly. — silver: q, ask, hedge=1; means: "she already knows his name and is teasing him".

A perceptron error that memory fixes. How magnificent was the sight from this height! — silver: excl, evaluate, narrator.

An error of all three. I will climb up yonder rock.” — silver: promise.

What distinguishes a promise from an intention is context and addressee, not form.

Irony. …but the horse let him sing on, and made his way quietly over the fields… — silver: narrate, indirect=1; means: "the lion's furious roaring is ironically called singing".

Irony is beyond the SLM with these features; it stays with the LLM.

More examples with full explanations from all three models — examples.md.

Conclusion by field

fieldqualityspeedwho
formperceptron 92.7 ≈ k-NN 92.5 > IGTree 89.6 (cross-validated)IGTree 2× faster than the perceptron, k-NN 100× slowerperceptron
actperceptron 70.3 / 38.0 > k-NN 68.1 / 26.0 > IGTree 64.1 / 23.9sameperceptron; k-NN as a second opinion
indirectnobody beats the baseline on accuracy (92.2–92.5 vs 92.7); macro-F1 is best for the perceptron (59.9)—LLM; the SLM only catches "Can you…?"
hedgetie: 98.0–98.1 (cross-validated); on the test set IGTree 98.5 / 84.2IGTree is the cheapest (86 nodes)IGTree or perceptron — equal
polarityweak: perceptron 55.4 / 45.7 vs baseline 52.6 / 23.0; IGTree below baseline—LLM for now; the SLM only as a hint
voice~98 % for all: genre + quotation marks—anyone, IGTree is cheapest

Honestly, by the stated criterion. The perceptron wins on quality for form, act, polarity, voice. On hedge it is a tie; on indirect nobody wins.

The little IGTree graph is faster at bare prediction (0.008 vs 0.016 ms) and 12 times smaller. But prediction for every model except k-NN is pennies next to the shared feature extraction (0.45 ms), so the speed advantage gives nothing in practice. Therefore the little graph is not pulling its weight yet.

The reason is visible in the IGTree paths. Gain ratio puts binary slots at the top (qm, inv, end, genre…), while lexical cues (w1, rlem) end up deep down, where only a few examples reach.

k-NN is almost level with the perceptron on form and useful as a second opinion. But it is 100 times slower and loses rare short acts.

Practical summary:

How to run

export PATH=$HOME/.cargo/bin:$PATH
cargo build --release -p prag
P=target/release/prag; R=$MOVA_DATA/runs/prag-2026-09-26; M=$MOVA_DATA/en/models/en.bin
$P select $MOVA_DATA/db/mova.duckdb $MOVA_DATA/annot/src/tatoeba-eng-cc0.tsv $M $R   # items.jsonl, tatoeba-ud.conllu, select.md
$P prompt $R 5                          # batch prompt without a call
$P silver $R --batches 5,37 --jobs 2    # pilot; then without --batches — the rest (finished batches are skipped)
$P gate $R                              # gates only: silver.tsv, rejects.tsv, silver-params.json
$P eval $R $M --gum $MOVA_DATA/en/ud/ud-treebanks-v2.18/UD_English-GUM/en_gum-ud-test.conllu   # report.md, results.json, examples.md, rust-*.tsv
D=target/release/db; B=$MOVA_DATA/db/mova.duckdb
$D migrate $B
$D load $B bronze ud-2.18 --kind prag --docs idprefix --match auto --run prag-2026-09-26:tatoeba-ud --origin prag-2026-09-26/tatoeba-ud $R/tatoeba-ud.conllu
$D prag $B $R/silver.tsv --run prag-2026-09-26 --source silver --tool opus --origin prag-2026-09-26/silver --params $R/silver-params.json --path $R
$D prag $B $R/rust-perceptron.tsv --run prag-2026-09-26:perceptron --source rust --origin prag-2026-09-26/perceptron --params $R/results.json   # likewise igtree, knn
$D sql $B sql/silver-vs-rust.sql

Variables: PRAG_MODEL (claude-opus-5-5), PRAG_EFFORT (high), DUCKDB. Tests: cargo test --release. They check:

What next

  1. Routing to the LLM: model disagreement plus a low margin → an Opus request. Measure the trigger's precision and recall, and its token cost per thousand sentences.
    • Flywheel. At some point the third will start to shrink. LLM answers on disagreements become SLM training data, so disagreement decreases. We measure the share of LLM calls per thousand sentences across versions — this is also the "data-centre offloading" curve. It will not reach zero: new domains and rare phenomena.
    • Agreement audit. Agreement hides errors: on the act field 15.5% of agreed sentences are wrong. So a small random sample of agreed sentences also goes to the LLM. Otherwise the SLM will learn errors on which all three models agree: examples are not truth (Smart annotator — layers, context, memory, "Small model and Opus: division of labour").
  2. Memory that pulls its weight: TRIBL2 (IGTree on top, IB1 below) or a different slot order in IGTree. For the perceptron — feature pairs (aux1×sperson, rform×you).
  3. A second silver wave for rare acts (thank, apologize, greet, promise, offer, warn): selection by cues, ~2–3k sentences. Only on assignment: this is an expensive run.
  4. polarity: evaluative words from the silver itself (pos/neg shares per lemma), negation scope over the evaluative word (neg × eval).
  5. Align frag/intj with GUM and maybe add multiple.
  6. A shared LLM-call crate for annot and prag (src/opus.rs is already generic).

Licence

Elamur Cosmic License (Elamur Cosmic License). Third-party data keeps its own terms, as noted where it is used.

This page in Markdown → https://md.mova.elamur.ai/free/en/docs/prag/