R&D

Training a local polish model that rewrites — not chats.

After speech-to-text, dictation is still messy: ums, spoken lists, “wait, I mean…”. We’re training a tiny on-device polish model that cleans that text for paste — without sending audio or transcripts to a cloud LLM, and without answering questions you only meant to type.

On-device weights Post-STT rewrite Experimental Rewrite only

The job

Clean dictation. Don’t answer it.

Product pages talk about free local ASR. This page is the gap: how we teach a small model to format and correct spoken text — and stay silent when the user is only dictating a question.

What polish should do

Drop filler, fix caps and punctuation, turn “number one…” into real lists, keep final intent after course-corrections — paste-ready prose.

What it must never do

“Do you think that’s a good idea?” must stay a question — not “Yes, I think…”. Chat priors kill dictation tools.

Timeline

How we got here (honest version)

Not a launch deck — a lab log. Small models, real failures, then a base model and disciplined data.

  1. Phase 0 Explore

    Liquid / LFM polish

    Tiny LFM-class models with LoRA course-correction. Fast footprint, weak Flow-like formatting, easy to over-index as “the” default.

    Lesson: useful experiment, not the product default.

  2. Phase 1 Chat prior

    MiniCPM LoRA — better lists, still a chatbot

    LoRA on a chat-style MiniCPM checkpoint (~1.5k–1.8k synthetic pairs). Strong on simple “Number one is X” lists. On questions, it still answered like an assistant.

    Lesson: LoRA on a chat model does not reliably kill answer-leak.

  3. Phase 2 Base + full SFT

    Qwen3.5 0.8B Base + full SFT on 500 pairs

    Switched to a pretrain-only base (not instruct/chat), full-weight SFT, and 500 carefully built train pairs — no legacy MiniCPM data mix. Holdout kept leak-free and large enough to trust.

    Lesson: prefer base over chat when the job is rewrite, not reply.

  4. Data Two-pass synth

    High-T raw, low-T gold — never one call

    Synthetic pairs are built in two temperatures: messy dictation seeds at high creativity, then perfect clean labels at low temperature. Mixing both in one generation blurs diversity and correctness.

    Pass 1 — seeds

    Diverse messy raw dictations only.

    Temp ~1.15

    Pass 2 — labels

    Boring, correct clean gold only.

    Temp ~0.1
  5. Measured +23 pp

    Holdout: learning is real

    On a stratified 96-example slice of a 500 holdout, pass rate moved 62.5% → 85.4%, answer-leak 3.1% → 0%, mean latency ~1.5s → ~0.6s.

    Verdict: the model learned. Remaining gaps = bullets and numbered vs checklist style.

  6. Phase 3 94.6% holdout

    Targeted weak-spot practice

    Extra two-pass data focused on bullets, strict numbered lists, checklists, mixed formats, and light cleanup that never drafts assistant text. Continue full SFT from the fused Phase-2 weights. Full 500 holdout: 94.6% pass, 0% answer-leak, ~0.6 s mean.

    Shipping defaults stay careful — this track is R&D toward open polish.

  7. Phase 4 Live

    General enumeration intent + app wire-up

    Live dictations like “the first one is apple, orange…” still stayed prose after Phase 3. Fix via more training on spoken list intent and prose contrast — not keyword rules. Continue SFT → qwen35-08b-polish-enum. Product path: MLX polish, bare train prompt, paste after polish, weights bundled in FormatTest.

    If a list form fails live, improve data + retrain — don’t hard-code triggers.

Results

Measured on the full 500-example holdout

Numbers from the full 500-example holdout after base SFT + targeted weak-spot practice (2026-07). Not loss curves — pass rate, answer-leak, and latency. (Earlier 96-sample check: 85.4% after first SFT only.)

Pass rate
+35.4 pp

59.2% → 94.6%

Answer leak
0% on sample

4.8% → 0%

Mean latency
~2.5× faster

~1.5s → ~0.6s

Untrained Qwen3.5-0.8B Base vs full SFT on 500 two-pass pairs. Same prompt contract, temperature 0 for product-style eval.

Overall polish metrics before and after SFT
Model Pass rate Answered questions? Latency (mean)
Base (no polish train) 62.5% Sometimes (3.1%) ~1.5 s
After 500-pair SFT 85.4% 0% on sample ~0.6 s

By skill (full 500)

Per-skill pass rates before and after SFT on full 500 holdout
Skill Before After
Keep questions as questions 64% 100%
Bullets 42% 97%
Checklists 44% 96%
Mixed list + prose 36% 92%
Numbered steps 31% 84%
Course-correction 96% 100%
Light cleanup 84% 92%
Preserve clean prose 87% 97%

Full 500 after Phase 3 (targeted). Phase 4 adds general enumeration training for spoken list intent that still failed live (“first one is…”) — not keyword heuristics.

Principles

What we refuse to regress

Engineering rules we publish so future us doesn’t “optimize” into a chatbot again.

01

Base over chat

Prefer base / pretrain weights for polish SFT. Instruct priors want to be helpful — we need a rewriter.

02

Full SFT when killing leak

LoRA on chat was not enough. Full-weight SFT (or high-capacity adapters) when answer-leak is the failure mode.

03

Two-pass data discipline

Wild inputs at high T; boring correct gold at low T. Never one sample that has to be both creative and perfect.

04

Holdouts with trap questions

Hundreds of held-out dictations, including “Do you think…?” traps. Measure pass rate + leak + latency — not loss alone.

05

Lists by intent, not keywords

Spoken enumeration should become structure because the model learned it — not because product code matched “first one is.”

Status

R&D, measured in public.

This is not the whole shipping story — it’s the polish gap product pages skip. Free local ASR first; open polish that doesn’t answer your questions, next.

Experimental · numbers from 2026-07 holdout runs · MIT open source