106 lines
5.7 KiB
Markdown
106 lines
5.7 KiB
Markdown
# Plan — Bible-faithful translation, "bulletproof for live preaching"
|
||||
|
|
|
|||
|
|
## The complaint
|
|||
|
|
Operator, watching the Spanish listener page during a live test:
|
|||
|
|
- "why is the voice asking questions to me? it keeps saying '¿Qué es eso?'"
|
|||
|
|
- "if i say 'john' it must be translated properly"
|
|||
|
|
- "make this bulletproof so native speakers in any language can completely
|
|||
|
|
understand what is being said in english. this will primarily be used for
|
|||
|
|
live preaching of the christian bible."
|
|||
|
|
|
|||
|
|
## Measured, not assumed
|
|||
|
|
Ran `Xenova/nllb-200-distilled-600M` fp32 (the shipped model, shipped dtype,
|
|||
|
|
shipped cache) against the exact lines from the screenshot plus stress cases,
|
|||
|
|
under four generation configs. Results:
|
|||
|
|
|
|||
|
|
| English in | Spanish out | verdict |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `Donald Trump.` | `Donald Trump. ¿Qué es eso?` | invented question |
|
|||
|
|
| `Amen.` | `- Sí, estoy bien.` | "Yes, I'm fine." — catastrophic |
|
|||
|
|
| `Hello, hello.` | `- Hola, está bien.` | invented content + subtitle dash |
|
|||
|
|
| `His name was John.` | `Se llamaba John.` | name left in English |
|
|||
|
|
| `Turn with me to John chapter 3.` | `…a Juan capítulo 3.` | same name, translated |
|
|||
|
|
| `Open up to the book, Matthew.` | `Abre al libro, Matthew.` | name left in English |
|
|||
|
|
|
|||
|
|
**The decisive finding:** `¿Qué es eso?` appears under ALL FOUR configs —
|
|||
|
|
no params, `max_new_tokens` cap, `no_repeat_ngram_size:3`, and `num_beams:2`.
|
|||
|
|
It is not a decoding-budget artifact. Generation parameters cannot fix it.
|
|||
|
|
Post-processing is the only reliable lever. (My first hypothesis was wrong;
|
|||
|
|
the probe is why this plan is not built on it.)
|
|||
|
|
|
|||
|
|
Secondary finding: cap + `no_repeat_ngram_size:3` cut the 11-case suite from
|
|||
|
|
5897ms to 3924ms. Strictly faster AND safer, so it goes in regardless.
|
|||
|
|
|
|||
|
|
## Existing machinery to build on (do not reinvent)
|
|||
|
|
- `applyTable()` / `termMatcher()` in engine.js — a single-pass, longest-first
|
|||
|
|
find-and-replace that already survived three real bugs: terms ending in
|
|||
|
|
punctuation, CJK word boundaries, and case-sensitivity ("job" → "Job").
|
|||
|
|
- `glossary.fixes` — source-side transcript corrections, operator-editable.
|
|||
|
|
- `glossary.overrides[lang]` — target-side per-language pins. Currently `{}`.
|
|||
|
|
- Both MT paths converge on `applyOverrides(text, lang)`:
|
|||
|
|
`translateSentence()` (live, per sentence) and `translateAll()` (batch).
|
|||
|
|
- `translateSentence` receives ONE sentence, so a 1-in-1-out clamp is safe there.
|
|||
|
|
|
|||
|
|
## The five layers
|
|||
|
|
|
|||
|
|
### L1 — source-side Bible vocabulary (before MT)
|
|||
|
|
Built-in `fixes` for common Whisper mishearings of scripture vocabulary
|
|||
|
|
("have a cook" → "Habakkuk" already exists as the only entry). Ship a real
|
|||
|
|
list. Must NOT clobber the operator's own fixes.
|
|||
|
|
|
|||
|
|
### L2 — generation parameters (mt-service.js)
|
|||
|
|
`max_new_tokens` derived from input token count, `no_repeat_ngram_size: 3`.
|
|||
|
|
Measured: faster and safer. Does NOT fix the hallucination — that is L3.
|
|||
|
|
|
|||
|
|
### L3 — post-MT hallucination guard ← the fix for the actual complaint
|
|||
|
|
Applied to every language, no per-language data needed:
|
|||
|
|
1. **Invented-question rule.** Source has no `?`/`?`, output contains `?`/`?`
|
|||
|
|
→ the question is hallucinated. Drop the offending clause. This is the
|
|||
|
|
direct fix for `¿Qué es eso?`. Precision is high: real scripture questions
|
|||
|
|
carry a `?` in the source.
|
|||
|
|
2. **Sentence-count clamp.** N sentence terminators in → at most N out
|
|||
|
|
(min 1). Kills appended-sentence hallucination generally.
|
|||
|
|
3. **Subtitle-dash strip.** NLLB emits leading `- ` / `– ` from its subtitle
|
|||
|
|
training data.
|
|||
|
|
4. **Length-ratio guard.** Output wildly longer than input → keep first
|
|||
|
|
sentence only. Needs a per-script ratio (CJK is far shorter per character).
|
|||
|
|
|
|||
|
|
### L4 — Bible lexicon, target side (per language)
|
|||
|
|
Canonical forms for the 66 book names, ~120 proper nouns (people, places),
|
|||
|
|
and key doctrinal terms. Applied through `applyTable` — the machinery is
|
|||
|
|
already hardened. Built-in table, SEPARATE from operator overrides, with the
|
|||
|
|
operator's entries winning so an app update never clobbers their work.
|
|||
|
|
|
|||
|
|
**Honesty constraint:** ship a language only where the canonical forms are
|
|||
|
|
verifiable. A wrong Korean Bible name is worse than NLLB's output. Structure
|
|||
|
|
the data so unverified languages simply have no entry and fall through to NLLB.
|
|||
|
|
|
|||
|
|
### L5 — liturgical phrasebook (skip MT entirely)
|
|||
|
|
`Amen.` must never reach NLLB. Short liturgical constants — Amen, Hallelujah,
|
|||
|
|
Hosanna, Glory to God, Praise the Lord — have stable known forms and are the
|
|||
|
|
highest-frequency utterances in preaching. Direct lookup, no inference. This
|
|||
|
|
is also the class that produces the worst failures, because NLLB has almost no
|
|||
|
|
context to work from on a 1-3 word input.
|
|||
|
|
|
|||
|
|
## What must not regress
|
|||
|
|
- Latency: EN 2-4s / ES 4-7s worst case. Every layer here is string work except
|
|||
|
|
L2, which is faster. No layer may add an inference pass.
|
|||
|
|
- `mtWorkers` stays 1 (benchmarked: 2 workers = 0.92x, slower).
|
|||
|
|
- Portuguese variants (`PtVariants`) run BEFORE `applyOverrides`; ordering with
|
|||
|
|
the new lexicon must be decided explicitly.
|
|||
|
|
- Operator's saved glossary must survive app updates.
|
|||
|
|
- `por_PT` is a pseudo-code aliased to `por_Latn` at the MT boundary only.
|
|||
|
|
|
|||
|
|
## Open questions for the audit
|
|||
|
|
1. Does the invented-question rule ever fire on legitimate output? Spanish
|
|||
|
|
inverts questions (`¿…?`); other languages may add a question particle where
|
|||
|
|
English used intonation with no `?`.
|
|||
|
|
2. Ordering: lexicon before or after the hallucination guard? Before or after
|
|||
|
|
PtVariants?
|
|||
|
|
3. Can Whisper be biased toward scripture vocabulary in transformers.js
|
|||
|
|
(an initial prompt), or is L1 find-and-replace the only available lever?
|
|||
|
|
4. Is a length-ratio guard safe across scripts, or does it need per-script
|
|||
|
|
calibration that we cannot verify for 200 languages?
|
|||
|
|
5. Which languages can we honestly ship a lexicon for, and how should the
|
|||
|
|
unverified ones behave?
|