The rule
When a 3rd-tone syllable is followed by another 3rd tone, the first is pronounced as a 2nd tone: 你好 nǐ hǎo is said ní hǎo. The change is obligatory — a native speaker cannot say two full 3rd tones in a row without sounding stilted. There are others: 不 bù becomes bú before a 4th tone (不对 bú duì), and 一 yī changes depending on the following tone and the word it is in.
Why it breaks scoring
A scripted engine derives the expected tone for each syllable from the reference text. Pinyin dictionaries store canonical tones: 你 is nǐ, tone 3, always. If the engine scores the learner against tone 3, a learner who correctly says ní is told their tone was wrong — and a learner who incorrectly says a full nǐ is told they were right.
We measured what this costs. On our expert-labelled Mandarin evaluation set (three raters, sentence-level recordings from real learners), scoring 3rd + 3rd tone contexts against the canonical tone gave a tone-error AUC of 0.50 — chance level. That is not a small degradation; it is the engine being unable to tell right from wrong in the single context where learners make the most tone errors (about a quarter of expert-labelled syllables in that context were marked wrong).
Where the fix belongs
The obvious fix — relabel training data so 3-3 sequences are trained as 2-3 — does not work, and we tested it. Learners produce correct sandhi only about one time in ten, so relabelling teaches the model that most 3-3 productions are "2nd tones", which pollutes the tone classifier for every other context.
The fix belongs at scoring time: accept either realisation where both are correct. For a 3rd tone followed by a 3rd tone, the accepted set is {3, 2}; for 不 before a 4th tone, {4, 2}. The engine still reports which tone it heard — so the feedback can say "you said a 2nd tone here, which is right" — but it does not mark the correct realisation as an error. This widens what counts as correct exactly where two things are correct, and nowhere else.
What we deliberately did not do
一 yī is not handled by an accept-set, and the reason is measured rather than assumed. Its sandhi is lexically conditioned: 一 keeps tone 1 in ordinals (第一), in counting, and at the end of an utterance; it becomes tone 4 before tones 1–3 and tone 2 before a 4th tone in other words. None of that is decidable from the pinyin sequence alone. When we applied the rule blindly, tone-error AUC on 一 syllables dropped from 0.82 to 0.69: widening the accepted set let genuinely wrong tones through and bought nothing, because the canonical target was already right most of the time. Handling 一 properly needs the word, not the syllable — which is future work, and it is stated as such in /v1/capabilities.
Character-aware, not just pinyin-aware
The 不 rule shows why the engine needs characters, not just pinyin. 部队, 布置 and 步骤 are all bù + 4th tone and must not be relabelled. With characters in the reference text, only 不 qualifies; with pinyin-only input the engine falls back to the syllable. This is one reason TonePerfect asks for Chinese characters for connected speech, and why its correction policy is scoped to task=connected_speech.
What this looks like in the response
For 你好 said correctly as ní hǎo:
{ "word": "你", "pinyin": "ni3", "score": 94,
"phones": [
{ "dimension": "initial", "expected": "n", "heard": "n", "score": 96, "decision": "no_correction" },
{ "dimension": "final", "expected": "i", "heard": "i", "score": 94, "decision": "no_correction" },
{ "dimension": "tone", "expected": 3, "heard": 2, "score": 93, "decision": "no_correction" }
] }Expected 3, heard 2, score 93, no correction. Your UI can explain the sandhi rule right there — a teaching moment instead of a false accusation.
Checklist for any Mandarin scoring integration
- Send characters, not only pinyin, so the engine can apply character-conditioned rules.
- Check what the vendor does on 3-3 sequences with a native recording of 你好 and 我想. If the first syllable is flagged, the engine scores against canonical tones.
- Show the heard tone even when there is no correction; it is how learners discover sandhi.
- Do not build your own sandhi rules on top of a vendor's canonical scores — you will double-apply them where the vendor already handles the context.