Per-tone results
Tone is scored separately from the initial and the final of each syllable. In words[].phones[] the row with "dimension": "tone" holds expected and heard as tone numbers (1–4, 5 for neutral), a grade and a decision. The same syllable in syllables[].components.tone adds agrees_with_prompt, a confidence label and alternatives: every tone the engine considered, with its probability.
| Field | Per syllable, for the initial, the final and the tone |
|---|---|
expected | What the reference requires: an initial such as sh, a final such as ang, or a tone number 1–5 (5 = neutral). |
heard | The engine’s estimate of what was said, in the same units. |
score | 0–100 display grade against native-speaker references. |
decision | review, uncertain or no_correction: whether your app should correct this component. |
agrees_with_prompt, confidence, alternatives | In syllables[].components: whether heard equals expected, a confidence label, and the ranked candidates with probabilities. |
start_ms, end_ms | The syllable’s position in the recording, for replaying exactly that slice. |
A wrong tone, scored
The reference text was 我想卖书 (“I want to sell books”, mài, 4th tone); the recording said 我想买书 (“buy books”, mǎi, 3rd tone). The real response for that syllable, trimmed to the documented fields:
{
"id": "req_example",
"language": "zh",
"locale": "zh-CN",
"model_version": "mandarin-human-v2-85741cdf3967-fbf73d85ee88",
"task": "connected_speech",
"status": "completed",
"assessable": true,
"reason": null,
"scores": {
"overall": 99.3,
"pronunciation": 97.8,
"initial": 97.8,
"final": 97.9,
"tone": 81.7,
"fluency": null,
"rhythm": null,
"integrity": null
},
"words": [
{
"word": "卖",
"pinyin": "mai4",
"score": 96.4,
"start_ms": 423,
"end_ms": 645,
"phones": [
{
"dimension": "initial",
"expected": "m",
"heard": "m",
"score": 96.7,
"decision": "no_correction",
"flagged": false
},
{
"dimension": "final",
"expected": "ai",
"heard": "ai",
"score": 98.1,
"decision": "no_correction",
"flagged": false
},
{
"dimension": "tone",
"expected": 4,
"heard": 3,
"score": 60,
"decision": "uncertain",
"flagged": false
}
]
}
],
"syllables": [
{
"index": 2,
"expected": {
"pinyin": "mai4",
"initial": "m",
"final": "ai",
"tone": 4
},
"span_ms": [
423,
645
],
"score": 96.4,
"components": {
"initial": {
"expected": "m",
"heard": "m",
"agrees_with_prompt": true,
"score": 96.7,
"confidence": "high",
"decision": "no_correction",
"advice": null
},
"final": {
"expected": "ai",
"heard": "ai",
"agrees_with_prompt": true,
"score": 98.1,
"confidence": "high",
"decision": "no_correction",
"advice": null
},
"tone": {
"expected": 4,
"heard": 3,
"agrees_with_prompt": false,
"score": 60,
"confidence": "high",
"alternatives": [
{
"label": 4,
"probability": 0.0004
},
{
"label": 3,
"probability": 0.9986
},
{
"label": 2,
"probability": 0.0008
},
{
"label": 1,
"probability": 0.0002
}
],
"decision": "uncertain",
"advice": null
}
}
}
],
"heard": {
"syllables": [
{
"initial": "m",
"final": "ai",
"tone": 3
}
]
},
"duration_seconds": 0.989,
"billing": {
"credits": 1,
"balance_credits": 2499
}
}The tone was heard as a 3rd (probability 0.9986) where a 4th was expected, and its grade dropped to 60 while the initial and final stayed above 96. The decision is uncertain, not review: the correction policy only says review when its evidence for an error is strong, and the display grade and the decision answer different questions. A practical UI shows the expected and heard tone for review, offers a gentle hint for uncertain, and stays quiet for no_correction.
Reading tones in code
for word in result["words"]:
tone = next(p for p in word["phones"] if p["dimension"] == "tone")
if tone["decision"] == "review":
print(f'{word["word"]}: expected tone {tone["expected"]}, heard tone {tone["heard"]}')
elif tone["decision"] == "uncertain":
print(f'{word["word"]}: listen again to the tone ({tone["score"]})')Tone sandhi
When a 3rd tone precedes another 3rd tone it is said as a 2nd (nǐ hǎo → ní hǎo), and 不 becomes 2nd tone before a 4th. The engine expects both changes, so a learner who applies them is not told the tone is wrong: in the quickstart response, 你 is heard as tone 2 with decision no_correction. The 一 change is deliberately not applied. Details in tone sandhi in pronunciation scoring.
Request
curl https://api.toneperfect.app/v1/assess \
-H "Authorization: Bearer $TONEPERFECT_API_KEY" \
-H "Idempotency-Key: lesson-42-attempt-1" \
-F "audio=@take.wav" \
-F "text=我想卖书" \
-F "language=zh-CN" \
-F "task=connected_speech"Known weak spots of the current Mandarin model: the neutral tone is not reliably told apart from a 1st tone, ü / i is the least sensitive final, and a retroflex said where a dental was expected (sh for s) is caught far less often than the reverse. The pack is labelled experimental in /v1/capabilities, and every response names the model_version that scored it. More on the Mandarin pack page and the methodology.