Skip to content

No cheap first-pass spellcheck on generated text: 'dyrpark' shipped to A1 learners #730

Description

@mircealungu

Problem

We have no cheap first-pass check on generated text, so the two things we do run
are all-or-nothing: either an expensive LLM pass, or nothing at all.

Concrete evidence, found while preparing the EuroCALL talk. A production A1
simplification of a dr.dk article (article 4894028) opens:

"Sommeren var meget varm i Europa. En dyrpark i Frankrig havde ti
krokodilleæg."

dyrpark is not a Danish word — it should be dyrepark. It shipped, in the
version served to A1 learners, who are precisely the readers least able to spot
it. A plain spellchecker catches that in under a millisecond.

Why this is the "Escalate to the LLM" pattern

The pattern is in our own catalogue
(https://llm-patterns.mircealungu.com/) and we have not applied it here:
run something cheap over everything, and spend the expensive check only on the
residue.

Today the grammar-correction pass is the only instrument, and it is an LLM call
per article. That is why it was added, disabled, and re-argued about
(7cafb9b7 Disable Haiku grammar correction pass after simplification) - the
cost is per-article regardless of whether anything is wrong.

Compare zeeguu/core/language/language_check.py, which gets this right for a
different question: lingua is nearly free, answers a closed question ("is this
plausibly Danish?"), and only the ambiguous cases need more. Spelling should
work the same way.

Do we have a multilingual spellchecker?

No. Nothing in requirements.txt and no spellcheck code in zeeguu/.

Coverage is not a problem. We offer 11 languages - da, nl, fr, de, it, sv,
pt, es, ro, en, el - and every one of them has a mature Hunspell dictionary.
(The language table holds 31 rows, but most are not live; do not size this
against that list.)

Hunspell via spylls (pure Python, no build step) or cyhunspell is the
obvious choice. pyspellchecker bundles only ~7 languages and would leave gaps;
symspellpy is faster but needs a frequency list built per language, which is
work we do not need to do given the dictionaries already exist.

Design notes

  • The goal is triage, not correction. We are not trying to fix the text; we
    are deciding whether it is worth an LLM call. So false positives are cheap
    (one wasted call) and false negatives ship a typo. Tune accordingly.
  • Proper nouns will dominate the noise. News text is full of Bjørnøya,
    place names, brands. A raw "any unknown token" trigger fires on everything, so
    the signal has to be a share of unknown tokens over a threshold, and probably
    ignore capitalised tokens mid-sentence.
  • Where to run it: after simplification, before the article is stored -
    the same place the grammar pass sits.
  • Worth measuring first: run hunspell over a sample of stored simplifications
    per language and see what the unknown-token rate actually looks like before
    picking a threshold.

Not scheduled

Filed for later; no work planned right now.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions